Completely removed preferences from app table.

This commit is contained in:
Peter Serwylo 2016-07-25 18:11:31 +10:00
parent d47967e03d
commit 3e3af3bbf3
11 changed files with 96 additions and 108 deletions

View File

@ -81,6 +81,8 @@ import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppPrefs;
import org.fdroid.fdroid.data.AppPrefsProvider;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.InstalledAppProvider;
import org.fdroid.fdroid.data.RepoProvider;
@ -317,8 +319,7 @@ public class AppDetails extends AppCompatActivity {
private String activeDownloadUrlString;
private LocalBroadcastManager localBroadcastManager;
private boolean startingIgnoreAll;
private int startingIgnoreThis;
private AppPrefs startingPrefs;
private final Context context = this;
@ -472,10 +473,9 @@ public class AppDetails extends AppCompatActivity {
.edit()
.putString(getPackageNameFromIntent(getIntent()), activeDownloadUrlString)
.apply();
if (app != null && (app.ignoreAllUpdates != startingIgnoreAll
|| app.ignoreThisUpdate != startingIgnoreThis)) {
if (app != null && !app.getPrefs(this).equals(startingPrefs)) {
Utils.debugLog(TAG, "Updating 'ignore updates', as it has changed since we started the activity...");
setIgnoreUpdates(app.packageName, app.ignoreAllUpdates, app.ignoreThisUpdate);
AppPrefsProvider.Helper.update(this, app, app.getPrefs(this));
}
unregisterDownloaderReceiver();
}
@ -646,18 +646,6 @@ public class AppDetails extends AppCompatActivity {
supportInvalidateOptionsMenu();
}
private void setIgnoreUpdates(String packageName, boolean ignoreAll, int ignoreVersionCode) {
Uri uri = AppProvider.getContentUri(packageName);
ContentValues values = new ContentValues(2);
values.put(Schema.AppTable.Cols.IGNORE_ALLUPDATES, ignoreAll ? 1 : 0);
values.put(Schema.AppTable.Cols.IGNORE_THISUPDATE, ignoreVersionCode);
getContentResolver().update(uri, values, null, null);
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
return new ConfigurationChangeHelper(activeDownloadUrlString, app);
@ -711,8 +699,7 @@ public class AppDetails extends AppCompatActivity {
app = newApp;
startingIgnoreAll = app.ignoreAllUpdates;
startingIgnoreThis = app.ignoreThisUpdate;
startingPrefs = app.getPrefs(this).createClone();
}
private void refreshApkList() {
@ -733,7 +720,7 @@ public class AppDetails extends AppCompatActivity {
return true;
}
if (packageManager.getLaunchIntentForPackage(app.packageName) != null && app.canAndWantToUpdate()) {
if (packageManager.getLaunchIntentForPackage(app.packageName) != null && app.canAndWantToUpdate(this)) {
MenuItemCompat.setShowAsAction(menu.add(
Menu.NONE, LAUNCH, 1, R.string.menu_launch)
.setIcon(R.drawable.ic_play_arrow_white),
@ -758,13 +745,13 @@ public class AppDetails extends AppCompatActivity {
menu.add(Menu.NONE, IGNOREALL, 2, R.string.menu_ignore_all)
.setIcon(R.drawable.ic_do_not_disturb_white)
.setCheckable(true)
.setChecked(app.ignoreAllUpdates);
.setChecked(app.getPrefs(context).ignoreAllUpdates);
if (app.hasUpdates()) {
menu.add(Menu.NONE, IGNORETHIS, 2, R.string.menu_ignore_this)
.setIcon(R.drawable.ic_do_not_disturb_white)
.setCheckable(true)
.setChecked(app.ignoreThisUpdate >= app.suggestedVersionCode);
.setChecked(app.getPrefs(context).ignoreThisUpdate >= app.suggestedVersionCode);
}
// Ignore on devices without Bluetooth
@ -880,17 +867,17 @@ public class AppDetails extends AppCompatActivity {
return true;
case IGNOREALL:
app.ignoreAllUpdates ^= true;
item.setChecked(app.ignoreAllUpdates);
app.getPrefs(this).ignoreAllUpdates ^= true;
item.setChecked(app.getPrefs(this).ignoreAllUpdates);
return true;
case IGNORETHIS:
if (app.ignoreThisUpdate >= app.suggestedVersionCode) {
app.ignoreThisUpdate = 0;
if (app.getPrefs(this).ignoreThisUpdate >= app.suggestedVersionCode) {
app.getPrefs(this).ignoreThisUpdate = 0;
} else {
app.ignoreThisUpdate = app.suggestedVersionCode;
app.getPrefs(this).ignoreThisUpdate = app.suggestedVersionCode;
}
item.setChecked(app.ignoreThisUpdate > 0);
item.setChecked(app.getPrefs(this).ignoreThisUpdate > 0);
return true;
case SEND_VIA_BLUETOOTH:
@ -1600,7 +1587,7 @@ public class AppDetails extends AppCompatActivity {
installed = true;
statusView.setText(getString(R.string.details_installed, app.installedVersionName));
NfcHelper.setAndroidBeam(appDetails, app.packageName);
if (app.canAndWantToUpdate()) {
if (app.canAndWantToUpdate(appDetails)) {
updateWanted = true;
btMain.setText(R.string.menu_upgrade);
} else {

View File

@ -103,15 +103,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
*/
public String[] requirements;
/**
* True if all updates for this app are to be ignored
*/
public boolean ignoreAllUpdates;
/**
* True if the current update for this app is to be ignored
*/
public int ignoreThisUpdate;
private AppPrefs prefs;
/**
* To be displayed at 48dp (x1.0)
@ -233,12 +225,6 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
case Cols.REQUIREMENTS:
requirements = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.IGNORE_ALLUPDATES:
ignoreAllUpdates = cursor.getInt(i) == 1;
break;
case Cols.IGNORE_THISUPDATE:
ignoreThisUpdate = cursor.getInt(i);
break;
case Cols.ICON_URL:
iconUrl = cursor.getString(i);
break;
@ -471,8 +457,6 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
values.put(Cols.ANTI_FEATURES, Utils.serializeCommaSeparatedString(antiFeatures));
values.put(Cols.REQUIREMENTS, Utils.serializeCommaSeparatedString(requirements));
values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0);
values.put(Cols.IGNORE_ALLUPDATES, ignoreAllUpdates ? 1 : 0);
values.put(Cols.IGNORE_THISUPDATE, ignoreThisUpdate);
return values;
}
@ -492,13 +476,21 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
return updates;
}
public AppPrefs getPrefs(Context context) {
if (prefs == null) {
prefs = AppPrefsProvider.Helper.getPrefsOrDefault(context, this);
}
return prefs;
}
/**
* True if there are new versions (apks) available and the user wants
* to be notified about them
*/
public boolean canAndWantToUpdate() {
public boolean canAndWantToUpdate(Context context) {
boolean canUpdate = hasUpdates();
boolean wantsUpdate = !ignoreAllUpdates && ignoreThisUpdate < suggestedVersionCode;
AppPrefs prefs = getPrefs(context);
boolean wantsUpdate = !prefs.ignoreAllUpdates && prefs.ignoreThisUpdate < suggestedVersionCode;
return canUpdate && wantsUpdate && !isFiltered();
}
@ -591,8 +583,6 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
dest.writeStringArray(this.categories);
dest.writeStringArray(this.antiFeatures);
dest.writeStringArray(this.requirements);
dest.writeByte(this.ignoreAllUpdates ? (byte) 1 : (byte) 0);
dest.writeInt(this.ignoreThisUpdate);
dest.writeString(this.iconUrl);
dest.writeString(this.iconUrlLarge);
dest.writeString(this.installedVersionName);
@ -631,8 +621,6 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
this.categories = in.createStringArray();
this.antiFeatures = in.createStringArray();
this.requirements = in.createStringArray();
this.ignoreAllUpdates = in.readByte() != 0;
this.ignoreThisUpdate = in.readInt();
this.iconUrl = in.readString();
this.iconUrlLarge = in.readString();
this.installedVersionName = in.readString();

View File

@ -5,12 +5,12 @@ public class AppPrefs extends ValueObject {
/**
* True if all updates for this app are to be ignored
*/
public final boolean ignoreAllUpdates;
public boolean ignoreAllUpdates;
/**
* True if the current update for this app is to be ignored
*/
public final int ignoreThisUpdate;
public int ignoreThisUpdate;
public AppPrefs(int ignoreThis, boolean ignoreAll) {
ignoreThisUpdate = ignoreThis;
@ -21,4 +21,14 @@ public class AppPrefs extends ValueObject {
return new AppPrefs(0, false);
}
@Override
public boolean equals(Object o) {
return o != null && o instanceof AppPrefs &&
((AppPrefs)o).ignoreAllUpdates == ignoreAllUpdates &&
((AppPrefs)o).ignoreThisUpdate == ignoreThisUpdate;
}
public AppPrefs createClone() {
return new AppPrefs(ignoreThisUpdate, ignoreAllUpdates);
}
}

View File

@ -14,6 +14,7 @@ import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.ApkTable;
import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.AppTable;
import org.fdroid.fdroid.data.Schema.AppTable.Cols;
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
@ -186,6 +187,7 @@ public class AppProvider extends FDroidProvider {
protected static class AppQuerySelection extends QuerySelection {
private boolean naturalJoinToInstalled;
private boolean leftJoinPrefs;
AppQuerySelection() {
// The same as no selection, because "1" will always resolve to true when executing the SQL query.
@ -217,12 +219,25 @@ public class AppProvider extends FDroidProvider {
return this;
}
public boolean leftJoinToPrefs() {
return leftJoinPrefs;
}
public AppQuerySelection requireLeftJoinPrefs() {
leftJoinPrefs = true;
return this;
}
public AppQuerySelection add(AppQuerySelection query) {
QuerySelection both = super.add(query);
AppQuerySelection bothWithJoin = new AppQuerySelection(both.getSelection(), both.getArgs());
if (this.naturalJoinToInstalled() || query.naturalJoinToInstalled()) {
bothWithJoin.requireNaturalInstalledTable();
}
if (this.leftJoinToPrefs() || query.leftJoinToPrefs()) {
bothWithJoin.requireLeftJoinPrefs();
}
return bothWithJoin;
}
@ -232,6 +247,7 @@ public class AppProvider extends FDroidProvider {
private boolean isSuggestedApkTableAdded;
private boolean requiresInstalledTable;
private boolean requiresLeftJoinToPrefs;
private boolean categoryFieldAdded;
private boolean countFieldAppended;
@ -262,6 +278,9 @@ public class AppProvider extends FDroidProvider {
if (selection.naturalJoinToInstalled()) {
naturalJoinToInstalledTable();
}
if (selection.leftJoinToPrefs()) {
leftJoinToPrefs();
}
}
// TODO: What if the selection requires a natural join, but we first get a left join
@ -276,6 +295,16 @@ public class AppProvider extends FDroidProvider {
}
}
public void leftJoinToPrefs() {
if (!requiresLeftJoinToPrefs) {
leftJoin(
AppPrefsTable.NAME,
"prefs",
"prefs." + AppPrefsTable.Cols.APP_ID + " = " + getTableName() + "." + Cols.ROW_ID);
requiresLeftJoinToPrefs = true;
}
}
public void leftJoinToInstalledTable() {
if (!requiresInstalledTable) {
leftJoin(
@ -527,11 +556,16 @@ public class AppProvider extends FDroidProvider {
private AppQuerySelection queryCanUpdate() {
final String app = getTableName();
final String ignoreCurrent = app + "." + Cols.IGNORE_THISUPDATE + "!= " + app + "." + Cols.SUGGESTED_VERSION_CODE;
final String ignoreAll = app + "." + Cols.IGNORE_ALLUPDATES + " != 1";
// Need to use COALESCE because the prefs join may not resolve any rows, which means the
// ignore* fields will be NULL. In that case, we want to instead use a default value of 0.
final String ignoreCurrent = " COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ", 0) != " + app + "." + Cols.SUGGESTED_VERSION_CODE;
final String ignoreAll = "COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_ALL_UPDATES + ", 0) != 1";
final String ignore = " (" + ignoreCurrent + " AND " + ignoreAll + ") ";
final String where = ignore + " AND " + app + "." + Cols.SUGGESTED_VERSION_CODE + " > installed." + InstalledAppTable.Cols.VERSION_CODE;
return new AppQuerySelection(where).requireNaturalInstalledTable();
return new AppQuerySelection(where).requireNaturalInstalledTable().requireLeftJoinPrefs();
}
private AppQuerySelection queryRepo(long repoId) {
@ -604,9 +638,9 @@ public class AppProvider extends FDroidProvider {
private AppQuerySelection queryIgnored() {
final String table = getTableName();
final String selection = table + "." + Cols.IGNORE_ALLUPDATES + " = 1 OR " +
table + "." + Cols.IGNORE_THISUPDATE + " >= " + table + "." + Cols.SUGGESTED_VERSION_CODE;
return new AppQuerySelection(selection);
final String selection = "COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_ALL_UPDATES + ", 0) = 1 OR " +
"COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ", 0) >= " + table + "." + Cols.SUGGESTED_VERSION_CODE;
return new AppQuerySelection(selection).requireLeftJoinPrefs();
}
private AppQuerySelection queryExcludeSwap() {

View File

@ -96,8 +96,6 @@ class DBHelper extends SQLiteOpenHelper {
+ AppTable.Cols.ADDED + " string,"
+ AppTable.Cols.LAST_UPDATED + " string,"
+ AppTable.Cols.IS_COMPATIBLE + " int not null,"
+ AppTable.Cols.IGNORE_ALLUPDATES + " int not null,"
+ AppTable.Cols.IGNORE_THISUPDATE + " int not null,"
+ AppTable.Cols.ICON_URL + " text, "
+ AppTable.Cols.ICON_URL_LARGE + " text, "
+ "primary key(" + AppTable.Cols.PACKAGE_NAME + "));";

View File

@ -22,20 +22,6 @@ public class RepoPersister {
private static final String TAG = "RepoPersister";
/**
* When an app already exists in the db, and we are updating it on the off chance that some
* values changed in the index, some fields should not be updated. Rather, they should be
* ignored, because they were explicitly set by the user, and hence can't be automatically
* overridden by the index.
*
* NOTE: In the future, these attributes will be moved to a join table, so that the app table
* is essentially completely transient, and can be nuked at any time.
*/
private static final String[] APP_FIELDS_TO_IGNORE = {
Schema.AppTable.Cols.IGNORE_ALLUPDATES,
Schema.AppTable.Cols.IGNORE_THISUPDATE,
};
/**
* Crappy benchmark with a Nexus 4, Android 5.0 on a fairly crappy internet connection I get:
* * 25 = 37 seconds
@ -219,13 +205,7 @@ public class RepoPersister {
*/
private ContentProviderOperation updateExistingApp(App app) {
Uri uri = TempAppProvider.getAppUri(app);
ContentValues values = app.toContentValues();
for (final String toIgnore : APP_FIELDS_TO_IGNORE) {
if (values.containsKey(toIgnore)) {
values.remove(toIgnore);
}
}
return ContentProviderOperation.newUpdate(uri).withValues(values).build();
return ContentProviderOperation.newUpdate(uri).withValues(app.toContentValues()).build();
}
/**

View File

@ -59,8 +59,6 @@ public interface Schema {
String CATEGORIES = "categories";
String ANTI_FEATURES = "antiFeatures";
String REQUIREMENTS = "requirements";
String IGNORE_ALLUPDATES = "ignoreAllUpdates";
String IGNORE_THISUPDATE = "ignoreThisUpdate";
String ICON_URL = "iconUrl";
String ICON_URL_LARGE = "iconUrlLarge";
@ -79,8 +77,7 @@ public interface Schema {
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, IGNORE_ALLUPDATES,
IGNORE_THISUPDATE, ICON_URL, ICON_URL_LARGE,
CATEGORIES, ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE,
SUGGESTED_VERSION_CODE, SuggestedApk.VERSION_NAME,
InstalledApp.VERSION_CODE, InstalledApp.VERSION_NAME,
InstalledApp.SIGNATURE,

View File

@ -123,7 +123,7 @@ public abstract class AppListAdapter extends CursorAdapter {
final String installedVersionString = app.installedVersionName;
if (app.canAndWantToUpdate() && showStatusUpdate()) {
if (app.canAndWantToUpdate(mContext) && showStatusUpdate()) {
return String.format(upgradeFromTo,
installedVersionString, app.getSuggestedVersionName());
}

View File

@ -50,8 +50,6 @@ public abstract class AppListFragment extends ListFragment implements
AppTable.Cols.InstalledApp.VERSION_NAME,
AppTable.Cols.SuggestedApk.VERSION_NAME,
AppTable.Cols.SUGGESTED_VERSION_CODE,
AppTable.Cols.IGNORE_ALLUPDATES,
AppTable.Cols.IGNORE_THISUPDATE,
AppTable.Cols.REQUIREMENTS, // Needed for filtering apps that require root.
};

View File

@ -191,8 +191,6 @@ public class Assert {
values.put(AppTable.Cols.DESCRIPTION, "test description");
values.put(AppTable.Cols.LICENSE, "GPL?");
values.put(AppTable.Cols.IS_COMPATIBLE, 1);
values.put(AppTable.Cols.IGNORE_ALLUPDATES, 0);
values.put(AppTable.Cols.IGNORE_THISUPDATE, 0);
values.putAll(additionalValues);

View File

@ -89,9 +89,8 @@ public class AppProviderTest extends FDroidProviderTest {
boolean ignoreAll, int ignoreVercode) {
ContentValues values = new ContentValues(3);
values.put(Cols.SUGGESTED_VERSION_CODE, suggestedVercode);
values.put(Cols.IGNORE_ALLUPDATES, ignoreAll);
values.put(Cols.IGNORE_THISUPDATE, ignoreVercode);
insertApp(packageName, "App: " + packageName, values);
App app = insertApp(packageName, "App: " + packageName, values);
AppPrefsProvider.Helper.update(context, app, new AppPrefs(ignoreVercode, ignoreAll));
InstalledAppTestUtils.install(context, packageName, installedVercode, "v" + installedVercode);
}
@ -113,7 +112,7 @@ public class AppProviderTest extends FDroidProviderTest {
// Can't "update", although can "install"...
App notInstalled = AppProvider.Helper.findByPackageName(r, "not installed");
assertFalse(notInstalled.canAndWantToUpdate());
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");
@ -121,21 +120,21 @@ public class AppProviderTest extends FDroidProviderTest {
App installedAlreadyLatestIgnoreLatest = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore latest");
App installedAlreadyLatestIgnoreOld = AppProvider.Helper.findByPackageName(r, "installed, already latest, ignore old");
assertFalse(installedOnlyOneVersionAvailable.canAndWantToUpdate());
assertFalse(installedAlreadyLatestNoIgnore.canAndWantToUpdate());
assertFalse(installedAlreadyLatestIgnoreAll.canAndWantToUpdate());
assertFalse(installedAlreadyLatestIgnoreLatest.canAndWantToUpdate());
assertFalse(installedAlreadyLatestIgnoreOld.canAndWantToUpdate());
assertFalse(installedOnlyOneVersionAvailable.canAndWantToUpdate(context));
assertFalse(installedAlreadyLatestNoIgnore.canAndWantToUpdate(context));
assertFalse(installedAlreadyLatestIgnoreAll.canAndWantToUpdate(context));
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");
assertTrue(installedOldNoIgnore.canAndWantToUpdate());
assertFalse(installedOldIgnoreAll.canAndWantToUpdate());
assertFalse(installedOldIgnoreLatest.canAndWantToUpdate());
assertTrue(installedOldIgnoreNewerNotLatest.canAndWantToUpdate());
assertTrue(installedOldNoIgnore.canAndWantToUpdate(context));
assertFalse(installedOldIgnoreAll.canAndWantToUpdate(context));
assertFalse(installedOldIgnoreLatest.canAndWantToUpdate(context));
assertTrue(installedOldIgnoreNewerNotLatest.canAndWantToUpdate(context));
Cursor canUpdateCursor = r.query(AppProvider.getCanUpdateUri(), Cols.ALL, null, null, null);
assertNotNull(canUpdateCursor);
@ -348,7 +347,7 @@ public class AppProviderTest extends FDroidProviderTest {
insertApp(id, name, values);
}
public void insertApp(String id, String name, ContentValues additionalValues) {
public App insertApp(String id, String name, ContentValues additionalValues) {
ContentValues values = new ContentValues();
values.put(Cols.PACKAGE_NAME, id);
@ -359,13 +358,12 @@ public class AppProviderTest extends FDroidProviderTest {
values.put(Cols.DESCRIPTION, "test description");
values.put(Cols.LICENSE, "GPL?");
values.put(Cols.IS_COMPATIBLE, 1);
values.put(Cols.IGNORE_ALLUPDATES, 0);
values.put(Cols.IGNORE_THISUPDATE, 0);
values.putAll(additionalValues);
Uri uri = AppProvider.getContentUri();
contentResolver.insert(uri, values);
return AppProvider.Helper.findByPackageName(context.getContentResolver(), id);
}
}