From f195c34a8bf90cc55e2f473210836c0bf7f24e5e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sat, 7 May 2016 00:30:05 +0200 Subject: [PATCH] make Apk the common internal data type Standardizing on Apk as the internal data type means that most of the data that is needed for the whole lifecycle of a given APK going through this process will be available regardless of the database changes. Once App instances are also included, then all of the data should be available separately from the database. This is important to support parallel operation. The index could be updated and an app could disappear while an APK of that app is being downloaded. In that case, it should not show blank notifications. Also, in AppDetail, the Apk instance is completely loaded from the db, so there should not be any nulls on the essential bits like packageName and download URL. --- .../installer/InstallManagerService.java | 82 +++++++++---------- 1 file changed, 37 insertions(+), 45 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 a617d9505..b801a8f55 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -10,13 +10,12 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.IBinder; -import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.LocalBroadcastManager; +import android.text.TextUtils; import org.fdroid.fdroid.AppDetails; -import org.fdroid.fdroid.FDroid; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Apk; @@ -93,7 +92,7 @@ public class InstallManagerService extends Service { String urlString = intent.getDataString(); Apk apk = ACTIVE_APKS.get(urlString); - Notification notification = createNotification(intent.getDataString(), apk.packageName).build(); + Notification notification = createNotification(intent.getDataString(), apk).build(); startForeground(NOTIFY_DOWNLOADING, notification); registerDownloaderReceivers(urlString); @@ -142,7 +141,7 @@ public class InstallManagerService extends Service { int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0); int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - Notification notification = createNotification(urlString, apk.packageName) + Notification notification = createNotification(urlString, apk) .setProgress(totalBytes, bytesRead, false) .build(); nm.notify(NOTIFY_DOWNLOADING, notification); @@ -153,7 +152,7 @@ public class InstallManagerService extends Service { public void onReceive(Context context, Intent intent) { String urlString = intent.getDataString(); Apk apk = ACTIVE_APKS.remove(urlString); - notifyDownloadComplete(apk.packageName, intent.getDataString()); + notifyDownloadComplete(apk, urlString); unregisterDownloaderReceivers(urlString); } }; @@ -178,12 +177,12 @@ public class InstallManagerService extends Service { }); } - private NotificationCompat.Builder createNotification(String urlString, @Nullable String packageName) { + private NotificationCompat.Builder createNotification(String urlString, Apk apk) { int downloadUrlId = urlString.hashCode(); return new NotificationCompat.Builder(this) .setAutoCancel(true) - .setContentIntent(getAppDetailsIntent(downloadUrlId, apk.packageName)) - .setContentTitle(getNotificationTitle(packageName)) + .setContentIntent(getAppDetailsIntent(downloadUrlId, apk)) + .setContentTitle(getNotificationTitle(urlString, apk)) .addAction(R.drawable.ic_cancel_black_24dp, getString(R.string.cancel), DownloaderService.getCancelPendingIntent(this, urlString)) .setSmallIcon(android.R.drawable.stat_sys_download) @@ -191,60 +190,53 @@ public class InstallManagerService extends Service { .setProgress(100, 0, true); } - /** - * If downloading an apk (i.e. packageName != null) then the title will indicate - * the name of the app which the apk belongs to. Otherwise, it will be a generic "Downloading..." - * message. - */ - private String getNotificationTitle(@Nullable String packageName) { - String title; - if (packageName != null) { - App app = AppProvider.Helper.findByPackageName( - getContentResolver(), packageName, new String[]{AppProvider.DataColumns.NAME}); - title = getString(R.string.downloading_apk, app.name); + private String getAppName(Apk apk) { + App app = AppProvider.Helper.findByPackageName( + getContentResolver(), apk.packageName, new String[]{AppProvider.DataColumns.NAME}); + if (app != null && !TextUtils.isEmpty(app.name)) { + return app.name; } else { - title = getString(R.string.downloading); + return null; } - return title; } - private PendingIntent getAppDetailsIntent(int requestCode, String packageName) { - TaskStackBuilder stackBuilder; - if (packageName != null) { - Intent notifyIntent = new Intent(getApplicationContext(), AppDetails.class) - .putExtra(AppDetails.EXTRA_APPID, packageName); - - stackBuilder = TaskStackBuilder - .create(getApplicationContext()) - .addParentStack(AppDetails.class) - .addNextIntent(notifyIntent); + private String getNotificationTitle(String urlString, Apk apk) { + String name = getAppName(apk); + if (TextUtils.isEmpty(name)) { + // this is ugly, but its better than nothing as a failsafe + return getString(R.string.downloading_apk, urlString); } else { - Intent notifyIntent = new Intent(getApplicationContext(), FDroid.class); - stackBuilder = TaskStackBuilder - .create(getApplicationContext()) - .addParentStack(FDroid.class) - .addNextIntent(notifyIntent); + return getString(R.string.downloading_apk, name); } + } - return stackBuilder.getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT); + /** + * Get a {@link PendingIntent} for a {@link Notification} to send when it + * is clicked. {@link AppDetails} handles {@code Intent}s that are missing + * or bad {@link AppDetails#EXTRA_APPID}, so it does not need to be checked + * here. + */ + private PendingIntent getAppDetailsIntent(int requestCode, Apk apk) { + Intent notifyIntent = new Intent(getApplicationContext(), AppDetails.class) + .putExtra(AppDetails.EXTRA_APPID, apk.packageName); + return TaskStackBuilder.create(getApplicationContext()) + .addParentStack(AppDetails.class) + .addNextIntent(notifyIntent) + .getPendingIntent(requestCode, PendingIntent.FLAG_UPDATE_CURRENT); } /** * Post a notification about a completed download. {@code packageName} must be a valid * and currently in the app index database. */ - private void notifyDownloadComplete(String packageName, String urlString) { + private void notifyDownloadComplete(Apk apk, String urlString) { String title; try { PackageManager pm = getPackageManager(); title = String.format(getString(R.string.tap_to_update_format), - pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0))); + pm.getApplicationLabel(pm.getApplicationInfo(apk.packageName, 0))); } catch (PackageManager.NameNotFoundException e) { - App app = AppProvider.Helper.findByPackageName(getContentResolver(), packageName, - new String[]{ - AppProvider.DataColumns.NAME, - }); - title = String.format(getString(R.string.tap_to_install_format), app.name); + title = String.format(getString(R.string.tap_to_install_format), getAppName(apk)); } int downloadUrlId = urlString.hashCode(); @@ -253,7 +245,7 @@ public class InstallManagerService extends Service { .setAutoCancel(true) .setContentTitle(title) .setSmallIcon(android.R.drawable.stat_sys_download_done) - .setContentIntent(getAppDetailsIntent(downloadUrlId, packageName)) + .setContentIntent(getAppDetailsIntent(downloadUrlId, apk)) .setContentText(getString(R.string.tap_to_install)); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.notify(downloadUrlId, builder.build());