Added new table to store user preferences.

Haven't yet migrated data from the old location in the app table yet.
This commit is contained in:
Peter Serwylo 2016-07-25 17:33:00 +10:00
parent 3c5f8756f4
commit d47967e03d
6 changed files with 280 additions and 0 deletions

View File

@ -95,6 +95,11 @@
android:name="org.fdroid.fdroid.data.InstalledAppProvider"
android:exported="false"/>
<provider
android:authorities="org.fdroid.fdroid.data.AppPrefsProvider"
android:name="org.fdroid.fdroid.data.AppPrefsProvider"
android:exported="false"/>
<provider
android:name="org.fdroid.fdroid.installer.ApkFileProvider"
android:authorities="org.fdroid.fdroid.installer.ApkFileProvider"

View File

@ -0,0 +1,24 @@
package org.fdroid.fdroid.data;
public class AppPrefs extends ValueObject {
/**
* True if all updates for this app are to be ignored
*/
public final boolean ignoreAllUpdates;
/**
* True if the current update for this app is to be ignored
*/
public final int ignoreThisUpdate;
public AppPrefs(int ignoreThis, boolean ignoreAll) {
ignoreThisUpdate = ignoreThis;
ignoreAllUpdates = ignoreAll;
}
public static AppPrefs createDefault() {
return new AppPrefs(0, false);
}
}

View File

