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.
This commit is contained in:
Hans-Christoph Steiner 2016-04-05 21:58:45 +02:00
parent 49635c224d
commit 721d4a300a
2 changed files with 38 additions and 8 deletions

View File

@ -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,8 +429,10 @@ public class AppDetails extends AppCompatActivity {
refreshApkList();
refreshHeader();
supportInvalidateOptionsMenu();
if (DownloaderService.isQueuedOrActive(activeDownloadUrlString)) {
registerDownloaderReceivers();
}
}
/**
* Remove progress listener, suppress progress bar, set downloadHandler to null.
@ -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);
}

View File

@ -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<String, Integer> queueWhats = new HashMap<String, Integer>();
private static final HashMap<String, Integer> QUEUE_WHATS = new HashMap<String, Integer>();
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}.
* <p>
* <p/>
* 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.
*