diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aef660433..84e2e3c7b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -95,6 +95,11 @@ android:name="org.fdroid.fdroid.data.InstalledAppProvider" android:exported="false"/> + + 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 + "."); + + } + } +} diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index c0597b420..3aa8a338d 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -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, diff --git a/app/src/main/java/org/fdroid/fdroid/data/Schema.java b/app/src/main/java/org/fdroid/fdroid/data/Schema.java index 7b39a045a..7735e554c 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -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"; diff --git a/app/src/test/java/org/fdroid/fdroid/data/AppPrefsProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/AppPrefsProviderTest.java new file mode 100644 index 000000000..81e18de0b --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/data/AppPrefsProviderTest.java @@ -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)); + } +}