@ -0,0 +1,173 @@
package org.fdroid.fdroid.data;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.AppPrefsTable.Cols;
public class AppPrefsProvider extends FDroidProvider {
private static final String TAG = "AppPrefsProvider";
public static final class Helper {
private Helper() { }
public static void update(Context context, App app, AppPrefs prefs) {
ContentValues values = new ContentValues(3);
values.put(Cols.IGNORE_ALL_UPDATES, prefs.ignoreAllUpdates);
values.put(Cols.IGNORE_THIS_UPDATE, prefs.ignoreThisUpdate);
if (getPrefsOrNull(context, app) == null) {
values.put(Cols.APP_ID, app.getId());
context.getContentResolver().insert(getContentUri(), values);
} else {
context.getContentResolver().update(getAppUri(app.getId()), values, null, null);
}
}
@NonNull
public static AppPrefs getPrefsOrDefault(Context context, App app) {
AppPrefs prefs = getPrefsOrNull(context, app);
return prefs == null ? AppPrefs.createDefault() : prefs;
}
@Nullable
public static AppPrefs getPrefsOrNull(Context context, App app) {
Cursor cursor = context.getContentResolver().query(getAppUri(app.getId()), Cols.ALL, null, null, null);
if (cursor == null) {
return null;
}
try {
if (cursor.getCount() == 0) {
return null;
} else {
cursor.moveToFirst();
return new AppPrefs(
cursor.getInt(cursor.getColumnIndexOrThrow(Cols.IGNORE_THIS_UPDATE)),
cursor.getInt(cursor.getColumnIndexOrThrow(Cols.IGNORE_ALL_UPDATES)) > 0);
}
} finally {
cursor.close();
}
}
}
private class Query extends QueryBuilder {
@Override
protected String getRequiredTables() {
return AppPrefsTable.NAME;
}
@Override
public void addField(String field) {
appendField(field, getTableName());
}
}
private static final String PROVIDER_NAME = "AppPrefsProvider";
private static final UriMatcher MATCHER = new UriMatcher(-1);
private static final String PATH_APP_ID = "appId";
static {
MATCHER.addURI(getAuthority(), PATH_APP_ID + "/#", CODE_SINGLE);
}
private static Uri getContentUri() {
return Uri.parse("content://" + getAuthority());
}
public static Uri getAppUri(long appId) {
return getContentUri().buildUpon().appendPath(PATH_APP_ID).appendPath(Long.toString(appId)).build();
}
@Override
protected String getTableName() {
return AppPrefsTable.NAME;
}
@Override
protected String getProviderName() {
return "AppPrefsProvider";
}
public static String getAuthority() {
return AUTHORITY + "." + PROVIDER_NAME;
}
@Override
protected UriMatcher getMatcher() {
return MATCHER;
}
protected QuerySelection querySingle(long appId) {
final String selection = getTableName() + "." + Cols.APP_ID + " = ?";
final String[] args = {Long.toString(appId)};
return new QuerySelection(selection, args);
}
@Override
public Cursor query(Uri uri, String[] projection, String customSelection, String[] selectionArgs, String sortOrder) {
QuerySelection selection = new QuerySelection(customSelection, selectionArgs);
switch (MATCHER.match(uri)) {
case CODE_SINGLE:
selection = selection.add(querySingle(Long.parseLong(uri.getLastPathSegment())));
break;
default:
Log.e(TAG, "Invalid URI for app content provider: " + uri);
throw new UnsupportedOperationException("Invalid URI for app content provider: " + uri);
}
Query query = new Query();
query.addSelection(selection);
query.addFields(projection);
query.addOrderBy(sortOrder);
Cursor cursor = LoggingQuery.query(db(), query.toString(), query.getArgs());
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
switch (MATCHER.match(uri)) {
default:
throw new UnsupportedOperationException("Delete not supported for " + uri + ".");
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
db().insertOrThrow(getTableName(), null, values);
if (!isApplyingBatch()) {
getContext().getContentResolver().notifyChange(uri, null);
}
return getAppUri(values.getAsLong(Cols.APP_ID));
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
switch (MATCHER.match(uri)) {
case CODE_SINGLE:
QuerySelection query = new QuerySelection(where, whereArgs)
.add(querySingle(Long.parseLong(uri.getLastPathSegment())));
return db().update(getTableName(), values, query.getSelection(), query.getArgs());
default:
throw new UnsupportedOperationException("Update not supported for " + uri + ".");
}
}
}

View File

@ -11,6 +11,7 @@ import android.util.Log;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.ApkTable;
import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.AppTable;
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
import org.fdroid.fdroid.data.Schema.RepoTable;
@ -101,6 +102,13 @@ class DBHelper extends SQLiteOpenHelper {
+ AppTable.Cols.ICON_URL_LARGE + " text, "
+ "primary key(" + AppTable.Cols.PACKAGE_NAME + "));";
private static final String CREATE_TABLE_APP_PREFS = "CREATE TABLE " + AppPrefsTable.NAME
+ " ( "
+ AppPrefsTable.Cols.APP_ID + " INT REFERENCES " + AppTable.NAME + "(" + AppTable.Cols.ROW_ID + ") ON DELETE CASCADE, "
+ AppPrefsTable.Cols.IGNORE_THIS_UPDATE+ " INT BOOLEAN NOT NULL, "
+ AppPrefsTable.Cols.IGNORE_ALL_UPDATES + " INT NOT NULL "
+ " );";
private static final String CREATE_TABLE_INSTALLED_APP = "CREATE TABLE " + InstalledAppTable.NAME
+ " ( "
+ InstalledAppTable.Cols.PACKAGE_NAME + " TEXT NOT NULL PRIMARY KEY, "
@ -219,6 +227,7 @@ class DBHelper extends SQLiteOpenHelper {
createAppApk(db);
db.execSQL(CREATE_TABLE_INSTALLED_APP);
db.execSQL(CREATE_TABLE_REPO);
db.execSQL(CREATE_TABLE_APP_PREFS);
insertRepo(
db,

View File

@ -9,6 +9,19 @@ import android.provider.BaseColumns;
*/
public interface Schema {
interface AppPrefsTable {
String NAME = "fdroid_appPrefs";
interface Cols extends BaseColumns {
String APP_ID = "appId";
String IGNORE_ALL_UPDATES = "ignoreAllUpdates";
String IGNORE_THIS_UPDATE = "ignoreThisUpdate";
String[] ALL = {APP_ID, IGNORE_ALL_UPDATES, IGNORE_THIS_UPDATE,};
}
}
interface AppTable {
String NAME = "fdroid_app";

View File

@ -0,0 +1,56 @@
package org.fdroid.fdroid.data;
import android.app.Application;
import org.fdroid.fdroid.Assert;
import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.TestUtils;
import org.fdroid.fdroid.data.Schema.AppPrefsTable.Cols;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@Config(constants = BuildConfig.class, application = Application.class)
@RunWith(RobolectricGradleTestRunner.class)
public class AppPrefsProviderTest extends FDroidProviderTest {
private static final String[] PROJ = Cols.ALL;
@Before
public void setup() {
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider());
}
@Test
public void newPreferences() {
App withPrefs = Assert.insertApp(context, "com.example.withPrefs", "With Prefs");
App withoutPrefs = Assert.insertApp(context, "com.example.withoutPrefs", "Without Prefs");
assertNull(AppPrefsProvider.Helper.getPrefsOrNull(context, withPrefs));
assertNull(AppPrefsProvider.Helper.getPrefsOrNull(context, withoutPrefs));
AppPrefs defaultPrefs = AppPrefsProvider.Helper.getPrefsOrDefault(context, withPrefs);
assertEquals(0, defaultPrefs.ignoreThisUpdate);
assertFalse(defaultPrefs.ignoreAllUpdates);
AppPrefsProvider.Helper.update(context, withPrefs, new AppPrefs(12, false));
AppPrefs newPrefs = AppPrefsProvider.Helper.getPrefsOrDefault(context, withPrefs);
assertEquals(12, newPrefs.ignoreThisUpdate);
assertFalse(newPrefs.ignoreAllUpdates);
AppPrefsProvider.Helper.update(context, withPrefs, new AppPrefs(14, true));
AppPrefs evenNewerPrefs = AppPrefsProvider.Helper.getPrefsOrDefault(context, withPrefs);
assertEquals(14, evenNewerPrefs.ignoreThisUpdate);
assertTrue(evenNewerPrefs.ignoreAllUpdates);
assertNull(AppPrefsProvider.Helper.getPrefsOrNull(context, withoutPrefs));
}
}