From 7ca4ee65397e57f671b239cba25d29201b8dc9c3 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 27 May 2019 15:28:41 +0200 Subject: [PATCH 01/12] rename InstalledAppProvider method after what it returns: last update --- .../java/org/fdroid/fdroid/data/InstalledAppProvider.java | 2 +- .../org/fdroid/fdroid/data/InstalledAppProviderService.java | 2 +- .../java/org/fdroid/fdroid/data/InstalledAppProviderTest.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java index 52d2e49b5..c764dc278 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -32,7 +32,7 @@ public class InstalledAppProvider extends FDroidProvider { * @return The keys are the package names, and their corresponding values are * the {@link PackageInfo#lastUpdateTime last update time} in milliseconds. */ - public static Map all(Context context) { + public static Map lastUpdateTimes(Context context) { Map cachedInfo = new HashMap<>(); diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java index e827edca7..b001f97a7 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProviderService.java @@ -176,7 +176,7 @@ public class InstalledAppProviderService extends JobIntentService { */ public static void compareToPackageManager(Context context) { Utils.debugLog(TAG, "Comparing package manager to our installed app cache."); - Map cachedInfo = InstalledAppProvider.Helper.all(context); + Map cachedInfo = InstalledAppProvider.Helper.lastUpdateTimes(context); List packageInfoList = context.getPackageManager() .getInstalledPackages(PackageManager.GET_SIGNATURES); diff --git a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java index 616677fd4..37585b317 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/InstalledAppProviderTest.java @@ -35,7 +35,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest { @Test public void insertSingleApp() { - Map foundBefore = InstalledAppProvider.Helper.all(RuntimeEnvironment.application); + Map foundBefore = InstalledAppProvider.Helper.lastUpdateTimes(RuntimeEnvironment.application); assertEquals(foundBefore.size(), 0); ContentValues values = new ContentValues(); @@ -49,7 +49,7 @@ public class InstalledAppProviderTest extends FDroidProviderTest { values.put(Cols.SIGNATURE, "000111222333444555666777888999aaabbbcccdddeeefff"); contentResolver.insert(InstalledAppProvider.getContentUri(), values); - Map foundAfter = InstalledAppProvider.Helper.all(RuntimeEnvironment.application); + Map foundAfter = InstalledAppProvider.Helper.lastUpdateTimes(RuntimeEnvironment.application); assertEquals(1, foundAfter.size()); assertEquals(100000000L, foundAfter.get("org.example.test-app").longValue()); From 301c2fff2d8a89bebb4613b16578dd90a2aea7d6 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 28 May 2019 14:14:02 +0200 Subject: [PATCH 02/12] remove redundant preference constants --- .../fdroid/views/panic/PanicPreferencesFragment.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java index 937c1b330..3cfd8ca32 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java @@ -33,9 +33,7 @@ import java.util.ArrayList; public class PanicPreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final String PREF_EXIT = Preferences.PREF_PANIC_EXIT; private static final String PREF_APP = "pref_panic_app"; - private static final String PREF_HIDE = Preferences.PREF_PANIC_HIDE; private PackageManager pm; private ListPreference prefApp; @@ -47,9 +45,9 @@ public class PanicPreferencesFragment extends PreferenceFragment addPreferencesFromResource(R.xml.preferences_panic); pm = getActivity().getPackageManager(); - prefExit = (CheckBoxPreference) findPreference(PREF_EXIT); + prefExit = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_EXIT); prefApp = (ListPreference) findPreference(PREF_APP); - prefHide = (CheckBoxPreference) findPreference(PREF_HIDE); + prefHide = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_HIDE); prefHide.setTitle(getString(R.string.panic_hide_title, getString(R.string.app_name))); if (PanicResponder.checkForDisconnectIntent(getActivity())) { @@ -98,11 +96,13 @@ public class PanicPreferencesFragment extends PreferenceFragment @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(PREF_HIDE) && sharedPreferences.getBoolean(PREF_HIDE, false)) { + if (key.equals(Preferences.PREF_PANIC_HIDE) + && sharedPreferences.getBoolean(Preferences.PREF_PANIC_HIDE, false)) { showHideConfirmationDialog(); } // disable "hiding" if "exit" gets disabled - if (key.equals(PREF_EXIT) && !sharedPreferences.getBoolean(PREF_EXIT, true)) { + if (key.equals(Preferences.PREF_PANIC_EXIT) + && !sharedPreferences.getBoolean(Preferences.PREF_PANIC_EXIT, true)) { prefHide.setChecked(false); } } From b400df3ac3b01d7ee5b2fd1bcf6ac6f3cea71c41 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 14:50:30 +0200 Subject: [PATCH 03/12] remove unused 'projection' argument from ApkProvider.findApkFromAnyRepo() One small victory in the ever lasting battle against creeping complexity! --- .../org/fdroid/fdroid/data/ApkProvider.java | 9 ++------- .../org/fdroid/fdroid/data/ApkProviderTest.java | 17 ----------------- 2 files changed, 2 insertions(+), 24 deletions(-) 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 00e6645fe..8d128568a 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java @@ -93,18 +93,13 @@ public class ApkProvider extends FDroidProvider { } public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode) { - return findApkFromAnyRepo(context, packageName, versionCode, null, Cols.ALL); + return findApkFromAnyRepo(context, packageName, versionCode, null); } public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode, String signature) { - return findApkFromAnyRepo(context, packageName, versionCode, signature, Cols.ALL); - } - - public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode, - @Nullable String signature, String[] projection) { final Uri uri = getApkFromAnyRepoUri(packageName, versionCode, signature); - return findByUri(context, uri, projection); + return findByUri(context, uri, Cols.ALL); } public static Apk findByUri(Context context, Uri uri, String[] projection) { 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 e063e409b..7a62f82d1 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/ApkProviderTest.java @@ -392,23 +392,6 @@ public class ApkProviderTest extends FDroidProviderTest { assertEquals("xxxxyyyy", apk.hash); assertEquals("a hash type", apk.hashType); - String[] projection = { - Cols.Package.PACKAGE_NAME, - Cols.HASH, - }; - - Apk apkLessFields = ApkProvider.Helper.findApkFromAnyRepo(context, "com.example", 11, null, projection); - - assertNotNull(apkLessFields); - - assertEquals("com.example", apkLessFields.packageName); - assertEquals("xxxxyyyy", apkLessFields.hash); - - // Didn't ask for these fields, so should be their default values... - assertNull(apkLessFields.hashType); - assertNull(apkLessFields.versionName); - assertEquals(0, apkLessFields.versionCode); - Apk notFound = ApkProvider.Helper.findApkFromAnyRepo(context, "com.doesnt.exist", 1000); assertNull(notFound); } From 38123311660a24259f91e376221313ab039d0612 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 15:20:18 +0200 Subject: [PATCH 04/12] stop crashing if the suggested version ends up being bogus It is possible for repo operators to specify a bad CurrentVersionCode for an app that is also in another repo, and cause confusion in the suggested version calculation. Or if one repo's index is very out of date. This adds a fallback for these cases, so at least it'll stop the crash and attempt the user's requested install. --- .../org/fdroid/fdroid/data/ApkProvider.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) 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 8d128568a..4a6135a59 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java @@ -86,10 +86,28 @@ public class ApkProvider extends FDroidProvider { *
  • If installed, limit to apks signed by the same signer as the installed apk.
  • *
  • Otherwise, limit to apks signed by the "preferred" signer (see {@link App#preferredSigner}).
  • * + * If all else fails, try to return some {@link Apk} that will install something, + * rather than returning a null and triggering a {@link NullPointerException}. */ + @Nullable public static Apk findSuggestedApk(Context context, App app) { - return findApkFromAnyRepo(context, app.packageName, app.suggestedVersionCode, - app.getMostAppropriateSignature()); + String mostAppropriateSignature = app.getMostAppropriateSignature(); + Apk apk = findApkFromAnyRepo(context, app.packageName, app.suggestedVersionCode, + mostAppropriateSignature); + if (apk == null && (mostAppropriateSignature == null || !app.isInstalled(context))) { + List apks = findByPackageName(context, app.packageName); + for (Apk availableApk : apks) { + if (availableApk.sig.equals(mostAppropriateSignature)) { + apk = availableApk; + break; + } + } + if (apk == null && apks.size() > 0) { + apk = apks.get(0); + } + } + return apk; + } public static Apk findApkFromAnyRepo(Context context, String packageName, int versionCode) { From 515b5b08f042c854f9069427ac34616e9a529e52 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 28 May 2019 16:33:27 +0200 Subject: [PATCH 05/12] set up Panic preference to choose apps to uninstall on trigger --- app/src/full/AndroidManifest.xml | 4 + .../views/panic/PanicPreferencesFragment.java | 50 ++++++ .../panic/SelectInstalledAppListAdapter.java | 30 ++++ .../SelectInstalledAppListItemController.java | 37 +++++ .../panic/SelectInstalledAppsActivity.java | 144 ++++++++++++++++++ app/src/full/res/drawable/check.xml | 8 + app/src/full/res/xml/preferences_panic.xml | 6 + .../java/org/fdroid/fdroid/Preferences.java | 20 +++ .../views/apps/AppListItemController.java | 44 +++++- .../fdroid/views/apps/AppListItemState.java | 26 +++- .../installed/InstalledAppListAdapter.java | 6 +- .../res/layout/installed_app_list_item.xml | 10 ++ app/src/main/res/values/strings.xml | 8 +- 13 files changed, 383 insertions(+), 10 deletions(-) create mode 100644 app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListAdapter.java create mode 100644 app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListItemController.java create mode 100644 app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java create mode 100644 app/src/full/res/drawable/check.xml diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml index a39ec62e8..7835b76a9 100644 --- a/app/src/full/AndroidManifest.xml +++ b/app/src/full/AndroidManifest.xml @@ -104,6 +104,10 @@ + + wipeSet = Preferences.get().getPanicWipeSet(); + categoryAppsToUninstall.removeAll(); + if (Panic.PACKAGE_NAME_NONE.equals(prefApp.getValue())) { + categoryAppsToUninstall.setEnabled(false); + return; + } + categoryAppsToUninstall.setEnabled(true); + if (wipeSet.size() > 0) { + for (String packageName : wipeSet) { + Preference preference = new Preference(getActivity()); + preference.setSingleLineTitle(true); + preference.setIntent(intent); + categoryAppsToUninstall.addPreference(preference); + try { + preference.setTitle(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0))); + preference.setIcon(pm.getApplicationIcon(packageName)); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + preference.setTitle(packageName); + } + } + } else { + Preference preference = new Preference(getActivity()); + preference.setIntent(intent); + Drawable icon = getResources().getDrawable(R.drawable.ic_add_circle_outline_white); + icon.setColorFilter(new LightingColorFilter(0, getResources().getColor(R.color.swap_light_grey_icon))); + preference.setSingleLineTitle(true); + preference.setTitle(R.string.panic_add_apps_to_uninstall); + preference.setIcon(icon); + categoryAppsToUninstall.addPreference(preference); + } } @Override @@ -156,6 +204,7 @@ public class PanicPreferencesFragment extends PreferenceFragment // disable destructive panic actions prefHide.setEnabled(false); + showWipeList(); } else { // try to display connected panic app try { @@ -163,6 +212,7 @@ public class PanicPreferencesFragment extends PreferenceFragment prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0))); prefApp.setIcon(pm.getApplicationIcon(packageName)); prefHide.setEnabled(true); + showWipeList(); } catch (PackageManager.NameNotFoundException e) { // revert back to no app, just to be safe PanicResponder.setTriggerPackageName(getActivity(), Panic.PACKAGE_NAME_NONE); diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListAdapter.java b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListAdapter.java new file mode 100644 index 000000000..e0c49c927 --- /dev/null +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListAdapter.java @@ -0,0 +1,30 @@ +package org.fdroid.fdroid.views.panic; + +import android.app.Activity; +import android.support.annotation.NonNull; +import android.view.View; +import android.view.ViewGroup; +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.views.installed.InstalledAppListAdapter; +import org.fdroid.fdroid.views.installed.InstalledAppListItemController; + +import java.util.Set; + +public class SelectInstalledAppListAdapter extends InstalledAppListAdapter { + private final Set selectedApps; + + SelectInstalledAppListAdapter(Activity activity) { + super(activity); + Preferences prefs = Preferences.get(); + selectedApps = prefs.getPanicWipeSet(); + prefs.setPanicTmpSelectedSet(selectedApps); + } + + @NonNull + @Override + public InstalledAppListItemController onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = activity.getLayoutInflater().inflate(R.layout.installed_app_list_item, parent, false); + return new SelectInstalledAppListItemController(activity, view, selectedApps); + } +} diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListItemController.java b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListItemController.java new file mode 100644 index 000000000..eac2c9f2d --- /dev/null +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppListItemController.java @@ -0,0 +1,37 @@ +package org.fdroid.fdroid.views.panic; + +import android.app.Activity; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.View; +import org.fdroid.fdroid.AppUpdateStatusManager; +import org.fdroid.fdroid.data.App; +import org.fdroid.fdroid.views.apps.AppListItemState; +import org.fdroid.fdroid.views.installed.InstalledAppListItemController; + +import java.util.Set; + +/** + * Shows the currently installed apps as a selectable list. + */ +public class SelectInstalledAppListItemController extends InstalledAppListItemController { + + private final Set selectedApps; + + public SelectInstalledAppListItemController(Activity activity, View itemView, Set selectedApps) { + super(activity, itemView); + this.selectedApps = selectedApps; + } + + @NonNull + @Override + protected AppListItemState getCurrentViewState( + @NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) { + return new AppListItemState(app).setCheckBoxStatus(selectedApps.contains(app.packageName)); + } + + @Override + protected void onActionButtonPressed(App app) { + super.onActionButtonPressed(app); + } +} diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java new file mode 100644 index 000000000..3d222cff9 --- /dev/null +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com + * Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.fdroid.fdroid.views.panic; + +import android.annotation.SuppressLint; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.LightingColorFilter; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.data.AppProvider; +import org.fdroid.fdroid.data.Schema; +import org.fdroid.fdroid.views.installed.InstalledAppListAdapter; + +public class SelectInstalledAppsActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { + + private InstalledAppListAdapter adapter; + private RecyclerView appList; + private TextView emptyState; + private int checkId; + + private Preferences prefs; + + @Override + protected void onCreate(Bundle savedInstanceState) { + + ((FDroidApp) getApplication()).applyTheme(this); + super.onCreate(savedInstanceState); + + setContentView(R.layout.installed_apps_layout); + + Toolbar toolbar = findViewById(R.id.toolbar); + toolbar.setTitle(getString(R.string.panic_add_apps_to_uninstall)); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + adapter = new SelectInstalledAppListAdapter(this); + + appList = findViewById(R.id.app_list); + appList.setHasFixedSize(true); + appList.setLayoutManager(new LinearLayoutManager(this)); + appList.setAdapter(adapter); + + emptyState = findViewById(R.id.empty_state); + } + + @Override + protected void onResume() { + super.onResume(); + + prefs = Preferences.get(); + + // Starts a new or restarts an existing Loader in this manager + getSupportLoaderManager().restartLoader(0, null, this); + } + + @NonNull + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new CursorLoader( + this, + AppProvider.getInstalledUri(), + Schema.AppMetadataTable.Cols.ALL, + null, null, null); + } + + @Override + public void onLoadFinished(@NonNull Loader loader, Cursor cursor) { + adapter.setApps(cursor); + + if (adapter.getItemCount() == 0) { + appList.setVisibility(View.GONE); + emptyState.setVisibility(View.VISIBLE); + } else { + appList.setVisibility(View.VISIBLE); + emptyState.setVisibility(View.GONE); + } + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + adapter.setApps(null); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuItem menuItem = menu.add(R.string.menu_select_for_wipe); + menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + checkId = menuItem.getItemId(); + if (FDroidApp.isAppThemeLight()) { + Resources resources = getResources(); + Drawable icon = resources.getDrawable(R.drawable.check); + icon.setColorFilter(new LightingColorFilter(0xffffffff, resources.getColor(android.R.color.white))); + menuItem.setIcon(icon); + } else { + menuItem.setIcon(R.drawable.check); + } + return true; + } + + @SuppressLint("ApplySharedPref") + @Override + public boolean onOptionsItemSelected(MenuItem item) { + finish(); + if (item.getItemId() == checkId) { + prefs.setPanicWipeSet(prefs.getPanicTmpSelectedSet()); + return true; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/full/res/drawable/check.xml b/app/src/full/res/drawable/check.xml new file mode 100644 index 000000000..064f846ae --- /dev/null +++ b/app/src/full/res/drawable/check.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/full/res/xml/preferences_panic.xml b/app/src/full/res/xml/preferences_panic.xml index e26d3eabb..79f9925fc 100644 --- a/app/src/full/res/xml/preferences_panic.xml +++ b/app/src/full/res/xml/preferences_panic.xml @@ -28,4 +28,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 46dc29b3b..e129bb7db 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -35,10 +35,12 @@ import org.fdroid.fdroid.installer.PrivilegedInstaller; import org.fdroid.fdroid.net.ConnectivityMonitorService; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -107,6 +109,8 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh public static final String PREF_PREVENT_SCREENSHOTS = "preventScreenshots"; public static final String PREF_PANIC_EXIT = "pref_panic_exit"; public static final String PREF_PANIC_HIDE = "pref_panic_hide"; + public static final String PREF_PANIC_WIPE_SET = "panicWipeSet"; + public static final String PREF_PANIC_TMP_SELECTED_SET = "panicTmpSelectedSet"; public static final String PREF_HIDE_ON_LONG_PRESS_SEARCH = "hideOnLongPressSearch"; public static final String PREF_HIDE_ALL_NOTIFICATIONS = "hideAllNotifications"; public static final String PREF_SEND_VERSION_AND_UUID_TO_SERVERS = "sendVersionAndUUIDToServers"; @@ -515,6 +519,22 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh return preferences.getBoolean(PREF_HIDE_ON_LONG_PRESS_SEARCH, IGNORED_B); } + public Set getPanicTmpSelectedSet() { + return preferences.getStringSet(Preferences.PREF_PANIC_TMP_SELECTED_SET, Collections.emptySet()); + } + + public void setPanicTmpSelectedSet(Set selectedSet) { + preferences.edit().putStringSet(Preferences.PREF_PANIC_TMP_SELECTED_SET, selectedSet).apply(); + } + + public Set getPanicWipeSet() { + return preferences.getStringSet(Preferences.PREF_PANIC_WIPE_SET, Collections.emptySet()); + } + + public void setPanicWipeSet(Set selectedSet) { + preferences.edit().putStringSet(Preferences.PREF_PANIC_WIPE_SET, selectedSet).apply(); + } + /** * Preference for whitelabel builds that are meant to be entirely controlled * by the server, without user interaction, e.g. "appliances". diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java index ad434b619..e8825bbbf 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java @@ -22,15 +22,15 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewOutlineProvider; import android.widget.Button; +import android.widget.CheckBox; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; - import com.nostra13.universalimageloader.core.ImageLoader; - import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus; +import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Apk; @@ -45,6 +45,7 @@ import org.fdroid.fdroid.views.updates.UpdatesAdapter; import java.io.File; import java.util.Iterator; +import java.util.Set; /** * Supports the following layouts: @@ -64,6 +65,8 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { private static final String TAG = "AppListItemController"; + private static Preferences prefs; + protected final Activity activity; @NonNull @@ -97,6 +100,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { @Nullable private final Button secondaryButton; + @Nullable + private final CheckBox checkBox; + @Nullable private App currentApp; @@ -107,6 +113,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { public AppListItemController(final Activity activity, View itemView) { super(itemView); this.activity = activity; + if (prefs == null) { + prefs = Preferences.get(); + } installButton = (ImageView) itemView.findViewById(R.id.install); if (installButton != null) { @@ -145,6 +154,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { cancelButton = (ImageButton) itemView.findViewById(R.id.cancel_button); actionButton = (Button) itemView.findViewById(R.id.action_button); secondaryButton = (Button) itemView.findViewById(R.id.secondary_button); + checkBox = itemView.findViewById(R.id.checkbox); if (actionButton != null) { actionButton.setEnabled(true); @@ -220,9 +230,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { /** * Override to respond to the user swiping an app to dismiss it from the list. - * @param app The app that was swiped away - * @param updatesAdapter The adapter. Can be used for refreshing the adapter with adapter.refreshStatuses(). * + * @param app The app that was swiped away + * @param updatesAdapter The adapter. Can be used for refreshing the adapter with adapter.refreshStatuses(). * @see #canDismiss() This must also be overridden and should return true. */ protected void onDismissApp(@NonNull App app, UpdatesAdapter updatesAdapter) { @@ -328,6 +338,18 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { secondaryStatus.setText(statusText); } } + + if (checkBox != null) { + if (viewState.shouldShowCheckBox()) { + itemView.setOnClickListener(selectInstalledAppListener); + checkBox.setChecked(viewState.isCheckBoxChecked()); + checkBox.setVisibility(View.VISIBLE); + status.setVisibility(View.GONE); + secondaryStatus.setVisibility(View.GONE); + } else { + checkBox.setVisibility(View.GONE); + } + } } @NonNull @@ -533,4 +555,18 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { InstallManagerService.cancel(activity, currentStatus.getCanonicalUrl()); } + + private final View.OnClickListener selectInstalledAppListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + Set wipeSet = prefs.getPanicTmpSelectedSet(); + checkBox.toggle(); + if (checkBox.isChecked()) { + wipeSet.add(currentApp.packageName); + } else { + wipeSet.remove(currentApp.packageName); + } + prefs.setPanicTmpSelectedSet(wipeSet); + } + }; } diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java index 2fef4de81..464626fe2 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java @@ -2,12 +2,12 @@ package org.fdroid.fdroid.views.apps; import android.support.annotation.NonNull; import android.support.annotation.Nullable; - import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.App; /** * A dumb model which is used to specify what should/should not be shown in an {@link AppListItemController}. + * * @see AppListItemController and its subclasses. */ public class AppListItemState { @@ -20,6 +20,8 @@ public class AppListItemState { private int progressCurrent = -1; private int progressMax = -1; private boolean showInstallButton; + private boolean showCheckBox; + private boolean isCheckBoxChecked; public AppListItemState(@NonNull App app) { this.app = app; @@ -63,6 +65,9 @@ public class AppListItemState { @Nullable public CharSequence getMainText() { + if (showCheckBox) { + return app.name; + } return mainText != null ? mainText : Utils.formatAppNameAndSummary(app.name, app.summary); @@ -113,4 +118,23 @@ public class AppListItemState { public CharSequence getSecondaryStatusText() { return secondaryStatusText; } + + public boolean shouldShowCheckBox() { + return showCheckBox; + } + + public boolean isCheckBoxChecked() { + return isCheckBoxChecked; + } + + /** + * Enable the {@link android.widget.CheckBox} display and set the on/off status + * e.g. {@link android.widget.CheckBox#isChecked()} + */ + public AppListItemState setCheckBoxStatus(boolean checked) { + this.showCheckBox = true; + this.isCheckBoxChecked = checked; + return this; + } + } diff --git a/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java index 0667afb96..b5ca5d692 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java @@ -11,14 +11,14 @@ import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.Schema; -class InstalledAppListAdapter extends RecyclerView.Adapter { +public class InstalledAppListAdapter extends RecyclerView.Adapter { - private final Activity activity; + protected final Activity activity; @Nullable private Cursor cursor; - InstalledAppListAdapter(Activity activity) { + protected InstalledAppListAdapter(Activity activity) { this.activity = activity; setHasStableIds(true); } diff --git a/app/src/main/res/layout/installed_app_list_item.xml b/app/src/main/res/layout/installed_app_list_item.xml index e65779c86..87b44a3a4 100644 --- a/app/src/main/res/layout/installed_app_list_item.xml +++ b/app/src/main/res/layout/installed_app_list_item.xml @@ -71,4 +71,14 @@ android:layout_marginStart="8dp" android:layout_marginLeft="8dp" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4c32706a1..e67a1b3b6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -206,6 +206,7 @@ This often occurs with apps installed via Google Play or other sources, if they Install Uninstall + Select for wipe Ignore All Updates Ignore This Update Website @@ -323,9 +324,12 @@ This often occurs with apps installed via Google Play or other sources, if they Panic button settings Actions to be taken in case of emergency - Exit App - App will be closed + Exit app + This app will be closed Destructive Actions + Will be uninstalled and all data deleted + Apps to be uninstalled and all data wiped + Add apps to be uninstalled and wiped Hide %s App will hide itself Remember how to restore From d762e187dca95292631f19fd5151fee492a953dc Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 17:17:17 +0200 Subject: [PATCH 06/12] update to PanicKit v1.0 --- app/build.gradle | 2 +- .../org/fdroid/fdroid/views/panic/PanicResponderActivity.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 47fa26d11..3269d24f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -148,7 +148,7 @@ dependencies { implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' implementation 'com.google.zxing:core:3.3.3' implementation 'info.guardianproject.netcipher:netcipher:2.0.0-beta1' - implementation 'info.guardianproject.panic:panic:0.5' + implementation 'info.guardianproject.panic:panic:1.0' implementation 'commons-io:commons-io:2.6' implementation 'commons-net:commons-net:3.6' implementation 'ch.acra:acra:4.9.1' diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java index d0c18e6ab..cd74bd0d9 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java @@ -21,7 +21,7 @@ public class PanicResponderActivity extends AppCompatActivity { super.onCreate(savedInstanceState); Intent intent = getIntent(); - if (intent == null || !Panic.isTriggerIntent(intent)) { + if (!Panic.isTriggerIntent(intent)) { finish(); return; } From 72f5398b7903e42fe4de841269ac9e1a768979af Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 17:20:09 +0200 Subject: [PATCH 07/12] panic: uninstall apps based destructive panic trigger This will uninstall the list of apps that the user has setup in the Panic Settings if Privileged Extension is installed. This also requires that the user set up a trusted connection between a panic trigger app (e.g. Ripple) and F-Droid. --- .../views/panic/PanicResponderActivity.java | 106 +++++++++++++++--- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java index cd74bd0d9..df139905c 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java @@ -1,17 +1,41 @@ package org.fdroid.fdroid.views.panic; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.util.Log; - -import org.fdroid.fdroid.Preferences; -import org.fdroid.fdroid.views.hiding.HidingManager; - import info.guardianproject.panic.Panic; import info.guardianproject.panic.PanicResponder; +import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.data.Apk; +import org.fdroid.fdroid.data.InstalledApp; +import org.fdroid.fdroid.data.InstalledAppProvider; +import org.fdroid.fdroid.installer.Installer; +import org.fdroid.fdroid.installer.InstallerService; +import org.fdroid.fdroid.installer.PrivilegedInstaller; +import org.fdroid.fdroid.views.hiding.HidingManager; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This {@link Activity} is purely to run events in response to a panic trigger. + * It needs to be an {@code Activity} rather than a {@link android.app.Service} + * so that it can fetch some of the required information about what sent the + * {@link Intent}. This is therefore an {@code Activity} without any UI, which + * is a special case in Android. All the code must be in + * {@link #onCreate(Bundle)} and {@link #finish()} must be called at the end of + * that method. + * + * @see PanicResponder#receivedTriggerFromConnectedApp(Activity) + */ public class PanicResponderActivity extends AppCompatActivity { private static final String TAG = PanicResponderActivity.class.getSimpleName(); @@ -29,12 +53,65 @@ public class PanicResponderActivity extends AppCompatActivity { // received intent from panic app Log.i(TAG, "Received Panic Trigger..."); - Preferences preferences = Preferences.get(); + final Preferences preferences = Preferences.get(); - if (PanicResponder.receivedTriggerFromConnectedApp(this)) { - Log.i(TAG, "Panic Trigger came from connected app"); + boolean receivedTriggerFromConnectedApp = PanicResponder.receivedTriggerFromConnectedApp(this); + final boolean runningAppUninstalls = PrivilegedInstaller.isDefault(this); - // Performing destructive panic responses + ArrayList wipeList = new ArrayList<>(preferences.getPanicWipeSet()); + preferences.setPanicWipeSet(Collections.emptySet()); + preferences.setPanicTmpSelectedSet(Collections.emptySet()); + + if (receivedTriggerFromConnectedApp && runningAppUninstalls && wipeList.size() > 0) { + + // if this app (e.g. F-Droid) is to be deleted, do it last + if (wipeList.contains(getPackageName())) { + wipeList.remove(getPackageName()); + wipeList.add(getPackageName()); + } + + final Context context = this; + final CountDownLatch latch = new CountDownLatch(1); + final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + final String lastToUninstall = wipeList.get(wipeList.size() - 1); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch ((intent.getAction())) { + case Installer.ACTION_UNINSTALL_INTERRUPTED: + case Installer.ACTION_UNINSTALL_COMPLETE: + latch.countDown(); + break; + } + } + }; + lbm.registerReceiver(receiver, Installer.getUninstallIntentFilter(lastToUninstall)); + + for (String packageName : wipeList) { + InstalledApp installedApp = InstalledAppProvider.Helper.findByPackageName(context, packageName); + InstallerService.uninstall(context, new Apk(installedApp)); + } + + // wait for apps to uninstall before triggering final responses + new Thread() { + @Override + public void run() { + try { + latch.await(10, TimeUnit.MINUTES); + } catch (InterruptedException e) { + // ignored + } + lbm.unregisterReceiver(receiver); + if (preferences.panicHide()) { + HidingManager.hide(context); + } + if (preferences.panicExit()) { + exitAndClear(); + } + } + }.start(); + } else if (receivedTriggerFromConnectedApp) { + // Performing destructive panic response if (preferences.panicHide()) { Log.i(TAG, "Hiding app..."); HidingManager.hide(this); @@ -42,13 +119,16 @@ public class PanicResponderActivity extends AppCompatActivity { } // exit and clear, if not deactivated - if (preferences.panicExit()) { - ExitActivity.exitAndRemoveFromRecentApps(this); - if (Build.VERSION.SDK_INT >= 21) { - finishAndRemoveTask(); - } + if (!runningAppUninstalls && preferences.panicExit()) { + exitAndClear(); } finish(); } + private void exitAndClear() { + ExitActivity.exitAndRemoveFromRecentApps(this); + if (Build.VERSION.SDK_INT >= 21) { + finishAndRemoveTask(); + } + } } From 3b53af0657fd4e494a2fbc8188996331063d73d6 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 21:00:41 +0200 Subject: [PATCH 08/12] panic: add destructive option to reset repos to defaults --- .../views/panic/PanicPreferencesFragment.java | 7 +++ .../views/panic/PanicResponderActivity.java | 40 ++++++++++++++ app/src/full/res/xml/preferences_panic.xml | 7 +++ .../java/org/fdroid/fdroid/Preferences.java | 5 ++ app/src/main/res/values/strings.xml | 2 + .../fdroid/data/FDroidProviderTest.java | 7 +++ .../fdroid/fdroid/data/RepoProviderTest.java | 7 --- .../fdroid/updater/Issue763MultiRepo.java | 6 --- .../panic/PanicResponderActivityTest.java | 53 +++++++++++++++++++ 9 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 app/src/testFull/java/org/fdroid/fdroid/views/panic/PanicResponderActivityTest.java diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java index 330630dd7..a0b111821 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java @@ -43,6 +43,7 @@ public class PanicPreferencesFragment extends PreferenceFragment private ListPreference prefApp; private CheckBoxPreference prefExit; private CheckBoxPreference prefHide; + private CheckBoxPreference prefResetRepos; private PreferenceCategory categoryAppsToUninstall; @Override @@ -54,6 +55,7 @@ public class PanicPreferencesFragment extends PreferenceFragment prefApp = (ListPreference) findPreference(PREF_APP); prefHide = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_HIDE); prefHide.setTitle(getString(R.string.panic_hide_title, getString(R.string.app_name))); + prefResetRepos = (CheckBoxPreference) findPreference(Preferences.PREF_PANIC_RESET_REPOS); categoryAppsToUninstall = (PreferenceCategory) findPreference("pref_panic_apps_to_uninstall"); if (PanicResponder.checkForDisconnectIntent(getActivity())) { @@ -77,9 +79,12 @@ public class PanicPreferencesFragment extends PreferenceFragment if (packageName.equals(Panic.PACKAGE_NAME_NONE)) { prefHide.setChecked(false); prefHide.setEnabled(false); + prefResetRepos.setChecked(false); + prefResetRepos.setEnabled(false); getActivity().setResult(Activity.RESULT_CANCELED); } else { prefHide.setEnabled(true); + prefResetRepos.setEnabled(true); } showPanicApp(packageName); return true; @@ -212,6 +217,7 @@ public class PanicPreferencesFragment extends PreferenceFragment prefApp.setSummary(pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0))); prefApp.setIcon(pm.getApplicationIcon(packageName)); prefHide.setEnabled(true); + prefResetRepos.setEnabled(true); showWipeList(); } catch (PackageManager.NameNotFoundException e) { // revert back to no app, just to be safe @@ -293,6 +299,7 @@ public class PanicPreferencesFragment extends PreferenceFragment @Override public void onCancel(DialogInterface dialogInterface) { prefHide.setChecked(false); + prefResetRepos.setChecked(false); } }); builder.setView(R.layout.dialog_app_hiding); diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java index df139905c..6f3c9d9c6 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicResponderActivity.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.views.panic; import android.app.Activity; import android.content.BroadcastReceiver; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -13,8 +14,12 @@ import info.guardianproject.panic.Panic; import info.guardianproject.panic.PanicResponder; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.data.Apk; +import org.fdroid.fdroid.data.DBHelper; import org.fdroid.fdroid.data.InstalledApp; import org.fdroid.fdroid.data.InstalledAppProvider; +import org.fdroid.fdroid.data.Repo; +import org.fdroid.fdroid.data.RepoProvider; +import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.InstallerService; import org.fdroid.fdroid.installer.PrivilegedInstaller; @@ -22,6 +27,8 @@ import org.fdroid.fdroid.views.hiding.HidingManager; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -102,6 +109,9 @@ public class PanicResponderActivity extends AppCompatActivity { // ignored } lbm.unregisterReceiver(receiver); + if (preferences.panicResetRepos()) { + resetRepos(context); + } if (preferences.panicHide()) { HidingManager.hide(context); } @@ -111,6 +121,9 @@ public class PanicResponderActivity extends AppCompatActivity { } }.start(); } else if (receivedTriggerFromConnectedApp) { + if (preferences.panicResetRepos()) { + resetRepos(this); + } // Performing destructive panic response if (preferences.panicHide()) { Log.i(TAG, "Hiding app..."); @@ -125,6 +138,33 @@ public class PanicResponderActivity extends AppCompatActivity { finish(); } + static void resetRepos(Context context) { + HashSet enabledAddresses = new HashSet<>(); + HashSet disabledAddresses = new HashSet<>(); + String[] defaultReposItems = DBHelper.loadInitialRepos(context).toArray(new String[0]); + for (int i = 1; i < defaultReposItems.length; i += DBHelper.REPO_XML_ITEM_COUNT) { + if ("1".equals(defaultReposItems[i + 3])) { + enabledAddresses.add(defaultReposItems[i]); + } else { + disabledAddresses.add(defaultReposItems[i]); + } + } + + List repos = RepoProvider.Helper.all(context); + for (Repo repo : repos) { + ContentValues values = new ContentValues(1); + if (enabledAddresses.contains(repo.address)) { + values.put(Schema.RepoTable.Cols.IN_USE, true); + RepoProvider.Helper.update(context, repo, values); + } else if (disabledAddresses.contains(repo.address)) { + values.put(Schema.RepoTable.Cols.IN_USE, false); + RepoProvider.Helper.update(context, repo, values); + } else { + RepoProvider.Helper.remove(context, repo.getId()); + } + } + } + private void exitAndClear() { ExitActivity.exitAndRemoveFromRecentApps(this); if (Build.VERSION.SDK_INT >= 21) { diff --git a/app/src/full/res/xml/preferences_panic.xml b/app/src/full/res/xml/preferences_panic.xml index 79f9925fc..b1ec1c1e6 100644 --- a/app/src/full/res/xml/preferences_panic.xml +++ b/app/src/full/res/xml/preferences_panic.xml @@ -26,6 +26,13 @@ android:summary="@string/panic_hide_summary" android:title="@string/panic_hide_title"/> + + App will hide itself Remember how to restore In a panic event, this will remove %1$s from the launcher. Only typing \"%2$d\" in the fake %3$s app can restore it. + Reset repos + Force the repo setup back to defaults Calculator diff --git a/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java index 416f564c5..54c3b5f9e 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/FDroidProviderTest.java @@ -1,5 +1,6 @@ package org.fdroid.fdroid.data; +import android.content.ContentValues; import android.content.ContextWrapper; import org.fdroid.fdroid.TestUtils; import org.junit.After; @@ -26,4 +27,10 @@ public abstract class FDroidProviderTest { DBHelper.clearDbHelperSingleton(); } + protected Repo setEnabled(Repo repo, boolean enabled) { + ContentValues enable = new ContentValues(1); + enable.put(Schema.RepoTable.Cols.IN_USE, enabled); + RepoProvider.Helper.update(context, repo, enable); + return RepoProvider.Helper.findByAddress(context, repo.address); + } } diff --git a/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java b/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java index de6326e4f..a77624248 100644 --- a/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java +++ b/app/src/test/java/org/fdroid/fdroid/data/RepoProviderTest.java @@ -77,13 +77,6 @@ public class RepoProviderTest extends FDroidProviderTest { assertEquals(0, RepoProvider.Helper.countEnabledRepos(context)); } - private Repo setEnabled(Repo repo, boolean enabled) { - ContentValues enable = new ContentValues(1); - enable.put(RepoTable.Cols.IN_USE, enabled); - RepoProvider.Helper.update(context, repo, enable); - return RepoProvider.Helper.findByAddress(context, repo.address); - } - @Test public void lastUpdated() { assertNull(RepoProvider.Helper.lastUpdate(context)); diff --git a/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java b/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java index 606c5b6e6..5eb61fa7b 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java @@ -35,12 +35,6 @@ public class Issue763MultiRepo extends MultiIndexUpdaterTest { antoxRepo = createRepo("Tox", "https://pkg.tox.chat/fdroid/repo", context, antoxCert); } - public void setEnabled(Repo repo, boolean enabled) { - ContentValues values = new ContentValues(1); - values.put(Schema.RepoTable.Cols.IN_USE, enabled ? 1 : 0); - RepoProvider.Helper.update(context, repo, values); - } - @Test public void antoxRepo() throws IndexUpdater.UpdateException { assertAntoxEmpty(); diff --git a/app/src/testFull/java/org/fdroid/fdroid/views/panic/PanicResponderActivityTest.java b/app/src/testFull/java/org/fdroid/fdroid/views/panic/PanicResponderActivityTest.java new file mode 100644 index 000000000..6c4fdca82 --- /dev/null +++ b/app/src/testFull/java/org/fdroid/fdroid/views/panic/PanicResponderActivityTest.java @@ -0,0 +1,53 @@ +package org.fdroid.fdroid.views.panic; + +import org.fdroid.fdroid.data.DBHelper; +import org.fdroid.fdroid.data.FDroidProviderTest; +import org.fdroid.fdroid.data.Repo; +import org.fdroid.fdroid.data.RepoProvider; +import org.fdroid.fdroid.data.RepoProviderTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +@RunWith(RobolectricTestRunner.class) +public class PanicResponderActivityTest extends FDroidProviderTest { + + /** + * The {@link DBHelper} class populates the default repos when it first creates a database. + * The names/URLs/signing certificates for these repos are all hard coded in the source/res. + */ + @Test + public void defaultRepos() { + List defaultRepos = RepoProvider.Helper.all(context); + assertEquals(defaultRepos.size(), 4); // based on app/src/main/res/default_repo.xml + + Repo gpRepo = RepoProvider.Helper.findByAddress(context, "https://guardianproject.info/fdroid/repo"); + setEnabled(gpRepo, true); + assertEquals(2, RepoProvider.Helper.countEnabledRepos(context)); + + PanicResponderActivity.resetRepos(context); + assertEquals(1, RepoProvider.Helper.countEnabledRepos(context)); + defaultRepos = RepoProvider.Helper.all(context); + assertEquals(4, defaultRepos.size()); + + RepoProviderTest.insertRepo( + context, + "https://mock-repo-1.example.com/fdroid/repo", + "Just a made up repo", + "ABCDEF1234567890", + "Mock Repo 1" + ); + defaultRepos = RepoProvider.Helper.all(context); + assertEquals(5, defaultRepos.size()); + assertEquals(2, RepoProvider.Helper.countEnabledRepos(context)); + + PanicResponderActivity.resetRepos(context); + defaultRepos = RepoProvider.Helper.all(context); + assertEquals(4, defaultRepos.size()); + assertEquals(1, RepoProvider.Helper.countEnabledRepos(context)); + } +} From 4c352aeadc5f1d7eea897452813da8281c07fd52 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 29 May 2019 23:20:16 +0200 Subject: [PATCH 09/12] color destructive panic prefs with red background --- .../panic/DestructiveCheckBoxPreference.java | 41 +++++++++++++++++++ .../views/panic/DestructivePreference.java | 36 ++++++++++++++++ .../views/panic/PanicPreferencesFragment.java | 2 +- app/src/full/res/values/colors.xml | 4 +- app/src/full/res/xml/preferences_panic.xml | 4 +- 5 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 app/src/full/java/org/fdroid/fdroid/views/panic/DestructiveCheckBoxPreference.java create mode 100644 app/src/full/java/org/fdroid/fdroid/views/panic/DestructivePreference.java diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/DestructiveCheckBoxPreference.java b/app/src/full/java/org/fdroid/fdroid/views/panic/DestructiveCheckBoxPreference.java new file mode 100644 index 000000000..4eb209880 --- /dev/null +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/DestructiveCheckBoxPreference.java @@ -0,0 +1,41 @@ +package org.fdroid.fdroid.views.panic; + +import android.content.Context; +import android.content.res.Resources; +import android.support.v7.preference.CheckBoxPreference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.R; + +public class DestructiveCheckBoxPreference extends CheckBoxPreference { + public DestructiveCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DestructiveCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public DestructiveCheckBoxPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DestructiveCheckBoxPreference(Context context) { + super(context); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + if (!holder.itemView.isEnabled()) { + return; + } + Resources resources = getContext().getResources(); + if (FDroidApp.isAppThemeLight()) { + holder.itemView.setBackgroundColor(resources.getColor(R.color.panic_destructive_light)); + } else { + holder.itemView.setBackgroundColor(resources.getColor(R.color.panic_destructive_dark)); + } + } +} diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/DestructivePreference.java b/app/src/full/java/org/fdroid/fdroid/views/panic/DestructivePreference.java new file mode 100644 index 000000000..990eff5bf --- /dev/null +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/DestructivePreference.java @@ -0,0 +1,36 @@ +package org.fdroid.fdroid.views.panic; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.R; + +public class DestructivePreference extends Preference { + public DestructivePreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DestructivePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public DestructivePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DestructivePreference(Context context) { + super(context); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + if (FDroidApp.isAppThemeLight()) { + holder.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.panic_destructive_light)); + } else { + holder.itemView.setBackgroundColor(getContext().getResources().getColor(R.color.panic_destructive_dark)); + } + } +} diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java index a0b111821..3eb275950 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/PanicPreferencesFragment.java @@ -117,7 +117,7 @@ public class PanicPreferencesFragment extends PreferenceFragment categoryAppsToUninstall.setEnabled(true); if (wipeSet.size() > 0) { for (String packageName : wipeSet) { - Preference preference = new Preference(getActivity()); + Preference preference = new DestructivePreference(getActivity()); preference.setSingleLineTitle(true); preference.setIntent(intent); categoryAppsToUninstall.addPreference(preference); diff --git a/app/src/full/res/values/colors.xml b/app/src/full/res/values/colors.xml index fe1fee70e..b7793017e 100644 --- a/app/src/full/res/values/colors.xml +++ b/app/src/full/res/values/colors.xml @@ -1,5 +1,5 @@ - + #ff7900 #27aae1 @@ -10,4 +10,6 @@ #4a4a4a #bbbbbb + #fff0f0 + #330000 diff --git a/app/src/full/res/xml/preferences_panic.xml b/app/src/full/res/xml/preferences_panic.xml index b1ec1c1e6..e512111be 100644 --- a/app/src/full/res/xml/preferences_panic.xml +++ b/app/src/full/res/xml/preferences_panic.xml @@ -19,14 +19,14 @@ android:title="@string/panic_app_setting_title" tools:icon="@drawable/ic_cancel"/> - - Date: Mon, 3 Jun 2019 15:35:09 +0200 Subject: [PATCH 10/12] InstalledAppProvider: use Android sematics for query methods Throughout Android, a null projection means return the full record. This is well documented. Also, null sortOrder means use default sort order. --- .../org/fdroid/fdroid/views/swap/SelectAppsView.java | 11 ++--------- .../org/fdroid/fdroid/data/InstalledAppProvider.java | 12 ++++++------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/views/swap/SelectAppsView.java b/app/src/full/java/org/fdroid/fdroid/views/swap/SelectAppsView.java index d45ffdb5f..38ecf5ba9 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/swap/SelectAppsView.java +++ b/app/src/full/java/org/fdroid/fdroid/views/swap/SelectAppsView.java @@ -30,7 +30,6 @@ import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.InstalledAppProvider; import org.fdroid.fdroid.data.Schema.InstalledAppTable; import org.fdroid.fdroid.localrepo.LocalRepoService; -import org.fdroid.fdroid.localrepo.SwapService; import org.fdroid.fdroid.localrepo.SwapView; public class SelectAppsView extends SwapView implements LoaderManager.LoaderCallbacks { @@ -61,7 +60,7 @@ public class SelectAppsView extends SwapView implements LoaderManager.LoaderCall listView = findViewById(R.id.list); adapter = new AppListAdapter(listView, getContext(), getContext().getContentResolver().query(InstalledAppProvider.getContentUri(), - InstalledAppTable.Cols.ALL, null, null, null)); + null, null, null, null)); listView.setAdapter(adapter); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); @@ -97,13 +96,7 @@ public class SelectAppsView extends SwapView implements LoaderManager.LoaderCall } else { uri = InstalledAppProvider.getSearchUri(currentFilterString); } - return new CursorLoader( - getActivity(), - uri, - InstalledAppTable.Cols.ALL, - null, - null, - InstalledAppTable.Cols.APPLICATION_LABEL); + return new CursorLoader(getActivity(), uri, null, null, null, null); } @Override diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java index c764dc278..548ad2fdb 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -37,8 +37,7 @@ public class InstalledAppProvider extends FDroidProvider { Map cachedInfo = new HashMap<>(); final Uri uri = InstalledAppProvider.getContentUri(); - final String[] projection = Cols.ALL; - Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); if (cursor != null) { if (cursor.getCount() > 0) { cursor.moveToFirst(); @@ -58,7 +57,7 @@ public class InstalledAppProvider extends FDroidProvider { @Nullable public static InstalledApp findByPackageName(Context context, String packageName) { - Cursor cursor = context.getContentResolver().query(getAppUri(packageName), Cols.ALL, null, null, null); + Cursor cursor = context.getContentResolver().query(getAppUri(packageName), null, null, null, null); if (cursor == null) { return null; } @@ -249,9 +248,10 @@ public class InstalledAppProvider extends FDroidProvider { } QueryBuilder query = new QueryBuilder(); - query.addFields(projection); - if (projection.length == 0) { - query.addField(Cols._ID); + if (projection == null || projection.length == 0) { + query.addFields(Cols.ALL); + } else { + query.addFields(projection); } query.addSelection(selection); query.addOrderBy(sortOrder); From bac0ae8f25a955b6cfbe70a12389a67dfb6146e4 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 11 Jun 2019 22:56:04 +0200 Subject: [PATCH 11/12] show all installed apps as possibilities for panic uninstall F-Droid should be able to uninstall any app, in theory, not just the apps that are listed in the index. This lays some groundwork for moving swap's SelectAppsView to the standard AppList elements used everywhere else. It also does a little bit towards getting rid of InstalledApp in favor of just reusing App. --- .../panic/SelectInstalledAppsActivity.java | 9 +--- .../fdroid/data/InstalledAppProvider.java | 52 ++++++++++++++++++- .../views/apps/AppListItemController.java | 11 +++- .../installed/InstalledAppListAdapter.java | 5 +- .../fdroid/data/InstalledAppProviderTest.java | 37 +++++++++++++ 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java index 3d222cff9..73ba4db50 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java @@ -40,8 +40,7 @@ import android.widget.TextView; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; -import org.fdroid.fdroid.data.AppProvider; -import org.fdroid.fdroid.data.Schema; +import org.fdroid.fdroid.data.InstalledAppProvider; import org.fdroid.fdroid.views.installed.InstalledAppListAdapter; public class SelectInstalledAppsActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { @@ -89,11 +88,7 @@ public class SelectInstalledAppsActivity extends AppCompatActivity implements Lo @NonNull @Override public Loader onCreateLoader(int id, Bundle args) { - return new CursorLoader( - this, - AppProvider.getInstalledUri(), - Schema.AppMetadataTable.Cols.ALL, - null, null, null); + return new CursorLoader(this, InstalledAppProvider.getAllAppsUri(), null, null, null, null); } @Override diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java index 548ad2fdb..5790a3117 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -15,9 +15,12 @@ import android.text.TextUtils; import android.util.Log; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.data.Schema.AppMetadataTable; import org.fdroid.fdroid.data.Schema.InstalledAppTable; import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols; +import org.fdroid.fdroid.data.Schema.PackageTable; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -28,6 +31,23 @@ public class InstalledAppProvider extends FDroidProvider { public static class Helper { + public static App[] all(Context context) { + ArrayList appList = new ArrayList<>(); + Cursor cursor = context.getContentResolver().query(InstalledAppProvider.getAllAppsUri(), + null, null, null, null); + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + appList.add(new App(cursor)); + cursor.moveToNext(); + } + } + cursor.close(); + } + return appList.toArray(new App[0]); + } + /** * @return The keys are the package names, and their corresponding values are * the {@link PackageInfo#lastUpdateTime last update time} in milliseconds. @@ -79,6 +99,8 @@ public class InstalledAppProvider extends FDroidProvider { private static final String PATH_SEARCH = "search"; private static final int CODE_SEARCH = CODE_SINGLE + 1; + private static final String PATH_ALL_APPS = "allApps"; + private static final int CODE_ALL_APPS = CODE_SEARCH + 1; private static final UriMatcher MATCHER = new UriMatcher(-1); @@ -99,6 +121,7 @@ public class InstalledAppProvider extends FDroidProvider { static { MATCHER.addURI(getAuthority(), null, CODE_LIST); MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", CODE_SEARCH); + MATCHER.addURI(getAuthority(), PATH_ALL_APPS, CODE_ALL_APPS); MATCHER.addURI(getAuthority(), "*", CODE_SINGLE); } @@ -106,6 +129,10 @@ public class InstalledAppProvider extends FDroidProvider { return Uri.parse("content://" + getAuthority()); } + public static Uri getAllAppsUri() { + return getContentUri().buildUpon().appendPath(PATH_ALL_APPS).build(); + } + /** * @return the {@link Uri} that points to a specific installed app */ @@ -228,6 +255,7 @@ public class InstalledAppProvider extends FDroidProvider { } QuerySelection selection = new QuerySelection(customSelection, selectionArgs); + QueryBuilder query = null; switch (MATCHER.match(uri)) { case CODE_LIST: selection = selectNotSystemSignature(selection); @@ -241,16 +269,30 @@ public class InstalledAppProvider extends FDroidProvider { selection = selection.add(querySearch(uri.getLastPathSegment())); break; + case CODE_ALL_APPS: + selection = selectNotSystemSignature(selection); + query = new QueryBuilder(); + query.addField(Cols._ID); + query.appendField(Cols.APPLICATION_LABEL, null, Schema.AppMetadataTable.Cols.NAME); + query.appendField(Cols.VERSION_CODE, null, AppMetadataTable.Cols.UPSTREAM_VERSION_CODE); + query.appendField(Cols.VERSION_NAME, null, AppMetadataTable.Cols.UPSTREAM_VERSION_NAME); + query.appendField(PackageTable.Cols.PACKAGE_NAME, PackageTable.NAME, + AppMetadataTable.Cols.Package.PACKAGE_NAME); + break; + default: String message = "Invalid URI for installed app content provider: " + uri; Log.e(TAG, message); throw new UnsupportedOperationException(message); } - QueryBuilder query = new QueryBuilder(); - if (projection == null || projection.length == 0) { + if (query != null) { // NOPMD + // the fields are already setup above + } else if (projection == null || projection.length == 0) { + query = new QueryBuilder(); query.addFields(Cols.ALL); } else { + query = new QueryBuilder(); query.addFields(projection); } query.addSelection(selection); @@ -279,6 +321,12 @@ public class InstalledAppProvider extends FDroidProvider { return count; } + /** + * {@link Cols.Package#NAME} is not included in the database here, because + * it is included only in the {@link PackageTable}, since there are large + * cross-table queries needed to handle the complexity of multiple repos + * potentially serving the same apps. + */ @Override public Uri insert(@NonNull Uri uri, ContentValues values) { diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java index e8825bbbf..0b87fc5c7 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java @@ -7,6 +7,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.graphics.Outline; import android.net.Uri; import android.os.Build; @@ -186,7 +187,15 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { public void bindModel(@NonNull App app) { currentApp = app; - ImageLoader.getInstance().displayImage(app.iconUrl, icon, Utils.getRepoAppDisplayImageOptions()); + if (app.iconUrl == null) { + try { + icon.setImageDrawable(activity.getPackageManager().getApplicationIcon(app.packageName)); + } catch (PackageManager.NameNotFoundException e) { + // ignored + } + } else { + ImageLoader.getInstance().displayImage(app.iconUrl, icon, Utils.getRepoAppDisplayImageOptions()); + } // Figures out the current install/update/download/etc status for the app we are viewing. // Then, asks the view to update itself to reflect this status. diff --git a/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java index b5ca5d692..57976a62b 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.views.installed; import android.app.Activity; import android.database.Cursor; +import android.provider.BaseColumns; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; @@ -9,7 +10,6 @@ import android.view.View; import android.view.ViewGroup; import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.App; -import org.fdroid.fdroid.data.Schema; public class InstalledAppListAdapter extends RecyclerView.Adapter { @@ -30,7 +30,8 @@ public class InstalledAppListAdapter extends RecyclerView.Adapter Date: Tue, 11 Jun 2019 23:48:05 +0200 Subject: [PATCH 12/12] code formatting to please checkstyle --- .../updater/AcceptableMultiIndexUpdaterTest.java | 1 - .../org/fdroid/fdroid/updater/AppIconsTest.java | 2 +- .../fdroid/updater/FDroidRepoUpdateTest.java | 1 - .../fdroid/fdroid/updater/Issue763MultiRepo.java | 9 ++++----- .../fdroid/updater/MultiIndexUpdaterTest.java | 5 ++--- .../updater/ProperMultiIndexUpdaterTest.java | 16 +++++++++++----- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/app/src/test/java/org/fdroid/fdroid/updater/AcceptableMultiIndexUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/updater/AcceptableMultiIndexUpdaterTest.java index cbec38da4..48b8c22c3 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/AcceptableMultiIndexUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/AcceptableMultiIndexUpdaterTest.java @@ -1,4 +1,3 @@ - package org.fdroid.fdroid.updater; import android.content.ContentValues; diff --git a/app/src/test/java/org/fdroid/fdroid/updater/AppIconsTest.java b/app/src/test/java/org/fdroid/fdroid/updater/AppIconsTest.java index e659549a6..195a1c262 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/AppIconsTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/AppIconsTest.java @@ -66,7 +66,7 @@ public class AppIconsTest extends MultiIndexUpdaterTest { private void assertIconUrl(String expectedUrl) { App app = AppProvider.Helper.findHighestPriorityMetadata(context.getContentResolver(), - "org.adaway", new String[] {Schema.AppMetadataTable.Cols.ICON_URL}); + "org.adaway", new String[]{Schema.AppMetadataTable.Cols.ICON_URL}); assertEquals(app.iconUrl, expectedUrl); } diff --git a/app/src/test/java/org/fdroid/fdroid/updater/FDroidRepoUpdateTest.java b/app/src/test/java/org/fdroid/fdroid/updater/FDroidRepoUpdateTest.java index 898f6d7fb..a332f4daf 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/FDroidRepoUpdateTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/FDroidRepoUpdateTest.java @@ -1,4 +1,3 @@ - package org.fdroid.fdroid.updater; import org.fdroid.fdroid.BuildConfig; diff --git a/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java b/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java index 5eb61fa7b..277d521ca 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/Issue763MultiRepo.java @@ -1,6 +1,5 @@ package org.fdroid.fdroid.updater; -import android.content.ContentValues; import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.IndexUpdater; import org.fdroid.fdroid.data.Apk; @@ -105,7 +104,7 @@ public class Issue763MultiRepo extends MultiIndexUpdaterTest { private void assertAntoxExists() { String packageName = "chat.tox.antox"; List actualApksAfterUpdate = ApkProvider.Helper.findByRepo(context, antoxRepo, Schema.ApkTable.Cols.ALL); - int[] expectedVersions = new int[] {15421}; + int[] expectedVersions = new int[]{15421}; assertApp(packageName, expectedVersions); assertApksExist(actualApksAfterUpdate, packageName, expectedVersions); @@ -115,17 +114,17 @@ public class Issue763MultiRepo extends MultiIndexUpdaterTest { List actualApksAfterUpdate = ApkProvider.Helper.findByRepo(context, microGRepo, Schema.ApkTable.Cols.ALL); String vendingPackage = "com.android.vending"; - int[] expectedVendingVersions = new int[] {1}; + int[] expectedVendingVersions = new int[]{1}; assertApp(vendingPackage, expectedVendingVersions); assertApksExist(actualApksAfterUpdate, vendingPackage, expectedVendingVersions); String gmsPackage = "com.google.android.gms"; - int[] expectedGmsVersions = new int[] {11059462, 10545451, 10545440, 10087438, 10087435, 9258259, 8492252, }; + int[] expectedGmsVersions = new int[]{11059462, 10545451, 10545440, 10087438, 10087435, 9258259, 8492252}; assertApp(gmsPackage, expectedGmsVersions); assertApksExist(actualApksAfterUpdate, gmsPackage, expectedGmsVersions); String gsfPackage = "com.google.android.gsf"; - int[] expectedGsfVersions = new int[] {8}; + int[] expectedGsfVersions = new int[]{8}; assertApp(gsfPackage, expectedGsfVersions); assertApksExist(actualApksAfterUpdate, gsfPackage, expectedGsfVersions); } diff --git a/app/src/test/java/org/fdroid/fdroid/updater/MultiIndexUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/updater/MultiIndexUpdaterTest.java index 2742dec5e..60f0cf9c4 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/MultiIndexUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/MultiIndexUpdaterTest.java @@ -1,14 +1,13 @@ - package org.fdroid.fdroid.updater; import android.content.ContentValues; import android.content.Context; import android.support.annotation.NonNull; import android.text.TextUtils; -import org.fdroid.fdroid.IndexV1Updater; -import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.IndexUpdater; import org.fdroid.fdroid.IndexUpdater.UpdateException; +import org.fdroid.fdroid.IndexV1Updater; +import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.TestUtils; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; diff --git a/app/src/test/java/org/fdroid/fdroid/updater/ProperMultiIndexUpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/updater/ProperMultiIndexUpdaterTest.java index 36b16d373..eab73ed39 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/ProperMultiIndexUpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/ProperMultiIndexUpdaterTest.java @@ -1,4 +1,3 @@ - package org.fdroid.fdroid.updater; import android.content.ContentValues; @@ -41,7 +40,8 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest { @Retention(RetentionPolicy.SOURCE) @StringDef({"Conflicting", "Normal"}) - public @interface RepoIdentifier { } + public @interface RepoIdentifier { + } @Test public void appsRemovedFromRepo() throws IndexUpdater.UpdateException { @@ -310,7 +310,9 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest { assertAdAwayMetadata(adaway, id); } - /** @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) */ + /** + * @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) + */ private void assertAdAwayMetadata(App adaway, @RepoIdentifier String id) { assertNotNull(adaway); assertEquals(String.format("AdAway", id), @@ -338,7 +340,9 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest { assertAdbMetadata(adb, id); } - /** @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) */ + /** + * @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) + */ private void assertAdbMetadata(App adb, @RepoIdentifier String id) { assertNotNull(adb); assertEquals("adbWireless", adb.name); @@ -355,7 +359,9 @@ public class ProperMultiIndexUpdaterTest extends MultiIndexUpdaterTest { assertCalendarMetadata(calendar, id); } - /** @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) */ + /** + * @see ProperMultiIndexUpdaterTest#assert2048Metadata(Repo, String) + */ private void assertCalendarMetadata(App calendar, @RepoIdentifier String id) { assertNotNull(calendar); assertEquals("Add to calendar",