From 595f72d5b28b93f0bc515d559b078bccf0a401db Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Tue, 12 Sep 2017 16:48:14 +0200 Subject: [PATCH] Calculate whether an app is an APK or not when updating repos. This improves performance when we need to decide whether or not apps are installed or not while scrolling through large lists. Fixes #1143. Also change Jackson tests to properly ignore App#isApk. --- .../org/fdroid/fdroid/IndexV1Updater.java | 6 +++++ .../main/java/org/fdroid/fdroid/data/App.java | 16 ++++++++++- .../java/org/fdroid/fdroid/data/DBHelper.java | 27 ++++++++++++++++++- .../java/org/fdroid/fdroid/data/Schema.java | 5 ++-- .../fdroid/updater/IndexV1UpdaterTest.java | 2 ++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java index b199ebbb4..046c7f38c 100644 --- a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java +++ b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java @@ -226,6 +226,12 @@ public class IndexV1Updater extends RepoUpdater { if (apks.size() > 0) { app.preferredSigner = apks.get(0).sig; + app.isApk = true; + for (Apk apk : apks) { + if (!apk.isApk()) { + app.isApk = false; + } + } } if (appCount % 50 == 0) { 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 7f91f6113..e621fd23b 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/App.java +++ b/app/src/main/java/org/fdroid/fdroid/data/App.java @@ -104,6 +104,8 @@ public class App extends ValueObject implements Comparable, Parcelable { @JsonIgnore @NonNull public String preferredSigner; + @JsonIgnore + public boolean isApk; @JacksonInject("repoId") public long repoId; @@ -347,6 +349,9 @@ public class App extends ValueObject implements Comparable, Parcelable { case Cols.WEAR_SCREENSHOTS: wearScreenshots = Utils.parseCommaSeparatedString(cursor.getString(i)); break; + case Cols.IS_APK: + isApk = cursor.getInt(i) == 1; + break; case Cols.InstalledApp.VERSION_CODE: installedVersionCode = cursor.getInt(i); break; @@ -854,12 +859,19 @@ public class App extends ValueObject implements Comparable, Parcelable { values.put(Cols.TV_SCREENSHOTS, Utils.serializeCommaSeparatedString(tvScreenshots)); values.put(Cols.WEAR_SCREENSHOTS, Utils.serializeCommaSeparatedString(wearScreenshots)); values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0); + values.put(Cols.IS_APK, isApk ? 1 : 0); return values; } public boolean isInstalled(Context context) { - return installedVersionCode > 0 || isMediaInstalled(context); + // First check isApk() before isMediaInstalled() because the latter is quite expensive, + // hitting the database for each apk version, then the disk to check for installed media. + return installedVersionCode > 0 || (!isApk() && isMediaInstalled(context)); + } + + private boolean isApk() { + return isApk; } public boolean isMediaInstalled(Context context) { @@ -1064,6 +1076,7 @@ public class App extends ValueObject implements Comparable, Parcelable { dest.writeStringArray(this.tenInchScreenshots); dest.writeStringArray(this.tvScreenshots); dest.writeStringArray(this.wearScreenshots); + dest.writeByte(this.isApk ? (byte) 1 : (byte) 0); dest.writeString(this.installedVersionName); dest.writeInt(this.installedVersionCode); dest.writeParcelable(this.installedApk, flags); @@ -1114,6 +1127,7 @@ public class App extends ValueObject implements Comparable, Parcelable { this.tenInchScreenshots = in.createStringArray(); this.tvScreenshots = in.createStringArray(); this.wearScreenshots = in.createStringArray(); + this.isApk = in.readByte() != 0; this.installedVersionName = in.readString(); this.installedVersionCode = in.readInt(); this.installedApk = in.readParcelable(Apk.class.getClassLoader()); 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 2e604392e..7dddc07b6 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -151,6 +151,7 @@ class DBHelper extends SQLiteOpenHelper { + AppMetadataTable.Cols.TEN_INCH_SCREENSHOTS + " string," + AppMetadataTable.Cols.TV_SCREENSHOTS + " string," + AppMetadataTable.Cols.WEAR_SCREENSHOTS + " string," + + AppMetadataTable.Cols.IS_APK + " boolean," + "primary key(" + AppMetadataTable.Cols.PACKAGE_ID + ", " + AppMetadataTable.Cols.REPO_ID + "));"; private static final String CREATE_TABLE_APP_PREFS = "CREATE TABLE " + AppPrefsTable.NAME @@ -193,7 +194,7 @@ class DBHelper extends SQLiteOpenHelper { + InstalledAppTable.Cols.HASH + " TEXT NOT NULL" + " );"; - protected static final int DB_VERSION = 73; + protected static final int DB_VERSION = 74; private final Context context; @@ -281,6 +282,30 @@ class DBHelper extends SQLiteOpenHelper { addIntegerPrimaryKeyToInstalledApps(db, oldVersion); addPreferredSignerToApp(db, oldVersion); updatePreferredSignerIfEmpty(db, oldVersion); + addIsAppToApp(db, oldVersion); + } + + private void addIsAppToApp(SQLiteDatabase db, int oldVersion) { + if (oldVersion >= 74) { + return; + } + + if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.IS_APK)) { + Log.i(TAG, "Figuring out whether each \"app\" is actually an app, or it represents other media."); + db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.IS_APK + " boolean;"); + + // Find all apks for which their filename DOESN'T end in ".apk", and if there is more than one, the + // corresponding app is updated to be marked as media. + String apkName = ApkTable.Cols.NAME; + String query = "UPDATE " + AppMetadataTable.NAME + " SET " + AppMetadataTable.Cols.IS_APK + " = (" + + " SELECT COUNT(*) FROM " + ApkTable.NAME + " AS apk" + + " WHERE " + + " " + ApkTable.Cols.APP_ID + " = " + AppMetadataTable.NAME + "." + AppMetadataTable.Cols.ROW_ID + + " AND SUBSTR(" + apkName + ", LENGTH(" + apkName + ") - 3) != '.apk'" + + ") = 0;"; + Log.i(TAG, query); + db.execSQL(query); + } } private void updatePreferredSignerIfEmpty(SQLiteDatabase db, int oldVersion) { 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 da3e80565..fcfdfd6a3 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -156,6 +156,7 @@ public interface Schema { String TEN_INCH_SCREENSHOTS = "tenInchScreenshots"; String TV_SCREENSHOTS = "tvScreenshots"; String WEAR_SCREENSHOTS = "wearScreenshots"; + String IS_APK = "isApk"; interface SuggestedApk { String VERSION_NAME = "suggestedApkVersion"; @@ -195,7 +196,7 @@ public interface Schema { ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE, FEATURE_GRAPHIC, PROMO_GRAPHIC, TV_BANNER, PHONE_SCREENSHOTS, SEVEN_INCH_SCREENSHOTS, TEN_INCH_SCREENSHOTS, TV_SCREENSHOTS, WEAR_SCREENSHOTS, - PREFERRED_SIGNER, SUGGESTED_VERSION_CODE, + PREFERRED_SIGNER, SUGGESTED_VERSION_CODE, IS_APK, }; /** @@ -211,7 +212,7 @@ public interface Schema { ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE, FEATURE_GRAPHIC, PROMO_GRAPHIC, TV_BANNER, PHONE_SCREENSHOTS, SEVEN_INCH_SCREENSHOTS, TEN_INCH_SCREENSHOTS, TV_SCREENSHOTS, WEAR_SCREENSHOTS, - PREFERRED_SIGNER, SUGGESTED_VERSION_CODE, SuggestedApk.VERSION_NAME, + PREFERRED_SIGNER, SUGGESTED_VERSION_CODE, IS_APK, SuggestedApk.VERSION_NAME, InstalledApp.VERSION_CODE, InstalledApp.VERSION_NAME, InstalledApp.SIGNATURE, Package.PACKAGE_NAME, }; diff --git a/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java index 41df377df..c070fea20 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java @@ -304,6 +304,7 @@ public class IndexV1UpdaterTest extends FDroidProviderTest { "installedSig", "installedVersionCode", "installedVersionName", + "isApk", "preferredSigner", "prefs", "TAG", @@ -335,6 +336,7 @@ public class IndexV1UpdaterTest extends FDroidProviderTest { "hash", "hashType", "incompatibleReasons", + "isApk", "maxSdkVersion", "minSdkVersion", "nativecode",