From 8fcf135280a29c81aa871ba1540ecdedfdfc1fb4 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Mon, 20 Jan 2020 21:42:25 +0100 Subject: [PATCH] add Stop action to download notifications --- app | 2 +- .../java/io/lbry/browser/DownloadManager.java | 56 +++++++++++++---- .../java/io/lbry/browser/LbrynetService.java | 60 ++++++++++++++----- .../java/io/lbry/browser/MainActivity.java | 22 +++++-- 4 files changed, 107 insertions(+), 33 deletions(-) diff --git a/app b/app index 32c1770c..46bfbd24 160000 --- a/app +++ b/app @@ -1 +1 @@ -Subproject commit 32c1770c34ecc5e80c5542aeb64ff11312704cea +Subproject commit 46bfbd242a841f770a59ec88f9c7fbd94e5a06da diff --git a/src/main/java/io/lbry/browser/DownloadManager.java b/src/main/java/io/lbry/browser/DownloadManager.java index b0673d53..5025dc99 100644 --- a/src/main/java/io/lbry/browser/DownloadManager.java +++ b/src/main/java/io/lbry/browser/DownloadManager.java @@ -10,6 +10,7 @@ import android.net.Uri; import android.os.Build; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import androidx.core.content.ContextCompat; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; @@ -31,6 +32,8 @@ public class DownloadManager { private List completedDownloads = new ArrayList(); + private Map downloadIdOutpointsMap = new HashMap(); + // maintain a map of uris to writtenBytes, so that we check if it's changed and don't flood RN with update events every 500ms private Map writtenDownloadBytes = new HashMap(); @@ -144,7 +147,15 @@ public class DownloadManager { } } - public void startDownload(String id, String filename) { + private Intent getDeleteDownloadIntent(String uri) { + Intent intent = new Intent(); + intent.setAction(LbrynetService.ACTION_DELETE_DOWNLOAD); + intent.putExtra("uri", uri); + intent.putExtra("nativeDelete", true); + return intent; + } + + public void startDownload(String id, String filename, String outpoint) { if (filename == null || filename.trim().length() == 0) { return; } @@ -152,20 +163,26 @@ public class DownloadManager { synchronized (this) { if (!isDownloadActive(id)) { activeDownloads.add(id); + downloadIdOutpointsMap.put(id, outpoint); } createNotificationChannel(); createNotificationGroup(); + PendingIntent stopDownloadIntent = PendingIntent.getBroadcast(context, 0, getDeleteDownloadIntent(id), PendingIntent.FLAG_CANCEL_CURRENT); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); // The file URI is used as the unique ID - builder.setContentIntent(getLaunchPendingIntent(id, context)) + builder.setColor(ContextCompat.getColor(context, R.color.lbryGreen)) + .setContentIntent(getLaunchPendingIntent(id, context)) .setContentTitle(String.format("Downloading %s", truncateFilename(filename))) .setGroup(GROUP_DOWNLOADS) .setPriority(NotificationCompat.PRIORITY_LOW) .setProgress(MAX_PROGRESS, 0, false) - .setSmallIcon(android.R.drawable.stat_sys_download); + .setSmallIcon(android.R.drawable.stat_sys_download) + .setOngoing(true) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopDownloadIntent); int notificationId = getNotificationId(id); downloadIdNotificationIdMap.put(id, notificationId); @@ -194,9 +211,13 @@ public class DownloadManager { if (builders.containsKey(notificationId)) { builder = builders.get(notificationId); } else { + PendingIntent stopDownloadIntent = PendingIntent.getBroadcast(context, 0, getDeleteDownloadIntent(id), PendingIntent.FLAG_CANCEL_CURRENT); builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); - builder.setContentTitle(String.format("Downloading %s", truncateFilename(filename))) - .setPriority(NotificationCompat.PRIORITY_LOW); + builder.setColor(ContextCompat.getColor(context, R.color.lbryGreen)) + .setContentTitle(String.format("Downloading %s", truncateFilename(filename))) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setOngoing(true) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopDownloadIntent); builders.put(notificationId, builder); } @@ -213,7 +234,9 @@ public class DownloadManager { .setContentText(String.format("%s", formatBytes(totalBytes))) .setGroup(GROUP_DOWNLOADS) .setProgress(0, 0, false) - .setSmallIcon(android.R.drawable.stat_sys_download_done); + .setSmallIcon(android.R.drawable.stat_sys_download_done) + .setOngoing(false); + builder.mActions.clear(); notificationManager.notify(notificationId, builder.build()); if (downloadIdNotificationIdMap.containsKey(id)) { @@ -258,11 +281,13 @@ public class DownloadManager { .setContentText(String.format("%s", formatBytes(totalBytes))) .setGroup(GROUP_DOWNLOADS) .setProgress(0, 0, false) - .setSmallIcon(android.R.drawable.stat_sys_download_done); - notificationManager.notify(notificationId, builder.build()); + .setSmallIcon(android.R.drawable.stat_sys_download_done) + .setOngoing(false); + builder.mActions.clear(); + notificationManager.notify(notificationId, builder.build()); - // If there are no more downloads and the group exists, set the icon to stop animating - checkGroupDownloadIcon(notificationManager); + // If there are no more downloads and the group exists, set the icon to stop animating + checkGroupDownloadIcon(notificationManager); } } @@ -295,11 +320,22 @@ public class DownloadManager { return completedDownloads; } + public String getOutpointForDownload(String uri) { + if (downloadIdOutpointsMap.containsKey(uri)) { + return downloadIdOutpointsMap.get(uri); + } + + return null; + } + public void deleteDownloadUri(String uri) { synchronized (this) { activeDownloads.remove(uri); completedDownloads.remove(uri); + if (downloadIdOutpointsMap.containsKey(uri)) { + downloadIdOutpointsMap.remove(uri); + } if (downloadIdNotificationIdMap.containsKey(uri)) { removeDownloadNotification(uri); } diff --git a/src/main/java/io/lbry/browser/LbrynetService.java b/src/main/java/io/lbry/browser/LbrynetService.java index bb6a653e..a57016da 100644 --- a/src/main/java/io/lbry/browser/LbrynetService.java +++ b/src/main/java/io/lbry/browser/LbrynetService.java @@ -123,7 +123,8 @@ public class LbrynetService extends PythonService { } } else if (ACTION_DELETE_DOWNLOAD.equals(action)) { String uri = intent.getStringExtra("uri"); - LbrynetService.this.deleteDownload(uri); + boolean nativeDelete = intent.getBooleanExtra("nativeDelete", false); + LbrynetService.this.deleteDownload(uri, nativeDelete); } else if (ACTION_CHECK_DOWNLOADS.equals(action)) { LbrynetService.this.checkDownloads(); } @@ -212,8 +213,10 @@ public class LbrynetService extends PythonService { if (streamManagerReady) { Map params = new HashMap(); params.put("page_size", 100); - params.put("status", "stopped"); - params.put("comparison", "ne"); + params.put("reverse", true); + params.put("sort", "added_on"); + /*params.put("status", "stopped"); + params.put("comparison", "ne");*/ String fileList = Utils.sdkCall("file_list", params); if (fileList != null) { @@ -266,7 +269,7 @@ public class LbrynetService extends PythonService { File file = new File(downloadPath); Intent intent = createDownloadEventIntent(uri, outpoint, item.toString()); intent.putExtra("action", "start"); - downloadManager.startDownload(uri, file.getName()); + downloadManager.startDownload(uri, file.getName(), outpoint); Context context = getApplicationContext(); if (context != null) { @@ -286,10 +289,45 @@ public class LbrynetService extends PythonService { }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - private void deleteDownload(String uri) { + private void deleteDownload(String uri, boolean nativeDelete) { + final String outpoint = downloadManager.getOutpointForDownload(uri); + if (nativeDelete && outpoint != null) { + // send call sdk to delete the file on the corresponding outpoint + removeDownloadFromManager(uri); + + (new AsyncTask() { + protected String doInBackground(Void... param) { + try { + Map params = new HashMap(); + params.put("outpoint", outpoint); + params.put("delete_from_download_dir", true); + return Utils.sdkCall("file_delete", params); + } catch (ConnectException ex) { + return null; + } + } + + protected void onPostExecute(String response) { + // after deletion, remove the download from the download manager + Intent intent = createDownloadEventIntent(uri, outpoint, null); + intent.putExtra("action", "abort"); + + Context context = getApplicationContext(); + if (context != null) { + context.sendBroadcast(intent); + } + } + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else { + removeDownloadFromManager(uri); + } + } + + private void removeDownloadFromManager(String uri) { if (downloadManager.isDownloadActive(uri)) { downloadManager.abortDownload(uri); } + downloadManager.deleteDownloadUri(uri); } @@ -359,23 +397,13 @@ public class LbrynetService extends PythonService { if (!completed && downloadPath != null) { downloadManager.clearWrittenBytesForDownload(uri); intent.putExtra("action", "start"); - downloadManager.startDownload(uri, file.getName()); + downloadManager.startDownload(uri, file.getName(), outpoint); if (context != null) { context.sendBroadcast(intent); } } } } - - // check download manager uris and clear downloads that may have been cancelled / deleted - /*List activeUris = downloadManager.getActiveDownloads(); - for (int i = 0; i < activeUris.size(); i++) { - String activeUri = activeUris.get(i); - if (!itemUris.contains(activeUri)) { - downloadManager.abortDownload(activeUri); - fileListUris.remove(activeUri); // remove URIs from the session that may have been deleted - } - }*/ } catch (JSONException ex) { // pass Log.e(TAG, ex.getMessage(), ex); diff --git a/src/main/java/io/lbry/browser/MainActivity.java b/src/main/java/io/lbry/browser/MainActivity.java index d4f269b7..bf4d9d12 100644 --- a/src/main/java/io/lbry/browser/MainActivity.java +++ b/src/main/java/io/lbry/browser/MainActivity.java @@ -212,17 +212,28 @@ public class MainActivity extends FragmentActivity implements DefaultHardwareBac String outpoint = intent.getStringExtra("outpoint"); String fileInfoJson = intent.getStringExtra("file_info"); - if (uri == null || outpoint == null || fileInfoJson == null) { + + if (uri == null || outpoint == null || (fileInfoJson == null && !"abort".equals(downloadAction))) { + return; + } + + String eventName = null; + WritableMap params = Arguments.createMap(); + params.putString("uri", uri); + params.putString("outpoint", outpoint); + + ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); + if ("abort".equals(downloadAction)) { + eventName = "onDownloadAborted"; + if (reactContext != null) { + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); + } return; } try { - String eventName = null; JSONObject json = new JSONObject(fileInfoJson); WritableMap fileInfo = JSONObjectToMap(json); - WritableMap params = Arguments.createMap(); - params.putString("uri", uri); - params.putString("outpoint", outpoint); params.putMap("fileInfo", fileInfo); if (DownloadManager.ACTION_UPDATE.equals(downloadAction)) { @@ -233,7 +244,6 @@ public class MainActivity extends FragmentActivity implements DefaultHardwareBac eventName = (DownloadManager.ACTION_START.equals(downloadAction)) ? "onDownloadStarted" : "onDownloadCompleted"; } - ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); if (reactContext != null) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params); }