Merge branch 'canonical-url-overhaul' into 'master'
Canonical URL overhaul Closes #1742, #1736, and #1727 See merge request fdroid/fdroidclient!809
This commit is contained in:
commit
727e9ed5dd
@ -70,7 +70,7 @@ public class HttpDownloaderTest {
|
|||||||
final HttpDownloader httpDownloader = new HttpDownloader(uri, destFile);
|
final HttpDownloader httpDownloader = new HttpDownloader(uri, destFile);
|
||||||
httpDownloader.setListener(new ProgressListener() {
|
httpDownloader.setListener(new ProgressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(String urlString, long bytesRead, long totalBytes) {
|
public void onProgress(long bytesRead, long totalBytes) {
|
||||||
receivedProgress = true;
|
receivedProgress = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -132,7 +132,7 @@ public class HttpDownloaderTest {
|
|||||||
final HttpDownloader httpDownloader = new HttpDownloader(uri, destFile);
|
final HttpDownloader httpDownloader = new HttpDownloader(uri, destFile);
|
||||||
httpDownloader.setListener(new ProgressListener() {
|
httpDownloader.setListener(new ProgressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(String urlString, long bytesRead, long totalBytes) {
|
public void onProgress(long bytesRead, long totalBytes) {
|
||||||
receivedProgress = true;
|
receivedProgress = true;
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
@ -314,10 +314,8 @@ public class SwapAppsView extends ListView implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (apk != null) {
|
if (apk != null) {
|
||||||
String urlString = apk.getUrl();
|
|
||||||
|
|
||||||
// TODO unregister receivers? or will they just die with this instance
|
// TODO unregister receivers? or will they just die with this instance
|
||||||
IntentFilter downloadFilter = DownloaderService.getIntentFilter(urlString);
|
IntentFilter downloadFilter = DownloaderService.getIntentFilter(apk.getCanonicalUrl());
|
||||||
localBroadcastManager.registerReceiver(downloadReceiver, downloadFilter);
|
localBroadcastManager.registerReceiver(downloadReceiver, downloadFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,9 +836,8 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void install(@NonNull final App app, @NonNull final Apk apk) {
|
public void install(@NonNull final App app, @NonNull final Apk apk) {
|
||||||
Uri downloadUri = Uri.parse(apk.getUrl());
|
|
||||||
localBroadcastManager.registerReceiver(installReceiver,
|
localBroadcastManager.registerReceiver(installReceiver,
|
||||||
Installer.getInstallIntentFilter(downloadUri));
|
Installer.getInstallIntentFilter(apk.getCanonicalUrl()));
|
||||||
InstallManagerService.queue(this, app, apk);
|
InstallManagerService.queue(this, app, apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +72,7 @@ public final class AppUpdateStatusManager {
|
|||||||
*/
|
*/
|
||||||
public static final String BROADCAST_APPSTATUS_REMOVED = "org.fdroid.fdroid.installer.appstatus.appchange.remove";
|
public static final String BROADCAST_APPSTATUS_REMOVED = "org.fdroid.fdroid.installer.appstatus.appchange.remove";
|
||||||
|
|
||||||
public static final String EXTRA_APK_URL = "urlstring";
|
|
||||||
public static final String EXTRA_STATUS = "status";
|
public static final String EXTRA_STATUS = "status";
|
||||||
|
|
||||||
public static final String EXTRA_REASON_FOR_CHANGE = "reason";
|
public static final String EXTRA_REASON_FOR_CHANGE = "reason";
|
||||||
|
|
||||||
public static final String REASON_READY_TO_INSTALL = "readytoinstall";
|
public static final String REASON_READY_TO_INSTALL = "readytoinstall";
|
||||||
@ -129,11 +127,11 @@ public final class AppUpdateStatusManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the unique ID used to represent this specific package's install process
|
* @return the unique ID used to represent this specific package's install process
|
||||||
* also known as {@code urlString}.
|
* also known as {@code canonicalUrl}.
|
||||||
* @see org.fdroid.fdroid.installer.InstallManagerService
|
* @see org.fdroid.fdroid.installer.InstallManagerService
|
||||||
*/
|
*/
|
||||||
public String getUniqueKey() {
|
public String getCanonicalUrl() {
|
||||||
return apk.getUrl();
|
return apk.getCanonicalUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,9 +223,9 @@ public final class AppUpdateStatusManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public AppUpdateStatus get(String key) {
|
public AppUpdateStatus get(String canonicalUrl) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
return appMapping.get(key);
|
return appMapping.get(canonicalUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +262,7 @@ public final class AppUpdateStatusManager {
|
|||||||
notifyChange(entry, isStatusUpdate);
|
notifyChange(entry, isStatusUpdate);
|
||||||
|
|
||||||
if (status == Status.Installed) {
|
if (status == Status.Installed) {
|
||||||
InstallManagerService.removePendingInstall(context, entry.getUniqueKey());
|
InstallManagerService.removePendingInstall(context, entry.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,11 +270,11 @@ public final class AppUpdateStatusManager {
|
|||||||
Utils.debugLog(LOGTAG, "Add APK " + apk.apkName + " with state " + status.name());
|
Utils.debugLog(LOGTAG, "Add APK " + apk.apkName + " with state " + status.name());
|
||||||
AppUpdateStatus entry = createAppEntry(apk, status, intent);
|
AppUpdateStatus entry = createAppEntry(apk, status, intent);
|
||||||
setEntryContentIntentIfEmpty(entry);
|
setEntryContentIntentIfEmpty(entry);
|
||||||
appMapping.put(entry.getUniqueKey(), entry);
|
appMapping.put(entry.getCanonicalUrl(), entry);
|
||||||
notifyAdd(entry);
|
notifyAdd(entry);
|
||||||
|
|
||||||
if (status == Status.Installed) {
|
if (status == Status.Installed) {
|
||||||
InstallManagerService.removePendingInstall(context, entry.getUniqueKey());
|
InstallManagerService.removePendingInstall(context, entry.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +289,7 @@ public final class AppUpdateStatusManager {
|
|||||||
private void notifyAdd(AppUpdateStatus entry) {
|
private void notifyAdd(AppUpdateStatus entry) {
|
||||||
if (!isBatchUpdating) {
|
if (!isBatchUpdating) {
|
||||||
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_ADDED);
|
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_ADDED);
|
||||||
broadcastIntent.putExtra(EXTRA_APK_URL, entry.getUniqueKey());
|
broadcastIntent.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, entry.getCanonicalUrl());
|
||||||
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
|
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
|
||||||
localBroadcastManager.sendBroadcast(broadcastIntent);
|
localBroadcastManager.sendBroadcast(broadcastIntent);
|
||||||
}
|
}
|
||||||
@ -300,7 +298,7 @@ public final class AppUpdateStatusManager {
|
|||||||
private void notifyChange(AppUpdateStatus entry, boolean isStatusUpdate) {
|
private void notifyChange(AppUpdateStatus entry, boolean isStatusUpdate) {
|
||||||
if (!isBatchUpdating) {
|
if (!isBatchUpdating) {
|
||||||
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_CHANGED);
|
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_CHANGED);
|
||||||
broadcastIntent.putExtra(EXTRA_APK_URL, entry.getUniqueKey());
|
broadcastIntent.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, entry.getCanonicalUrl());
|
||||||
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
|
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
|
||||||
broadcastIntent.putExtra(EXTRA_IS_STATUS_UPDATE, isStatusUpdate);
|
broadcastIntent.putExtra(EXTRA_IS_STATUS_UPDATE, isStatusUpdate);
|
||||||
localBroadcastManager.sendBroadcast(broadcastIntent);
|
localBroadcastManager.sendBroadcast(broadcastIntent);
|
||||||
@ -310,7 +308,7 @@ public final class AppUpdateStatusManager {
|
|||||||
private void notifyRemove(AppUpdateStatus entry) {
|
private void notifyRemove(AppUpdateStatus entry) {
|
||||||
if (!isBatchUpdating) {
|
if (!isBatchUpdating) {
|
||||||
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_REMOVED);
|
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_REMOVED);
|
||||||
broadcastIntent.putExtra(EXTRA_APK_URL, entry.getUniqueKey());
|
broadcastIntent.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, entry.getCanonicalUrl());
|
||||||
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
|
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
|
||||||
localBroadcastManager.sendBroadcast(broadcastIntent);
|
localBroadcastManager.sendBroadcast(broadcastIntent);
|
||||||
}
|
}
|
||||||
@ -321,7 +319,7 @@ public final class AppUpdateStatusManager {
|
|||||||
ContentResolver resolver = context.getContentResolver();
|
ContentResolver resolver = context.getContentResolver();
|
||||||
App app = AppProvider.Helper.findSpecificApp(resolver, apk.packageName, apk.repoId);
|
App app = AppProvider.Helper.findSpecificApp(resolver, apk.packageName, apk.repoId);
|
||||||
AppUpdateStatus ret = new AppUpdateStatus(app, apk, status, intent);
|
AppUpdateStatus ret = new AppUpdateStatus(app, apk, status, intent);
|
||||||
appMapping.put(apk.getUrl(), ret);
|
appMapping.put(apk.getCanonicalUrl(), ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +345,7 @@ public final class AppUpdateStatusManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(apk.getUrl());
|
AppUpdateStatus entry = appMapping.get(apk.getCanonicalUrl());
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
updateApkInternal(entry, status, pendingIntent);
|
updateApkInternal(entry, status, pendingIntent);
|
||||||
} else {
|
} else {
|
||||||
@ -359,9 +357,9 @@ public final class AppUpdateStatusManager {
|
|||||||
/**
|
/**
|
||||||
* @param pendingIntent Action when notification is clicked. Can be null for default action(s)
|
* @param pendingIntent Action when notification is clicked. Can be null for default action(s)
|
||||||
*/
|
*/
|
||||||
public void updateApk(String key, @NonNull Status status, @Nullable PendingIntent pendingIntent) {
|
public void updateApk(String canonicalUrl, @NonNull Status status, @Nullable PendingIntent pendingIntent) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(key);
|
AppUpdateStatus entry = appMapping.get(canonicalUrl);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
updateApkInternal(entry, status, pendingIntent);
|
updateApkInternal(entry, status, pendingIntent);
|
||||||
}
|
}
|
||||||
@ -369,9 +367,9 @@ public final class AppUpdateStatusManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Apk getApk(String key) {
|
public Apk getApk(String canonicalUrl) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(key);
|
AppUpdateStatus entry = appMapping.get(canonicalUrl);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
return entry.apk;
|
return entry.apk;
|
||||||
}
|
}
|
||||||
@ -382,13 +380,13 @@ public final class AppUpdateStatusManager {
|
|||||||
/**
|
/**
|
||||||
* Remove an APK from being tracked, since it is now considered {@link Status#Installed}
|
* Remove an APK from being tracked, since it is now considered {@link Status#Installed}
|
||||||
*
|
*
|
||||||
* @param key the unique ID for the install process, also called {@code urlString}
|
* @param canonicalUrl the unique ID for the install process
|
||||||
* @see org.fdroid.fdroid.installer.InstallManagerService
|
* @see org.fdroid.fdroid.installer.InstallManagerService
|
||||||
*/
|
*/
|
||||||
public void removeApk(String key) {
|
public void removeApk(String canonicalUrl) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
InstallManagerService.removePendingInstall(context, key);
|
InstallManagerService.removePendingInstall(context, canonicalUrl);
|
||||||
AppUpdateStatus entry = appMapping.remove(key);
|
AppUpdateStatus entry = appMapping.remove(canonicalUrl);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
Utils.debugLog(LOGTAG, "Remove APK " + entry.apk.apkName);
|
Utils.debugLog(LOGTAG, "Remove APK " + entry.apk.apkName);
|
||||||
notifyRemove(entry);
|
notifyRemove(entry);
|
||||||
@ -396,9 +394,9 @@ public final class AppUpdateStatusManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshApk(String key) {
|
public void refreshApk(String canonicalUrl) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(key);
|
AppUpdateStatus entry = appMapping.get(canonicalUrl);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
Utils.debugLog(LOGTAG, "Refresh APK " + entry.apk.apkName);
|
Utils.debugLog(LOGTAG, "Refresh APK " + entry.apk.apkName);
|
||||||
notifyChange(entry, true);
|
notifyChange(entry, true);
|
||||||
@ -406,9 +404,9 @@ public final class AppUpdateStatusManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateApkProgress(String key, long max, long current) {
|
public void updateApkProgress(String canonicalUrl, long max, long current) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(key);
|
AppUpdateStatus entry = appMapping.get(canonicalUrl);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
entry.progressMax = max;
|
entry.progressMax = max;
|
||||||
entry.progressCurrent = current;
|
entry.progressCurrent = current;
|
||||||
@ -420,22 +418,22 @@ public final class AppUpdateStatusManager {
|
|||||||
/**
|
/**
|
||||||
* @param errorText If null, then it is likely because the user cancelled the download.
|
* @param errorText If null, then it is likely because the user cancelled the download.
|
||||||
*/
|
*/
|
||||||
public void setDownloadError(String url, @Nullable String errorText) {
|
public void setDownloadError(String canonicalUrl, @Nullable String errorText) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(url);
|
AppUpdateStatus entry = appMapping.get(canonicalUrl);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
entry.status = Status.DownloadInterrupted;
|
entry.status = Status.DownloadInterrupted;
|
||||||
entry.errorText = errorText;
|
entry.errorText = errorText;
|
||||||
entry.intent = null;
|
entry.intent = null;
|
||||||
notifyChange(entry, true);
|
notifyChange(entry, true);
|
||||||
removeApk(url);
|
removeApk(canonicalUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setApkError(Apk apk, String errorText) {
|
public void setApkError(Apk apk, String errorText) {
|
||||||
synchronized (appMapping) {
|
synchronized (appMapping) {
|
||||||
AppUpdateStatus entry = appMapping.get(apk.getUrl());
|
AppUpdateStatus entry = appMapping.get(apk.getCanonicalUrl());
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
entry = createAppEntry(apk, Status.InstallError, null);
|
entry = createAppEntry(apk, Status.InstallError, null);
|
||||||
}
|
}
|
||||||
@ -444,7 +442,7 @@ public final class AppUpdateStatusManager {
|
|||||||
entry.intent = getAppErrorIntent(entry);
|
entry.intent = getAppErrorIntent(entry);
|
||||||
notifyChange(entry, false);
|
notifyChange(entry, false);
|
||||||
|
|
||||||
InstallManagerService.removePendingInstall(context, entry.getUniqueKey());
|
InstallManagerService.removePendingInstall(context, entry.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ public class IndexUpdater {
|
|||||||
JarFile jarFile = new JarFile(downloadedFile, true);
|
JarFile jarFile = new JarFile(downloadedFile, true);
|
||||||
JarEntry indexEntry = (JarEntry) jarFile.getEntry(IndexUpdater.DATA_FILE_NAME);
|
JarEntry indexEntry = (JarEntry) jarFile.getEntry(IndexUpdater.DATA_FILE_NAME);
|
||||||
indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry),
|
indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry),
|
||||||
processIndexListener, repo.address, (int) indexEntry.getSize());
|
processIndexListener, (int) indexEntry.getSize());
|
||||||
|
|
||||||
// Process the index...
|
// Process the index...
|
||||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||||
@ -254,14 +254,14 @@ public class IndexUpdater {
|
|||||||
|
|
||||||
protected final ProgressListener downloadListener = new ProgressListener() {
|
protected final ProgressListener downloadListener = new ProgressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(String urlString, long bytesRead, long totalBytes) {
|
public void onProgress(long bytesRead, long totalBytes) {
|
||||||
UpdateService.reportDownloadProgress(context, IndexUpdater.this, bytesRead, totalBytes);
|
UpdateService.reportDownloadProgress(context, IndexUpdater.this, bytesRead, totalBytes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected final ProgressListener processIndexListener = new ProgressListener() {
|
protected final ProgressListener processIndexListener = new ProgressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(String urlString, long bytesRead, long totalBytes) {
|
public void onProgress(long bytesRead, long totalBytes) {
|
||||||
UpdateService.reportProcessIndexProgress(context, IndexUpdater.this, bytesRead, totalBytes);
|
UpdateService.reportProcessIndexProgress(context, IndexUpdater.this, bytesRead, totalBytes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -198,7 +198,7 @@ public class IndexV1Updater extends IndexUpdater {
|
|||||||
JarFile jarFile = new JarFile(outputFile, true);
|
JarFile jarFile = new JarFile(outputFile, true);
|
||||||
JarEntry indexEntry = (JarEntry) jarFile.getEntry(DATA_FILE_NAME);
|
JarEntry indexEntry = (JarEntry) jarFile.getEntry(DATA_FILE_NAME);
|
||||||
InputStream indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry),
|
InputStream indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry),
|
||||||
processIndexListener, repo.address, (int) indexEntry.getSize());
|
processIndexListener, (int) indexEntry.getSize());
|
||||||
processIndexV1(indexInputStream, indexEntry, cacheTag);
|
processIndexV1(indexInputStream, indexEntry, cacheTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver {
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(context);
|
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(context);
|
||||||
String notificationKey = intent.getStringExtra(NotificationHelper.EXTRA_NOTIFICATION_KEY);
|
String canonicalUrl = intent.getStringExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case NotificationHelper.BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED:
|
case NotificationHelper.BROADCAST_NOTIFICATIONS_ALL_UPDATES_CLEARED:
|
||||||
manager.clearAllUpdates();
|
manager.clearAllUpdates();
|
||||||
@ -25,13 +25,13 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver {
|
|||||||
case NotificationHelper.BROADCAST_NOTIFICATIONS_UPDATE_CLEARED:
|
case NotificationHelper.BROADCAST_NOTIFICATIONS_UPDATE_CLEARED:
|
||||||
// If clearing apps in state "InstallError" (like when auto-cancelling) we
|
// If clearing apps in state "InstallError" (like when auto-cancelling) we
|
||||||
// remove them from the status manager entirely.
|
// remove them from the status manager entirely.
|
||||||
AppUpdateStatusManager.AppUpdateStatus appUpdateStatus = manager.get(notificationKey);
|
AppUpdateStatusManager.AppUpdateStatus appUpdateStatus = manager.get(canonicalUrl);
|
||||||
if (appUpdateStatus != null && appUpdateStatus.status == AppUpdateStatusManager.Status.InstallError) {
|
if (appUpdateStatus != null && appUpdateStatus.status == AppUpdateStatusManager.Status.InstallError) {
|
||||||
manager.removeApk(notificationKey);
|
manager.removeApk(canonicalUrl);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotificationHelper.BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED:
|
case NotificationHelper.BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED:
|
||||||
manager.removeApk(notificationKey);
|
manager.removeApk(canonicalUrl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,6 @@ class NotificationHelper {
|
|||||||
private static final int MAX_UPDATES_TO_SHOW = 5;
|
private static final int MAX_UPDATES_TO_SHOW = 5;
|
||||||
private static final int MAX_INSTALLED_TO_SHOW = 10;
|
private static final int MAX_INSTALLED_TO_SHOW = 10;
|
||||||
|
|
||||||
/**
|
|
||||||
* Unique ID used to represent this specific package's install process,
|
|
||||||
* including {@link Notification}s, also known as {@code urlString}.
|
|
||||||
*
|
|
||||||
* @see org.fdroid.fdroid.installer.InstallManagerService
|
|
||||||
*/
|
|
||||||
static final String EXTRA_NOTIFICATION_KEY = "key";
|
|
||||||
private static final String GROUP_UPDATES = "updates";
|
private static final String GROUP_UPDATES = "updates";
|
||||||
private static final String GROUP_INSTALLED = "installed";
|
private static final String GROUP_INSTALLED = "installed";
|
||||||
|
|
||||||
@ -93,14 +86,14 @@ class NotificationHelper {
|
|||||||
case AppUpdateStatusManager.BROADCAST_APPSTATUS_ADDED:
|
case AppUpdateStatusManager.BROADCAST_APPSTATUS_ADDED:
|
||||||
updateStatusLists();
|
updateStatusLists();
|
||||||
createSummaryNotifications();
|
createSummaryNotifications();
|
||||||
url = intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL);
|
url = intent.getStringExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
entry = appUpdateStatusManager.get(url);
|
entry = appUpdateStatusManager.get(url);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
createNotification(entry);
|
createNotification(entry);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AppUpdateStatusManager.BROADCAST_APPSTATUS_CHANGED:
|
case AppUpdateStatusManager.BROADCAST_APPSTATUS_CHANGED:
|
||||||
url = intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL);
|
url = intent.getStringExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
entry = appUpdateStatusManager.get(url);
|
entry = appUpdateStatusManager.get(url);
|
||||||
updateStatusLists();
|
updateStatusLists();
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
@ -111,7 +104,7 @@ class NotificationHelper {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED:
|
case AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED:
|
||||||
url = intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL);
|
url = intent.getStringExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
notificationManager.cancel(url, NOTIFY_ID_INSTALLED);
|
notificationManager.cancel(url, NOTIFY_ID_INSTALLED);
|
||||||
notificationManager.cancel(url, NOTIFY_ID_UPDATES);
|
notificationManager.cancel(url, NOTIFY_ID_UPDATES);
|
||||||
updateStatusLists();
|
updateStatusLists();
|
||||||
@ -164,8 +157,8 @@ class NotificationHelper {
|
|||||||
|
|
||||||
private void createNotification(AppUpdateStatusManager.AppUpdateStatus entry) {
|
private void createNotification(AppUpdateStatusManager.AppUpdateStatus entry) {
|
||||||
if (shouldIgnoreEntry(entry)) {
|
if (shouldIgnoreEntry(entry)) {
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_UPDATES);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_UPDATES);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_INSTALLED);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_INSTALLED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,23 +170,23 @@ class NotificationHelper {
|
|||||||
if (entry.status == AppUpdateStatusManager.Status.Installed) {
|
if (entry.status == AppUpdateStatusManager.Status.Installed) {
|
||||||
if (useStackedNotifications()) {
|
if (useStackedNotifications()) {
|
||||||
notification = createInstalledNotification(entry);
|
notification = createInstalledNotification(entry);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_UPDATES);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_UPDATES);
|
||||||
notificationManager.notify(entry.getUniqueKey(), NOTIFY_ID_INSTALLED, notification);
|
notificationManager.notify(entry.getCanonicalUrl(), NOTIFY_ID_INSTALLED, notification);
|
||||||
} else if (installed.size() == 1) {
|
} else if (installed.size() == 1) {
|
||||||
notification = createInstalledNotification(entry);
|
notification = createInstalledNotification(entry);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_UPDATES);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_UPDATES);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_INSTALLED);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_INSTALLED);
|
||||||
notificationManager.notify(GROUP_INSTALLED, NOTIFY_ID_INSTALLED, notification);
|
notificationManager.notify(GROUP_INSTALLED, NOTIFY_ID_INSTALLED, notification);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (useStackedNotifications()) {
|
if (useStackedNotifications()) {
|
||||||
notification = createUpdateNotification(entry);
|
notification = createUpdateNotification(entry);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_INSTALLED);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_INSTALLED);
|
||||||
notificationManager.notify(entry.getUniqueKey(), NOTIFY_ID_UPDATES, notification);
|
notificationManager.notify(entry.getCanonicalUrl(), NOTIFY_ID_UPDATES, notification);
|
||||||
} else if (updates.size() == 1) {
|
} else if (updates.size() == 1) {
|
||||||
notification = createUpdateNotification(entry);
|
notification = createUpdateNotification(entry);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_UPDATES);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_UPDATES);
|
||||||
notificationManager.cancel(entry.getUniqueKey(), NOTIFY_ID_INSTALLED);
|
notificationManager.cancel(entry.getCanonicalUrl(), NOTIFY_ID_INSTALLED);
|
||||||
notificationManager.notify(GROUP_UPDATES, NOTIFY_ID_UPDATES, notification);
|
notificationManager.notify(GROUP_UPDATES, NOTIFY_ID_UPDATES, notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,7 +339,7 @@ class NotificationHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_UPDATE_CLEARED);
|
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_UPDATE_CLEARED);
|
||||||
intentDeleted.putExtra(EXTRA_NOTIFICATION_KEY, entry.getUniqueKey());
|
intentDeleted.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, entry.getCanonicalUrl());
|
||||||
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
|
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
|
||||||
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
builder.setDeleteIntent(piDeleted);
|
builder.setDeleteIntent(piDeleted);
|
||||||
@ -435,7 +428,7 @@ class NotificationHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED);
|
Intent intentDeleted = new Intent(BROADCAST_NOTIFICATIONS_INSTALLED_CLEARED);
|
||||||
intentDeleted.putExtra(EXTRA_NOTIFICATION_KEY, entry.getUniqueKey());
|
intentDeleted.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, entry.getCanonicalUrl());
|
||||||
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
|
intentDeleted.setClass(context, NotificationBroadcastReceiver.class);
|
||||||
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent piDeleted = PendingIntent.getBroadcast(context, 0, intentDeleted, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
builder.setDeleteIntent(piDeleted);
|
builder.setDeleteIntent(piDeleted);
|
||||||
@ -540,7 +533,7 @@ class NotificationHelper {
|
|||||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||||
// Need to check that the notification is still valid, and also that the image
|
// Need to check that the notification is still valid, and also that the image
|
||||||
// is indeed cached now, so we won't get stuck in an endless loop.
|
// is indeed cached now, so we won't get stuck in an endless loop.
|
||||||
AppUpdateStatusManager.AppUpdateStatus oldEntry = appUpdateStatusManager.get(entry.getUniqueKey());
|
AppUpdateStatusManager.AppUpdateStatus oldEntry = appUpdateStatusManager.get(entry.getCanonicalUrl());
|
||||||
if (oldEntry != null
|
if (oldEntry != null
|
||||||
&& oldEntry.app != null
|
&& oldEntry.app != null
|
||||||
&& oldEntry.app.iconUrl != null
|
&& oldEntry.app.iconUrl != null
|
||||||
|
@ -9,7 +9,6 @@ import java.io.InputStream;
|
|||||||
class ProgressBufferedInputStream extends BufferedInputStream {
|
class ProgressBufferedInputStream extends BufferedInputStream {
|
||||||
|
|
||||||
private final ProgressListener progressListener;
|
private final ProgressListener progressListener;
|
||||||
private final String urlString;
|
|
||||||
private final int totalBytes;
|
private final int totalBytes;
|
||||||
|
|
||||||
private int currentBytes;
|
private int currentBytes;
|
||||||
@ -18,10 +17,9 @@ class ProgressBufferedInputStream extends BufferedInputStream {
|
|||||||
* Reports progress to the specified {@link ProgressListener}, with the
|
* Reports progress to the specified {@link ProgressListener}, with the
|
||||||
* progress based on the {@code totalBytes}.
|
* progress based on the {@code totalBytes}.
|
||||||
*/
|
*/
|
||||||
ProgressBufferedInputStream(InputStream in, ProgressListener progressListener, String urlString, int totalBytes) {
|
ProgressBufferedInputStream(InputStream in, ProgressListener progressListener, int totalBytes) {
|
||||||
super(in);
|
super(in);
|
||||||
this.progressListener = progressListener;
|
this.progressListener = progressListener;
|
||||||
this.urlString = urlString;
|
|
||||||
this.totalBytes = totalBytes;
|
this.totalBytes = totalBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +31,7 @@ class ProgressBufferedInputStream extends BufferedInputStream {
|
|||||||
* the digits changing because it looks pretty, < 9000 since the reads won't
|
* the digits changing because it looks pretty, < 9000 since the reads won't
|
||||||
* line up exactly */
|
* line up exactly */
|
||||||
if (currentBytes % 333333 < 9000) {
|
if (currentBytes % 333333 < 9000) {
|
||||||
progressListener.onProgress(urlString, currentBytes, totalBytes);
|
progressListener.onProgress(currentBytes, totalBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.read(buffer, byteOffset, byteCount);
|
return super.read(buffer, byteOffset, byteCount);
|
||||||
|
@ -19,6 +19,6 @@ import java.net.URL;
|
|||||||
*/
|
*/
|
||||||
public interface ProgressListener {
|
public interface ProgressListener {
|
||||||
|
|
||||||
void onProgress(String urlString, long bytesRead, long totalBytes);
|
void onProgress(long bytesRead, long totalBytes);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -263,8 +263,17 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URL that points to the canonical download source for this
|
||||||
|
* package. This is also used as the unique ID for tracking downloading,
|
||||||
|
* progress, and notifications throughout the whole install process. It
|
||||||
|
* is guaranteed to uniquely represent this file since it points to a file
|
||||||
|
* on the file system of the canonical webserver.
|
||||||
|
*
|
||||||
|
* @see org.fdroid.fdroid.installer.InstallManagerService
|
||||||
|
*/
|
||||||
@JsonIgnore // prevent tests from failing due to nulls in checkRepoAddress()
|
@JsonIgnore // prevent tests from failing due to nulls in checkRepoAddress()
|
||||||
public String getUrl() {
|
public String getCanonicalUrl() {
|
||||||
checkRepoAddress();
|
checkRepoAddress();
|
||||||
return repoAddress + "/" + apkName.replace(" ", "%20");
|
return repoAddress + "/" + apkName.replace(" ", "%20");
|
||||||
}
|
}
|
||||||
@ -527,7 +536,7 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
|
|||||||
public File getMediaInstallPath(Context context) {
|
public File getMediaInstallPath(Context context) {
|
||||||
File path = Environment.getExternalStoragePublicDirectory(
|
File path = Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_DOWNLOADS); // Default for all other non-apk/media files
|
Environment.DIRECTORY_DOWNLOADS); // Default for all other non-apk/media files
|
||||||
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(this.getUrl());
|
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(this.getCanonicalUrl());
|
||||||
if (TextUtils.isEmpty(fileExtension)) return path;
|
if (TextUtils.isEmpty(fileExtension)) return path;
|
||||||
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||||
String[] mimeType = mimeTypeMap.getMimeTypeFromExtension(fileExtension).split("/");
|
String[] mimeType = mimeTypeMap.getMimeTypeFromExtension(fileExtension).split("/");
|
||||||
|
@ -237,7 +237,7 @@ public class InstalledAppProviderService extends JobIntentService {
|
|||||||
PackageInfo packageInfo = getPackageInfo(intent, packageName);
|
PackageInfo packageInfo = getPackageInfo(intent, packageName);
|
||||||
if (packageInfo != null) {
|
if (packageInfo != null) {
|
||||||
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(packageName)) {
|
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(packageName)) {
|
||||||
ausm.updateApk(status.getUniqueKey(), AppUpdateStatusManager.Status.Installed, null);
|
ausm.updateApk(status.getCanonicalUrl(), AppUpdateStatusManager.Status.Installed, null);
|
||||||
}
|
}
|
||||||
File apk = getPathToInstalledApk(packageInfo);
|
File apk = getPathToInstalledApk(packageInfo);
|
||||||
if (apk == null) {
|
if (apk == null) {
|
||||||
@ -258,7 +258,7 @@ public class InstalledAppProviderService extends JobIntentService {
|
|||||||
} else if (ACTION_DELETE.equals(action)) {
|
} else if (ACTION_DELETE.equals(action)) {
|
||||||
deleteAppFromDb(this, packageName);
|
deleteAppFromDb(this, packageName);
|
||||||
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(packageName)) {
|
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(packageName)) {
|
||||||
ausm.updateApk(status.getUniqueKey(), AppUpdateStatusManager.Status.InstallError, null);
|
ausm.updateApk(status.getCanonicalUrl(), AppUpdateStatusManager.Status.InstallError, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packageChangeNotifier.onNext(packageName);
|
packageChangeNotifier.onNext(packageName);
|
||||||
|
@ -113,10 +113,16 @@ public class ApkCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the full path for where an APK URL will be downloaded into.
|
* Get the full path for where an package URL will be downloaded into.
|
||||||
*/
|
*/
|
||||||
public static SanitizedFile getApkDownloadPath(Context context, String urlString) {
|
public static SanitizedFile getApkDownloadPath(Context context, String urlString) {
|
||||||
Uri uri = Uri.parse(urlString);
|
return getApkDownloadPath(context, Uri.parse(urlString));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full path for where an package URL will be downloaded into.
|
||||||
|
*/
|
||||||
|
public static SanitizedFile getApkDownloadPath(Context context, Uri uri) {
|
||||||
File dir = new File(getApkCacheDir(context), uri.getHost() + "-" + uri.getPort());
|
File dir = new File(getApkCacheDir(context), uri.getHost() + "-" + uri.getPort());
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
dir.mkdirs();
|
dir.mkdirs();
|
||||||
|
@ -43,11 +43,11 @@ public class DefaultInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installPackageInternal(Uri localApkUri, Uri downloadUri) {
|
protected void installPackageInternal(Uri localApkUri, Uri canonicalUri) {
|
||||||
|
|
||||||
Intent installIntent = new Intent(context, DefaultInstallerActivity.class);
|
Intent installIntent = new Intent(context, DefaultInstallerActivity.class);
|
||||||
installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE);
|
installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE);
|
||||||
installIntent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
|
installIntent.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, canonicalUri);
|
||||||
installIntent.putExtra(Installer.EXTRA_APK, apk);
|
installIntent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
installIntent.setData(localApkUri);
|
installIntent.setData(localApkUri);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ public class DefaultInstaller extends Installer {
|
|||||||
installIntent,
|
installIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_USER_INTERACTION,
|
sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_USER_INTERACTION,
|
||||||
installPendingIntent);
|
installPendingIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,10 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
private static final int REQUEST_CODE_INSTALL = 0;
|
private static final int REQUEST_CODE_INSTALL = 0;
|
||||||
private static final int REQUEST_CODE_UNINSTALL = 1;
|
private static final int REQUEST_CODE_UNINSTALL = 1;
|
||||||
|
|
||||||
private Uri downloadUri;
|
/**
|
||||||
|
* @see InstallManagerService
|
||||||
|
*/
|
||||||
|
private Uri canonicalUri;
|
||||||
|
|
||||||
// for the broadcasts
|
// for the broadcasts
|
||||||
private DefaultInstaller installer;
|
private DefaultInstaller installer;
|
||||||
@ -63,7 +66,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
installer = new DefaultInstaller(this, apk);
|
installer = new DefaultInstaller(this, apk);
|
||||||
if (ACTION_INSTALL_PACKAGE.equals(action)) {
|
if (ACTION_INSTALL_PACKAGE.equals(action)) {
|
||||||
Uri localApkUri = intent.getData();
|
Uri localApkUri = intent.getData();
|
||||||
downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
canonicalUri = intent.getParcelableExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
installPackage(localApkUri);
|
installPackage(localApkUri);
|
||||||
} else if (ACTION_UNINSTALL_PACKAGE.equals(action)) {
|
} else if (ACTION_UNINSTALL_PACKAGE.equals(action)) {
|
||||||
uninstallPackage(apk.packageName);
|
uninstallPackage(apk.packageName);
|
||||||
@ -120,7 +123,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
startActivityForResult(intent, REQUEST_CODE_INSTALL);
|
startActivityForResult(intent, REQUEST_CODE_INSTALL);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
Log.e(TAG, "ActivityNotFoundException", e);
|
Log.e(TAG, "ActivityNotFoundException", e);
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED,
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED,
|
||||||
"This Android rom does not support ACTION_INSTALL_PACKAGE!");
|
"This Android rom does not support ACTION_INSTALL_PACKAGE!");
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
@ -169,23 +172,23 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
* never executed on Androids < 4.0
|
* never executed on Androids < 4.0
|
||||||
*/
|
*/
|
||||||
if (Build.VERSION.SDK_INT < 14) {
|
if (Build.VERSION.SDK_INT < 14) {
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_COMPLETE);
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_COMPLETE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case Activity.RESULT_OK:
|
case Activity.RESULT_OK:
|
||||||
installer.sendBroadcastInstall(downloadUri,
|
installer.sendBroadcastInstall(canonicalUri,
|
||||||
Installer.ACTION_INSTALL_COMPLETE);
|
Installer.ACTION_INSTALL_COMPLETE);
|
||||||
break;
|
break;
|
||||||
case Activity.RESULT_CANCELED:
|
case Activity.RESULT_CANCELED:
|
||||||
installer.sendBroadcastInstall(downloadUri,
|
installer.sendBroadcastInstall(canonicalUri,
|
||||||
Installer.ACTION_INSTALL_INTERRUPTED);
|
Installer.ACTION_INSTALL_INTERRUPTED);
|
||||||
break;
|
break;
|
||||||
case Activity.RESULT_FIRST_USER:
|
case Activity.RESULT_FIRST_USER:
|
||||||
default:
|
default:
|
||||||
// AOSP returns Activity.RESULT_FIRST_USER on error
|
// AOSP returns Activity.RESULT_FIRST_USER on error
|
||||||
installer.sendBroadcastInstall(downloadUri,
|
installer.sendBroadcastInstall(canonicalUri,
|
||||||
Installer.ACTION_INSTALL_INTERRUPTED,
|
Installer.ACTION_INSTALL_INTERRUPTED,
|
||||||
getString(R.string.install_error_unknown));
|
getString(R.string.install_error_unknown));
|
||||||
break;
|
break;
|
||||||
|
@ -23,7 +23,6 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
|
|
||||||
@ -44,15 +43,15 @@ public class FileInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installPackage(Uri localApkUri, Uri downloadUri) {
|
public void installPackage(Uri localApkUri, Uri canonicalUri) {
|
||||||
installPackageInternal(localApkUri, downloadUri);
|
installPackageInternal(localApkUri, canonicalUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installPackageInternal(Uri localApkUri, Uri downloadUri) {
|
protected void installPackageInternal(Uri localApkUri, Uri canonicalUri) {
|
||||||
Intent installIntent = new Intent(context, FileInstallerActivity.class);
|
Intent installIntent = new Intent(context, FileInstallerActivity.class);
|
||||||
installIntent.setAction(FileInstallerActivity.ACTION_INSTALL_FILE);
|
installIntent.setAction(FileInstallerActivity.ACTION_INSTALL_FILE);
|
||||||
installIntent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
|
installIntent.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, canonicalUri);
|
||||||
installIntent.putExtra(Installer.EXTRA_APK, apk);
|
installIntent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
installIntent.setData(localApkUri);
|
installIntent.setData(localApkUri);
|
||||||
|
|
||||||
@ -62,7 +61,7 @@ public class FileInstaller extends Installer {
|
|||||||
installIntent,
|
installIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_USER_INTERACTION,
|
sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_USER_INTERACTION,
|
||||||
installPendingIntent);
|
installPendingIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import android.support.v4.content.ContextCompat;
|
|||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
@ -40,7 +39,10 @@ public class FileInstallerActivity extends FragmentActivity {
|
|||||||
|
|
||||||
private Apk apk;
|
private Apk apk;
|
||||||
private Uri localApkUri;
|
private Uri localApkUri;
|
||||||
private Uri downloadUri;
|
/**
|
||||||
|
* @see InstallManagerService
|
||||||
|
*/
|
||||||
|
private Uri canonicalUri;
|
||||||
|
|
||||||
private int act = 0;
|
private int act = 0;
|
||||||
|
|
||||||
@ -51,12 +53,12 @@ public class FileInstallerActivity extends FragmentActivity {
|
|||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
localApkUri = intent.getData();
|
localApkUri = intent.getData();
|
||||||
downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
canonicalUri = intent.getParcelableExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
installer = new FileInstaller(this, apk);
|
installer = new FileInstaller(this, apk);
|
||||||
if (ACTION_INSTALL_FILE.equals(action)) {
|
if (ACTION_INSTALL_FILE.equals(action)) {
|
||||||
if (hasStoragePermission()) {
|
if (hasStoragePermission()) {
|
||||||
installPackage(localApkUri, downloadUri, apk);
|
installPackage(localApkUri, canonicalUri, apk);
|
||||||
} else {
|
} else {
|
||||||
requestPermission();
|
requestPermission();
|
||||||
act = 1;
|
act = 1;
|
||||||
@ -110,7 +112,7 @@ public class FileInstallerActivity extends FragmentActivity {
|
|||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
if (act == 1) {
|
if (act == 1) {
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
||||||
} else if (act == 2) {
|
} else if (act == 2) {
|
||||||
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
|
||||||
}
|
}
|
||||||
@ -129,13 +131,13 @@ public class FileInstallerActivity extends FragmentActivity {
|
|||||||
if (grantResults.length > 0
|
if (grantResults.length > 0
|
||||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
if (act == 1) {
|
if (act == 1) {
|
||||||
installPackage(localApkUri, downloadUri, apk);
|
installPackage(localApkUri, canonicalUri, apk);
|
||||||
} else if (act == 2) {
|
} else if (act == 2) {
|
||||||
uninstallPackage(apk);
|
uninstallPackage(apk);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (act == 1) {
|
if (act == 1) {
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
||||||
} else if (act == 2) {
|
} else if (act == 2) {
|
||||||
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
|
||||||
}
|
}
|
||||||
@ -144,7 +146,7 @@ public class FileInstallerActivity extends FragmentActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installPackage(Uri localApkUri, Uri downloadUri, Apk apk) {
|
private void installPackage(Uri localApkUri, Uri canonicalUri, Apk apk) {
|
||||||
Utils.debugLog(TAG, "Installing: " + localApkUri.getPath());
|
Utils.debugLog(TAG, "Installing: " + localApkUri.getPath());
|
||||||
File path = apk.getMediaInstallPath(activity.getApplicationContext());
|
File path = apk.getMediaInstallPath(activity.getApplicationContext());
|
||||||
path.mkdirs();
|
path.mkdirs();
|
||||||
@ -152,15 +154,15 @@ public class FileInstallerActivity extends FragmentActivity {
|
|||||||
FileUtils.copyFileToDirectory(new File(localApkUri.getPath()), path);
|
FileUtils.copyFileToDirectory(new File(localApkUri.getPath()), path);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Utils.debugLog(TAG, "Failed to copy: " + e.getMessage());
|
Utils.debugLog(TAG, "Failed to copy: " + e.getMessage());
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
||||||
}
|
}
|
||||||
if (apk.isMediaInstalled(activity.getApplicationContext())) { // Copying worked
|
if (apk.isMediaInstalled(activity.getApplicationContext())) { // Copying worked
|
||||||
Utils.debugLog(TAG, "Copying worked: " + localApkUri.getPath());
|
Utils.debugLog(TAG, "Copying worked: " + localApkUri.getPath());
|
||||||
Toast.makeText(this, String.format(this.getString(R.string.app_installed_media), path.toString()),
|
Toast.makeText(this, String.format(this.getString(R.string.app_installed_media), path.toString()),
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_COMPLETE);
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_COMPLETE);
|
||||||
} else {
|
} else {
|
||||||
installer.sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
installer.sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED);
|
||||||
}
|
}
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -66,9 +65,10 @@ import java.io.IOException;
|
|||||||
* APK on different servers, signed by different keys, or even different builds.
|
* APK on different servers, signed by different keys, or even different builds.
|
||||||
* <p><ul>
|
* <p><ul>
|
||||||
* <li>for a {@link Uri} ID, use {@code Uri}, {@link Intent#getData()}
|
* <li>for a {@link Uri} ID, use {@code Uri}, {@link Intent#getData()}
|
||||||
* <li>for a {@code String} ID, use {@code urlString}, {@link Uri#toString()}, or
|
* <li>for a {@code String} ID, use {@code canonicalUrl}, {@link Uri#toString()}, or
|
||||||
* {@link Intent#getDataString()}
|
* {@link Intent#getDataString()}
|
||||||
* <li>for an {@code int} ID, use {@link String#hashCode()} or {@link Uri#hashCode()}
|
* <li>for an {@code int} ID, use {@link String#hashCode()} or {@link Uri#hashCode()}
|
||||||
|
* <li>for an {@link Intent} extra, use {@link org.fdroid.fdroid.net.Downloader#EXTRA_CANONICAL_URL}
|
||||||
* </ul></p>
|
* </ul></p>
|
||||||
* The implementations of {@link Uri#toString()} and {@link Intent#getDataString()} both
|
* The implementations of {@link Uri#toString()} and {@link Intent#getDataString()} both
|
||||||
* include caching of the generated {@code String}, so it should be plenty fast.
|
* include caching of the generated {@code String}, so it should be plenty fast.
|
||||||
@ -147,24 +147,25 @@ public class InstallManagerService extends Service {
|
|||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
Utils.debugLog(TAG, "onStartCommand " + intent);
|
Utils.debugLog(TAG, "onStartCommand " + intent);
|
||||||
|
|
||||||
String urlString = intent.getDataString();
|
String canonicalUrl = intent.getDataString();
|
||||||
if (TextUtils.isEmpty(urlString)) {
|
if (TextUtils.isEmpty(canonicalUrl)) {
|
||||||
Utils.debugLog(TAG, "empty urlString, nothing to do");
|
Utils.debugLog(TAG, "empty canonicalUrl, nothing to do");
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
|
||||||
if (ACTION_CANCEL.equals(action)) {
|
if (ACTION_CANCEL.equals(action)) {
|
||||||
DownloaderService.cancel(this, urlString);
|
DownloaderService.cancel(this, canonicalUrl);
|
||||||
Apk apk = appUpdateStatusManager.getApk(urlString);
|
Apk apk = appUpdateStatusManager.getApk(canonicalUrl);
|
||||||
if (apk != null) {
|
if (apk != null) {
|
||||||
|
Utils.debugLog(TAG, "also canceling OBB downloads");
|
||||||
DownloaderService.cancel(this, apk.getPatchObbUrl());
|
DownloaderService.cancel(this, apk.getPatchObbUrl());
|
||||||
DownloaderService.cancel(this, apk.getMainObbUrl());
|
DownloaderService.cancel(this, apk.getMainObbUrl());
|
||||||
}
|
}
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
} else if (ACTION_INSTALL.equals(action)) {
|
} else if (ACTION_INSTALL.equals(action)) {
|
||||||
if (!isPendingInstall(urlString)) {
|
if (!isPendingInstall(canonicalUrl)) {
|
||||||
Log.i(TAG, "Ignoring INSTALL that is not Pending Install: " + intent);
|
Log.i(TAG, "Ignoring INSTALL that is not Pending Install: " + intent);
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
@ -174,14 +175,14 @@ public class InstallManagerService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!intent.hasExtra(EXTRA_APP) || !intent.hasExtra(EXTRA_APK)) {
|
if (!intent.hasExtra(EXTRA_APP) || !intent.hasExtra(EXTRA_APK)) {
|
||||||
Utils.debugLog(TAG, urlString + " did not include both an App and Apk instance, ignoring");
|
Utils.debugLog(TAG, canonicalUrl + " did not include both an App and Apk instance, ignoring");
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
|
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
|
||||||
&& !DownloaderService.isQueuedOrActive(urlString)) {
|
&& !DownloaderService.isQueuedOrActive(canonicalUrl)) {
|
||||||
Utils.debugLog(TAG, urlString + " finished downloading while InstallManagerService was killed.");
|
Utils.debugLog(TAG, canonicalUrl + " finished downloading while InstallManagerService was killed.");
|
||||||
appUpdateStatusManager.removeApk(urlString);
|
appUpdateStatusManager.removeApk(canonicalUrl);
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,23 +206,23 @@ public class InstallManagerService extends Service {
|
|||||||
|
|
||||||
appUpdateStatusManager.addApk(apk, AppUpdateStatusManager.Status.Downloading, null);
|
appUpdateStatusManager.addApk(apk, AppUpdateStatusManager.Status.Downloading, null);
|
||||||
|
|
||||||
registerPackageDownloaderReceivers(urlString);
|
registerPackageDownloaderReceivers(canonicalUrl);
|
||||||
getObb(urlString, apk.getMainObbUrl(), apk.getMainObbFile(), apk.obbMainFileSha256);
|
getMainObb(canonicalUrl, apk);
|
||||||
getObb(urlString, apk.getPatchObbUrl(), apk.getPatchObbFile(), apk.obbPatchFileSha256);
|
getPatchObb(canonicalUrl, apk);
|
||||||
|
|
||||||
File apkFilePath = ApkCache.getApkDownloadPath(this, apk.getUrl());
|
File apkFilePath = ApkCache.getApkDownloadPath(this, apk.getCanonicalUrl());
|
||||||
long apkFileSize = apkFilePath.length();
|
long apkFileSize = apkFilePath.length();
|
||||||
if (!apkFilePath.exists() || apkFileSize < apk.size) {
|
if (!apkFilePath.exists() || apkFileSize < apk.size) {
|
||||||
Utils.debugLog(TAG, "download " + urlString + " " + apkFilePath);
|
Utils.debugLog(TAG, "download " + canonicalUrl + " " + apkFilePath);
|
||||||
DownloaderService.queue(this, switchUrlToNewMirror(urlString, apk.repoId), apk.repoId, urlString);
|
DownloaderService.queueUsingRandomMirror(this, apk.repoId, canonicalUrl);
|
||||||
} else if (ApkCache.apkIsCached(apkFilePath, apk)) {
|
} else if (ApkCache.apkIsCached(apkFilePath, apk)) {
|
||||||
Utils.debugLog(TAG, "skip download, we have it, straight to install " + urlString + " " + apkFilePath);
|
Utils.debugLog(TAG, "skip download, we have it, straight to install " + canonicalUrl + " " + apkFilePath);
|
||||||
sendBroadcast(intent.getData(), Downloader.ACTION_STARTED, apkFilePath);
|
sendBroadcast(intent.getData(), Downloader.ACTION_STARTED, apkFilePath);
|
||||||
sendBroadcast(intent.getData(), Downloader.ACTION_COMPLETE, apkFilePath);
|
sendBroadcast(intent.getData(), Downloader.ACTION_COMPLETE, apkFilePath);
|
||||||
} else {
|
} else {
|
||||||
Utils.debugLog(TAG, "delete and download again " + urlString + " " + apkFilePath);
|
Utils.debugLog(TAG, "delete and download again " + canonicalUrl + " " + apkFilePath);
|
||||||
apkFilePath.delete();
|
apkFilePath.delete();
|
||||||
DownloaderService.queue(this, switchUrlToNewMirror(urlString, apk.repoId), apk.repoId, urlString);
|
DownloaderService.queueUsingRandomMirror(this, apk.repoId, canonicalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return START_REDELIVER_INTENT; // if killed before completion, retry Intent
|
return START_REDELIVER_INTENT; // if killed before completion, retry Intent
|
||||||
@ -234,22 +235,12 @@ public class InstallManagerService extends Service {
|
|||||||
localBroadcastManager.sendBroadcast(intent);
|
localBroadcastManager.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void getMainObb(final String canonicalUrl, Apk apk) {
|
||||||
* Tries to return a version of {@code urlString} from a mirror, if there
|
getObb(canonicalUrl, apk.getMainObbUrl(), apk.getMainObbFile(), apk.obbMainFileSha256, apk.repoId);
|
||||||
* is an error, it just returns {@code urlString}.
|
|
||||||
*
|
|
||||||
* @see FDroidApp#getNewMirrorOnError(String, org.fdroid.fdroid.data.Repo)
|
|
||||||
*/
|
|
||||||
public String getNewMirrorOnError(@Nullable String urlString, long repoId) {
|
|
||||||
try {
|
|
||||||
return FDroidApp.getNewMirrorOnError(urlString, RepoProvider.Helper.findById(this, repoId));
|
|
||||||
} catch (IOException e) {
|
|
||||||
return urlString;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String switchUrlToNewMirror(@Nullable String urlString, long repoId) {
|
private void getPatchObb(final String canonicalUrl, Apk apk) {
|
||||||
return FDroidApp.switchUrlToNewMirror(urlString, RepoProvider.Helper.findById(this, repoId));
|
getObb(canonicalUrl, apk.getPatchObbUrl(), apk.getPatchObbFile(), apk.obbPatchFileSha256, apk.repoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,8 +250,8 @@ public class InstallManagerService extends Service {
|
|||||||
*
|
*
|
||||||
* @see <a href="https://developer.android.com/google/play/expansion-files.html">APK Expansion Files</a>
|
* @see <a href="https://developer.android.com/google/play/expansion-files.html">APK Expansion Files</a>
|
||||||
*/
|
*/
|
||||||
private void getObb(final String urlString, String obbUrlString,
|
private void getObb(final String canonicalUrl, String obbUrlString,
|
||||||
final File obbDestFile, final String hash) {
|
final File obbDestFile, final String hash, final long repoId) {
|
||||||
if (obbDestFile == null || obbDestFile.exists() || TextUtils.isEmpty(obbUrlString)) {
|
if (obbDestFile == null || obbDestFile.exists() || TextUtils.isEmpty(obbUrlString)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -278,7 +269,7 @@ public class InstallManagerService extends Service {
|
|||||||
|
|
||||||
long bytesRead = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
long bytesRead = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
long totalBytes = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
long totalBytes = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
appUpdateStatusManager.updateApkProgress(urlString, totalBytes, bytesRead);
|
appUpdateStatusManager.updateApkProgress(canonicalUrl, totalBytes, bytesRead);
|
||||||
} else if (Downloader.ACTION_COMPLETE.equals(action)) {
|
} else if (Downloader.ACTION_COMPLETE.equals(action)) {
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
||||||
@ -310,22 +301,22 @@ public class InstallManagerService extends Service {
|
|||||||
} else if (Downloader.ACTION_INTERRUPTED.equals(action)) {
|
} else if (Downloader.ACTION_INTERRUPTED.equals(action)) {
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
} else if (Downloader.ACTION_CONNECTION_FAILED.equals(action)) {
|
} else if (Downloader.ACTION_CONNECTION_FAILED.equals(action)) {
|
||||||
DownloaderService.queue(context, getNewMirrorOnError(urlString, 0), 0, urlString);
|
DownloaderService.queueUsingDifferentMirror(context, repoId, canonicalUrl);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("intent action not handled!");
|
throw new RuntimeException("intent action not handled!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
DownloaderService.queue(this, switchUrlToNewMirror(obbUrlString, 0), 0, obbUrlString);
|
DownloaderService.queueUsingRandomMirror(this, repoId, obbUrlString);
|
||||||
localBroadcastManager.registerReceiver(downloadReceiver,
|
localBroadcastManager.registerReceiver(downloadReceiver,
|
||||||
DownloaderService.getIntentFilter(obbUrlString));
|
DownloaderService.getIntentFilter(obbUrlString));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a {@link BroadcastReceiver} for tracking download progress for a
|
* Register a {@link BroadcastReceiver} for tracking download progress for a
|
||||||
* give {@code urlString}. There can be multiple of these registered at a time.
|
* give {@code canonicalUrl}. There can be multiple of these registered at a time.
|
||||||
*/
|
*/
|
||||||
private void registerPackageDownloaderReceivers(String urlString) {
|
private void registerPackageDownloaderReceivers(String canonicalUrl) {
|
||||||
|
|
||||||
BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
|
BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@ -334,52 +325,58 @@ public class InstallManagerService extends Service {
|
|||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Uri downloadUri = intent.getData();
|
Uri canonicalUri = intent.getData();
|
||||||
String urlString = downloadUri.toString();
|
String canonicalUrl = intent.getDataString();
|
||||||
long repoId = intent.getLongExtra(Downloader.EXTRA_REPO_ID, 0);
|
long repoId = intent.getLongExtra(Downloader.EXTRA_REPO_ID, 0);
|
||||||
String mirrorUrlString = intent.getStringExtra(Downloader.EXTRA_MIRROR_URL);
|
|
||||||
|
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Downloader.ACTION_STARTED:
|
case Downloader.ACTION_STARTED:
|
||||||
// App should currently be in the "PendingDownload" state, so this changes it to "Downloading".
|
// App should currently be in the "PendingDownload" state, so this changes it to "Downloading".
|
||||||
Intent intentObject = new Intent(context, InstallManagerService.class);
|
Intent intentObject = new Intent(context, InstallManagerService.class);
|
||||||
intentObject.setAction(ACTION_CANCEL);
|
intentObject.setAction(ACTION_CANCEL);
|
||||||
intentObject.setData(downloadUri);
|
intentObject.setData(canonicalUri);
|
||||||
PendingIntent action = PendingIntent.getService(context, 0, intentObject, 0);
|
PendingIntent action = PendingIntent.getService(context, 0, intentObject, 0);
|
||||||
appUpdateStatusManager.updateApk(urlString, AppUpdateStatusManager.Status.Downloading, action);
|
appUpdateStatusManager.updateApk(canonicalUrl,
|
||||||
|
AppUpdateStatusManager.Status.Downloading, action);
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_PROGRESS:
|
case Downloader.ACTION_PROGRESS:
|
||||||
long bytesRead = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
long bytesRead = intent.getLongExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||||
long totalBytes = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
long totalBytes = intent.getLongExtra(Downloader.EXTRA_TOTAL_BYTES, 0);
|
||||||
appUpdateStatusManager.updateApkProgress(urlString, totalBytes, bytesRead);
|
appUpdateStatusManager.updateApkProgress(canonicalUrl, totalBytes, bytesRead);
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_COMPLETE:
|
case Downloader.ACTION_COMPLETE:
|
||||||
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
File localFile = new File(intent.getStringExtra(Downloader.EXTRA_DOWNLOAD_PATH));
|
||||||
Uri localApkUri = Uri.fromFile(localFile);
|
Uri localApkUri = Uri.fromFile(localFile);
|
||||||
|
|
||||||
Utils.debugLog(TAG, "download completed of " + mirrorUrlString + " to " + localApkUri);
|
Utils.debugLog(TAG, "download completed of "
|
||||||
appUpdateStatusManager.updateApk(urlString, AppUpdateStatusManager.Status.ReadyToInstall, null);
|
+ intent.getStringExtra(Downloader.EXTRA_MIRROR_URL) + " to " + localApkUri);
|
||||||
|
appUpdateStatusManager.updateApk(canonicalUrl,
|
||||||
|
AppUpdateStatusManager.Status.ReadyToInstall, null);
|
||||||
|
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
registerInstallReceiver(downloadUri);
|
registerInstallReceiver(canonicalUrl);
|
||||||
|
|
||||||
Apk apk = appUpdateStatusManager.getApk(urlString);
|
Apk apk = appUpdateStatusManager.getApk(canonicalUrl);
|
||||||
if (apk != null) {
|
if (apk != null) {
|
||||||
InstallerService.install(context, localApkUri, downloadUri, apk);
|
InstallerService.install(context, localApkUri, canonicalUri, apk);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_INTERRUPTED:
|
case Downloader.ACTION_INTERRUPTED:
|
||||||
appUpdateStatusManager.setDownloadError(urlString, intent.getStringExtra(Downloader.EXTRA_ERROR_MESSAGE));
|
appUpdateStatusManager.setDownloadError(canonicalUrl,
|
||||||
|
intent.getStringExtra(Downloader.EXTRA_ERROR_MESSAGE));
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
break;
|
break;
|
||||||
case Downloader.ACTION_CONNECTION_FAILED:
|
case Downloader.ACTION_CONNECTION_FAILED:
|
||||||
|
// TODO move this logic into DownloaderService to hide the mirror URL stuff from this class
|
||||||
try {
|
try {
|
||||||
String currentUrlString = FDroidApp.getNewMirrorOnError(mirrorUrlString,
|
String currentUrlString = FDroidApp.getNewMirrorOnError(
|
||||||
|
intent.getStringExtra(Downloader.EXTRA_MIRROR_URL),
|
||||||
RepoProvider.Helper.findById(InstallManagerService.this, repoId));
|
RepoProvider.Helper.findById(InstallManagerService.this, repoId));
|
||||||
DownloaderService.queue(context, currentUrlString, repoId, urlString);
|
DownloaderService.queue(context, currentUrlString, repoId, canonicalUrl);
|
||||||
DownloaderService.setTimeout(FDroidApp.getTimeout());
|
DownloaderService.setTimeout(FDroidApp.getTimeout());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
appUpdateStatusManager.setDownloadError(urlString, intent.getStringExtra(Downloader.EXTRA_ERROR_MESSAGE));
|
appUpdateStatusManager.setDownloadError(canonicalUrl,
|
||||||
|
intent.getStringExtra(Downloader.EXTRA_ERROR_MESSAGE));
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -390,14 +387,14 @@ public class InstallManagerService extends Service {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localBroadcastManager.registerReceiver(downloadReceiver,
|
localBroadcastManager.registerReceiver(downloadReceiver,
|
||||||
DownloaderService.getIntentFilter(urlString));
|
DownloaderService.getIntentFilter(canonicalUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a {@link BroadcastReceiver} for tracking install progress for a
|
* Register a {@link BroadcastReceiver} for tracking install progress for a
|
||||||
* give {@link Uri}. There can be multiple of these registered at a time.
|
* give {@link Uri}. There can be multiple of these registered at a time.
|
||||||
*/
|
*/
|
||||||
private void registerInstallReceiver(Uri downloadUri) {
|
private void registerInstallReceiver(String canonicalUrl) {
|
||||||
|
|
||||||
BroadcastReceiver installReceiver = new BroadcastReceiver() {
|
BroadcastReceiver installReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@ -406,15 +403,17 @@ public class InstallManagerService extends Service {
|
|||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String downloadUrl = intent.getDataString();
|
String canonicalUrl = intent.getDataString();
|
||||||
Apk apk;
|
Apk apk;
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Installer.ACTION_INSTALL_STARTED:
|
case Installer.ACTION_INSTALL_STARTED:
|
||||||
appUpdateStatusManager.updateApk(downloadUrl, AppUpdateStatusManager.Status.Installing, null);
|
appUpdateStatusManager.updateApk(canonicalUrl,
|
||||||
|
AppUpdateStatusManager.Status.Installing, null);
|
||||||
break;
|
break;
|
||||||
case Installer.ACTION_INSTALL_COMPLETE:
|
case Installer.ACTION_INSTALL_COMPLETE:
|
||||||
appUpdateStatusManager.updateApk(downloadUrl, AppUpdateStatusManager.Status.Installed, null);
|
appUpdateStatusManager.updateApk(canonicalUrl,
|
||||||
Apk apkComplete = appUpdateStatusManager.getApk(downloadUrl);
|
AppUpdateStatusManager.Status.Installed, null);
|
||||||
|
Apk apkComplete = appUpdateStatusManager.getApk(canonicalUrl);
|
||||||
|
|
||||||
if (apkComplete != null && apkComplete.isApk()) {
|
if (apkComplete != null && apkComplete.isApk()) {
|
||||||
try {
|
try {
|
||||||
@ -432,7 +431,7 @@ public class InstallManagerService extends Service {
|
|||||||
if (!TextUtils.isEmpty(errorMessage)) {
|
if (!TextUtils.isEmpty(errorMessage)) {
|
||||||
appUpdateStatusManager.setApkError(apk, errorMessage);
|
appUpdateStatusManager.setApkError(apk, errorMessage);
|
||||||
} else {
|
} else {
|
||||||
appUpdateStatusManager.removeApk(downloadUrl);
|
appUpdateStatusManager.removeApk(canonicalUrl);
|
||||||
}
|
}
|
||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
break;
|
break;
|
||||||
@ -448,7 +447,7 @@ public class InstallManagerService extends Service {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localBroadcastManager.registerReceiver(installReceiver,
|
localBroadcastManager.registerReceiver(installReceiver,
|
||||||
Installer.getInstallIntentFilter(downloadUri));
|
Installer.getInstallIntentFilter(canonicalUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -464,23 +463,23 @@ public class InstallManagerService extends Service {
|
|||||||
* @param context this app's {@link Context}
|
* @param context this app's {@link Context}
|
||||||
*/
|
*/
|
||||||
public static void queue(Context context, App app, @NonNull Apk apk) {
|
public static void queue(Context context, App app, @NonNull Apk apk) {
|
||||||
String urlString = apk.getUrl();
|
String canonicalUrl = apk.getCanonicalUrl();
|
||||||
AppUpdateStatusManager.getInstance(context).addApk(apk, AppUpdateStatusManager.Status.PendingInstall, null);
|
AppUpdateStatusManager.getInstance(context).addApk(apk, AppUpdateStatusManager.Status.PendingInstall, null);
|
||||||
putPendingInstall(context, urlString, apk.packageName);
|
putPendingInstall(context, canonicalUrl, apk.packageName);
|
||||||
Utils.debugLog(TAG, "queue " + app.packageName + " " + apk.versionCode + " from " + urlString);
|
Utils.debugLog(TAG, "queue " + app.packageName + " " + apk.versionCode + " from " + canonicalUrl);
|
||||||
Intent intent = new Intent(context, InstallManagerService.class);
|
Intent intent = new Intent(context, InstallManagerService.class);
|
||||||
intent.setAction(ACTION_INSTALL);
|
intent.setAction(ACTION_INSTALL);
|
||||||
intent.setData(Uri.parse(urlString));
|
intent.setData(Uri.parse(canonicalUrl));
|
||||||
intent.putExtra(EXTRA_APP, app);
|
intent.putExtra(EXTRA_APP, app);
|
||||||
intent.putExtra(EXTRA_APK, apk);
|
intent.putExtra(EXTRA_APK, apk);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cancel(Context context, String urlString) {
|
public static void cancel(Context context, String canonicalUrl) {
|
||||||
removePendingInstall(context, urlString);
|
removePendingInstall(context, canonicalUrl);
|
||||||
Intent intent = new Intent(context, InstallManagerService.class);
|
Intent intent = new Intent(context, InstallManagerService.class);
|
||||||
intent.setAction(ACTION_CANCEL);
|
intent.setAction(ACTION_CANCEL);
|
||||||
intent.setData(Uri.parse(urlString));
|
intent.setData(Uri.parse(canonicalUrl));
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,29 +490,29 @@ public class InstallManagerService extends Service {
|
|||||||
* completed, or the device lost power in the middle of the install
|
* completed, or the device lost power in the middle of the install
|
||||||
* process.
|
* process.
|
||||||
*/
|
*/
|
||||||
public boolean isPendingInstall(String urlString) {
|
public boolean isPendingInstall(String canonicalUrl) {
|
||||||
return pendingInstalls.contains(urlString);
|
return pendingInstalls.contains(canonicalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a given APK as in the process of being installed, with
|
* Mark a given APK as in the process of being installed, with
|
||||||
* the {@code urlString} of the download used as the unique ID,
|
* the {@code canonicalUrl} of the download used as the unique ID,
|
||||||
* and the file hash used to verify that things are the same.
|
* and the file hash used to verify that things are the same.
|
||||||
*
|
*
|
||||||
* @see #isPendingInstall(String)
|
* @see #isPendingInstall(String)
|
||||||
*/
|
*/
|
||||||
public static void putPendingInstall(Context context, String urlString, String packageName) {
|
public static void putPendingInstall(Context context, String canonicalUrl, String packageName) {
|
||||||
if (pendingInstalls == null) {
|
if (pendingInstalls == null) {
|
||||||
pendingInstalls = getPendingInstalls(context);
|
pendingInstalls = getPendingInstalls(context);
|
||||||
}
|
}
|
||||||
pendingInstalls.edit().putString(urlString, packageName).apply();
|
pendingInstalls.edit().putString(canonicalUrl, packageName).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removePendingInstall(Context context, String urlString) {
|
public static void removePendingInstall(Context context, String canonicalUrl) {
|
||||||
if (pendingInstalls == null) {
|
if (pendingInstalls == null) {
|
||||||
pendingInstalls = getPendingInstalls(context);
|
pendingInstalls = getPendingInstalls(context);
|
||||||
}
|
}
|
||||||
pendingInstalls.edit().remove(urlString).apply();
|
pendingInstalls.edit().remove(canonicalUrl).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SharedPreferences getPendingInstalls(Context context) {
|
private static SharedPreferences getPendingInstalls(Context context) {
|
||||||
|
@ -64,15 +64,6 @@ public abstract class Installer {
|
|||||||
public static final String ACTION_UNINSTALL_INTERRUPTED = "org.fdroid.fdroid.installer.Installer.action.UNINSTALL_INTERRUPTED";
|
public static final String ACTION_UNINSTALL_INTERRUPTED = "org.fdroid.fdroid.installer.Installer.action.UNINSTALL_INTERRUPTED";
|
||||||
public static final String ACTION_UNINSTALL_USER_INTERACTION = "org.fdroid.fdroid.installer.Installer.action.UNINSTALL_USER_INTERACTION";
|
public static final String ACTION_UNINSTALL_USER_INTERACTION = "org.fdroid.fdroid.installer.Installer.action.UNINSTALL_USER_INTERACTION";
|
||||||
|
|
||||||
/**
|
|
||||||
* The URI where the APK was originally downloaded from. This is also used
|
|
||||||
* as the unique ID representing this in the whole install process in
|
|
||||||
* {@link InstallManagerService}, there is is generally known as the
|
|
||||||
* "download URL" since it is the URL used to download the APK.
|
|
||||||
*
|
|
||||||
* @see Intent#EXTRA_ORIGINATING_URI
|
|
||||||
*/
|
|
||||||
static final String EXTRA_DOWNLOAD_URI = "org.fdroid.fdroid.installer.Installer.extra.DOWNLOAD_URI";
|
|
||||||
public static final String EXTRA_APK = "org.fdroid.fdroid.installer.Installer.extra.APK";
|
public static final String EXTRA_APK = "org.fdroid.fdroid.installer.Installer.extra.APK";
|
||||||
public static final String EXTRA_USER_INTERACTION_PI = "org.fdroid.fdroid.installer.Installer.extra.USER_INTERACTION_PI";
|
public static final String EXTRA_USER_INTERACTION_PI = "org.fdroid.fdroid.installer.Installer.extra.USER_INTERACTION_PI";
|
||||||
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.installer.Installer.extra.ERROR_MESSAGE";
|
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.installer.Installer.extra.ERROR_MESSAGE";
|
||||||
@ -165,23 +156,23 @@ public abstract class Installer {
|
|||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastInstall(Uri downloadUri, String action, PendingIntent pendingIntent) {
|
void sendBroadcastInstall(Uri canonicalUri, String action, PendingIntent pendingIntent) {
|
||||||
sendBroadcastInstall(context, downloadUri, action, apk, pendingIntent, null);
|
sendBroadcastInstall(context, canonicalUri, action, apk, pendingIntent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastInstall(Uri downloadUri, String action) {
|
void sendBroadcastInstall(Uri canonicalUri, String action) {
|
||||||
sendBroadcastInstall(context, downloadUri, action, apk, null, null);
|
sendBroadcastInstall(context, canonicalUri, action, apk, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastInstall(Uri downloadUri, String action, String errorMessage) {
|
void sendBroadcastInstall(Uri canonicalUri, String action, String errorMessage) {
|
||||||
sendBroadcastInstall(context, downloadUri, action, apk, null, errorMessage);
|
sendBroadcastInstall(context, canonicalUri, action, apk, null, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sendBroadcastInstall(Context context,
|
static void sendBroadcastInstall(Context context,
|
||||||
Uri downloadUri, String action, Apk apk,
|
Uri canonicalUri, String action, Apk apk,
|
||||||
PendingIntent pendingIntent, String errorMessage) {
|
PendingIntent pendingIntent, String errorMessage) {
|
||||||
Intent intent = new Intent(action);
|
Intent intent = new Intent(action);
|
||||||
intent.setData(downloadUri);
|
intent.setData(canonicalUri);
|
||||||
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
|
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
|
||||||
intent.putExtra(Installer.EXTRA_APK, apk);
|
intent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
if (!TextUtils.isEmpty(errorMessage)) {
|
if (!TextUtils.isEmpty(errorMessage)) {
|
||||||
@ -226,20 +217,34 @@ public abstract class Installer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an {@link IntentFilter} for matching events from the install
|
* Gets an {@link IntentFilter} for matching events from the install
|
||||||
* process based on the original download URL as a {@link Uri}.
|
* process based on {@code canonicalUri}, which is the global unique
|
||||||
|
* ID for a package going through the install process.
|
||||||
|
*
|
||||||
|
* @see InstallManagerService for more about {@code canonicalUri}
|
||||||
*/
|
*/
|
||||||
public static IntentFilter getInstallIntentFilter(Uri uri) {
|
public static IntentFilter getInstallIntentFilter(Uri canonicalUri) {
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(Installer.ACTION_INSTALL_STARTED);
|
intentFilter.addAction(Installer.ACTION_INSTALL_STARTED);
|
||||||
intentFilter.addAction(Installer.ACTION_INSTALL_COMPLETE);
|
intentFilter.addAction(Installer.ACTION_INSTALL_COMPLETE);
|
||||||
intentFilter.addAction(Installer.ACTION_INSTALL_INTERRUPTED);
|
intentFilter.addAction(Installer.ACTION_INSTALL_INTERRUPTED);
|
||||||
intentFilter.addAction(Installer.ACTION_INSTALL_USER_INTERACTION);
|
intentFilter.addAction(Installer.ACTION_INSTALL_USER_INTERACTION);
|
||||||
intentFilter.addDataScheme(uri.getScheme());
|
intentFilter.addDataScheme(canonicalUri.getScheme());
|
||||||
intentFilter.addDataAuthority(uri.getHost(), String.valueOf(uri.getPort()));
|
intentFilter.addDataAuthority(canonicalUri.getHost(), String.valueOf(canonicalUri.getPort()));
|
||||||
intentFilter.addDataPath(uri.getPath(), PatternMatcher.PATTERN_LITERAL);
|
intentFilter.addDataPath(canonicalUri.getPath(), PatternMatcher.PATTERN_LITERAL);
|
||||||
return intentFilter;
|
return intentFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an {@link IntentFilter} for matching events from the install
|
||||||
|
* process based on {@code canonicalUrl}, which is the global unique
|
||||||
|
* ID for a package going through the install process.
|
||||||
|
*
|
||||||
|
* @see InstallManagerService for more about {@code canonicalUrl}
|
||||||
|
*/
|
||||||
|
public static IntentFilter getInstallIntentFilter(String canonicalUrl) {
|
||||||
|
return getInstallIntentFilter(Uri.parse(canonicalUrl));
|
||||||
|
}
|
||||||
|
|
||||||
public static IntentFilter getUninstallIntentFilter(String packageName) {
|
public static IntentFilter getUninstallIntentFilter(String packageName) {
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(Installer.ACTION_UNINSTALL_STARTED);
|
intentFilter.addAction(Installer.ACTION_UNINSTALL_STARTED);
|
||||||
@ -263,19 +268,19 @@ public abstract class Installer {
|
|||||||
* permissions that the APK is requesting.
|
* permissions that the APK is requesting.
|
||||||
*
|
*
|
||||||
* @param localApkUri points to the local copy of the APK to be installed
|
* @param localApkUri points to the local copy of the APK to be installed
|
||||||
* @param downloadUri serves as the unique ID for all actions related to the
|
* @param canonicalUri serves as the unique ID for all actions related to the
|
||||||
* installation of that specific APK
|
* installation of that specific APK
|
||||||
* @see InstallManagerService
|
* @see InstallManagerService
|
||||||
* @see <a href="https://issuetracker.google.com/issues/37091886">ACTION_INSTALL_PACKAGE Fails For Any Possible Uri</a>
|
* @see <a href="https://issuetracker.google.com/issues/37091886">ACTION_INSTALL_PACKAGE Fails For Any Possible Uri</a>
|
||||||
*/
|
*/
|
||||||
public void installPackage(Uri localApkUri, Uri downloadUri) {
|
public void installPackage(Uri localApkUri, Uri canonicalUri) {
|
||||||
Uri sanitizedUri;
|
Uri sanitizedUri;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sanitizedUri = ApkFileProvider.getSafeUri(context, localApkUri, apk);
|
sanitizedUri = ApkFileProvider.getSafeUri(context, localApkUri, apk);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Utils.debugLog(TAG, e.getMessage(), e);
|
Utils.debugLog(TAG, e.getMessage(), e);
|
||||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
|
sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +290,7 @@ public abstract class Installer {
|
|||||||
apkVerifier.verifyApk();
|
apkVerifier.verifyApk();
|
||||||
} catch (ApkVerifier.ApkVerificationException e) {
|
} catch (ApkVerifier.ApkVerificationException e) {
|
||||||
Utils.debugLog(TAG, e.getMessage(), e);
|
Utils.debugLog(TAG, e.getMessage(), e);
|
||||||
sendBroadcastInstall(downloadUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
|
sendBroadcastInstall(canonicalUri, Installer.ACTION_INSTALL_INTERRUPTED, e.getMessage());
|
||||||
return;
|
return;
|
||||||
} catch (ApkVerifier.ApkPermissionUnequalException e) {
|
} catch (ApkVerifier.ApkPermissionUnequalException e) {
|
||||||
// if permissions of apk are not the ones listed in the repo
|
// if permissions of apk are not the ones listed in the repo
|
||||||
@ -295,15 +300,15 @@ public abstract class Installer {
|
|||||||
Utils.debugLog(TAG, e.getMessage(), e);
|
Utils.debugLog(TAG, e.getMessage(), e);
|
||||||
Utils.debugLog(TAG, "Falling back to AOSP DefaultInstaller!");
|
Utils.debugLog(TAG, "Falling back to AOSP DefaultInstaller!");
|
||||||
DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
|
DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
|
||||||
defaultInstaller.installPackageInternal(sanitizedUri, downloadUri);
|
defaultInstaller.installPackageInternal(sanitizedUri, canonicalUri);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
installPackageInternal(sanitizedUri, downloadUri);
|
installPackageInternal(sanitizedUri, canonicalUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void installPackageInternal(Uri localApkUri, Uri downloadUri);
|
protected abstract void installPackageInternal(Uri localApkUri, Uri canonicalUri);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uninstall app as defined by {@link Installer#apk} in
|
* Uninstall app as defined by {@link Installer#apk} in
|
||||||
|
@ -28,10 +28,10 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.v4.app.JobIntentService;
|
import android.support.v4.app.JobIntentService;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||||
import org.fdroid.fdroid.views.AppDetailsActivity;
|
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.data.App;
|
import org.fdroid.fdroid.data.App;
|
||||||
|
import org.fdroid.fdroid.views.AppDetailsActivity;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
@ -74,8 +74,8 @@ public class InstallerService extends JobIntentService {
|
|||||||
|
|
||||||
if (ACTION_INSTALL.equals(intent.getAction())) {
|
if (ACTION_INSTALL.equals(intent.getAction())) {
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
Uri downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
Uri canonicalUri = intent.getParcelableExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL);
|
||||||
installer.installPackage(uri, downloadUri);
|
installer.installPackage(uri, canonicalUri);
|
||||||
} else if (ACTION_UNINSTALL.equals(intent.getAction())) {
|
} else if (ACTION_UNINSTALL.equals(intent.getAction())) {
|
||||||
installer.uninstallPackage();
|
installer.uninstallPackage();
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@ -113,17 +113,18 @@ public class InstallerService extends JobIntentService {
|
|||||||
*
|
*
|
||||||
* @param context this app's {@link Context}
|
* @param context this app's {@link Context}
|
||||||
* @param localApkUri {@link Uri} pointing to (downloaded) local apk file
|
* @param localApkUri {@link Uri} pointing to (downloaded) local apk file
|
||||||
* @param downloadUri {@link Uri} where the apk has been downloaded from
|
* @param canonicalUri {@link Uri} used as the global unique ID for the package
|
||||||
* @param apk apk object of app that should be installed
|
* @param apk apk object of app that should be installed
|
||||||
* @see #uninstall(Context, Apk)
|
* @see #uninstall(Context, Apk)
|
||||||
|
* @see InstallManagerService
|
||||||
*/
|
*/
|
||||||
public static void install(Context context, Uri localApkUri, Uri downloadUri, Apk apk) {
|
public static void install(Context context, Uri localApkUri, Uri canonicalUri, Apk apk) {
|
||||||
Installer.sendBroadcastInstall(context, downloadUri, Installer.ACTION_INSTALL_STARTED, apk,
|
Installer.sendBroadcastInstall(context, canonicalUri, Installer.ACTION_INSTALL_STARTED, apk,
|
||||||
null, null);
|
null, null);
|
||||||
Intent intent = new Intent(context, InstallerService.class);
|
Intent intent = new Intent(context, InstallerService.class);
|
||||||
intent.setAction(ACTION_INSTALL);
|
intent.setAction(ACTION_INSTALL);
|
||||||
intent.setData(localApkUri);
|
intent.setData(localApkUri);
|
||||||
intent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
|
intent.putExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL, canonicalUri);
|
||||||
intent.putExtra(Installer.EXTRA_APK, apk);
|
intent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
enqueueWork(context, intent);
|
enqueueWork(context, intent);
|
||||||
}
|
}
|
||||||
|
@ -308,7 +308,7 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void installPackageInternal(final Uri localApkUri, final Uri downloadUri) {
|
protected void installPackageInternal(final Uri localApkUri, final Uri canonicalUri) {
|
||||||
ServiceConnection mServiceConnection = new ServiceConnection() {
|
ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
|
IPrivilegedService privService = IPrivilegedService.Stub.asInterface(service);
|
||||||
@ -317,9 +317,9 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
@Override
|
@Override
|
||||||
public void handleResult(String packageName, int returnCode) throws RemoteException {
|
public void handleResult(String packageName, int returnCode) throws RemoteException {
|
||||||
if (returnCode == INSTALL_SUCCEEDED) {
|
if (returnCode == INSTALL_SUCCEEDED) {
|
||||||
sendBroadcastInstall(downloadUri, ACTION_INSTALL_COMPLETE);
|
sendBroadcastInstall(canonicalUri, ACTION_INSTALL_COMPLETE);
|
||||||
} else {
|
} else {
|
||||||
sendBroadcastInstall(downloadUri, ACTION_INSTALL_INTERRUPTED,
|
sendBroadcastInstall(canonicalUri, ACTION_INSTALL_INTERRUPTED,
|
||||||
"Error " + returnCode + ": "
|
"Error " + returnCode + ": "
|
||||||
+ INSTALL_RETURN_CODES.get(returnCode));
|
+ INSTALL_RETURN_CODES.get(returnCode));
|
||||||
}
|
}
|
||||||
@ -329,7 +329,7 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
try {
|
try {
|
||||||
boolean hasPermissions = privService.hasPrivilegedPermissions();
|
boolean hasPermissions = privService.hasPrivilegedPermissions();
|
||||||
if (!hasPermissions) {
|
if (!hasPermissions) {
|
||||||
sendBroadcastInstall(downloadUri, ACTION_INSTALL_INTERRUPTED,
|
sendBroadcastInstall(canonicalUri, ACTION_INSTALL_INTERRUPTED,
|
||||||
context.getString(R.string.system_install_denied_permissions));
|
context.getString(R.string.system_install_denied_permissions));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
null, callback);
|
null, callback);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "RemoteException", e);
|
Log.e(TAG, "RemoteException", e);
|
||||||
sendBroadcastInstall(downloadUri, ACTION_INSTALL_INTERRUPTED,
|
sendBroadcastInstall(canonicalUri, ACTION_INSTALL_INTERRUPTED,
|
||||||
"connecting to privileged service failed");
|
"connecting to privileged service failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,16 @@ public abstract class Downloader {
|
|||||||
public static final String EXTRA_BYTES_READ = "org.fdroid.fdroid.net.Downloader.extra.BYTES_READ";
|
public static final String EXTRA_BYTES_READ = "org.fdroid.fdroid.net.Downloader.extra.BYTES_READ";
|
||||||
public static final String EXTRA_TOTAL_BYTES = "org.fdroid.fdroid.net.Downloader.extra.TOTAL_BYTES";
|
public static final String EXTRA_TOTAL_BYTES = "org.fdroid.fdroid.net.Downloader.extra.TOTAL_BYTES";
|
||||||
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.Downloader.extra.ERROR_MESSAGE";
|
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.Downloader.extra.ERROR_MESSAGE";
|
||||||
public static final String EXTRA_REPO_ID = "org.fdroid.fdroid.net.Downloader.extra.ERROR_REPO_ID";
|
public static final String EXTRA_REPO_ID = "org.fdroid.fdroid.net.Downloader.extra.REPO_ID";
|
||||||
public static final String EXTRA_CANONICAL_URL = "org.fdroid.fdroid.net.Downloader.extra.ERROR_CANONICAL_URL";
|
public static final String EXTRA_MIRROR_URL = "org.fdroid.fdroid.net.Downloader.extra.MIRROR_URL";
|
||||||
public static final String EXTRA_MIRROR_URL = "org.fdroid.fdroid.net.Downloader.extra.ERROR_MIRROR_URL";
|
/**
|
||||||
|
* Unique ID used to represent this specific package's install process,
|
||||||
|
* including {@link android.app.Notification}s, also known as {@code canonicalUrl}.
|
||||||
|
*
|
||||||
|
* @see org.fdroid.fdroid.installer.InstallManagerService
|
||||||
|
* @see android.content.Intent#EXTRA_ORIGINATING_URI
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_CANONICAL_URL = "org.fdroid.fdroid.net.Downloader.extra.CANONICAL_URL";
|
||||||
|
|
||||||
public static final int DEFAULT_TIMEOUT = 10000;
|
public static final int DEFAULT_TIMEOUT = 10000;
|
||||||
public static final int SECOND_TIMEOUT = (int) DateUtils.MINUTE_IN_MILLIS;
|
public static final int SECOND_TIMEOUT = (int) DateUtils.MINUTE_IN_MILLIS;
|
||||||
@ -204,10 +211,16 @@ public abstract class Downloader {
|
|||||||
* Send progress updates on a timer to avoid flooding receivers with pointless events.
|
* Send progress updates on a timer to avoid flooding receivers with pointless events.
|
||||||
*/
|
*/
|
||||||
private final TimerTask progressTask = new TimerTask() {
|
private final TimerTask progressTask = new TimerTask() {
|
||||||
|
private long lastBytesRead = Long.MIN_VALUE;
|
||||||
|
private long lastTotalBytes = Long.MIN_VALUE;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (downloaderProgressListener != null) {
|
if (downloaderProgressListener != null
|
||||||
downloaderProgressListener.onProgress(urlString, bytesRead, totalBytes);
|
&& (bytesRead != lastBytesRead || totalBytes != lastTotalBytes)) {
|
||||||
|
downloaderProgressListener.onProgress(bytesRead, totalBytes);
|
||||||
|
lastBytesRead = bytesRead;
|
||||||
|
lastTotalBytes = totalBytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -32,9 +32,13 @@ import android.os.Process;
|
|||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.LogPrinter;
|
||||||
|
import org.fdroid.fdroid.BuildConfig;
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.ProgressListener;
|
import org.fdroid.fdroid.ProgressListener;
|
||||||
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.RepoProvider;
|
||||||
import org.fdroid.fdroid.data.SanitizedFile;
|
import org.fdroid.fdroid.data.SanitizedFile;
|
||||||
import org.fdroid.fdroid.installer.ApkCache;
|
import org.fdroid.fdroid.installer.ApkCache;
|
||||||
|
|
||||||
@ -73,17 +77,18 @@ import java.net.UnknownHostException;
|
|||||||
* long as necessary (and will not block the application's main loop), but
|
* long as necessary (and will not block the application's main loop), but
|
||||||
* only one request will be processed at a time.
|
* only one request will be processed at a time.
|
||||||
* <p>
|
* <p>
|
||||||
* The full URL for the file to download is also used as the unique ID to
|
* The Canonical URL for the file to download is also used as the unique ID to
|
||||||
* represent the download itself throughout F-Droid. This follows the model
|
* represent the download itself throughout F-Droid. This follows the model
|
||||||
* of {@link Intent#setData(Uri)}, where the core data of an {@code Intent} is
|
* of {@link Intent#setData(Uri)}, where the core data of an {@code Intent} is
|
||||||
* a {@code Uri}. For places that need an {@code int} ID,
|
* a {@code Uri}. For places that need an {@code int} ID,
|
||||||
* {@link String#hashCode()} should be used to get a reproducible, unique {@code int}
|
* {@link String#hashCode()} should be used to get a reproducible, unique {@code int}
|
||||||
* from any {@code urlString}. The full URL is guaranteed to be unique since
|
* from any {@code canonicalUrl}. That full URL is guaranteed to be unique since
|
||||||
* it points to a file on a filesystem. This is more important with media files
|
* it points to a file on a filesystem. This is more important with media files
|
||||||
* than with APKs since there is not reliable standard for a unique ID for
|
* than with APKs since there is not reliable standard for a unique ID for
|
||||||
* media files, unlike APKs with {@code packageName} and {@code versionCode}.
|
* media files, unlike APKs with {@code packageName} and {@code versionCode}.
|
||||||
*
|
*
|
||||||
* @see android.app.IntentService
|
* @see android.app.IntentService
|
||||||
|
* @see org.fdroid.fdroid.installer.InstallManagerService
|
||||||
*/
|
*/
|
||||||
public class DownloaderService extends Service {
|
public class DownloaderService extends Service {
|
||||||
private static final String TAG = "DownloaderService";
|
private static final String TAG = "DownloaderService";
|
||||||
@ -94,10 +99,13 @@ public class DownloaderService extends Service {
|
|||||||
private volatile Looper serviceLooper;
|
private volatile Looper serviceLooper;
|
||||||
private static volatile ServiceHandler serviceHandler;
|
private static volatile ServiceHandler serviceHandler;
|
||||||
private static volatile Downloader downloader;
|
private static volatile Downloader downloader;
|
||||||
|
private static volatile String activeCanonicalUrl;
|
||||||
private LocalBroadcastManager localBroadcastManager;
|
private LocalBroadcastManager localBroadcastManager;
|
||||||
private static volatile int timeout;
|
private static volatile int timeout;
|
||||||
|
|
||||||
private final class ServiceHandler extends Handler {
|
private final class ServiceHandler extends Handler {
|
||||||
|
static final String TAG = "ServiceHandler";
|
||||||
|
|
||||||
ServiceHandler(Looper looper) {
|
ServiceHandler(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
}
|
}
|
||||||
@ -119,6 +127,9 @@ public class DownloaderService extends Service {
|
|||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
serviceLooper = thread.getLooper();
|
serviceLooper = thread.getLooper();
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
serviceLooper.setMessageLogging(new LogPrinter(Log.DEBUG, ServiceHandler.TAG));
|
||||||
|
}
|
||||||
serviceHandler = new ServiceHandler(serviceLooper);
|
serviceHandler = new ServiceHandler(serviceLooper);
|
||||||
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
||||||
}
|
}
|
||||||
@ -131,21 +142,27 @@ public class DownloaderService extends Service {
|
|||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
String uriString = intent.getDataString();
|
String downloadUrl = intent.getDataString();
|
||||||
if (uriString == null) {
|
if (downloadUrl == null) {
|
||||||
Utils.debugLog(TAG, "Received Intent with no URI: " + intent);
|
Utils.debugLog(TAG, "Received Intent with no URI: " + intent);
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
String canonicalUrl = intent.getStringExtra(Downloader.EXTRA_CANONICAL_URL);
|
||||||
|
if (canonicalUrl == null) {
|
||||||
|
Utils.debugLog(TAG, "Received Intent with no EXTRA_CANONICAL_URL: " + intent);
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
if (ACTION_CANCEL.equals(intent.getAction())) {
|
if (ACTION_CANCEL.equals(intent.getAction())) {
|
||||||
Utils.debugLog(TAG, "Cancelling download of " + uriString);
|
Utils.debugLog(TAG, "Cancelling download of " + canonicalUrl.hashCode() + "/" + canonicalUrl
|
||||||
Integer whatToRemove = uriString.hashCode();
|
+ " downloading from " + downloadUrl);
|
||||||
|
Integer whatToRemove = canonicalUrl.hashCode();
|
||||||
if (serviceHandler.hasMessages(whatToRemove)) {
|
if (serviceHandler.hasMessages(whatToRemove)) {
|
||||||
Utils.debugLog(TAG, "Removing download with ID of " + whatToRemove
|
Utils.debugLog(TAG, "Removing download with ID of " + whatToRemove
|
||||||
+ " from service handler, then sending interrupted event.");
|
+ " from service handler, then sending interrupted event.");
|
||||||
serviceHandler.removeMessages(whatToRemove);
|
serviceHandler.removeMessages(whatToRemove);
|
||||||
sendBroadcast(intent.getData(), Downloader.ACTION_INTERRUPTED);
|
sendCancelledBroadcast(intent.getData(), canonicalUrl);
|
||||||
} else if (isActive(uriString)) {
|
} else if (isActive(canonicalUrl)) {
|
||||||
downloader.cancelDownload();
|
downloader.cancelDownload();
|
||||||
} else {
|
} else {
|
||||||
Utils.debugLog(TAG, "ACTION_CANCEL called on something not queued or running"
|
Utils.debugLog(TAG, "ACTION_CANCEL called on something not queued or running"
|
||||||
@ -155,9 +172,10 @@ public class DownloaderService extends Service {
|
|||||||
Message msg = serviceHandler.obtainMessage();
|
Message msg = serviceHandler.obtainMessage();
|
||||||
msg.arg1 = startId;
|
msg.arg1 = startId;
|
||||||
msg.obj = intent;
|
msg.obj = intent;
|
||||||
msg.what = uriString.hashCode();
|
msg.what = canonicalUrl.hashCode();
|
||||||
serviceHandler.sendMessage(msg);
|
serviceHandler.sendMessage(msg);
|
||||||
Utils.debugLog(TAG, "Queued download of " + uriString);
|
Utils.debugLog(TAG, "Queued download of " + canonicalUrl.hashCode() + "/" + canonicalUrl
|
||||||
|
+ " using " + downloadUrl);
|
||||||
} else {
|
} else {
|
||||||
Utils.debugLog(TAG, "Received Intent with unknown action: " + intent);
|
Utils.debugLog(TAG, "Received Intent with unknown action: " + intent);
|
||||||
}
|
}
|
||||||
@ -198,18 +216,19 @@ public class DownloaderService extends Service {
|
|||||||
*/
|
*/
|
||||||
private void handleIntent(Intent intent) {
|
private void handleIntent(Intent intent) {
|
||||||
final Uri uri = intent.getData();
|
final Uri uri = intent.getData();
|
||||||
long repoId = intent.getLongExtra(Downloader.EXTRA_REPO_ID, 0);
|
final long repoId = intent.getLongExtra(Downloader.EXTRA_REPO_ID, 0);
|
||||||
String canonicalUrlString = intent.getStringExtra(Downloader.EXTRA_CANONICAL_URL);
|
final Uri canonicalUrl = Uri.parse(intent.getStringExtra(Downloader.EXTRA_CANONICAL_URL));
|
||||||
final SanitizedFile localFile = ApkCache.getApkDownloadPath(this, canonicalUrlString);
|
final SanitizedFile localFile = ApkCache.getApkDownloadPath(this, canonicalUrl);
|
||||||
sendBroadcast(uri, Downloader.ACTION_STARTED, localFile, repoId, canonicalUrlString);
|
sendBroadcast(uri, Downloader.ACTION_STARTED, localFile, repoId, canonicalUrl);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
activeCanonicalUrl = canonicalUrl.toString();
|
||||||
downloader = DownloaderFactory.create(this, uri, localFile);
|
downloader = DownloaderFactory.create(this, uri, localFile);
|
||||||
downloader.setListener(new ProgressListener() {
|
downloader.setListener(new ProgressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgress(String urlString, long bytesRead, long totalBytes) {
|
public void onProgress(long bytesRead, long totalBytes) {
|
||||||
Intent intent = new Intent(Downloader.ACTION_PROGRESS);
|
Intent intent = new Intent(Downloader.ACTION_PROGRESS);
|
||||||
intent.setData(uri);
|
intent.setData(canonicalUrl);
|
||||||
intent.putExtra(Downloader.EXTRA_BYTES_READ, bytesRead);
|
intent.putExtra(Downloader.EXTRA_BYTES_READ, bytesRead);
|
||||||
intent.putExtra(Downloader.EXTRA_TOTAL_BYTES, totalBytes);
|
intent.putExtra(Downloader.EXTRA_TOTAL_BYTES, totalBytes);
|
||||||
localBroadcastManager.sendBroadcast(intent);
|
localBroadcastManager.sendBroadcast(intent);
|
||||||
@ -219,47 +238,44 @@ public class DownloaderService extends Service {
|
|||||||
downloader.download();
|
downloader.download();
|
||||||
if (downloader.isNotFound()) {
|
if (downloader.isNotFound()) {
|
||||||
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, localFile, getString(R.string.download_404),
|
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, localFile, getString(R.string.download_404),
|
||||||
repoId, canonicalUrlString);
|
repoId, canonicalUrl);
|
||||||
} else {
|
} else {
|
||||||
sendBroadcast(uri, Downloader.ACTION_COMPLETE, localFile, repoId, canonicalUrlString);
|
sendBroadcast(uri, Downloader.ACTION_COMPLETE, localFile, repoId, canonicalUrl);
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, localFile, repoId, canonicalUrlString);
|
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, localFile, repoId, canonicalUrl);
|
||||||
} catch (ConnectException | HttpRetryException | NoRouteToHostException | SocketTimeoutException
|
} catch (ConnectException | HttpRetryException | NoRouteToHostException | SocketTimeoutException
|
||||||
| SSLHandshakeException | SSLKeyException | SSLPeerUnverifiedException | SSLProtocolException
|
| SSLHandshakeException | SSLKeyException | SSLPeerUnverifiedException | SSLProtocolException
|
||||||
| ProtocolException | UnknownHostException e) {
|
| ProtocolException | UnknownHostException e) {
|
||||||
// if the above list of exceptions changes, also change it in IndexV1Updater.update()
|
// if the above list of exceptions changes, also change it in IndexV1Updater.update()
|
||||||
Log.e(TAG, e.getLocalizedMessage());
|
Log.e(TAG, e.getLocalizedMessage());
|
||||||
sendBroadcast(uri, Downloader.ACTION_CONNECTION_FAILED, localFile, repoId, canonicalUrlString);
|
sendBroadcast(uri, Downloader.ACTION_CONNECTION_FAILED, localFile, repoId, canonicalUrl);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, localFile,
|
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, localFile,
|
||||||
e.getLocalizedMessage(), repoId, canonicalUrlString);
|
e.getLocalizedMessage(), repoId, canonicalUrl);
|
||||||
} finally {
|
} finally {
|
||||||
if (downloader != null) {
|
if (downloader != null) {
|
||||||
downloader.close();
|
downloader.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
downloader = null;
|
downloader = null;
|
||||||
|
activeCanonicalUrl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendBroadcast(Uri uri, String action) {
|
private void sendCancelledBroadcast(Uri uri, String canonicalUrl) {
|
||||||
sendBroadcast(uri, action, null, null);
|
sendBroadcast(uri, Downloader.ACTION_INTERRUPTED, null, 0, Uri.parse(canonicalUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendBroadcast(Uri uri, String action, File file, long repoId, String originalUrlString) {
|
private void sendBroadcast(Uri uri, String action, File file, long repoId, Uri canonicalUrl) {
|
||||||
sendBroadcast(uri, action, file, null, repoId, originalUrlString);
|
sendBroadcast(uri, action, file, null, repoId, canonicalUrl);
|
||||||
}
|
|
||||||
|
|
||||||
private void sendBroadcast(Uri uri, String action, File file, String errorMessage) {
|
|
||||||
sendBroadcast(uri, action, file, errorMessage, 0, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendBroadcast(Uri uri, String action, File file, String errorMessage, long repoId,
|
private void sendBroadcast(Uri uri, String action, File file, String errorMessage, long repoId,
|
||||||
String originalUrlString) {
|
Uri canonicalUrl) {
|
||||||
Intent intent = new Intent(action);
|
Intent intent = new Intent(action);
|
||||||
if (originalUrlString != null) {
|
if (canonicalUrl != null) {
|
||||||
intent.setData(Uri.parse(originalUrlString));
|
intent.setData(canonicalUrl);
|
||||||
}
|
}
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
intent.putExtra(Downloader.EXTRA_DOWNLOAD_PATH, file.getAbsolutePath());
|
intent.putExtra(Downloader.EXTRA_DOWNLOAD_PATH, file.getAbsolutePath());
|
||||||
@ -278,41 +294,72 @@ public class DownloaderService extends Service {
|
|||||||
* All notifications are sent as an {@link Intent} via local broadcasts to be received by
|
* All notifications are sent as an {@link Intent} via local broadcasts to be received by
|
||||||
*
|
*
|
||||||
* @param context this app's {@link Context}
|
* @param context this app's {@link Context}
|
||||||
* @param mirrorUrlString The URL to add to the download queue
|
* @param mirrorUrl The URL to add to the download queue
|
||||||
* @param repoId the database ID number representing one repo
|
* @param repoId the database ID number representing one repo
|
||||||
* @param urlString the URL used as the unique ID throughout F-Droid
|
* @param canonicalUrl the URL used as the unique ID throughout F-Droid
|
||||||
* @see #cancel(Context, String)
|
* @see #cancel(Context, String)
|
||||||
*/
|
*/
|
||||||
public static void queue(Context context, String mirrorUrlString, long repoId, String urlString) {
|
public static void queue(Context context, String mirrorUrl, long repoId, String canonicalUrl) {
|
||||||
if (TextUtils.isEmpty(mirrorUrlString)) {
|
if (TextUtils.isEmpty(mirrorUrl)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Utils.debugLog(TAG, "Preparing " + mirrorUrlString + " to go into the download queue");
|
Utils.debugLog(TAG, "Queue download " + canonicalUrl.hashCode() + "/" + canonicalUrl
|
||||||
|
+ " using " + mirrorUrl);
|
||||||
Intent intent = new Intent(context, DownloaderService.class);
|
Intent intent = new Intent(context, DownloaderService.class);
|
||||||
intent.setAction(ACTION_QUEUE);
|
intent.setAction(ACTION_QUEUE);
|
||||||
intent.setData(Uri.parse(mirrorUrlString));
|
intent.setData(Uri.parse(mirrorUrl));
|
||||||
intent.putExtra(Downloader.EXTRA_REPO_ID, repoId);
|
intent.putExtra(Downloader.EXTRA_REPO_ID, repoId);
|
||||||
intent.putExtra(Downloader.EXTRA_CANONICAL_URL, urlString);
|
intent.putExtra(Downloader.EXTRA_CANONICAL_URL, canonicalUrl);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a package to the download queue, choosing a random mirror to
|
||||||
|
* download from.
|
||||||
|
*
|
||||||
|
* @param canonicalUrl the URL used as the unique ID throughout F-Droid,
|
||||||
|
* needed here to support canceling active downloads
|
||||||
|
*/
|
||||||
|
public static void queueUsingRandomMirror(Context context, long repoId, String canonicalUrl) {
|
||||||
|
String mirrorUrl = FDroidApp.switchUrlToNewMirror(canonicalUrl,
|
||||||
|
RepoProvider.Helper.findById(context, repoId));
|
||||||
|
queue(context, mirrorUrl, repoId, canonicalUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to return a version of {@code urlString} from a mirror, if there
|
||||||
|
* is an error, it just returns {@code urlString}.
|
||||||
|
*
|
||||||
|
* @see FDroidApp#getNewMirrorOnError(String, org.fdroid.fdroid.data.Repo)
|
||||||
|
*/
|
||||||
|
public static void queueUsingDifferentMirror(Context context, long repoId, String canonicalUrl) {
|
||||||
|
try {
|
||||||
|
String mirrorUrl = FDroidApp.getNewMirrorOnError(canonicalUrl,
|
||||||
|
RepoProvider.Helper.findById(context, repoId));
|
||||||
|
queue(context, mirrorUrl, repoId, canonicalUrl);
|
||||||
|
} catch (IOException e) {
|
||||||
|
queue(context, canonicalUrl, repoId, canonicalUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a URL to the download queue, even if it is currently downloading.
|
* Remove a URL to the download queue, even if it is currently downloading.
|
||||||
* <p>
|
* <p>
|
||||||
* All notifications are sent as an {@link Intent} via local broadcasts to be received by
|
* All notifications are sent as an {@link Intent} via local broadcasts to be received by
|
||||||
*
|
*
|
||||||
* @param context this app's {@link Context}
|
* @param context this app's {@link Context}
|
||||||
* @param urlString The URL to remove from the download queue
|
* @param canonicalUrl The URL to remove from the download queue
|
||||||
* @see #queue(Context, String, long, String)
|
* @see #queue(Context, String, long, String)
|
||||||
*/
|
*/
|
||||||
public static void cancel(Context context, String urlString) {
|
public static void cancel(Context context, String canonicalUrl) {
|
||||||
if (TextUtils.isEmpty(urlString)) {
|
if (TextUtils.isEmpty(canonicalUrl)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Utils.debugLog(TAG, "Preparing cancellation of " + urlString + " download");
|
Utils.debugLog(TAG, "Send cancel for " + canonicalUrl.hashCode() + "/" + canonicalUrl);
|
||||||
Intent intent = new Intent(context, DownloaderService.class);
|
Intent intent = new Intent(context, DownloaderService.class);
|
||||||
intent.setAction(ACTION_CANCEL);
|
intent.setAction(ACTION_CANCEL);
|
||||||
intent.setData(Uri.parse(urlString));
|
intent.setData(Uri.parse(canonicalUrl));
|
||||||
|
intent.putExtra(Downloader.EXTRA_CANONICAL_URL, canonicalUrl);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,21 +368,21 @@ public class DownloaderService extends Service {
|
|||||||
* This is useful for checking whether to re-register {@link android.content.BroadcastReceiver}s
|
* This is useful for checking whether to re-register {@link android.content.BroadcastReceiver}s
|
||||||
* in {@link android.app.Activity#onResume()}.
|
* in {@link android.app.Activity#onResume()}.
|
||||||
*/
|
*/
|
||||||
public static boolean isQueuedOrActive(String urlString) {
|
public static boolean isQueuedOrActive(String canonicalUrl) {
|
||||||
if (TextUtils.isEmpty(urlString)) { //NOPMD - suggests unreadable format
|
if (TextUtils.isEmpty(canonicalUrl)) { //NOPMD - suggests unreadable format
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (serviceHandler == null) {
|
if (serviceHandler == null) {
|
||||||
return false; // this service is not even running
|
return false; // this service is not even running
|
||||||
}
|
}
|
||||||
return serviceHandler.hasMessages(urlString.hashCode()) || isActive(urlString);
|
return serviceHandler.hasMessages(canonicalUrl.hashCode()) || isActive(canonicalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a URL is actively being downloaded.
|
* Check if a URL is actively being downloaded.
|
||||||
*/
|
*/
|
||||||
private static boolean isActive(String urlString) {
|
private static boolean isActive(String downloadUrl) {
|
||||||
return downloader != null && TextUtils.equals(urlString, downloader.urlString);
|
return downloader != null && TextUtils.equals(downloadUrl, activeCanonicalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setTimeout(int ms) {
|
public static void setTimeout(int ms) {
|
||||||
@ -345,10 +392,10 @@ public class DownloaderService extends Service {
|
|||||||
/**
|
/**
|
||||||
* Get a prepared {@link IntentFilter} for use for matching this service's action events.
|
* Get a prepared {@link IntentFilter} for use for matching this service's action events.
|
||||||
*
|
*
|
||||||
* @param urlString The full file URL to match.
|
* @param canonicalUrl the URL used as the unique ID for the specific package
|
||||||
*/
|
*/
|
||||||
public static IntentFilter getIntentFilter(String urlString) {
|
public static IntentFilter getIntentFilter(String canonicalUrl) {
|
||||||
Uri uri = Uri.parse(urlString);
|
Uri uri = Uri.parse(canonicalUrl);
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(Downloader.ACTION_STARTED);
|
intentFilter.addAction(Downloader.ACTION_STARTED);
|
||||||
intentFilter.addAction(Downloader.ACTION_PROGRESS);
|
intentFilter.addAction(Downloader.ACTION_PROGRESS);
|
||||||
|
@ -171,9 +171,9 @@ public class AppDetailsActivity extends AppCompatActivity
|
|||||||
AppUpdateStatusManager ausm = AppUpdateStatusManager.getInstance(this);
|
AppUpdateStatusManager ausm = AppUpdateStatusManager.getInstance(this);
|
||||||
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(app.packageName)) {
|
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(app.packageName)) {
|
||||||
if (status.status == AppUpdateStatusManager.Status.Installed) {
|
if (status.status == AppUpdateStatusManager.Status.Installed) {
|
||||||
ausm.removeApk(status.getUniqueKey());
|
ausm.removeApk(status.getCanonicalUrl());
|
||||||
} else {
|
} else {
|
||||||
ausm.refreshApk(status.getUniqueKey());
|
ausm.refreshApk(status.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,7 +449,7 @@ public class AppDetailsActivity extends AppCompatActivity
|
|||||||
if (justReceived) {
|
if (justReceived) {
|
||||||
adapter.setIndeterminateProgress(R.string.installing);
|
adapter.setIndeterminateProgress(R.string.installing);
|
||||||
localBroadcastManager.registerReceiver(installReceiver,
|
localBroadcastManager.registerReceiver(installReceiver,
|
||||||
Installer.getInstallIntentFilter(Uri.parse(newStatus.getUniqueKey())));
|
Installer.getInstallIntentFilter(newStatus.getCanonicalUrl()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -459,7 +459,7 @@ public class AppDetailsActivity extends AppCompatActivity
|
|||||||
Toast.makeText(this, R.string.details_notinstalled, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.details_notinstalled, Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
String msg = newStatus.errorText;
|
String msg = newStatus.errorText;
|
||||||
if (!newStatus.getUniqueKey().equals(msg)) msg += " " + newStatus.getUniqueKey();
|
if (!newStatus.getCanonicalUrl().equals(msg)) msg += " " + newStatus.getCanonicalUrl();
|
||||||
Toast.makeText(this, R.string.download_error, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.download_error, Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
@ -491,9 +491,9 @@ public class AppDetailsActivity extends AppCompatActivity
|
|||||||
AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED);
|
AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED);
|
||||||
if (currentStatus != null
|
if (currentStatus != null
|
||||||
&& isRemoving
|
&& isRemoving
|
||||||
&& !TextUtils.equals(status.getUniqueKey(), currentStatus.getUniqueKey())) {
|
&& !TextUtils.equals(status.getCanonicalUrl(), currentStatus.getCanonicalUrl())) {
|
||||||
Utils.debugLog(TAG, "Ignoring app status change because it belongs to "
|
Utils.debugLog(TAG, "Ignoring app status change because it belongs to "
|
||||||
+ status.getUniqueKey() + " not " + currentStatus.getUniqueKey());
|
+ status.getCanonicalUrl() + " not " + currentStatus.getCanonicalUrl());
|
||||||
} else if (status != null && !TextUtils.equals(status.apk.packageName, app.packageName)) {
|
} else if (status != null && !TextUtils.equals(status.apk.packageName, app.packageName)) {
|
||||||
Utils.debugLog(TAG, "Ignoring app status change because it belongs to "
|
Utils.debugLog(TAG, "Ignoring app status change because it belongs to "
|
||||||
+ status.apk.packageName + " not " + app.packageName);
|
+ status.apk.packageName + " not " + app.packageName);
|
||||||
@ -650,7 +650,7 @@ public class AppDetailsActivity extends AppCompatActivity
|
|||||||
AppUpdateStatusManager ausm = AppUpdateStatusManager.getInstance(this);
|
AppUpdateStatusManager ausm = AppUpdateStatusManager.getInstance(this);
|
||||||
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(packageName)) {
|
for (AppUpdateStatusManager.AppUpdateStatus status : ausm.getByPackageName(packageName)) {
|
||||||
if (status.status == AppUpdateStatusManager.Status.Installed) {
|
if (status.status == AppUpdateStatusManager.Status.Installed) {
|
||||||
ausm.removeApk(status.getUniqueKey());
|
ausm.removeApk(status.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
@ -720,7 +720,7 @@ public class AppDetailsActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
public void installCancel() {
|
public void installCancel() {
|
||||||
if (currentStatus != null) {
|
if (currentStatus != null) {
|
||||||
InstallManagerService.cancel(this, currentStatus.getUniqueKey());
|
InstallManagerService.cancel(this, currentStatus.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,15 +477,15 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
// Once it is explicitly launched by the user, then we can pretty much forget about
|
// Once it is explicitly launched by the user, then we can pretty much forget about
|
||||||
// any sort of notification that the app was successfully installed. It should be
|
// any sort of notification that the app was successfully installed. It should be
|
||||||
// apparent to the user because they just launched it.
|
// apparent to the user because they just launched it.
|
||||||
AppUpdateStatusManager.getInstance(activity).removeApk(currentStatus.getUniqueKey());
|
AppUpdateStatusManager.getInstance(activity).removeApk(currentStatus.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
|
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
|
||||||
String urlString = currentStatus.apk.getUrl();
|
String canonicalUrl = currentStatus.apk.getCanonicalUrl();
|
||||||
File apkFilePath = ApkCache.getApkDownloadPath(activity, urlString);
|
File apkFilePath = ApkCache.getApkDownloadPath(activity, canonicalUrl);
|
||||||
Utils.debugLog(TAG, "skip download, we have already downloaded " + currentStatus.apk.getUrl() +
|
Utils.debugLog(TAG, "skip download, we have already downloaded " + currentStatus.apk.getCanonicalUrl() +
|
||||||
" to " + apkFilePath);
|
" to " + apkFilePath);
|
||||||
|
|
||||||
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity);
|
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity);
|
||||||
@ -505,10 +505,10 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Uri apkDownloadUri = Uri.parse(urlString);
|
Uri canonicalUri = Uri.parse(canonicalUrl);
|
||||||
broadcastManager.registerReceiver(receiver, Installer.getInstallIntentFilter(apkDownloadUri));
|
broadcastManager.registerReceiver(receiver, Installer.getInstallIntentFilter(canonicalUri));
|
||||||
Installer installer = InstallerFactory.create(activity, currentStatus.apk);
|
Installer installer = InstallerFactory.create(activity, currentStatus.apk);
|
||||||
installer.installPackage(Uri.parse(apkFilePath.toURI().toString()), apkDownloadUri);
|
installer.installPackage(Uri.parse(apkFilePath.toURI().toString()), canonicalUri);
|
||||||
} else {
|
} else {
|
||||||
final Apk suggestedApk = ApkProvider.Helper.findSuggestedApk(activity, app);
|
final Apk suggestedApk = ApkProvider.Helper.findSuggestedApk(activity, app);
|
||||||
InstallManagerService.queue(activity, app, suggestedApk);
|
InstallManagerService.queue(activity, app, suggestedApk);
|
||||||
@ -534,6 +534,6 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallManagerService.cancel(activity, currentStatus.getUniqueKey());
|
InstallManagerService.cancel(activity, currentStatus.getCanonicalUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,8 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have moved into the ReadyToInstall or Installed state.
|
// Check if we have moved into the ReadyToInstall or Installed state.
|
||||||
AppUpdateStatus status = manager.get(intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL));
|
AppUpdateStatus status = manager.get(
|
||||||
|
intent.getStringExtra(org.fdroid.fdroid.net.Downloader.EXTRA_CANONICAL_URL));
|
||||||
boolean isStatusChange = intent.getBooleanExtra(AppUpdateStatusManager.EXTRA_IS_STATUS_UPDATE, false);
|
boolean isStatusChange = intent.getBooleanExtra(AppUpdateStatusManager.EXTRA_IS_STATUS_UPDATE, false);
|
||||||
if (isStatusChange
|
if (isStatusChange
|
||||||
&& status != null
|
&& status != null
|
||||||
|
@ -59,7 +59,7 @@ public class AppStatusListItemController extends AppListItemController {
|
|||||||
CharSequence message = null;
|
CharSequence message = null;
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(activity);
|
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(activity);
|
||||||
manager.removeApk(status.getUniqueKey());
|
manager.removeApk(status.getCanonicalUrl());
|
||||||
switch (status.status) {
|
switch (status.status) {
|
||||||
case Downloading:
|
case Downloading:
|
||||||
cancelDownload();
|
cancelDownload();
|
||||||
|
@ -5,12 +5,10 @@ import android.app.PendingIntent;
|
|||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.fdroid.fdroid.AppUpdateStatusManager;
|
import org.fdroid.fdroid.AppUpdateStatusManager;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
@ -73,8 +71,8 @@ public class KnownVulnAppListItemController extends AppListItemController {
|
|||||||
Apk suggestedApk = ApkProvider.Helper.findSuggestedApk(activity, app);
|
Apk suggestedApk = ApkProvider.Helper.findSuggestedApk(activity, app);
|
||||||
if (shouldUpgradeInsteadOfUninstall(app, suggestedApk)) {
|
if (shouldUpgradeInsteadOfUninstall(app, suggestedApk)) {
|
||||||
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(activity);
|
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(activity);
|
||||||
Uri uri = Uri.parse(suggestedApk.getUrl());
|
manager.registerReceiver(installReceiver,
|
||||||
manager.registerReceiver(installReceiver, Installer.getInstallIntentFilter(uri));
|
Installer.getInstallIntentFilter(suggestedApk.getCanonicalUrl()));
|
||||||
InstallManagerService.queue(activity, app, suggestedApk);
|
InstallManagerService.queue(activity, app, suggestedApk);
|
||||||
} else {
|
} else {
|
||||||
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(activity);
|
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(activity);
|
||||||
@ -141,7 +139,8 @@ public class KnownVulnAppListItemController extends AppListItemController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
uninstallPendingIntent.send();
|
uninstallPendingIntent.send();
|
||||||
} catch (PendingIntent.CanceledException ignored) { }
|
} catch (PendingIntent.CanceledException ignored) {
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user