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.
This commit is contained in:
Peter Serwylo 2017-09-12 16:48:14 +02:00 committed by Hans-Christoph Steiner
parent ac1dce24d2
commit 595f72d5b2
5 changed files with 52 additions and 4 deletions

View File

@ -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) {

View File

@ -104,6 +104,8 @@ public class App extends ValueObject implements Comparable<App>, 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<App>, 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<App>, 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<App>, 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<App>, 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());

View File

@ -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) {

View File

@ -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,
};

View File

@ -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",