From c9a6cc305111557b68b87f828b4d493f2c89cd49 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 31 Aug 2016 21:52:52 +0200 Subject: [PATCH 1/3] handle install broadcasts after InstallManagerService was killed If InstallManagerService was killed, it'll forget all of its state. If it is killed while an install process is running, and that install fails, InstallManagerService will receive a broadcast about the error but then it can't find anything about the app in question besides its download URL. That is enough to control the notification, but not enough to get the name of the app in question. This is a workaround by showing the APK filename when the app name cannot be found. Ideally, the packageName would somehow magically be delivered to InstallManagerService in this case, but the Installer stuff doesn't always have it to send. With android-23, there is getActiveNotifications(), which we might be able to use to stash the packageName and fetch it as needed. --- .../installer/InstallManagerService.java | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java index d8a5d8bc0..eb77f757e 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -16,7 +16,6 @@ import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; -import org.acra.ACRA; import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; @@ -249,22 +248,12 @@ public class InstallManagerService extends Service { // show notification if app details is not visible if (!TextUtils.isEmpty(errorMessage)) { - try { - // temp setup to debug https://gitlab.com/fdroid/fdroidclient/issues/698 - App app = getAppFromActive(downloadUrl); - - // show notification if app details is not visible - if (AppDetails.isAppVisible(app.packageName)) { - cancelNotification(downloadUrl); - } else { - String title = String.format( - getString(R.string.install_error_notify_title), - app.name); - notifyError(downloadUrl, title, errorMessage); - } - } catch (NullPointerException e) { //NOPMD - ACRA.getErrorReporter().handleException( - new IllegalStateException(errorMessage, e)); + App app = getAppFromActive(downloadUrl); + // show notification if app details is not visible + if (app != null && AppDetails.isAppVisible(app.packageName)) { + cancelNotification(downloadUrl); + } else { + notifyError(downloadUrl, app, errorMessage); } } removeFromActive(downloadUrl); @@ -358,9 +347,19 @@ public class InstallManagerService extends Service { notificationManager.notify(downloadUrlId, notification); } - private void notifyError(String urlString, String title, String text) { + private void notifyError(String urlString, App app, String text) { int downloadUrlId = urlString.hashCode(); + String name; + if (app == null) { + // if we have nothing else, show the APK filename + String path = Uri.parse(urlString).getPath(); + name = path.substring(path.lastIndexOf('/'), path.length()); + } else { + name = app.name; + } + String title = String.format(getString(R.string.install_error_notify_title), name); + Intent errorDialogIntent = new Intent(this, ErrorDialogActivity.class); errorDialogIntent.putExtra( ErrorDialogActivity.EXTRA_TITLE, title); @@ -397,8 +396,16 @@ public class InstallManagerService extends Service { ACTIVE_APPS.put(app.packageName, app); } + /** + * Always returns an {@link Apk} instance to avoid annoying null guards. + */ private static Apk getApkFromActive(String urlString) { - return ACTIVE_APKS.get(urlString); + Apk apk = ACTIVE_APKS.get(urlString); + if (apk == null) { + return new Apk(); + } else { + return apk; + } } /** From 530144bec64bf7b1f6f91d4da98544816722d2a3 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 1 Sep 2016 15:49:15 +0200 Subject: [PATCH 2/3] don't show notification if the app name is unknown #720 This gets rid of the notifications that say "Tap to Install Unknown", and instead just cancels the notification. The downloaded APK will still be cached, so when the user goes to click install or update again, it won't need to download it again. closes #758 --- .../org/fdroid/fdroid/installer/InstallManagerService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java index eb77f757e..320789a5f 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -331,7 +331,12 @@ public class InstallManagerService extends Service { title = String.format(getString(R.string.tap_to_update_format), pm.getApplicationLabel(pm.getApplicationInfo(apk.packageName, 0))); } catch (PackageManager.NameNotFoundException e) { - title = String.format(getString(R.string.tap_to_install_format), getAppName(apk)); + // TODO use packageName to fetch App instance from database if not cached + String name = getAppName(apk); + if (TextUtils.isEmpty(name) || name.equals(new App().name)) { + return; // do not have a name to display, so leave notification as is + } + title = String.format(getString(R.string.tap_to_install_format), name); } int downloadUrlId = urlString.hashCode(); From 973174cc7b02ffba181a0b5b118ed4fa31bab3ed Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 1 Sep 2016 17:49:12 +0200 Subject: [PATCH 3/3] privileged mode means auto-downloads are actually installs When the Privileged Extension is working, then enabling the preference "Automatically download updates" will actually install those updates in the background. So the preference should communicate that to the user. So now it serves as a global "allow background updates" #16 closes #106 --- .../main/java/org/fdroid/fdroid/Preferences.java | 4 ++-- .../fdroid/fdroid/installer/InstallerFactory.java | 13 +------------ .../fdroid/installer/PrivilegedInstaller.java | 9 +++++++++ .../fdroid/views/fragments/PreferencesFragment.java | 7 +++++++ app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 64d6ad5c7..1973d4dd0 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -45,7 +45,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh public static final String PREF_UPD_INTERVAL = "updateInterval"; public static final String PREF_UPD_WIFI_ONLY = "updateOnWifiOnly"; - public static final String PREF_UPD_AUTO_DOWNLOAD = "updateAutoDownload"; + public static final String PREF_AUTO_DOWNLOAD_INSTALL_UPDATES = "updateAutoDownload"; public static final String PREF_UPD_NOTIFY = "updateNotify"; public static final String PREF_UPD_HISTORY = "updateHistoryDays"; public static final String PREF_ROOTED = "rooted"; @@ -222,7 +222,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh } public boolean isAutoDownloadEnabled() { - return preferences.getBoolean(PREF_UPD_AUTO_DOWNLOAD, false); + return preferences.getBoolean(PREF_AUTO_DOWNLOAD_INSTALL_UPDATES, false); } public boolean isUpdateOnlyOnWifi() { diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java index c2ff1f8a8..3c19ae42c 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallerFactory.java @@ -21,7 +21,6 @@ package org.fdroid.fdroid.installer; import android.content.Context; -import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Apk; @@ -46,9 +45,7 @@ public class InstallerFactory { && apk.packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) { // special case for "F-Droid Privileged Extension" installer = new ExtensionInstaller(context); - } else if (isPrivilegedInstallerEnabled() - && PrivilegedInstaller.isExtensionInstalledCorrectly(context) - == PrivilegedInstaller.IS_EXTENSION_INSTALLED_YES) { + } else if (PrivilegedInstaller.isDefault(context)) { Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller"); installer = new PrivilegedInstaller(context); } else { @@ -57,12 +54,4 @@ public class InstallerFactory { return installer; } - - /** - * Extension has privileged permissions and preference is enabled? - */ - private static boolean isPrivilegedInstallerEnabled() { - return Preferences.get().isPrivilegedInstallerEnabled(); - } - } diff --git a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java index f964bed03..8649f729b 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java @@ -30,6 +30,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.privileged.IPrivilegedCallback; @@ -296,6 +297,14 @@ public class PrivilegedInstaller extends Installer { return IS_EXTENSION_INSTALLED_YES; } + /** + * Extension has privileged permissions and preference is enabled? + */ + public static boolean isDefault(Context context) { + return Preferences.get().isPrivilegedInstallerEnabled() + && isExtensionInstalledCorrectly(context) == IS_EXTENSION_INSTALLED_YES; + } + @Override protected void installPackageInternal(final Uri localApkUri, final Uri downloadUri, Apk apk) { sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_STARTED); diff --git a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java index e61f4a9a8..4304b87c6 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java +++ b/app/src/main/java/org/fdroid/fdroid/views/fragments/PreferencesFragment.java @@ -50,6 +50,7 @@ public class PreferencesFragment extends PreferenceFragment private static final int REQUEST_INSTALL_ORBOT = 0x1234; private CheckBoxPreference enableProxyCheckPref; private CheckBoxPreference useTorCheckPref; + private Preference updateAutoDownloadPref; private Preference updatePrivilegedExtensionPref; private long currentKeepCacheTime; @@ -59,6 +60,7 @@ public class PreferencesFragment extends PreferenceFragment addPreferencesFromResource(R.xml.preferences); useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR); enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY); + updateAutoDownloadPref = findPreference(Preferences.PREF_AUTO_DOWNLOAD_INSTALL_UPDATES); updatePrivilegedExtensionPref = findPreference(Preferences.PREF_UNINSTALL_PRIVILEGED_APP); } @@ -282,6 +284,11 @@ public class PreferencesFragment extends PreferenceFragment return true; } }); + + if (PrivilegedInstaller.isDefault(getContext())) { + updateAutoDownloadPref.setTitle(R.string.update_auto_install); + updateAutoDownloadPref.setSummary(R.string.update_auto_install_summary); + } } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb03f4fbc..a3a30b88f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,6 +22,8 @@ Update app lists automatically only on Wi-Fi Automatically download updates Download the update files in the background + Automatically install updates + Download and install update apps in the background Update notifications Show a notification when updates are available Update history