From 721d4a300a6f0df2b135e419f4f4e6110fcf7a92 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 5 Apr 2016 21:58:45 +0200 Subject: [PATCH] add method to check if a URL is being handled by DownloaderService This also saves the activeDownloadUrlString per packageName. Both are necessary so that AppDetails can accurately display the current state of a background download. Saving this per-packageName allows there to be multiple active downloads in the background, then when people move around AppDetails, it'll restore the progress meter and button state when coming back to the app that they clicked install on. By definition, there is just one DownloaderService enforced by Android with a single active Downloader instance enforced by the DownloaderService. That means using a static variable maps directly to those conditions and provides a really simple, implementation, especially compared to what would have to happen to do it via messages from the thread and any Activities. If this ends up blocking testing or something, it can always be changed when someone implements those tests. --- .../java/org/fdroid/fdroid/AppDetails.java | 17 ++++++++++- .../fdroid/fdroid/net/DownloaderService.java | 29 ++++++++++++++----- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/AppDetails.java b/app/src/main/java/org/fdroid/fdroid/AppDetails.java index 1725011b7..9312efa85 100644 --- a/app/src/main/java/org/fdroid/fdroid/AppDetails.java +++ b/app/src/main/java/org/fdroid/fdroid/AppDetails.java @@ -79,6 +79,7 @@ import com.nostra13.universalimageloader.core.assist.ImageScaleType; import org.fdroid.fdroid.Utils.CommaSeparatedList; import org.fdroid.fdroid.compat.PackageManagerCompat; +import org.fdroid.fdroid.compat.PreferencesCompat; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.App; @@ -428,7 +429,9 @@ public class AppDetails extends AppCompatActivity { refreshApkList(); refreshHeader(); supportInvalidateOptionsMenu(); - registerDownloaderReceivers(); + if (DownloaderService.isQueuedOrActive(activeDownloadUrlString)) { + registerDownloaderReceivers(); + } } /** @@ -448,6 +451,10 @@ public class AppDetails extends AppCompatActivity { @Override protected void onPause() { super.onPause(); + // save the active URL for this app in case we come back + PreferencesCompat.apply(getPreferences(MODE_PRIVATE) + .edit() + .putString(getPackageNameFromIntent(getIntent()), activeDownloadUrlString)); if (app != null && (app.ignoreAllUpdates != startingIgnoreAll || app.ignoreThisUpdate != startingIgnoreThis)) { Utils.debugLog(TAG, "Updating 'ignore updates', as it has changed since we started the activity..."); @@ -569,6 +576,14 @@ public class AppDetails extends AppCompatActivity { Utils.debugLog(TAG, "Getting application details for " + packageName); App newApp = null; + String urlString = getPreferences(MODE_PRIVATE).getString(packageName, null); + if (DownloaderService.isQueuedOrActive(urlString)) { + activeDownloadUrlString = urlString; + } else { + // this URL is no longer active, remove it + PreferencesCompat.apply(getPreferences(MODE_PRIVATE).edit().remove(packageName)); + } + if (!TextUtils.isEmpty(packageName)) { newApp = AppProvider.Helper.findByPackageName(getContentResolver(), packageName); } diff --git a/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java b/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java index 532a0ffbb..ab4573992 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java +++ b/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java @@ -73,11 +73,11 @@ public class DownloaderService extends Service { private static final String ACTION_CANCEL = "org.fdroid.fdroid.net.DownloaderService.action.CANCEL"; private volatile Looper serviceLooper; - private volatile ServiceHandler serviceHandler; - private volatile Downloader downloader; + private static volatile ServiceHandler serviceHandler; + private static volatile Downloader downloader; private LocalBroadcastManager localBroadcastManager; - private final HashMap queueWhats = new HashMap(); + private static final HashMap QUEUE_WHATS = new HashMap(); private int what; private final class ServiceHandler extends Handler { @@ -117,7 +117,7 @@ public class DownloaderService extends Service { } if (ACTION_CANCEL.equals(intent.getAction())) { Log.i(TAG, "Removed " + intent); - Integer what = queueWhats.remove(uriString); + Integer what = QUEUE_WHATS.remove(uriString); if (what != null && serviceHandler.hasMessages(what)) { // the URL is in the queue, remove it serviceHandler.removeMessages(what); @@ -134,8 +134,8 @@ public class DownloaderService extends Service { msg.obj = intent; msg.what = what++; serviceHandler.sendMessage(msg); - Log.i(TAG, "queueWhats.put(" + uriString + ", " + msg.what); - queueWhats.put(uriString, msg.what); + Log.i(TAG, "QUEUE_WHATS.put(" + uriString + ", " + msg.what); + QUEUE_WHATS.put(uriString, msg.what); } else { Log.e(TAG, "Received Intent with unknown action: " + intent); } @@ -170,7 +170,7 @@ public class DownloaderService extends Service { * the same DownloaderService, but it will not hold up anything else. * When all requests have been handled, the DownloaderService stops itself, * so you should not ever call {@link #stopSelf}. - *

+ *

* Downloads are put into subdirectories based on hostname/port of each repo * to prevent files with the same names from conflicting. Each repo enforces * unique APK file names on the server side. @@ -260,6 +260,21 @@ public class DownloaderService extends Service { context.startService(intent); } + /** + * Check if a URL is waiting in the queue for downloading or if actively + * being downloaded. This is useful for checking whether to re-register + * {@link android.content.BroadcastReceiver}s in + * {@link android.app.Activity#onResume()} + */ + public static boolean isQueuedOrActive(String urlString) { + if (TextUtils.isEmpty(urlString)) { + return false; + } + Integer what = QUEUE_WHATS.get(urlString); + return (what != null && serviceHandler.hasMessages(what)) + || (downloader != null && TextUtils.equals(urlString, downloader.sourceUrl.toString())); + } + /** * Get a prepared {@link IntentFilter} for use for matching this service's action events. *