From 4e8e14802969b11e8fc3e9e3df8e30bacdbffd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 28 May 2016 22:21:48 +0300 Subject: [PATCH] Display install errors as notify/dialog --- .../java/org/fdroid/fdroid/AppDetails.java | 115 +++++------------- .../fdroid/installer/DefaultInstaller.java | 45 ++----- .../installer/DefaultInstallerActivity.java | 11 +- .../fdroid/installer/ExtensionInstaller.java | 19 +-- .../installer/InstallManagerService.java | 45 ++++++- .../fdroid/fdroid/installer/Installer.java | 3 + .../fdroid/installer/PrivilegedInstaller.java | 5 + app/src/main/res/values/strings.xml | 5 +- 8 files changed, 112 insertions(+), 136 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/AppDetails.java b/app/src/main/java/org/fdroid/fdroid/AppDetails.java index d3a16343f..79efbc780 100644 --- a/app/src/main/java/org/fdroid/fdroid/AppDetails.java +++ b/app/src/main/java/org/fdroid/fdroid/AppDetails.java @@ -562,55 +562,28 @@ public class AppDetails extends AppCompatActivity { } case Installer.ACTION_INSTALL_COMPLETE: { headerFragment.removeProgress(); + localBroadcastManager.unregisterReceiver(this); - - PackageManagerCompat.setInstaller(packageManager, app.packageName); - - onAppChanged(); break; } case Installer.ACTION_INSTALL_INTERRUPTED: { headerFragment.removeProgress(); + onAppChanged(); + + String errorMessage = + intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE); + + if (!TextUtils.isEmpty(errorMessage)) { + Log.e(TAG, "Installer aborted with errorMessage: " + errorMessage); + + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(AppDetails.this); + alertBuilder.setTitle(R.string.install_error_notify_title); + alertBuilder.setMessage(errorMessage); + alertBuilder.setNeutralButton(android.R.string.ok, null); + alertBuilder.create().show(); + } + localBroadcastManager.unregisterReceiver(this); - - - // TODO: old error handling code: -// if (errorCode == InstallerCallback.ERROR_CODE_CANCELED) { -// return; -// } -// final int title, body; -// if (operation == InstallerCallback.OPERATION_INSTALL) { -// title = R.string.install_error_title; -// switch (errorCode) { -// case ERROR_CODE_CANNOT_PARSE: -// body = R.string.install_error_cannot_parse; -// break; -// default: // ERROR_CODE_OTHER -// body = R.string.install_error_unknown; -// break; -// } -// } else { // InstallerCallback.OPERATION_DELETE -// title = R.string.uninstall_error_title; -// switch (errorCode) { -// default: // ERROR_CODE_OTHER -// body = R.string.uninstall_error_unknown; -// break; -// } -// } -// runOnUiThread(new Runnable() { -// @Override -// public void run() { -// onAppChanged(); -// -// Log.e(TAG, "Installer aborted with errorCode: " + errorCode); -// -// AlertDialog.Builder alertBuilder = new AlertDialog.Builder(AppDetails.this); -// alertBuilder.setTitle(title); -// alertBuilder.setMessage(body); -// alertBuilder.setNeutralButton(android.R.string.ok, null); -// alertBuilder.create().show(); -// } -// }); break; } case Installer.ACTION_INSTALL_USER_INTERACTION: { @@ -643,52 +616,28 @@ public class AppDetails extends AppCompatActivity { } case Installer.ACTION_UNINSTALL_COMPLETE: { headerFragment.removeProgress(); - localBroadcastManager.unregisterReceiver(this); - onAppChanged(); + + localBroadcastManager.unregisterReceiver(this); break; } case Installer.ACTION_UNINSTALL_INTERRUPTED: { headerFragment.removeProgress(); - localBroadcastManager.unregisterReceiver(this); - // TODO: old error handling code: -// if (errorCode == InstallerCallback.ERROR_CODE_CANCELED) { -// return; -// } -// final int title, body; -// if (operation == InstallerCallback.OPERATION_INSTALL) { -// title = R.string.install_error_title; -// switch (errorCode) { -// case ERROR_CODE_CANNOT_PARSE: -// body = R.string.install_error_cannot_parse; -// break; -// default: // ERROR_CODE_OTHER -// body = R.string.install_error_unknown; -// break; -// } -// } else { // InstallerCallback.OPERATION_DELETE -// title = R.string.uninstall_error_title; -// switch (errorCode) { -// default: // ERROR_CODE_OTHER -// body = R.string.uninstall_error_unknown; -// break; -// } -// } -// runOnUiThread(new Runnable() { -// @Override -// public void run() { -// onAppChanged(); -// -// Log.e(TAG, "Installer aborted with errorCode: " + errorCode); -// -// AlertDialog.Builder alertBuilder = new AlertDialog.Builder(AppDetails.this); -// alertBuilder.setTitle(title); -// alertBuilder.setMessage(body); -// alertBuilder.setNeutralButton(android.R.string.ok, null); -// alertBuilder.create().show(); -// } -// }); + String errorMessage = + intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE); + + if (!TextUtils.isEmpty(errorMessage)) { + Log.e(TAG, "Installer aborted with errorMessage: " + errorMessage); + + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(AppDetails.this); + alertBuilder.setTitle(R.string.uninstall_error_notify_title); + alertBuilder.setMessage(errorMessage); + alertBuilder.setNeutralButton(android.R.string.ok, null); + alertBuilder.create().show(); + } + + localBroadcastManager.unregisterReceiver(this); break; } case Installer.ACTION_UNINSTALL_USER_INTERACTION: { diff --git a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java index d31d2641b..d43f6d040 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java @@ -25,9 +25,7 @@ import android.content.Intent; import android.net.Uri; import android.util.Log; -import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.Utils; -import org.fdroid.fdroid.privileged.install.InstallExtensionDialogActivity; import java.io.File; @@ -43,36 +41,22 @@ public class DefaultInstaller extends Installer { protected void installPackage(Uri uri, Uri originatingUri, String packageName) { sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_STARTED); - Utils.debugLog(TAG, "ACTION_INSTALL uri: " + uri + " file: " + new File(uri.getPath())); + Utils.debugLog(TAG, "DefaultInstaller uri: " + uri + " file: " + new File(uri.getPath())); Uri sanitizedUri; try { sanitizedUri = Installer.prepareApkFile(mContext, uri, packageName); } catch (Installer.InstallFailedException e) { Log.e(TAG, "prepareApkFile failed", e); + sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_INTERRUPTED, + e.getMessage()); return; } - Intent installIntent; - // special case: F-Droid Privileged Extension - if (packageName != null && packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) { - - // extension must be signed with the same public key as main F-Droid - // NOTE: Disabled for debug builds to be able to use official extension from repo - ApkSignatureVerifier signatureVerifier = new ApkSignatureVerifier(mContext); - if (!BuildConfig.DEBUG && !signatureVerifier.hasFDroidSignature(new File(sanitizedUri.getPath()))) { - throw new RuntimeException("APK signature of extension not correct!"); - } - - installIntent = new Intent(mContext, InstallExtensionDialogActivity.class); - installIntent.setAction(InstallExtensionDialogActivity.ACTION_INSTALL); - installIntent.setData(sanitizedUri); - } else { - installIntent = new Intent(mContext, DefaultInstallerActivity.class); - installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE); - installIntent.putExtra(DefaultInstallerActivity.EXTRA_ORIGINATING_URI, originatingUri); - installIntent.setData(sanitizedUri); - } + Intent installIntent = new Intent(mContext, DefaultInstallerActivity.class); + installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE); + installIntent.putExtra(DefaultInstallerActivity.EXTRA_ORIGINATING_URI, originatingUri); + installIntent.setData(sanitizedUri); PendingIntent installPendingIntent = PendingIntent.getActivity( mContext.getApplicationContext(), @@ -88,17 +72,10 @@ public class DefaultInstaller extends Installer { protected void uninstallPackage(String packageName) { sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED); - Intent uninstallIntent; - // special case: F-Droid Privileged Extension - if (packageName != null && packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) { - uninstallIntent = new Intent(mContext, InstallExtensionDialogActivity.class); - uninstallIntent.setAction(InstallExtensionDialogActivity.ACTION_UNINSTALL); - } else { - uninstallIntent = new Intent(mContext, DefaultInstallerActivity.class); - uninstallIntent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE); - uninstallIntent.putExtra( - DefaultInstallerActivity.EXTRA_UNINSTALL_PACKAGE_NAME, packageName); - } + Intent uninstallIntent = new Intent(mContext, DefaultInstallerActivity.class); + uninstallIntent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE); + uninstallIntent.putExtra( + DefaultInstallerActivity.EXTRA_UNINSTALL_PACKAGE_NAME, packageName); PendingIntent uninstallPendingIntent = PendingIntent.getActivity( mContext.getApplicationContext(), packageName.hashCode(), diff --git a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java index aada32327..eb5e1ccb5 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstallerActivity.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.util.Log; +import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; /** @@ -207,9 +208,10 @@ public class DefaultInstallerActivity extends FragmentActivity { } default: case Activity.RESULT_FIRST_USER: { - // AOSP actually returns Activity.RESULT_FIRST_USER if something breaks + // AOSP returns Activity.RESULT_FIRST_USER on error installer.sendBroadcastInstall(mInstallUri, mInstallOriginatingUri, - Installer.ACTION_INSTALL_INTERRUPTED, "error"); + Installer.ACTION_INSTALL_INTERRUPTED, + getString(R.string.install_error_unknown)); break; } } @@ -237,11 +239,10 @@ public class DefaultInstallerActivity extends FragmentActivity { } default: case Activity.RESULT_FIRST_USER: { - // AOSP UninstallAppProgress actually returns - // Activity.RESULT_FIRST_USER if something breaks + // AOSP UninstallAppProgress returns RESULT_FIRST_USER on error installer.sendBroadcastUninstall(mUninstallPackageName, Installer.ACTION_UNINSTALL_INTERRUPTED, - "error"); + getString(R.string.uninstall_error_unknown)); break; } } diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java index 887d18290..c778a6897 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java @@ -43,13 +43,13 @@ public class ExtensionInstaller extends Installer { @Override protected void installPackage(Uri uri, Uri originatingUri, String packageName) { - sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_STARTED); - Uri sanitizedUri; try { sanitizedUri = Installer.prepareApkFile(mContext, uri, packageName); } catch (InstallFailedException e) { Log.e(TAG, "prepareApkFile failed", e); + sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_INTERRUPTED, + e.getMessage()); return; } @@ -57,10 +57,10 @@ public class ExtensionInstaller extends Installer { // NOTE: Disabled for debug builds to be able to use official extension from repo ApkSignatureVerifier signatureVerifier = new ApkSignatureVerifier(mContext); if (!BuildConfig.DEBUG && !signatureVerifier.hasFDroidSignature(new File(sanitizedUri.getPath()))) { - throw new RuntimeException("APK signature of extension not correct!"); + sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_INTERRUPTED, + "APK signature of extension not correct!"); } - Intent installIntent; - installIntent = new Intent(mContext, InstallExtensionDialogActivity.class); + Intent installIntent = new Intent(mContext, InstallExtensionDialogActivity.class); installIntent.setAction(InstallExtensionDialogActivity.ACTION_INSTALL); installIntent.setData(sanitizedUri); @@ -72,14 +72,16 @@ public class ExtensionInstaller extends Installer { sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_USER_INTERACTION, installPendingIntent); + + // don't use broadcasts for the rest of this special installer + sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_COMPLETE); } @Override protected void uninstallPackage(String packageName) { sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED); - Intent uninstallIntent; - uninstallIntent = new Intent(mContext, InstallExtensionDialogActivity.class); + Intent uninstallIntent = new Intent(mContext, InstallExtensionDialogActivity.class); uninstallIntent.setAction(InstallExtensionDialogActivity.ACTION_UNINSTALL); PendingIntent uninstallPendingIntent = PendingIntent.getActivity( @@ -90,5 +92,8 @@ public class ExtensionInstaller extends Installer { sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent); + + // don't use broadcasts for the rest of this special installer + sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_COMPLETE); } } 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 2630c6081..8dcaa8001 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -19,6 +19,7 @@ import android.text.TextUtils; import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.compat.PackageManagerCompat; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.net.Downloader; @@ -274,15 +275,26 @@ public class InstallManagerService extends Service { Uri originatingUri = intent.getParcelableExtra(Installer.EXTRA_ORIGINATING_URI); String urlString = originatingUri.toString(); - removeFromActive(urlString); + Apk apk = removeFromActive(urlString); + + PackageManagerCompat.setInstaller(getPackageManager(), apk.packageName); localBroadcastManager.unregisterReceiver(this); - break; } case Installer.ACTION_INSTALL_INTERRUPTED: { - localBroadcastManager.unregisterReceiver(this); + Uri originatingUri = + intent.getParcelableExtra(Installer.EXTRA_ORIGINATING_URI); + String urlString = originatingUri.toString(); + String errorMessage = + intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE); + if (!TextUtils.isEmpty(errorMessage)) { + App app = getAppFromActive(urlString); + notifyError(app, urlString, errorMessage, false); + } + + localBroadcastManager.unregisterReceiver(this); break; } case Installer.ACTION_INSTALL_USER_INTERACTION: { @@ -292,7 +304,7 @@ public class InstallManagerService extends Service { intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI); Utils.debugLog(TAG, "originatingUri: " + originatingUri); - Apk apk = getFromActive(originatingUri.toString()); + Apk apk = getApkFromActive(originatingUri.toString()); // show notification if app details is not visible if (AppDetails.isAppVisible(apk.packageName)) { cancelNotification(originatingUri.toString()); @@ -379,6 +391,25 @@ public class InstallManagerService extends Service { notificationManager.notify(downloadUrlId, notification); } + private void notifyError(App app, String urlString, String text, boolean uninstall) { + String title; + if (uninstall) { + title = String.format(getString(R.string.uninstall_error_notify_title), app.name); + } else { + title = String.format(getString(R.string.install_error_notify_title), app.name); + } + + int downloadUrlId = urlString.hashCode(); + NotificationCompat.Builder builder = + new NotificationCompat.Builder(this) + .setAutoCancel(true) + .setContentTitle(title) + .setSmallIcon(R.drawable.ic_issues) + .setContentText(text); + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + nm.notify(downloadUrlId, builder.build()); + } + /** * Cancel the {@link Notification} tied to {@code urlString}, which is the * unique ID used to represent a given APK file. {@link String#hashCode()} @@ -393,7 +424,7 @@ public class InstallManagerService extends Service { ACTIVE_APPS.put(app.packageName, app); } - private static Apk getFromActive(String urlString) { + private static Apk getApkFromActive(String urlString) { return ACTIVE_APKS.get(urlString); } @@ -404,6 +435,10 @@ public class InstallManagerService extends Service { * {@link BroadcastReceiver}s, in which case {@code urlString} would not * find anything in the active maps. */ + private static App getAppFromActive(String urlString) { + return ACTIVE_APPS.get(getApkFromActive(urlString).packageName); + } + private static Apk removeFromActive(String urlString) { Apk apk = ACTIVE_APKS.remove(urlString); if (apk != null) { diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java index b53d5e06e..3663ecd5c 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java @@ -160,6 +160,9 @@ public abstract class Installer { // if (count < 0) { // mCallback.onError(InstallerCallback.OPERATION_INSTALL, // InstallerCallback.ERROR_CODE_CANNOT_PARSE); + +// install_error_cannot_parse + // return; // } // if (count > 0) { 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 e1158aa67..4307b98a4 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java @@ -151,6 +151,8 @@ public class PrivilegedInstaller extends Installer { sanitizedUri = Installer.prepareApkFile(mContext, uri, packageName); } catch (Installer.InstallFailedException e) { Log.e(TAG, "prepareApkFile failed", e); + sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_INTERRUPTED, + e.getMessage()); return; } @@ -299,6 +301,9 @@ public class PrivilegedInstaller extends Installer { // } else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) { // mCallback.onError(InstallerCallback.OPERATION_INSTALL, // InstallerCallback.ERROR_CODE_CANNOT_PARSE); + +// install_error_cannot_parse + // } else { // Activity.RESULT_CANCELED // mCallback.onError(InstallerCallback.OPERATION_INSTALL, // InstallerCallback.ERROR_CODE_CANCELED); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d07727e72..a9b4acd8e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -267,10 +267,8 @@ Requesting root access… Root access denied Either your Android device is not rooted or you have denied root access for F-Droid. - Install error Failed to install due to an unknown error An error occurred while parsing the package - Uninstall error Failed to uninstall due to an unknown error F-Droid Privileged Extension is not available This option is only available when F-Droid Privileged Extension is installed. @@ -365,6 +363,9 @@ Download completed, tap to install Download unsuccessful Waiting to start download… + Error installing %s + Error uninstalling %s + New: Provided by %1$s.