diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 70cecf9cb..eb2332ace 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -100,6 +100,11 @@ android:name="org.fdroid.fdroid.data.AppPrefsProvider" android:exported="false"/> + + , Parcelable { public String[] incompatibleReasons; /** - * The numeric primary key of the App table, which is used to join apks. + * The numeric primary key of the Package table, which is used to join apks. */ public long appId; @@ -91,7 +91,7 @@ public class Apk extends ValueObject implements Comparable, Parcelable { case Cols.FEATURES: features = Utils.parseCommaSeparatedString(cursor.getString(i)); break; - case Cols.App.PACKAGE_NAME: + case Cols.Package.PACKAGE_NAME: packageName = cursor.getString(i); break; case Cols.IS_COMPATIBLE: 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 3c635c35f..e79380132 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java @@ -11,6 +11,7 @@ import android.util.Log; import org.fdroid.fdroid.data.Schema.ApkTable; import org.fdroid.fdroid.data.Schema.ApkTable.Cols; import org.fdroid.fdroid.data.Schema.AppMetadataTable; +import org.fdroid.fdroid.data.Schema.PackageTable; import org.fdroid.fdroid.data.Schema.RepoTable; import java.util.ArrayList; @@ -189,7 +190,7 @@ public class ApkProvider extends FDroidProvider { static { REPO_FIELDS.put(Cols.Repo.VERSION, RepoTable.Cols.VERSION); REPO_FIELDS.put(Cols.Repo.ADDRESS, RepoTable.Cols.ADDRESS); - PACKAGE_FIELDS.put(Cols.App.PACKAGE_NAME, AppMetadataTable.Cols.PACKAGE_NAME); + 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); @@ -317,9 +318,11 @@ public class ApkProvider extends FDroidProvider { protected String getRequiredTables() { final String apk = getTableName(); final String app = getAppTableName(); + final String pkg = PackageTable.NAME; return apk + " AS apk " + - " LEFT JOIN " + app + " AS app ON (app." + AppMetadataTable.Cols.ROW_ID + " = apk." + Cols.APP_ID + ")"; + " LEFT JOIN " + app + " AS app ON (app." + AppMetadataTable.Cols.ROW_ID + " = apk." + Cols.APP_ID + ")" + + " LEFT JOIN " + pkg + " AS pkg ON (pkg." + PackageTable.Cols.ROW_ID + " = app." + AppMetadataTable.Cols.PACKAGE_ID + ")"; } @Override @@ -340,7 +343,7 @@ public class ApkProvider extends FDroidProvider { } private void addPackageField(String field, String alias) { - appendField(field, "app", alias); + appendField(field, "pkg", alias); } private void addRepoField(String field, String alias) { @@ -354,12 +357,7 @@ public class ApkProvider extends FDroidProvider { } private QuerySelection queryPackage(String packageName) { - return queryPackage(packageName, true); - } - - private QuerySelection queryPackage(String packageName, boolean includeTableAlias) { - String alias = includeTableAlias ? "apk." : ""; - final String selection = alias + Cols.APP_ID + " = (" + getAppIdFromPackageNameQuery() + ")"; + final String selection = "pkg." + PackageTable.Cols.PACKAGE_NAME + " = ?"; final String[] args = {packageName}; return new QuerySelection(selection, args); } @@ -370,7 +368,9 @@ public class ApkProvider extends FDroidProvider { private QuerySelection querySingleFromAnyRepo(Uri uri, boolean includeAlias) { String alias = includeAlias ? "apk." : ""; - final String selection = alias + Cols.VERSION_CODE + " = ? and " + alias + Cols.APP_ID + " = (" + getAppIdFromPackageNameQuery() + ")"; + + // TODO: Change the = to an IN to deal with multiple apps? + final String selection = alias + Cols.VERSION_CODE + " = ? and " + alias + Cols.APP_ID + " = (" + getMetadataIdFromPackageNameQuery() + ")"; final String[] args = { // First (0th) path segment is the word "apk", // and we are not interested in it. @@ -403,7 +403,7 @@ public class ApkProvider extends FDroidProvider { } private QuerySelection queryRepoApps(long repoId, String packageNames) { - return queryRepo(repoId).add(AppProvider.queryPackageNames(packageNames, "app." + AppMetadataTable.Cols.PACKAGE_NAME)); + return queryRepo(repoId).add(AppProvider.queryPackageNames(packageNames, "pkg." + PackageTable.Cols.PACKAGE_NAME)); } protected QuerySelection queryApks(String apkKeys) { @@ -418,6 +418,7 @@ public class ApkProvider extends FDroidProvider { "You tried to query " + apkDetails.length); } String alias = includeAlias ? "apk." : ""; + String metadataAlias = includeAlias ? "app." : ""; final String[] args = new String[apkDetails.length * 2]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < apkDetails.length; i++) { @@ -430,10 +431,10 @@ public class ApkProvider extends FDroidProvider { sb.append(" OR "); } sb.append(" ( ") - .append(alias) - .append(Cols.APP_ID) + .append(metadataAlias) + .append(AppMetadataTable.Cols.PACKAGE_ID) .append(" = (") - .append(getAppIdFromPackageNameQuery()) + .append(getPackageIdFromPackageNameQuery()) .append(") AND ") .append(alias) .append(Cols.VERSION_CODE) @@ -442,8 +443,13 @@ public class ApkProvider extends FDroidProvider { return new QuerySelection(sb.toString(), args); } - private String getAppIdFromPackageNameQuery() { - return "SELECT " + AppMetadataTable.Cols.ROW_ID + " FROM " + getAppTableName() + " WHERE " + AppMetadataTable.Cols.PACKAGE_NAME + " = ?"; + // TODO: This could return many rows of app metadata + private String getMetadataIdFromPackageNameQuery() { + return "SELECT app." + AppMetadataTable.Cols.ROW_ID + " " + + "FROM " + AppMetadataTable.NAME + " AS app " + + "JOIN " + PackageTable.NAME + " AS pkg ON ( " + + " app." + AppMetadataTable.Cols.PACKAGE_ID + " = pkg." + PackageTable.Cols.ROW_ID + " ) " + + "WHERE pkg." + PackageTable.Cols.PACKAGE_NAME + " = ?"; } @Override @@ -535,10 +541,6 @@ public class ApkProvider extends FDroidProvider { query = query.add(queryRepo(Long.parseLong(uri.getLastPathSegment()), false)); break; - case CODE_PACKAGE: - query = query.add(queryPackage(uri.getLastPathSegment(), false)); - break; - case CODE_APKS: query = query.add(queryApks(uri.getLastPathSegment(), false)); break; diff --git a/app/src/main/java/org/fdroid/fdroid/data/App.java b/app/src/main/java/org/fdroid/fdroid/data/App.java index cfd6c7c74..1f5c257e8 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -46,6 +46,7 @@ public class App extends ValueObject implements Comparable, Parcelable { public String packageName = "unknown"; public String name = "Unknown"; + public long repoId; public String summary = "Unknown application"; public String icon; @@ -147,10 +148,13 @@ public class App extends ValueObject implements Comparable, Parcelable { case Cols.ROW_ID: id = cursor.getLong(i); break; + case Cols.REPO_ID: + repoId = cursor.getLong(i); + break; case Cols.IS_COMPATIBLE: compatible = cursor.getInt(i) == 1; break; - case Cols.PACKAGE_NAME: + case Cols.Package.PACKAGE_NAME: packageName = cursor.getString(i); break; case Cols.NAME: @@ -430,8 +434,9 @@ public class App extends ValueObject implements Comparable, Parcelable { final ContentValues values = new ContentValues(); // Intentionally don't put "ROW_ID" in here, because we don't ever want to change that // primary key generated by sqlite. - values.put(Cols.PACKAGE_NAME, packageName); + values.put(Cols.Package.PACKAGE_NAME, packageName); values.put(Cols.NAME, name); + values.put(Cols.REPO_ID, repoId); values.put(Cols.SUMMARY, summary); values.put(Cols.ICON, icon); values.put(Cols.ICON_URL, iconUrl); @@ -560,6 +565,7 @@ public class App extends ValueObject implements Comparable, Parcelable { dest.writeByte(this.compatible ? (byte) 1 : (byte) 0); dest.writeString(this.packageName); dest.writeString(this.name); + dest.writeLong(this.repoId); dest.writeString(this.summary); dest.writeString(this.icon); dest.writeString(this.description); @@ -596,6 +602,7 @@ public class App extends ValueObject implements Comparable, Parcelable { this.compatible = in.readByte() != 0; this.packageName = in.readString(); this.name = in.readString(); + this.repoId = in.readLong(); this.summary = in.readString(); this.icon = in.readString(); this.description = in.readString(); 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 256884f35..3d4bbe282 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/AppProvider.java @@ -18,6 +18,7 @@ import org.fdroid.fdroid.data.Schema.AppPrefsTable; import org.fdroid.fdroid.data.Schema.AppMetadataTable; import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; import org.fdroid.fdroid.data.Schema.InstalledAppTable; +import org.fdroid.fdroid.data.Schema.PackageTable; import org.fdroid.fdroid.data.Schema.RepoTable; import java.util.ArrayList; @@ -120,13 +121,17 @@ public class AppProvider extends FDroidProvider { return categories; } - public static App findByPackageName(ContentResolver resolver, String packageName) { - return findByPackageName(resolver, packageName, Cols.ALL); + public static App findHighestPriorityMetadata(ContentResolver resolver, String packageName) { + throw new UnsupportedOperationException("TODO: Pull back the metadata with the highest priority for packageName"); } - public static App findByPackageName(ContentResolver resolver, String packageName, + public static App findByPackageName(ContentResolver resolver, String packageName, long repoId) { + return findByPackageName(resolver, packageName, repoId, Cols.ALL); + } + + public static App findByPackageName(ContentResolver resolver, String packageName, long repoId, String[] projection) { - final Uri uri = getContentUri(packageName); + final Uri uri = getAppUri(packageName, repoId); return cursorToApp(resolver.query(uri, projection, null, null, null)); } @@ -254,11 +259,13 @@ public class AppProvider extends FDroidProvider { @Override protected String getRequiredTables() { + final String pkg = PackageTable.NAME; final String app = getTableName(); final String apk = getApkTableName(); final String repo = RepoTable.NAME; - return app + + return pkg + + " JOIN " + app + " ON (" + app + "." + Cols.PACKAGE_ID + " = " + pkg + "." + PackageTable.Cols.ROW_ID + ") " + " LEFT JOIN " + apk + " ON (" + apk + "." + ApkTable.Cols.APP_ID + " = " + app + "." + Cols.ROW_ID + ") " + " LEFT JOIN " + repo + " ON (" + apk + "." + ApkTable.Cols.REPO_ID + " = " + repo + "." + RepoTable.Cols._ID + ") "; } @@ -291,7 +298,7 @@ public class AppProvider extends FDroidProvider { join( InstalledAppTable.NAME, "installed", - "installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = " + getTableName() + "." + Cols.PACKAGE_NAME); + "installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = " + PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME); requiresInstalledTable = true; } } @@ -301,7 +308,7 @@ public class AppProvider extends FDroidProvider { leftJoin( AppPrefsTable.NAME, "prefs", - "prefs." + AppPrefsTable.Cols.PACKAGE_NAME + " = " + getTableName() + "." + Cols.PACKAGE_NAME); + "prefs." + AppPrefsTable.Cols.PACKAGE_NAME + " = " + PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME); requiresLeftJoinToPrefs = true; } } @@ -311,7 +318,7 @@ public class AppProvider extends FDroidProvider { leftJoin( InstalledAppTable.NAME, "installed", - "installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = " + getTableName() + "." + Cols.PACKAGE_NAME); + "installed." + InstalledAppTable.Cols.PACKAGE_NAME + " = " + PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME); requiresInstalledTable = true; } } @@ -319,6 +326,9 @@ public class AppProvider extends FDroidProvider { @Override public void addField(String field) { switch (field) { + case Cols.Package.PACKAGE_NAME: + appendField(PackageTable.Cols.PACKAGE_NAME, PackageTable.NAME, Cols.Package.PACKAGE_NAME); + break; case Cols.SuggestedApk.VERSION_NAME: addSuggestedApkVersionField(); break; @@ -404,6 +414,7 @@ public class AppProvider extends FDroidProvider { private static final String PATH_SEARCH_REPO = "searchRepo"; private static final String PATH_NO_APKS = "noApks"; protected static final String PATH_APPS = "apps"; + protected static final String PATH_APP = "app"; private static final String PATH_RECENTLY_UPDATED = "recentlyUpdated"; private static final String PATH_NEWLY_ADDED = "newlyAdded"; private static final String PATH_CATEGORY = "category"; @@ -437,7 +448,7 @@ public class AppProvider extends FDroidProvider { MATCHER.addURI(getAuthority(), PATH_CAN_UPDATE, CAN_UPDATE); MATCHER.addURI(getAuthority(), PATH_INSTALLED, INSTALLED); MATCHER.addURI(getAuthority(), PATH_NO_APKS, NO_APKS); - MATCHER.addURI(getAuthority(), "*", CODE_SINGLE); + MATCHER.addURI(getAuthority(), PATH_APP + "/#/*", CODE_SINGLE); } public static Uri getContentUri() { @@ -486,6 +497,19 @@ public class AppProvider extends FDroidProvider { return getContentUri(app.packageName); } + public static Uri getAppUri(App app) { + return getAppUri(app.packageName, app.repoId); + } + + public static Uri getAppUri(String packageName, long repoId) { + return getContentUri() + .buildUpon() + .appendPath(PATH_APP) + .appendPath(Long.toString(repoId)) + .appendPath(packageName) + .build(); + } + public static Uri getContentUri(String packageName) { return Uri.withAppendedPath(getContentUri(), packageName); } @@ -590,7 +614,7 @@ public class AppProvider extends FDroidProvider { final String app = getTableName(); final String[] columns = { - app + "." + Cols.PACKAGE_NAME, + PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME, app + "." + Cols.NAME, app + "." + Cols.SUMMARY, app + "." + Cols.DESCRIPTION, @@ -624,12 +648,25 @@ public class AppProvider extends FDroidProvider { return new AppQuerySelection(selection.toString(), selectionKeywords); } - protected AppQuerySelection querySingle(String packageName) { - final String selection = getTableName() + "." + Cols.PACKAGE_NAME + " = ?"; + protected AppQuerySelection querySingle(String packageName, long repoId) { + final String selection = PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME + " = ?"; final String[] args = {packageName}; return new AppQuerySelection(selection, args); } + /** + * Same as {@link AppProvider#querySingle(String, long)} except it is used for the purpose + * of an UPDATE query rather than a SELECT query. This means that it must use a subquery to get + * the {@link Cols.Package#PACKAGE_ID} rather than the join which is already in place for that + * table. + */ + protected AppQuerySelection querySingleForUpdate(String packageName, long repoId) { + final String selection = Cols.PACKAGE_ID + " = (" + getPackageIdFromPackageNameQuery() + + ") AND " + Cols.REPO_ID + " = ? "; + final String[] args = {packageName, Long.toString(repoId)}; + return new AppQuerySelection(selection, args); + } + private AppQuerySelection queryExcludeSwap() { // fdroid_repo will have null fields if the LEFT JOIN didn't resolve, e.g. due to there // being no apks for the app in the result set. In that case, we can't tell if it is from @@ -697,7 +734,10 @@ public class AppProvider extends FDroidProvider { break; case CODE_SINGLE: - selection = selection.add(querySingle(uri.getLastPathSegment())); + List pathParts = uri.getPathSegments(); + long repoId = Long.parseLong(pathParts.get(1)); + String packageName = pathParts.get(2); + selection = selection.add(querySingle(packageName, repoId)); break; case CAN_UPDATE: @@ -799,11 +839,15 @@ public class AppProvider extends FDroidProvider { @Override public Uri insert(Uri uri, ContentValues values) { + long packageId = PackageProvider.Helper.ensureExists(getContext(), values.getAsString(Cols.Package.PACKAGE_NAME)); + values.remove(Cols.Package.PACKAGE_NAME); + values.put(Cols.PACKAGE_ID, packageId); + db().insertOrThrow(getTableName(), null, values); if (!isApplyingBatch()) { getContext().getContentResolver().notifyChange(uri, null); } - return getContentUri(values.getAsString(Cols.PACKAGE_NAME)); + return getAppUri(values.getAsString(PackageTable.Cols.PACKAGE_NAME), values.getAsLong(Cols.REPO_ID)); } @Override @@ -816,7 +860,10 @@ public class AppProvider extends FDroidProvider { return 0; case CODE_SINGLE: - query = query.add(querySingle(uri.getLastPathSegment())); + List pathParts = uri.getPathSegments(); + long repoId = Long.parseLong(pathParts.get(1)); + String packageName = pathParts.get(2); + query = query.add(querySingleForUpdate(packageName, repoId)); break; default: 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 14e87a7eb..d79335940 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -103,7 +103,8 @@ class DBHelper extends SQLiteOpenHelper { static final String CREATE_TABLE_APP_METADATA = "CREATE TABLE " + AppMetadataTable.NAME + " ( " - + AppMetadataTable.Cols.PACKAGE_NAME + " text not null, " + + AppMetadataTable.Cols.PACKAGE_ID + " integer not null, " + + AppMetadataTable.Cols.REPO_ID + " integer not null, " + AppMetadataTable.Cols.NAME + " text not null, " + AppMetadataTable.Cols.SUMMARY + " text not null, " + AppMetadataTable.Cols.ICON + " text, " @@ -130,7 +131,7 @@ class DBHelper extends SQLiteOpenHelper { + AppMetadataTable.Cols.IS_COMPATIBLE + " int not null," + AppMetadataTable.Cols.ICON_URL + " text, " + AppMetadataTable.Cols.ICON_URL_LARGE + " text, " - + "primary key(" + AppMetadataTable.Cols.PACKAGE_NAME + "));"; + + "primary key(" + AppMetadataTable.Cols.PACKAGE_ID + ", " + AppMetadataTable.Cols.REPO_ID + "));"; private static final String CREATE_TABLE_APP_PREFS = "CREATE TABLE " + AppPrefsTable.NAME + " ( " @@ -434,7 +435,7 @@ class DBHelper extends SQLiteOpenHelper { + AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ", " + AppPrefsTable.Cols.IGNORE_ALL_UPDATES + ") SELECT " - + AppMetadataTable.Cols.PACKAGE_NAME + ", " + + "id, " + "ignoreThisUpdate, " + "ignoreAllUpdates " + "FROM " + AppMetadataTable.NAME + " " @@ -543,7 +544,7 @@ class DBHelper extends SQLiteOpenHelper { final String update = "UPDATE " + ApkTable.NAME + " SET " + ApkTable.Cols.APP_ID + " = ( " + "SELECT app." + AppMetadataTable.Cols.ROW_ID + " " + "FROM " + AppMetadataTable.NAME + " AS app " + - "WHERE " + ApkTable.NAME + ".id = app." + AppMetadataTable.Cols.PACKAGE_NAME + ")"; + "WHERE " + ApkTable.NAME + ".id = app.id)"; Log.i(TAG, "Updating foreign key from " + ApkTable.NAME + " to " + AppMetadataTable.NAME + " to use numeric foreign key."); Utils.debugLog(TAG, update); db.execSQL(update); @@ -840,10 +841,17 @@ class DBHelper extends SQLiteOpenHelper { } Utils.debugLog(TAG, "Ensuring indexes exist for " + AppMetadataTable.NAME); - db.execSQL("CREATE INDEX IF NOT EXISTS app_id on " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.PACKAGE_NAME + ");"); db.execSQL("CREATE INDEX IF NOT EXISTS name on " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.NAME + ");"); // Used for sorting most lists db.execSQL("CREATE INDEX IF NOT EXISTS added on " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.ADDED + ");"); // Used for sorting "newly added" + if (columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.PACKAGE_ID)) { + db.execSQL("CREATE INDEX IF NOT EXISTS metadata_packageId ON " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.PACKAGE_ID + ");"); + } + + if (columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.REPO_ID)) { + db.execSQL("CREATE INDEX IF NOT EXISTS metadata_repoId ON " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.REPO_ID + ");"); + } + Utils.debugLog(TAG, "Ensuring indexes exist for " + ApkTable.NAME); db.execSQL("CREATE INDEX IF NOT EXISTS apk_vercode on " + ApkTable.NAME + " (" + ApkTable.Cols.VERSION_CODE + ");"); db.execSQL("CREATE INDEX IF NOT EXISTS apk_appId on " + ApkTable.NAME + " (" + ApkTable.Cols.APP_ID + ");"); diff --git a/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java b/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java index c1c046be7..26f6f28fb 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java @@ -157,4 +157,8 @@ public abstract class FDroidProvider extends ContentProvider { } } } + + protected String getPackageIdFromPackageNameQuery() { + return "SELECT " + Schema.PackageTable.Cols.ROW_ID + " FROM " + Schema.PackageTable.NAME + " WHERE " + Schema.PackageTable.Cols.PACKAGE_NAME + " = ?"; + } } diff --git a/app/src/main/java/org/fdroid/fdroid/data/PackageProvider.java b/app/src/main/java/org/fdroid/fdroid/data/PackageProvider.java new file mode 100644 index 000000000..9c3d6942c --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/data/PackageProvider.java @@ -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.util.Log; + +import org.fdroid.fdroid.data.Schema.PackageTable; +import org.fdroid.fdroid.data.Schema.PackageTable.Cols; + +public class PackageProvider extends FDroidProvider { + + private static final String TAG = "PackageProvider"; + + public static final class Helper { + private Helper() { } + + public static long ensureExists(Context context, String packageName) { + long id = getPackageId(context, packageName); + if (id <= 0) { + ContentValues values = new ContentValues(1); + values.put(Cols.PACKAGE_NAME, packageName); + Uri uri = context.getContentResolver().insert(getContentUri(), values); + id = Long.parseLong(uri.getLastPathSegment()); + } + return id; + } + + public static long getPackageId(Context context, String packageName) { + Cursor cursor = context.getContentResolver().query(getPackageUri(packageName), Cols.ALL, null, null, null); + if (cursor == null) { + return 0; + } + + try { + if (cursor.getCount() == 0) { + return 0; + } else { + cursor.moveToFirst(); + return cursor.getLong(cursor.getColumnIndexOrThrow(Cols.PACKAGE_NAME)); + } + } finally { + cursor.close(); + } + } + } + + private class Query extends QueryBuilder { + + @Override + protected String getRequiredTables() { + return PackageTable.NAME; + } + + @Override + public void addField(String field) { + appendField(field, getTableName()); + } + } + + private static final String PROVIDER_NAME = "PackageProvider"; + + private static final UriMatcher MATCHER = new UriMatcher(-1); + + private static final String PATH_PACKAGE_NAME = "packageName"; + private static final String PATH_PACKAGE_ID = "packageId"; + + static { + MATCHER.addURI(getAuthority(), PATH_PACKAGE_NAME + "/*", CODE_SINGLE); + } + + private static Uri getContentUri() { + return Uri.parse("content://" + getAuthority()); + } + + public static Uri getPackageUri(String packageName) { + return getContentUri() + .buildUpon() + .appendPath(PATH_PACKAGE_NAME) + .appendPath(packageName) + .build(); + } + + /** + * Not actually used as part of the external API to this content provider. + * Rather, used as a mechanism for returning the ID of a newly inserted row after calling + * {@link android.content.ContentProvider#insert(Uri, ContentValues)}, as that is only able + * to return a {@link Uri}. The {@link Uri#getLastPathSegment()} of this URI contains a + * {@link Long} which is the {@link PackageTable.Cols#ROW_ID} of the newly inserted row. + */ + private static Uri getPackageIdUri(long packageId) { + return getContentUri() + .buildUpon() + .appendPath(PATH_PACKAGE_ID) + .appendPath(Long.toString(packageId)) + .build(); + } + + @Override + protected String getTableName() { + return PackageTable.NAME; + } + + @Override + protected String getProviderName() { + return "PackageProvider"; + } + + public static String getAuthority() { + return AUTHORITY + "." + PROVIDER_NAME; + } + + @Override + protected UriMatcher getMatcher() { + return MATCHER; + } + + protected QuerySelection querySingle(String packageName) { + final String selection = getTableName() + "." + Cols.PACKAGE_NAME + " = ?"; + final String[] args = {packageName}; + 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(uri.getLastPathSegment())); + break; + + default: + Log.e(TAG, "Invalid URI for content provider: " + uri); + throw new UnsupportedOperationException("Invalid URI for 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; + } + + /** + * Deleting of packages is not required. + * It doesn't matter if we have a package name in the database after the package is no longer + * present in the repo any more. They wont take up much space, and it is the presence of rows + * in the {@link Schema.MetadataTable} which decides whether something is available in the + * F-Droid client or not. + */ + @Override + public int delete(Uri uri, String where, String[] whereArgs) { + throw new UnsupportedOperationException("Delete not supported for " + uri + "."); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + long rowId = db().insertOrThrow(getTableName(), null, values); + getContext().getContentResolver().notifyChange(AppProvider.getCanUpdateUri(), null); + return getPackageIdUri(rowId); + } + + @Override + public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { + throw new UnsupportedOperationException("Update not supported for " + uri + "."); + } +} 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 68e2912fc..9e403af20 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java +++ b/app/src/main/java/org/fdroid/fdroid/data/RepoPersister.java @@ -143,7 +143,7 @@ public class RepoPersister { for (App app : apps) { packageNames.add(app.packageName); } - String[] projection = {Schema.AppMetadataTable.Cols.ROW_ID, Schema.AppMetadataTable.Cols.PACKAGE_NAME}; + String[] projection = {Schema.AppMetadataTable.Cols.ROW_ID, Schema.AppMetadataTable.Cols.Package.PACKAGE_NAME}; List fromDb = TempAppProvider.Helper.findByPackageNames(context, packageNames, projection); Map ids = new HashMap<>(fromDb.size()); @@ -175,7 +175,7 @@ public class RepoPersister { */ private ArrayList insertOrUpdateApks(List packages) { String[] projection = new String[]{ - Schema.ApkTable.Cols.App.PACKAGE_NAME, + Schema.ApkTable.Cols.Package.PACKAGE_NAME, Schema.ApkTable.Cols.VERSION_CODE, }; List existingApks = ApkProvider.Helper.knownApks(context, packages, projection); @@ -204,7 +204,7 @@ public class RepoPersister { * Does not do any checks to see if the app already exists or not. */ private ContentProviderOperation updateExistingApp(App app) { - Uri uri = TempAppProvider.getAppUri(app); + Uri uri = TempAppProvider.getAppUri(app.packageName, app.repoId); return ContentProviderOperation.newUpdate(uri).withValues(app.toContentValues()).build(); } @@ -224,8 +224,8 @@ public class RepoPersister { * array. */ private boolean isAppInDatabase(App app) { - String[] fields = {Schema.AppMetadataTable.Cols.PACKAGE_NAME}; - App found = AppProvider.Helper.findByPackageName(context.getContentResolver(), app.packageName, fields); + String[] fields = {Schema.AppMetadataTable.Cols.Package.PACKAGE_NAME}; + App found = AppProvider.Helper.findByPackageName(context.getContentResolver(), app.packageName, repo.id, fields); return found != null; } @@ -255,7 +255,7 @@ public class RepoPersister { */ @Nullable private ContentProviderOperation deleteOrphanedApks(List apps, Map> packages) { - String[] projection = new String[]{Schema.ApkTable.Cols.App.PACKAGE_NAME, Schema.ApkTable.Cols.VERSION_CODE}; + 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 toDelete = new ArrayList<>(); 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 b9037deab..eee638a18 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -53,7 +53,8 @@ public interface Schema { String ROW_ID = "rowid"; String _COUNT = "_count"; String IS_COMPATIBLE = "compatible"; - String PACKAGE_NAME = "id"; + String PACKAGE_ID = "packageId"; + String REPO_ID = "repoId"; String NAME = "name"; String SUMMARY = "summary"; String ICON = "icon"; @@ -90,13 +91,17 @@ public interface Schema { String SIGNATURE = "installedSig"; } + interface Package { + String PACKAGE_NAME = "package_packageName"; + } + /** * Each of the physical columns in the sqlite table. Differs from {@link Cols#ALL} in * that it doesn't include fields which are aliases of other fields (e.g. {@link Cols#_ID} * or which are from other related tables (e.g. {@link Cols.SuggestedApk#VERSION_NAME}). */ String[] ALL_COLS = { - ROW_ID, IS_COMPATIBLE, PACKAGE_NAME, NAME, SUMMARY, ICON, DESCRIPTION, + ROW_ID, PACKAGE_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION, LICENSE, AUTHOR, EMAIL, WEB_URL, TRACKER_URL, SOURCE_URL, CHANGELOG_URL, DONATE_URL, BITCOIN_ADDR, LITECOIN_ADDR, FLATTR_ID, UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED, @@ -110,14 +115,14 @@ public interface Schema { * @see Cols#ALL_COLS */ String[] ALL = { - _ID, ROW_ID, IS_COMPATIBLE, PACKAGE_NAME, NAME, SUMMARY, ICON, DESCRIPTION, + _ID, ROW_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION, LICENSE, AUTHOR, EMAIL, WEB_URL, TRACKER_URL, SOURCE_URL, CHANGELOG_URL, DONATE_URL, BITCOIN_ADDR, LITECOIN_ADDR, FLATTR_ID, UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED, CATEGORIES, ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE, SUGGESTED_VERSION_CODE, SuggestedApk.VERSION_NAME, InstalledApp.VERSION_CODE, InstalledApp.VERSION_NAME, - InstalledApp.SIGNATURE, + InstalledApp.SIGNATURE, Package.PACKAGE_NAME }; } } @@ -163,8 +168,8 @@ public interface Schema { String ADDRESS = "repoAddress"; } - interface App { - String PACKAGE_NAME = "appPackageName"; + interface Package { + String PACKAGE_NAME = "package_packageName"; } /** @@ -181,7 +186,7 @@ public interface Schema { * @see AppMetadataTable.Cols#ALL */ String[] ALL = { - _ID, APP_ID, App.PACKAGE_NAME, VERSION_NAME, REPO_ID, HASH, VERSION_CODE, NAME, + _ID, APP_ID, Package.PACKAGE_NAME, VERSION_NAME, REPO_ID, HASH, VERSION_CODE, NAME, SIZE, SIGNATURE, SOURCE_NAME, MIN_SDK_VERSION, TARGET_SDK_VERSION, MAX_SDK_VERSION, PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, IS_COMPATIBLE, Repo.VERSION, Repo.ADDRESS, INCOMPATIBLE_REASONS, 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 955e82cde..9927fc28d 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/TempAppProvider.java @@ -13,6 +13,7 @@ import java.util.List; import org.fdroid.fdroid.data.Schema.ApkTable; import org.fdroid.fdroid.data.Schema.AppMetadataTable; +import org.fdroid.fdroid.data.Schema.PackageTable; /** * This class does all of its operations in a temporary sqlite table. @@ -41,7 +42,7 @@ public class TempAppProvider extends AppProvider { MATCHER.addURI(getAuthority(), PATH_INIT, CODE_INIT); MATCHER.addURI(getAuthority(), PATH_COMMIT, CODE_COMMIT); MATCHER.addURI(getAuthority(), PATH_APPS + "/*", APPS); - MATCHER.addURI(getAuthority(), "*", CODE_SINGLE); + MATCHER.addURI(getAuthority(), PATH_APP + "/#/*", CODE_SINGLE); } @Override @@ -57,8 +58,13 @@ public class TempAppProvider extends AppProvider { return Uri.parse("content://" + getAuthority()); } - public static Uri getAppUri(App app) { - return Uri.withAppendedPath(getContentUri(), app.packageName); + public static Uri getAppUri(String packageName, long repoId) { + return getContentUri() + .buildUpon() + .appendPath(PATH_APP) + .appendPath(Long.toString(repoId)) + .appendPath(packageName) + .build(); } public static Uri getAppsUri(List apps) { @@ -69,7 +75,7 @@ public class TempAppProvider extends AppProvider { } private AppQuerySelection queryApps(String packageNames) { - return queryPackageNames(packageNames, getTableName() + "." + AppMetadataTable.Cols.PACKAGE_NAME); + return queryPackageNames(packageNames, PackageTable.NAME + "." + PackageTable.Cols.PACKAGE_NAME); } public static class Helper { @@ -126,7 +132,13 @@ public class TempAppProvider extends AppProvider { throw new UnsupportedOperationException("Update not supported for " + uri + "."); } - QuerySelection query = new QuerySelection(where, whereArgs).add(querySingle(uri.getLastPathSegment())); + List pathParts = uri.getPathSegments(); + String packageName = pathParts.get(2); + long repoId = Long.parseLong(pathParts.get(1)); + QuerySelection query = new QuerySelection(where, whereArgs).add(querySingleForUpdate(packageName, repoId)); + + // Package names for apps cannot change... + values.remove(AppMetadataTable.Cols.Package.PACKAGE_NAME); int count = db().update(getTableName(), values, query.getSelection(), query.getArgs()); if (!isApplyingBatch()) { @@ -163,7 +175,7 @@ public class TempAppProvider extends AppProvider { db.execSQL("ATTACH DATABASE ':memory:' AS " + DB); db.execSQL(DBHelper.CREATE_TABLE_APP_METADATA.replaceFirst(AppMetadataTable.NAME, DB + "." + getTableName())); db.execSQL(copyData(AppMetadataTable.Cols.ALL_COLS, AppMetadataTable.NAME, DB + "." + getTableName())); - db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_id ON " + getTableName() + " (" + AppMetadataTable.Cols.PACKAGE_NAME + ");"); + db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_id ON " + getTableName() + " (" + AppMetadataTable.Cols.PACKAGE_ID + ");"); db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_upstreamVercode ON " + getTableName() + " (" + AppMetadataTable.Cols.UPSTREAM_VERSION_CODE + ");"); db.execSQL("CREATE INDEX IF NOT EXISTS " + DB + ".app_compatible ON " + getTableName() + " (" + AppMetadataTable.Cols.IS_COMPATIBLE + ");"); } diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java index 2e3564507..d97e58341 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -259,7 +259,7 @@ public class InstallManagerService extends Service { App app = getAppFromActive(downloadUrl); if (app == null) { ContentResolver resolver = context.getContentResolver(); - app = AppProvider.Helper.findByPackageName(resolver, apk.packageName); + app = AppProvider.Helper.findByPackageName(resolver, apk.packageName, apk.repo); } // show notification if app details is not visible if (app != null && AppDetails.isAppVisible(app.packageName)) { @@ -346,7 +346,7 @@ public class InstallManagerService extends Service { String name = getAppName(apk); if (TextUtils.isEmpty(name) || name.equals(new App().name)) { ContentResolver resolver = getContentResolver(); - App app = AppProvider.Helper.findByPackageName(resolver, apk.packageName); + App app = AppProvider.Helper.findByPackageName(resolver, apk.packageName, apk.repo); if (app == null || TextUtils.isEmpty(app.name)) { return; // do not have a name to display, so leave notification as is } diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java index a14996357..967df1ac5 100644 --- a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java @@ -191,7 +191,7 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel intent = getIntent(); Uri uri = intent.getData(); Apk apk = ApkProvider.Helper.findByUri(this, uri, Schema.ApkTable.Cols.ALL); - app = AppProvider.Helper.findByPackageName(getContentResolver(), apk.packageName); + app = AppProvider.Helper.findByPackageName(getContentResolver(), apk.packageName, apk.repo, Schema.AppMetadataTable.Cols.ALL); appDiff = new AppDiff(getPackageManager(), apk); diff --git a/app/src/main/java/org/fdroid/fdroid/views/fragments/AppListFragment.java b/app/src/main/java/org/fdroid/fdroid/views/fragments/AppListFragment.java index 7ffa038b5..188861f01 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/fragments/AppListFragment.java +++ b/app/src/main/java/org/fdroid/fdroid/views/fragments/AppListFragment.java @@ -39,7 +39,7 @@ public abstract class AppListFragment extends ListFragment implements private static final String[] APP_PROJECTION = { AppMetadataTable.Cols._ID, // Required for cursor loader to work. - AppMetadataTable.Cols.PACKAGE_NAME, + AppMetadataTable.Cols.Package.PACKAGE_NAME, AppMetadataTable.Cols.NAME, AppMetadataTable.Cols.SUMMARY, AppMetadataTable.Cols.IS_COMPATIBLE, diff --git a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java index 0b570506c..d14f831f4 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java +++ b/app/src/main/java/org/fdroid/fdroid/views/swap/SwapAppsView.java @@ -289,7 +289,8 @@ public class SwapAppsView extends ListView implements public void onChange(boolean selfChange) { Activity activity = getActivity(); if (activity != null) { - app = AppProvider.Helper.findByPackageName(getActivity().getContentResolver(), app.packageName); + app = AppProvider.Helper.findByPackageName(getActivity().getContentResolver(), + app.packageName, app.repoId, AppMetadataTable.Cols.ALL); resetView(); } } @@ -318,7 +319,7 @@ public class SwapAppsView extends ListView implements // implemented on API-16, so leaving like this for now. getActivity().getContentResolver().unregisterContentObserver(appObserver); getActivity().getContentResolver().registerContentObserver( - AppProvider.getContentUri(this.app.packageName), true, appObserver); + AppProvider.getAppUri(this.app), true, appObserver); } resetView(); } diff --git a/app/src/test/java/org/fdroid/fdroid/Assert.java b/app/src/test/java/org/fdroid/fdroid/Assert.java index 61f6fe223..adbd30db4 100644 --- a/app/src/test/java/org/fdroid/fdroid/Assert.java +++ b/app/src/test/java/org/fdroid/fdroid/Assert.java @@ -183,7 +183,8 @@ public class Assert { public static App insertApp(Context context, String packageName, String name, ContentValues additionalValues) { ContentValues values = new ContentValues(); - values.put(AppMetadataTable.Cols.PACKAGE_NAME, packageName); + values.put(AppMetadataTable.Cols.REPO_ID, 1); + values.put(AppMetadataTable.Cols.Package.PACKAGE_NAME, packageName); values.put(AppMetadataTable.Cols.NAME, name); // Required fields (NOT NULL in the database). @@ -197,14 +198,14 @@ public class Assert { Uri uri = AppProvider.getContentUri(); context.getContentResolver().insert(uri, values); - return AppProvider.Helper.findByPackageName(context.getContentResolver(), packageName); + return AppProvider.Helper.findByPackageName(context.getContentResolver(), packageName, 1); } private static App ensureApp(Context context, String packageName) { - App app = AppProvider.Helper.findByPackageName(context.getContentResolver(), packageName); + App app = AppProvider.Helper.findByPackageName(context.getContentResolver(), packageName, 1); if (app == null) { insertApp(context, packageName, packageName); - app = AppProvider.Helper.findByPackageName(context.getContentResolver(), packageName); + app = AppProvider.Helper.findByPackageName(context.getContentResolver(), packageName, 1); } assertNotNull(app); return app; 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 95a153071..efbfada6e 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -284,7 +284,7 @@ public class ApkProviderTest extends FDroidProviderTest { Collections.addAll(apksToCheck, unknown); String[] projection = { - Cols.App.PACKAGE_NAME, + Cols.Package.PACKAGE_NAME, Cols.VERSION_CODE, }; @@ -424,7 +424,7 @@ public class ApkProviderTest extends FDroidProviderTest { assertEquals("a hash type", apk.hashType); String[] projection = { - Cols.App.PACKAGE_NAME, + Cols.Package.PACKAGE_NAME, Cols.HASH, }; diff --git a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java index f6dd4995a..b0ccd5e6e 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/AppProviderTest.java @@ -68,7 +68,7 @@ public class AppProviderTest extends FDroidProviderTest { @Test public void testCantFindApp() { - assertNull(AppProvider.Helper.findByPackageName(context.getContentResolver(), "com.example.doesnt-exist")); + assertNull(AppProvider.Helper.findByPackageName(context.getContentResolver(), "com.example.doesnt-exist", 1)); } @Test @@ -111,14 +111,14 @@ public class AppProviderTest extends FDroidProviderTest { ContentResolver r = context.getContentResolver(); // Can't "update", although can "install"... - App notInstalled = AppProvider.Helper.findByPackageName(r, "not installed"); + App notInstalled = AppProvider.Helper.findByPackageName(r, "not installed", 1); assertFalse(notInstalled.canAndWantToUpdate(context)); - App installedOnlyOneVersionAvailable = AppProvider.Helper.findByPackageName(r, "installed, only one version available"); - App installedAlreadyLatestNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, already latest, no ignore"); - App installedAlreadyLatestIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore all"); - App installedAlreadyLatestIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore latest"); - App installedAlreadyLatestIgnoreOld = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore old"); + App installedOnlyOneVersionAvailable = AppProvider.Helper.findByPackageName(r, "installed, only one version available", 1); + App installedAlreadyLatestNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, already latest, no ignore", 1); + App installedAlreadyLatestIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore all", 1); + App installedAlreadyLatestIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore latest", 1); + App installedAlreadyLatestIgnoreOld = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore old", 1); assertFalse(installedOnlyOneVersionAvailable.canAndWantToUpdate(context)); assertFalse(installedAlreadyLatestNoIgnore.canAndWantToUpdate(context)); @@ -126,10 +126,10 @@ public class AppProviderTest extends FDroidProviderTest { assertFalse(installedAlreadyLatestIgnoreLatest.canAndWantToUpdate(context)); assertFalse(installedAlreadyLatestIgnoreOld.canAndWantToUpdate(context)); - App installedOldNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, old version, no ignore"); - App installedOldIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore all"); - App installedOldIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore latest"); - App installedOldIgnoreNewerNotLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore newer, but not latest"); + App installedOldNoIgnore = AppProvider.Helper.findByPackageName(r, "installed, old version, no ignore", 1); + App installedOldIgnoreAll = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore all", 1); + App installedOldIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore latest", 1); + App installedOldIgnoreNewerNotLatest = AppProvider.Helper.findByPackageName(r, "installed, old version, ignore newer, but not latest", 1); assertTrue(installedOldNoIgnore.canAndWantToUpdate(context)); assertFalse(installedOldIgnoreAll.canAndWantToUpdate(context)); @@ -169,7 +169,7 @@ public class AppProviderTest extends FDroidProviderTest { assertResultCount(contentResolver, 10, AppProvider.getContentUri(), PROJ); - String[] projection = {Cols.PACKAGE_NAME}; + String[] projection = {Cols.Package.PACKAGE_NAME}; List canUpdateApps = AppProvider.Helper.findCanUpdate(context, projection); String[] expectedCanUpdate = { @@ -239,7 +239,7 @@ public class AppProviderTest extends FDroidProviderTest { assertEquals("org.fdroid.fdroid", app.packageName); assertEquals("F-Droid", app.name); - App otherApp = AppProvider.Helper.findByPackageName(context.getContentResolver(), "org.fdroid.fdroid"); + App otherApp = AppProvider.Helper.findByPackageName(context.getContentResolver(), "org.fdroid.fdroid", 1); assertNotNull(otherApp); assertEquals("org.fdroid.fdroid", otherApp.packageName); assertEquals("F-Droid", otherApp.name); @@ -260,7 +260,7 @@ public class AppProviderTest extends FDroidProviderTest { String[] projection = new String[] { Cols._ID, Cols.NAME, - Cols.PACKAGE_NAME, + Cols.Package.PACKAGE_NAME, }; return contentResolver.query(AppProvider.getContentUri(), projection, null, null, null); } @@ -356,7 +356,8 @@ public class AppProviderTest extends FDroidProviderTest { public App insertApp(String id, String name, ContentValues additionalValues) { ContentValues values = new ContentValues(); - values.put(Cols.PACKAGE_NAME, id); + values.put(Cols.Package.PACKAGE_NAME, id); + values.put(Cols.REPO_ID, 1); values.put(Cols.NAME, name); // Required fields (NOT NULL in the database). @@ -370,6 +371,6 @@ public class AppProviderTest extends FDroidProviderTest { Uri uri = AppProvider.getContentUri(); contentResolver.insert(uri, values); - return AppProvider.Helper.findByPackageName(context.getContentResolver(), id); + return AppProvider.Helper.findByPackageName(context.getContentResolver(), id, 1); } } 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 2c5f067e0..5f4d445bb 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ProviderUriTests.java @@ -93,10 +93,10 @@ public class ProviderUriTests { assertValidUri(resolver, AppProvider.getCanUpdateUri(), "content://org.fdroid.fdroid.data.AppProvider/canUpdate", projection); App app = new App(); + app.repoId = 1; app.packageName = "org.fdroid.fdroid"; - assertValidUri(resolver, AppProvider.getContentUri(app), "content://org.fdroid.fdroid.data.AppProvider/org.fdroid.fdroid", projection); - assertValidUri(resolver, AppProvider.getContentUri("org.fdroid.fdroid"), "content://org.fdroid.fdroid.data.AppProvider/org.fdroid.fdroid", projection); + assertValidUri(resolver, AppProvider.getAppUri(app), "content://org.fdroid.fdroid.data.AppProvider/app/1/org.fdroid.fdroid", projection); } @Test