Display install errors as notify/dialog

This commit is contained in:
Dominik Schürmann 2016-05-28 22:21:48 +03:00
parent de1d310499
commit 4e8e148029
8 changed files with 112 additions and 136 deletions

View File

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

View File

@ -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);
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);
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(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -267,10 +267,8 @@
<string name="requesting_root_access_body">Requesting root access…</string>
<string name="root_access_denied_title">Root access denied</string>
<string name="root_access_denied_body">Either your Android device is not rooted or you have denied root access for F-Droid.</string>
<string name="install_error_title">Install error</string>
<string name="install_error_unknown">Failed to install due to an unknown error</string>
<string name="install_error_cannot_parse">An error occurred while parsing the package</string>
<string name="uninstall_error_title">Uninstall error</string>
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
<string name="system_install_denied_title">F-Droid Privileged Extension is not available</string>
<string name="system_install_denied_body">This option is only available when F-Droid Privileged Extension is installed.</string>
@ -365,6 +363,9 @@
<string name="tap_to_install">Download completed, tap to install</string>
<string name="download_error">Download unsuccessful</string>
<string name="download_pending">Waiting to start download…</string>
<string name="install_error_notify_title">Error installing %s</string>
<string name="uninstall_error_notify_title">Error uninstalling %s</string>
<string name="perms_new_perm_prefix">New: </string>
<string name="perms_description_app">Provided by %1$s.</string>