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.
This commit is contained in:
Hans-Christoph Steiner 2016-05-07 00:30:05 +02:00
parent dded004321
commit f195c34a8b

View File

@ -10,13 +10,12 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder; import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
@ -93,7 +92,7 @@ public class InstallManagerService extends Service {
String urlString = intent.getDataString(); String urlString = intent.getDataString();
Apk apk = ACTIVE_APKS.get(urlString); 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); startForeground(NOTIFY_DOWNLOADING, notification);
registerDownloaderReceivers(urlString); registerDownloaderReceivers(urlString);
@ -142,7 +141,7 @@ public class InstallManagerService extends Service {
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0); int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0); int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = createNotification(urlString, apk.packageName) Notification notification = createNotification(urlString, apk)
.setProgress(totalBytes, bytesRead, false) .setProgress(totalBytes, bytesRead, false)
.build(); .build();
nm.notify(NOTIFY_DOWNLOADING, notification); nm.notify(NOTIFY_DOWNLOADING, notification);
@ -153,7 +152,7 @@ public class InstallManagerService extends Service {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String urlString = intent.getDataString(); String urlString = intent.getDataString();
Apk apk = ACTIVE_APKS.remove(urlString); Apk apk = ACTIVE_APKS.remove(urlString);
notifyDownloadComplete(apk.packageName, intent.getDataString()); notifyDownloadComplete(apk, urlString);
unregisterDownloaderReceivers(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(); int downloadUrlId = urlString.hashCode();
return new NotificationCompat.Builder(this) return new NotificationCompat.Builder(this)
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(getAppDetailsIntent(downloadUrlId, apk.packageName)) .setContentIntent(getAppDetailsIntent(downloadUrlId, apk))
.setContentTitle(getNotificationTitle(packageName)) .setContentTitle(getNotificationTitle(urlString, apk))
.addAction(R.drawable.ic_cancel_black_24dp, getString(R.string.cancel), .addAction(R.drawable.ic_cancel_black_24dp, getString(R.string.cancel),
DownloaderService.getCancelPendingIntent(this, urlString)) DownloaderService.getCancelPendingIntent(this, urlString))
.setSmallIcon(android.R.drawable.stat_sys_download) .setSmallIcon(android.R.drawable.stat_sys_download)
@ -191,60 +190,53 @@ public class InstallManagerService extends Service {
.setProgress(100, 0, true); .setProgress(100, 0, true);
} }
/** private String getAppName(Apk apk) {
* If downloading an apk (i.e. <code>packageName != null</code>) then the title will indicate App app = AppProvider.Helper.findByPackageName(
* the name of the app which the apk belongs to. Otherwise, it will be a generic "Downloading..." getContentResolver(), apk.packageName, new String[]{AppProvider.DataColumns.NAME});
* message. if (app != null && !TextUtils.isEmpty(app.name)) {
*/ return app.name;
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);
} else { } else {
title = getString(R.string.downloading); return null;
} }
return title;
} }
private PendingIntent getAppDetailsIntent(int requestCode, String packageName) { private String getNotificationTitle(String urlString, Apk apk) {
TaskStackBuilder stackBuilder; String name = getAppName(apk);
if (packageName != null) { if (TextUtils.isEmpty(name)) {
Intent notifyIntent = new Intent(getApplicationContext(), AppDetails.class) // this is ugly, but its better than nothing as a failsafe
.putExtra(AppDetails.EXTRA_APPID, packageName); return getString(R.string.downloading_apk, urlString);
stackBuilder = TaskStackBuilder
.create(getApplicationContext())
.addParentStack(AppDetails.class)
.addNextIntent(notifyIntent);
} else { } else {
Intent notifyIntent = new Intent(getApplicationContext(), FDroid.class); return getString(R.string.downloading_apk, name);
stackBuilder = TaskStackBuilder
.create(getApplicationContext())
.addParentStack(FDroid.class)
.addNextIntent(notifyIntent);
} }
}
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 * Post a notification about a completed download. {@code packageName} must be a valid
* and currently in the app index database. * and currently in the app index database.
*/ */
private void notifyDownloadComplete(String packageName, String urlString) { private void notifyDownloadComplete(Apk apk, String urlString) {
String title; String title;
try { try {
PackageManager pm = getPackageManager(); PackageManager pm = getPackageManager();
title = String.format(getString(R.string.tap_to_update_format), 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) { } catch (PackageManager.NameNotFoundException e) {
App app = AppProvider.Helper.findByPackageName(getContentResolver(), packageName, title = String.format(getString(R.string.tap_to_install_format), getAppName(apk));
new String[]{
AppProvider.DataColumns.NAME,
});
title = String.format(getString(R.string.tap_to_install_format), app.name);
} }
int downloadUrlId = urlString.hashCode(); int downloadUrlId = urlString.hashCode();
@ -253,7 +245,7 @@ public class InstallManagerService extends Service {
.setAutoCancel(true) .setAutoCancel(true)
.setContentTitle(title) .setContentTitle(title)
.setSmallIcon(android.R.drawable.stat_sys_download_done) .setSmallIcon(android.R.drawable.stat_sys_download_done)
.setContentIntent(getAppDetailsIntent(downloadUrlId, packageName)) .setContentIntent(getAppDetailsIntent(downloadUrlId, apk))
.setContentText(getString(R.string.tap_to_install)); .setContentText(getString(R.string.tap_to_install));
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify(downloadUrlId, builder.build()); nm.notify(downloadUrlId, builder.build());