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));
+ }
+}