From a8d8e6569849b56e6b7251483edcfaa045521e16 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 20 Oct 2016 09:49:22 +1100 Subject: [PATCH 1/5] Added query for 'top X apps in category' and associated test --- .../org/fdroid/fdroid/data/AppProvider.java | 31 ++++++++++++++--- .../org/fdroid/fdroid/data/QueryBuilder.java | 11 +++++- .../fdroid/fdroid/data/TempAppProvider.java | 2 +- .../fdroid/data/CategoryProviderTest.java | 34 +++++++++++++++++++ 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java index 3b6972915..c8e2581c4 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java @@ -378,6 +378,7 @@ public class AppProvider extends FDroidProvider { private static final String PATH_HIGHEST_PRIORITY = "highestPriority"; private static final String PATH_CALC_PREFERRED_METADATA = "calcPreferredMetadata"; private static final String PATH_CALC_SUGGESTED_APKS = "calcNonRepoDetailsFromIndex"; + private static final String PATH_TOP_FROM_CATEGORY = "topFromCategory"; private static final int CAN_UPDATE = CODE_SINGLE + 1; private static final int INSTALLED = CAN_UPDATE + 1; @@ -392,6 +393,7 @@ public class AppProvider extends FDroidProvider { private static final int SEARCH_CAN_UPDATE = SEARCH_INSTALLED + 1; private static final int HIGHEST_PRIORITY = SEARCH_CAN_UPDATE + 1; private static final int CALC_PREFERRED_METADATA = HIGHEST_PRIORITY + 1; + private static final int TOP_FROM_CATEGORY = CALC_PREFERRED_METADATA + 1; static { MATCHER.addURI(getAuthority(), null, CODE_LIST); @@ -409,6 +411,7 @@ public class AppProvider extends FDroidProvider { MATCHER.addURI(getAuthority(), PATH_HIGHEST_PRIORITY + "/*", HIGHEST_PRIORITY); MATCHER.addURI(getAuthority(), PATH_SPECIFIC_APP + "/#/*", CODE_SINGLE); MATCHER.addURI(getAuthority(), PATH_CALC_PREFERRED_METADATA, CALC_PREFERRED_METADATA); + MATCHER.addURI(getAuthority(), PATH_TOP_FROM_CATEGORY + "/#/*", TOP_FROM_CATEGORY); } public static Uri getContentUri() { @@ -429,9 +432,17 @@ public class AppProvider extends FDroidProvider { public static Uri getCategoryUri(String category) { return getContentUri().buildUpon() - .appendPath(PATH_CATEGORY) - .appendPath(category) - .build(); + .appendPath(PATH_CATEGORY) + .appendPath(category) + .build(); + } + + public static Uri getTopFromCategoryUri(String category, int limit) { + return getContentUri().buildUpon() + .appendPath(PATH_TOP_FROM_CATEGORY) + .appendPath(Integer.toString(limit)) + .appendPath(category) + .build(); } public static Uri getInstalledUri() { @@ -702,6 +713,8 @@ public class AppProvider extends FDroidProvider { // querying from. boolean repoIsKnown = false; + int limit = 0; + switch (MATCHER.match(uri)) { case CALC_PREFERRED_METADATA: updatePreferredMetadata(); @@ -761,6 +774,13 @@ public class AppProvider extends FDroidProvider { includeSwap = false; break; + case TOP_FROM_CATEGORY: + List parts = uri.getPathSegments(); + selection = selection.add(queryCategory(parts.get(2))); + limit = Integer.parseInt(parts.get(1)); + includeSwap = false; + break; + case RECENTLY_UPDATED: sortOrder = getTableName() + "." + Cols.LAST_UPDATED + " DESC"; selection = selection.add(queryRecentlyUpdated()); @@ -787,14 +807,14 @@ public class AppProvider extends FDroidProvider { selection = selection.add(queryHighestPriority()); } - return runQuery(uri, selection, projection, includeSwap, sortOrder); + return runQuery(uri, selection, projection, includeSwap, sortOrder, limit); } /** * Helper method used by both the genuine {@link AppProvider} and the temporary version used * by the repo updater ({@link TempAppProvider}). */ - protected Cursor runQuery(Uri uri, AppQuerySelection selection, String[] projection, boolean includeSwap, String sortOrder) { + protected Cursor runQuery(Uri uri, AppQuerySelection selection, String[] projection, boolean includeSwap, String sortOrder, int limit) { if (!includeSwap) { selection = selection.add(queryExcludeSwap()); } @@ -807,6 +827,7 @@ public class AppProvider extends FDroidProvider { query.addSelection(selection); query.addFields(projection); // TODO: Make the order of addFields/addSelection not dependent on each other... query.addOrderBy(sortOrder); + query.addLimit(limit); Cursor cursor = LoggingQuery.query(db(), query.toString(), query.getArgs()); cursor.setNotificationUri(getContext().getContentResolver(), uri); diff --git a/app/src/main/java/org/fdroid/fdroid/data/QueryBuilder.java b/app/src/main/java/org/fdroid/fdroid/data/QueryBuilder.java index 86f664fe9..25da47389 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/QueryBuilder.java +++ b/app/src/main/java/org/fdroid/fdroid/data/QueryBuilder.java @@ -14,6 +14,7 @@ abstract class QueryBuilder { private String selection; private String[] selectionArgs; private final List orderBys = new ArrayList<>(); + private int limit = 0; protected abstract String getRequiredTables(); @@ -88,6 +89,10 @@ abstract class QueryBuilder { } } + public void addLimit(int limit) { + this.limit = limit; + } + public String[] getArgs() { List args = new ArrayList<>(); @@ -156,7 +161,11 @@ abstract class QueryBuilder { return tables.toString(); } + private String limitSql() { + return limit > 0 ? " LIMIT " + limit : ""; + } + public String toString() { - return "SELECT " + distinctSql() + fieldsSql() + " FROM " + tablesSql() + whereSql() + groupBySql() + orderBySql(); + return "SELECT " + distinctSql() + fieldsSql() + " FROM " + tablesSql() + whereSql() + groupBySql() + orderBySql() + limitSql(); } } diff --git a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java index 910de6205..a1a339bda 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java @@ -196,7 +196,7 @@ public class TempAppProvider extends AppProvider { break; } - return super.runQuery(uri, selection, projection, true, sortOrder); + return super.runQuery(uri, selection, projection, true, sortOrder, 0); } private void ensureTempTableDetached(SQLiteDatabase db) { diff --git a/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java index f093a7c89..1befeab9d 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java @@ -88,6 +88,40 @@ public class CategoryProviderTest extends FDroidProviderTest { AppProviderTest.assertContainsOnlyIds(apps, expectedPackages); } + @Test + public void topAppsFromCategory() { + insertAppWithCategory("com.dog", "Dog", "Animal"); + insertAppWithCategory("com.cat", "Cat", "Animal"); + insertAppWithCategory("com.bird", "Bird", "Animal"); + insertAppWithCategory("com.snake", "Snake", "Animal"); + insertAppWithCategory("com.rat", "Rat", "Animal"); + + insertAppWithCategory("com.rock", "Rock", "Mineral"); + insertAppWithCategory("com.stone", "Stone", "Mineral"); + insertAppWithCategory("com.boulder", "Boulder", "Mineral"); + + insertAppWithCategory("com.banana", "Banana", "Vegetable"); + insertAppWithCategory("com.tomato", "Tomato", "Vegetable"); + + assertContainsOnly(topAppsFromCategory("Animal", 3), new String[] {"com.bird", "com.cat", "com.dog", }); + assertContainsOnly(topAppsFromCategory("Animal", 2), new String[] {"com.bird", "com.cat", }); + assertContainsOnly(topAppsFromCategory("Animal", 1), new String[] {"com.bird", }); + + assertContainsOnly(topAppsFromCategory("Mineral", 2), new String[] {"com.boulder", "com.rock", }); + + assertContainsOnly(topAppsFromCategory("Vegetable", 10), new String[] {"com.banana", "com.tomato", }); + } + + public String[] topAppsFromCategory(String category, int numToGet) { + List apps = AppProvider.Helper.cursorToList(contentResolver.query(AppProvider.getTopFromCategoryUri(category, numToGet), Cols.ALL, null, null, Cols.NAME)); + String[] packageNames = new String[apps.size()]; + for (int i = 0; i < apps.size(); i++) { + packageNames[i] = apps.get(i).packageName; + } + + return packageNames; + } + @Test public void testCategoriesSingle() { insertAppWithCategory("com.dog", "Dog", "Animal"); From 25d2659b9361bc15a06acdd61199892ca3afb535 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 1 Nov 2016 00:14:54 +1100 Subject: [PATCH 2/5] Be more explicit about searching categories with free form text. --- .../org/fdroid/fdroid/data/AppProvider.java | 58 +++++++++++++------ .../fragments/AvailableAppsFragment.java | 2 +- .../fdroid/data/CategoryProviderTest.java | 27 +++++++++ .../fdroid/fdroid/data/ProviderUriTests.java | 13 +++-- 4 files changed, 76 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java index c8e2581c4..342126c3b 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.net.Uri; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; @@ -382,8 +383,9 @@ public class AppProvider extends FDroidProvider { private static final int CAN_UPDATE = CODE_SINGLE + 1; private static final int INSTALLED = CAN_UPDATE + 1; - private static final int SEARCH = INSTALLED + 1; - private static final int RECENTLY_UPDATED = SEARCH + 1; + private static final int SEARCH_TEXT = INSTALLED + 1; + private static final int SEARCH_TEXT_AND_CATEGORIES = SEARCH_TEXT + 1; + private static final int RECENTLY_UPDATED = SEARCH_TEXT_AND_CATEGORIES + 1; private static final int NEWLY_ADDED = RECENTLY_UPDATED + 1; private static final int CATEGORY = NEWLY_ADDED + 1; private static final int CALC_SUGGESTED_APKS = CATEGORY + 1; @@ -401,7 +403,8 @@ public class AppProvider extends FDroidProvider { MATCHER.addURI(getAuthority(), PATH_RECENTLY_UPDATED, RECENTLY_UPDATED); MATCHER.addURI(getAuthority(), PATH_NEWLY_ADDED, NEWLY_ADDED); MATCHER.addURI(getAuthority(), PATH_CATEGORY + "/*", CATEGORY); - MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", SEARCH); + MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*/*", SEARCH_TEXT_AND_CATEGORIES); + MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", SEARCH_TEXT); MATCHER.addURI(getAuthority(), PATH_SEARCH_INSTALLED + "/*", SEARCH_INSTALLED); MATCHER.addURI(getAuthority(), PATH_SEARCH_CAN_UPDATE + "/*", SEARCH_CAN_UPDATE); MATCHER.addURI(getAuthority(), PATH_SEARCH_REPO + "/*/*", SEARCH_REPO); @@ -489,15 +492,23 @@ public class AppProvider extends FDroidProvider { return Uri.withAppendedPath(getContentUri(), packageName); } - public static Uri getSearchUri(String query) { - if (TextUtils.isEmpty(query)) { + public static Uri getSearchUri(String query, @Nullable String category) { + if (TextUtils.isEmpty(query) && TextUtils.isEmpty(category)) { // Return all the things for an empty search. return getContentUri(); + } else if (TextUtils.isEmpty(query)) { + return getCategoryUri(category); } - return getContentUri().buildUpon() + + Uri.Builder builder = getContentUri().buildUpon() .appendPath(PATH_SEARCH) - .appendPath(query) - .build(); + .appendPath(query); + + if (!TextUtils.isEmpty(category)) { + builder.appendPath(category); + } + + return builder.build(); } public static Uri getSearchInstalledUri(String query) { @@ -594,7 +605,6 @@ public class AppProvider extends FDroidProvider { final String app = getTableName(); final String[] columns = { PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME, - CategoryTable.NAME + "." + CategoryTable.Cols.NAME, app + "." + Cols.NAME, app + "." + Cols.SUMMARY, app + "." + Cols.DESCRIPTION, @@ -687,6 +697,10 @@ public class AppProvider extends FDroidProvider { } private AppQuerySelection queryCategory(String category) { + if (TextUtils.isEmpty(category)) { + return new AppQuerySelection(); + } + final String selection = CategoryTable.NAME + "." + CategoryTable.Cols.NAME + " = ? "; final String[] args = {category}; return new AppQuerySelection(selection, args); @@ -715,6 +729,7 @@ public class AppProvider extends FDroidProvider { int limit = 0; + List pathSegments = uri.getPathSegments(); switch (MATCHER.match(uri)) { case CALC_PREFERRED_METADATA: updatePreferredMetadata(); @@ -725,9 +740,8 @@ public class AppProvider extends FDroidProvider { break; case CODE_SINGLE: - List pathParts = uri.getPathSegments(); - long repoId = Long.parseLong(pathParts.get(1)); - String packageName = pathParts.get(2); + long repoId = Long.parseLong(pathSegments.get(1)); + String packageName = pathSegments.get(2); selection = selection.add(querySingle(packageName, repoId)); repoIsKnown = true; break; @@ -747,8 +761,15 @@ public class AppProvider extends FDroidProvider { includeSwap = false; break; - case SEARCH: - selection = selection.add(querySearch(uri.getLastPathSegment())); + case SEARCH_TEXT: + selection = selection.add(querySearch(pathSegments.get(1))); + includeSwap = false; + break; + + case SEARCH_TEXT_AND_CATEGORIES: + selection = selection + .add(querySearch(pathSegments.get(1))) + .add(queryCategory(pathSegments.get(2))); includeSwap = false; break; @@ -764,8 +785,8 @@ public class AppProvider extends FDroidProvider { case SEARCH_REPO: selection = selection - .add(querySearch(uri.getPathSegments().get(2))) - .add(queryRepo(Long.parseLong(uri.getPathSegments().get(1)))); + .add(querySearch(pathSegments.get(2))) + .add(queryRepo(Long.parseLong(pathSegments.get(1)))); repoIsKnown = true; break; @@ -775,9 +796,8 @@ public class AppProvider extends FDroidProvider { break; case TOP_FROM_CATEGORY: - List parts = uri.getPathSegments(); - selection = selection.add(queryCategory(parts.get(2))); - limit = Integer.parseInt(parts.get(1)); + selection = selection.add(queryCategory(pathSegments.get(2))); + limit = Integer.parseInt(pathSegments.get(1)); includeSwap = false; break; diff --git a/app/src/main/java/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java b/app/src/main/java/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java index dfd4587aa..8cd21d398 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java +++ b/app/src/main/java/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java @@ -185,7 +185,7 @@ public class AvailableAppsFragment extends AppListFragment implements @Override protected Uri getDataUri(String query) { - return AppProvider.getSearchUri(query); + return AppProvider.getSearchUri(query, null); } @Override diff --git a/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java index 1befeab9d..937383e3f 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java @@ -38,6 +38,33 @@ public class CategoryProviderTest extends FDroidProviderTest { // use a separate table in the future, these should still pass. // ======================================================================== + @Test + public void queryFreeTextAndCategories() { + insertAppWithCategory("com.dog", "Dog", "Animal"); + insertAppWithCategory("com.cat", "Cat", "Animal"); + insertAppWithCategory("com.crow", "Crow", "Animal,Bird"); + insertAppWithCategory("com.chicken", "Chicken", "Animal,Bird,Food"); + insertAppWithCategory("com.dog-statue", "Dog Statue", "Animal,Mineral"); + insertAppWithCategory("com.rock", "Rock", "Mineral"); + insertAppWithCategory("com.banana", "Banana", "Food"); + insertAppWithCategory("com.dog-food", "Dog Food", "Food"); + + assertPackagesInUri(AppProvider.getSearchUri("dog", "Animal"), new String[] { + "com.dog", + "com.dog-statue", + }); + + assertPackagesInUri(AppProvider.getSearchUri("dog", "Food"), new String[] { + "com.dog-food", + }); + + assertPackagesInUri(AppProvider.getSearchUri("dog", null), new String[] { + "com.dog", + "com.dog-statue", + "com.dog-food", + }); + } + @Test public void queryAppsInCategories() { insertAppWithCategory("com.dog", "Dog", "Animal"); diff --git a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java index 2aa284e9d..410285fc3 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -84,10 +84,15 @@ public class ProviderUriTests { ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); String[] projection = new String[] {Schema.AppMetadataTable.Cols._ID}; assertValidUri(resolver, AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider", projection); - assertValidUri(resolver, AppProvider.getSearchUri("'searching!'"), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'", projection); - assertValidUri(resolver, AppProvider.getSearchUri("/"), "content://org.fdroid.fdroid.data.AppProvider/search/%2F", projection); - assertValidUri(resolver, AppProvider.getSearchUri(""), "content://org.fdroid.fdroid.data.AppProvider", projection); - assertValidUri(resolver, AppProvider.getSearchUri(null), "content://org.fdroid.fdroid.data.AppProvider", projection); + assertValidUri(resolver, AppProvider.getSearchUri("'searching!'", null), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'", projection); + assertValidUri(resolver, AppProvider.getSearchUri("'searching!'", "Games"), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'/Games", projection); + assertValidUri(resolver, AppProvider.getSearchUri("/", null), "content://org.fdroid.fdroid.data.AppProvider/search/%2F", projection); + assertValidUri(resolver, AppProvider.getSearchUri("/", "Games"), "content://org.fdroid.fdroid.data.AppProvider/search/%2F/Games", projection); + assertValidUri(resolver, AppProvider.getSearchUri("", null), "content://org.fdroid.fdroid.data.AppProvider", projection); + assertValidUri(resolver, AppProvider.getCategoryUri("Games"), "content://org.fdroid.fdroid.data.AppProvider/category/Games", projection); + assertValidUri(resolver, AppProvider.getSearchUri("", "Games"), "content://org.fdroid.fdroid.data.AppProvider/category/Games", projection); + assertValidUri(resolver, AppProvider.getSearchUri((String) null, null), "content://org.fdroid.fdroid.data.AppProvider", projection); + assertValidUri(resolver, AppProvider.getSearchUri((String) null, "Games"), "content://org.fdroid.fdroid.data.AppProvider/category/Games", projection); assertValidUri(resolver, AppProvider.getInstalledUri(), "content://org.fdroid.fdroid.data.AppProvider/installed", projection); assertValidUri(resolver, AppProvider.getCanUpdateUri(), "content://org.fdroid.fdroid.data.AppProvider/canUpdate", projection); From f4c03c6baa1042bf18a46a5fe7fa43537e7b43cc Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 1 Nov 2016 00:24:10 +1100 Subject: [PATCH 3/5] Make category searching case insensitive. Only works for ASCII :( --- app/src/main/java/org/fdroid/fdroid/data/AppProvider.java | 5 ++++- .../main/java/org/fdroid/fdroid/data/CategoryProvider.java | 2 +- .../java/org/fdroid/fdroid/data/CategoryProviderTest.java | 7 +++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java index 342126c3b..5ba551ecb 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java @@ -701,7 +701,10 @@ public class AppProvider extends FDroidProvider { return new AppQuerySelection(); } - final String selection = CategoryTable.NAME + "." + CategoryTable.Cols.NAME + " = ? "; + // Note, the COLLATE NOCASE only works for ASCII columns. The "ICU extension" for SQLite + // provides proper case management for Unicode characters, but is not something provided + // by Android. + final String selection = CategoryTable.NAME + "." + CategoryTable.Cols.NAME + " = ? COLLATE NOCASE "; final String[] args = {category}; return new AppQuerySelection(selection, args); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java b/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java index f8aa3badd..900237533 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java @@ -185,7 +185,7 @@ public class CategoryProvider extends FDroidProvider { } protected QuerySelection querySingle(String categoryName) { - final String selection = getTableName() + "." + Cols.NAME + " = ?"; + final String selection = getTableName() + "." + Cols.NAME + " = ? COLLATE NOCASE"; final String[] args = {categoryName}; return new QuerySelection(selection, args); } diff --git a/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java index 937383e3f..fb73d0fb6 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/CategoryProviderTest.java @@ -82,6 +82,13 @@ public class CategoryProviderTest extends FDroidProviderTest { "com.chicken", }); + assertPackagesInCategory("animal", new String[] { + "com.dog", + "com.cat", + "com.crow", + "com.chicken", + }); + assertPackagesInCategory("Bird", new String[]{ "com.crow", "com.chicken", From bdde162f56eb59b2e4aa83d41965121641da2a96 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Mon, 19 Dec 2016 12:56:07 +1100 Subject: [PATCH 4/5] Notify when new categories are available, old ones are no longer available. Whether a category is "available" or not is not a function of whether it is in the category table or not. Rather, it is a function of whether there are any active apps/apks which are in that category. Thus, don't notify after inserting a category (the notification was wrong anyway as it was trying to notify the AppProvider Uri instead of the ContentProvider one). Instead, do it after a repo update is complete. --- .../main/java/org/fdroid/fdroid/data/CategoryProvider.java | 6 ++++-- .../main/java/org/fdroid/fdroid/data/TempAppProvider.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java b/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java index 900237533..74e3209be 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/CategoryProvider.java @@ -134,7 +134,7 @@ public class CategoryProvider extends FDroidProvider { MATCHER.addURI(getAuthority(), PATH_ALL_CATEGORIES, CODE_LIST); } - private static Uri getContentUri() { + static Uri getContentUri() { return Uri.parse("content://" + getAuthority()); } @@ -239,7 +239,9 @@ public class CategoryProvider extends FDroidProvider { @Override public Uri insert(@NonNull Uri uri, ContentValues values) { long rowId = db().insertOrThrow(getTableName(), null, values); - getContext().getContentResolver().notifyChange(AppProvider.getCanUpdateUri(), null); + // Don't try and notify listeners here, because it will instead happen when the TempAppProvider + // is committed (when the AppProvider and ApkProviders notify their listeners). There is no + // other time where categories get added (at time of writing) so this should be okay. return getCategoryIdUri(rowId); } diff --git a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java index a1a339bda..296861008 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java @@ -253,6 +253,7 @@ public class TempAppProvider extends AppProvider { getContext().getContentResolver().notifyChange(AppProvider.getContentUri(), null); getContext().getContentResolver().notifyChange(ApkProvider.getContentUri(), null); + getContext().getContentResolver().notifyChange(CategoryProvider.getContentUri(), null); } finally { db.endTransaction(); db.execSQL("DETACH DATABASE " + DB); // Can't be done in a transaction. From a93904d90725762ab76f2f72ee0df9c7c8344ea1 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Thu, 17 Nov 2016 12:11:34 +1100 Subject: [PATCH 5/5] Added preference to manage repositories As this is no longer a top level menu item, it is now the first item in the settings, under "Updates". --- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 988bae209..ea70e6a6c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,6 +109,7 @@ Update Repos Repositories + Add additional sources of applications Share F-Droid by Bluetooth Settings About diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index da67b38da..fd75a1db9 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,6 +1,14 @@ + + +