diff --git a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java index 61e310d51..138f96132 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java @@ -37,10 +37,19 @@ public class ApkProvider extends FDroidProvider { public static void update(Context context, Apk apk) { ContentResolver resolver = context.getContentResolver(); - Uri uri = getApkFromAnyRepoUri(apk.packageName, apk.versionCode); + Uri uri = getApkFromRepoUri(apk); resolver.update(uri, apk.toContentValues(), null, null); } + public static Uri getApkFromRepoUri(Apk apk) { + return getContentUri() + .buildUpon() + .appendPath(PATH_APK_FROM_REPO) + .appendPath(Long.toString(apk.appId)) + .appendPath(Integer.toString(apk.versionCode)) + .build(); + } + public static List cursorToList(Cursor cursor) { int knownApkCount = cursor != null ? cursor.getCount() : 0; List apks = new ArrayList<>(knownApkCount); @@ -71,7 +80,7 @@ public class ApkProvider extends FDroidProvider { * Find all apks for a particular app, but limit it to those originating from the * specified repo. */ - public static List find(Context context, Repo repo, List apps, String[] projection) { + public static List findByUri(Context context, Repo repo, List apps, String[] projection) { ContentResolver resolver = context.getContentResolver(); final Uri uri = getContentUriForApps(repo, apps); Cursor cursor = resolver.query(uri, projection, null, null, null); @@ -172,9 +181,12 @@ public class ApkProvider extends FDroidProvider { private static final int CODE_REPO_APPS = CODE_APKS + 1; protected static final int CODE_REPO_APK = CODE_REPO_APPS + 1; private static final int CODE_APK_ROW_ID = CODE_REPO_APK + 1; + static final int CODE_APK_FROM_ANY_REPO = CODE_APK_ROW_ID + 1; + static final int CODE_APK_FROM_REPO = CODE_APK_FROM_ANY_REPO + 1; private static final String PROVIDER_NAME = "ApkProvider"; - protected static final String PATH_APK = "apk"; + protected static final String PATH_APK_FROM_ANY_REPO = "apk-any-repo"; + protected static final String PATH_APK_FROM_REPO = "apk-from-repo"; private static final String PATH_APKS = "apks"; private static final String PATH_APP = "app"; private static final String PATH_REPO = "repo"; @@ -193,7 +205,8 @@ public class ApkProvider extends FDroidProvider { PACKAGE_FIELDS.put(Cols.Package.PACKAGE_NAME, PackageTable.Cols.PACKAGE_NAME); MATCHER.addURI(getAuthority(), PATH_REPO + "/#", CODE_REPO); - MATCHER.addURI(getAuthority(), PATH_APK + "/#/*", CODE_SINGLE); + MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*", CODE_APK_FROM_ANY_REPO); + MATCHER.addURI(getAuthority(), PATH_APK_FROM_REPO + "/#/#", CODE_APK_FROM_REPO); MATCHER.addURI(getAuthority(), PATH_APKS + "/*", CODE_APKS); MATCHER.addURI(getAuthority(), PATH_APP + "/*", CODE_PACKAGE); MATCHER.addURI(getAuthority(), PATH_REPO_APPS + "/#/*", CODE_REPO_APPS); @@ -240,7 +253,7 @@ public class ApkProvider extends FDroidProvider { public static Uri getApkFromAnyRepoUri(String packageName, int versionCode) { return getContentUri() .buildUpon() - .appendPath(PATH_APK) + .appendPath(PATH_APK_FROM_ANY_REPO) .appendPath(Integer.toString(versionCode)) .appendPath(packageName) .build(); @@ -397,6 +410,21 @@ public class ApkProvider extends FDroidProvider { return new QuerySelection(selection, args); } + /** + * Doesn't prefix column names with table alias. This is so that it can be used in UPDATE + * queries. Note that this lack of table alias prefixes means this can't be used for general + * constraints in a regular select query within {@link ApkProvider} as the queries specify + * aliases for the apk table. + */ + private QuerySelection querySingleWithAppId(Uri uri) { + List path = uri.getPathSegments(); + String appId = path.get(1); + String versionCode = path.get(2); + final String selection = Cols.APP_ID + " = ? AND " + Cols.VERSION_CODE + " = ? "; + final String[] args = {appId, versionCode}; + return new QuerySelection(selection, args); + } + protected QuerySelection queryRepo(long repoId) { return queryRepo(repoId, true); } @@ -465,7 +493,7 @@ public class ApkProvider extends FDroidProvider { case CODE_LIST: break; - case CODE_SINGLE: + case CODE_APK_FROM_ANY_REPO: query = query.add(querySingleFromAnyRepo(uri)); break; @@ -568,7 +596,7 @@ public class ApkProvider extends FDroidProvider { @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { - if (MATCHER.match(uri) != CODE_SINGLE) { + if (MATCHER.match(uri) != CODE_APK_FROM_REPO) { throw new UnsupportedOperationException("Cannot update anything other than a single apk."); } return performUpdateUnchecked(uri, values, where, whereArgs); @@ -579,7 +607,7 @@ public class ApkProvider extends FDroidProvider { removeFieldsFromOtherTables(values); QuerySelection query = new QuerySelection(where, whereArgs); - query = query.add(querySingleFromAnyRepo(uri, false)); + query = query.add(querySingleWithAppId(uri)); int numRows = db().update(getTableName(), values, query.getSelection(), query.getArgs()); if (!isApplyingBatch()) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java b/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java index 6a405a9ba..63e9d76fc 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java +++ b/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java @@ -258,7 +258,7 @@ public class RepoPersister { @Nullable private ContentProviderOperation deleteOrphanedApks(List apps, Map> packages) { String[] projection = new String[]{Schema.ApkTable.Cols.Package.PACKAGE_NAME, Schema.ApkTable.Cols.VERSION_CODE}; - List existing = ApkProvider.Helper.find(context, repo, apps, projection); + List existing = ApkProvider.Helper.findByUri(context, repo, apps, projection); List toDelete = new ArrayList<>(); for (Apk existingApk : existing) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java b/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java index 8cc83f773..e7465e30b 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/TempApkProvider.java @@ -27,7 +27,8 @@ public class TempApkProvider extends ApkProvider { static { MATCHER.addURI(getAuthority(), PATH_INIT, CODE_INIT); - MATCHER.addURI(getAuthority(), PATH_APK + "/#/*", CODE_SINGLE); + MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*", CODE_APK_FROM_ANY_REPO); + MATCHER.addURI(getAuthority(), PATH_APK_FROM_REPO + "/#/#", CODE_APK_FROM_REPO); MATCHER.addURI(getAuthority(), PATH_REPO_APK + "/#/*", CODE_REPO_APK); } @@ -52,9 +53,9 @@ public class TempApkProvider extends ApkProvider { public static Uri getApkUri(Apk apk) { return getContentUri() .buildUpon() - .appendPath(PATH_APK) + .appendPath(PATH_APK_FROM_REPO) + .appendPath(Long.toString(apk.appId)) .appendPath(Integer.toString(apk.versionCode)) - .appendPath(apk.packageName) .build(); } @@ -96,7 +97,7 @@ public class TempApkProvider extends ApkProvider { @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { - if (MATCHER.match(uri) != CODE_SINGLE) { + if (MATCHER.match(uri) != CODE_APK_FROM_REPO) { throw new UnsupportedOperationException("Cannot update anything other than a single apk."); } diff --git a/app/src/test/java/org/fdroid/fdroid/Assert.java b/app/src/test/java/org/fdroid/fdroid/Assert.java index ba7085050..27036a5f3 100644 --- a/app/src/test/java/org/fdroid/fdroid/Assert.java +++ b/app/src/test/java/org/fdroid/fdroid/Assert.java @@ -201,7 +201,7 @@ public class Assert { return AppProvider.Helper.findSpecificApp(context.getContentResolver(), packageName, 1, AppMetadataTable.Cols.ALL); } - private static App ensureApp(Context context, String packageName) { + public static App ensureApp(Context context, String packageName) { App app = AppProvider.Helper.findSpecificApp(context.getContentResolver(), packageName, 1, AppMetadataTable.Cols.ALL); if (app == null) { insertApp(context, packageName, packageName); diff --git a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java index efbfada6e..0f21dd119 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -242,41 +242,45 @@ public class ApkProviderTest extends FDroidProviderTest { @Test public void testKnownApks() { + App fdroid = Assert.ensureApp(context, "org.fdroid.fdroid"); for (int i = 0; i < 7; i++) { - Assert.insertApk(context, "org.fdroid.fdroid", i); + Assert.insertApk(context, fdroid, i); } + App exampleOrg = Assert.ensureApp(context, "org.example"); for (int i = 0; i < 9; i++) { - Assert.insertApk(context, "org.example", i); + Assert.insertApk(context, exampleOrg, i); } + App exampleCom = Assert.ensureApp(context, "com.example"); for (int i = 0; i < 3; i++) { - Assert.insertApk(context, "com.example", i); + Assert.insertApk(context, exampleCom, i); } - Assert.insertApk(context, "com.apk.thingo", 1); + App thingo = Assert.ensureApp(context, "com.apk.thingo"); + Assert.insertApk(context, thingo, 1); Apk[] known = { - new MockApk("org.fdroid.fdroid", 1), - new MockApk("org.fdroid.fdroid", 3), - new MockApk("org.fdroid.fdroid", 5), + new MockApk(fdroid, 1), + new MockApk(fdroid, 3), + new MockApk(fdroid, 5), - new MockApk("com.example", 1), - new MockApk("com.example", 2), + new MockApk(exampleCom, 1), + new MockApk(exampleCom, 2), }; Apk[] unknown = { - new MockApk("org.fdroid.fdroid", 7), - new MockApk("org.fdroid.fdroid", 9), - new MockApk("org.fdroid.fdroid", 11), - new MockApk("org.fdroid.fdroid", 13), + new MockApk(fdroid, 7), + new MockApk(fdroid, 9), + new MockApk(fdroid, 11), + new MockApk(fdroid, 13), - new MockApk("com.example", 3), - new MockApk("com.example", 4), - new MockApk("com.example", 5), + new MockApk(exampleCom, 3), + new MockApk(exampleCom, 4), + new MockApk(exampleCom, 5), - new MockApk("info.example", 1), - new MockApk("info.example", 2), + new MockApk(-10, 1), + new MockApk(-10, 2), }; List apksToCheck = new ArrayList<>(known.length + unknown.length); @@ -285,6 +289,7 @@ public class ApkProviderTest extends FDroidProviderTest { String[] projection = { Cols.Package.PACKAGE_NAME, + Cols.APP_ID, Cols.VERSION_CODE, }; 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 5cfa3c37a..788c3aa31 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -135,9 +135,9 @@ public class ProviderUriTests { assertValidUri(resolver, ApkProvider.getContentUri(), "content://org.fdroid.fdroid.data.ApkProvider", projection); assertValidUri(resolver, ApkProvider.getAppUri("org.fdroid.fdroid"), "content://org.fdroid.fdroid.data.ApkProvider/app/org.fdroid.fdroid", projection); - assertValidUri(resolver, ApkProvider.getApkFromAnyRepoUri(new MockApk("org.fdroid.fdroid", 100)), "content://org.fdroid.fdroid.data.ApkProvider/apk/100/org.fdroid.fdroid", projection); + assertValidUri(resolver, ApkProvider.getApkFromAnyRepoUri(new MockApk("org.fdroid.fdroid", 100)), "content://org.fdroid.fdroid.data.ApkProvider/apk-any-repo/100/org.fdroid.fdroid", projection); assertValidUri(resolver, ApkProvider.getContentUri(apks), projection); - assertValidUri(resolver, ApkProvider.getApkFromAnyRepoUri("org.fdroid.fdroid", 100), "content://org.fdroid.fdroid.data.ApkProvider/apk/100/org.fdroid.fdroid", projection); + assertValidUri(resolver, ApkProvider.getApkFromAnyRepoUri("org.fdroid.fdroid", 100), "content://org.fdroid.fdroid.data.ApkProvider/apk-any-repo/100/org.fdroid.fdroid", projection); assertValidUri(resolver, ApkProvider.getRepoUri(1000), "content://org.fdroid.fdroid.data.ApkProvider/repo/1000", projection); } diff --git a/app/src/test/java/org/fdroid/fdroid/mock/MockApk.java b/app/src/test/java/org/fdroid/fdroid/mock/MockApk.java index be7dff5a5..35592526e 100644 --- a/app/src/test/java/org/fdroid/fdroid/mock/MockApk.java +++ b/app/src/test/java/org/fdroid/fdroid/mock/MockApk.java @@ -1,6 +1,7 @@ package org.fdroid.fdroid.mock; import org.fdroid.fdroid.data.Apk; +import org.fdroid.fdroid.data.App; public class MockApk extends Apk { @@ -9,4 +10,14 @@ public class MockApk extends Apk { this.versionCode = versionCode; } + public MockApk(App app, int versionCode) { + this.appId = app.getId(); + this.versionCode = versionCode; + } + + public MockApk(long appId, int versionCode) { + this.appId = appId; + this.versionCode = versionCode; + } + }