From 57d6b72f5af7996405f437b5da88a702534797ec Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Sat, 28 Dec 2019 16:06:14 +0100 Subject: [PATCH] First run changes (#809) * don't request for storage permission on first startup * app head: download button tweaks * fix download manager --- app | 2 +- .../java/io/lbry/browser/LbrynetService.java | 165 +++++++++--------- .../java/io/lbry/browser/MainActivity.java | 42 +++-- src/main/java/io/lbry/browser/Utils.java | 12 ++ .../browser/reactmodules/UtilityModule.java | 20 +++ src/main/python/lbrynetservice.py | 4 +- 6 files changed, 146 insertions(+), 99 deletions(-) diff --git a/app b/app index 54b10c8..d362d9e 160000 --- a/app +++ b/app @@ -1 +1 @@ -Subproject commit 54b10c818e5c49a74044d076841f9e57a6ad1dc1 +Subproject commit d362d9e8dd9e0f87262bcb61f996182633e7f008 diff --git a/src/main/java/io/lbry/browser/LbrynetService.java b/src/main/java/io/lbry/browser/LbrynetService.java index 9c0aed9..6fc7e2b 100644 --- a/src/main/java/io/lbry/browser/LbrynetService.java +++ b/src/main/java/io/lbry/browser/LbrynetService.java @@ -242,27 +242,30 @@ public class LbrynetService extends PythonService { try { JSONObject response = new JSONObject(fileList); if (!response.has("error")) { - JSONArray fileItems = response.optJSONArray("result"); - if (fileItems != null && fileItems.length() > 0) { - // TODO: Create Java FileItem class - JSONObject item = fileItems.getJSONObject(0); - String downloadPath = item.isNull("download_path") ? null : item.getString("download_path"); - if (downloadPath == null || downloadPath.trim().length() == 0) { - return; - } - String claimId = item.getString("claim_id"); - String claimName = item.getString("claim_name"); - String uri = String.format("lbry://%s#%s", claimName, claimId); + JSONObject result = response.getJSONObject("result"); + if (result != null) { + JSONArray fileItems = response.optJSONArray("items"); + if (fileItems != null && fileItems.length() > 0) { + // TODO: Create Java FileItem class + JSONObject item = fileItems.getJSONObject(0); + String downloadPath = item.isNull("download_path") ? null : item.getString("download_path"); + if (downloadPath == null || downloadPath.trim().length() == 0) { + return; + } + String claimId = item.getString("claim_id"); + String claimName = item.getString("claim_name"); + String uri = String.format("lbry://%s#%s", claimName, claimId); - if (!downloadManager.isDownloadActive(uri) && !downloadManager.isDownloadCompleted(uri)) { - File file = new File(downloadPath); - Intent intent = createDownloadEventIntent(uri, outpoint, item.toString()); - intent.putExtra("action", "start"); - downloadManager.startDownload(uri, file.getName()); + if (!downloadManager.isDownloadActive(uri) && !downloadManager.isDownloadCompleted(uri)) { + File file = new File(downloadPath); + Intent intent = createDownloadEventIntent(uri, outpoint, item.toString()); + intent.putExtra("action", "start"); + downloadManager.startDownload(uri, file.getName()); - Context context = getApplicationContext(); - if (context != null) { - context.sendBroadcast(intent); + Context context = getApplicationContext(); + if (context != null) { + context.sendBroadcast(intent); + } } } } @@ -287,74 +290,78 @@ public class LbrynetService extends PythonService { private void handlePollFileResponse(JSONObject response) { Context context = getApplicationContext(); if (response.has("result")) { - JSONArray fileItems = response.optJSONArray("result"); - if (fileItems != null) { - try { - //List itemUris = new ArrayList(); - for (int i = 0; i < fileItems.length(); i++) { - JSONObject item = fileItems.getJSONObject(i); - String downloadPath = item.isNull("download_path") ? null : item.getString("download_path"); - if (downloadPath == null || downloadPath.trim().length() == 0) { - continue; - } - - String claimId = item.getString("claim_id"); - String claimName = item.getString("claim_name"); - String uri = String.format("lbry://%s#%s", claimName, claimId); - boolean completed = item.getBoolean("completed"); - double writtenBytes = item.optDouble("written_bytes", -1); - double totalBytes = item.optDouble("total_bytes", -1); - String outpoint = item.getString("outpoint"); - - if (downloadManager.isDownloadActive(uri) && (writtenBytes == -1 || totalBytes == -1)) { - // possibly deleted, abort the download - downloadManager.abortDownload(uri); - continue; - } - - File file = new File(downloadPath); - Intent intent = createDownloadEventIntent(uri, outpoint, item.toString()); - if (downloadManager.isDownloadActive(uri)) { - if (writtenBytes >= totalBytes || completed) { - // completed download - intent.putExtra("action", "complete"); - downloadManager.completeDownload(uri, file.getName(), totalBytes); - } else { - intent.putExtra("action", "update"); - intent.putExtra("progress", (writtenBytes / totalBytes) * 100); - downloadManager.updateDownload(uri, file.getName(), writtenBytes, totalBytes); - } - - if (context != null) { - context.sendBroadcast(intent); - } - } else { - if (writtenBytes == -1 || writtenBytes >= totalBytes) { - // do not start a download that is considered completed + JSONObject result = response.optJSONObject("result"); + if (result != null) { + JSONArray fileItems = result.optJSONArray("items"); + if (fileItems != null) { + try { + //List itemUris = new ArrayList(); + for (int i = 0; i < fileItems.length(); i++) { + JSONObject item = fileItems.getJSONObject(i); + String downloadPath = item.isNull("download_path") ? null : item.getString("download_path"); + if (downloadPath == null || downloadPath.trim().length() == 0) { continue; } - if (!completed && downloadPath != null) { - intent.putExtra("action", "start"); - downloadManager.startDownload(uri, file.getName()); + + String claimId = item.getString("claim_id"); + String claimName = item.getString("claim_name"); + String uri = String.format("lbry://%s#%s", claimName, claimId); + + boolean completed = item.getBoolean("completed"); + double writtenBytes = item.optDouble("written_bytes", -1); + double totalBytes = item.optDouble("total_bytes", -1); + String outpoint = item.getString("outpoint"); + + if (downloadManager.isDownloadActive(uri) && (writtenBytes == -1 || totalBytes == -1)) { + // possibly deleted, abort the download + downloadManager.abortDownload(uri); + continue; + } + + File file = new File(downloadPath); + Intent intent = createDownloadEventIntent(uri, outpoint, item.toString()); + if (downloadManager.isDownloadActive(uri)) { + if (writtenBytes >= totalBytes || completed) { + // completed download + intent.putExtra("action", "complete"); + downloadManager.completeDownload(uri, file.getName(), totalBytes); + } else { + intent.putExtra("action", "update"); + intent.putExtra("progress", (writtenBytes / totalBytes) * 100); + downloadManager.updateDownload(uri, file.getName(), writtenBytes, totalBytes); + } + if (context != null) { context.sendBroadcast(intent); } + } else { + if (writtenBytes == -1 || writtenBytes >= totalBytes) { + // do not start a download that is considered completed + continue; + } + if (!completed && downloadPath != null) { + intent.putExtra("action", "start"); + downloadManager.startDownload(uri, file.getName()); + 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); + // 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 4e0071a..6b0587c 100644 --- a/src/main/java/io/lbry/browser/MainActivity.java +++ b/src/main/java/io/lbry/browser/MainActivity.java @@ -38,6 +38,8 @@ import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.ReadableNativeArray; +import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.core.PermissionAwareActivity; import com.facebook.react.modules.core.PermissionListener; @@ -128,14 +130,6 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand @Override protected void onCreate(Bundle savedInstanceState) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // Request external storage permission on Android version >= 6 - checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, - STORAGE_PERMISSION_REQ_CODE, - "LBRY requires access to your device storage to be able to download files and media.", - this); - } - super.onCreate(savedInstanceState); currentActivity = this; @@ -365,6 +359,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); switch (requestCode) { case STORAGE_PERMISSION_REQ_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { @@ -373,22 +368,25 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); } - } else { - // Permission not granted. Show a message and terminate the application - Toast.makeText(this, - "LBRY requires access to your device storage to be able to download files and media." + - " Please enable the storage permission and restart the app.", Toast.LENGTH_LONG).show(); - if (serviceRunning) { - ServiceHelper.stop(this, LbrynetService.class); + if (reactContext != null) { + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("onStoragePermissionGranted", null); + } + } else { + // Permission not granted + /*Toast.makeText(this, + "LBRY requires access to your device storage to be able to download files and media." + + " Please enable the storage permission and restart the app.", Toast.LENGTH_LONG).show();*/ + if (reactContext != null) { + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("onStoragePermissionRefused", null); } - finish(); } break; case PHONE_STATE_PERMISSION_REQ_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission granted. Emit an onPhoneStatePermissionGranted event - ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); if (reactContext != null) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("onPhoneStatePermissionGranted", null); @@ -403,7 +401,6 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand case RECEIVE_SMS_PERMISSION_REQ_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission granted. Emit an onPhoneStatePermissionGranted event - ReactContext reactContext = mReactInstanceManager.getCurrentReactContext(); if (reactContext != null) { reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("onReceiveSmsPermissionGranted", null); @@ -609,6 +606,15 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand true); } + public static void checkStoragePermission(Context context) { + // Request read phone state permission + checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, + STORAGE_PERMISSION_REQ_CODE, + "LBRY requires access to your device storage to be able to download files and media.", + context, + true); + } + private boolean isServiceRunning(Class serviceClass) { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) { diff --git a/src/main/java/io/lbry/browser/Utils.java b/src/main/java/io/lbry/browser/Utils.java index dd43238..c35b94b 100644 --- a/src/main/java/io/lbry/browser/Utils.java +++ b/src/main/java/io/lbry/browser/Utils.java @@ -62,6 +62,8 @@ public final class Utils { public static final String SDK_URL = "http://127.0.0.1:5279"; + public static final String SP_DOWNLOAD_DIR_KEY = "download_dir"; + public static String getAndroidRelease() { return android.os.Build.VERSION.RELEASE; } @@ -114,6 +116,16 @@ public final class Utils { return file.getAbsolutePath(); } + public static String getConfiguredDownloadDirectory(Context context) { + // use the default folder (usually [private files]/Download with WRITE_EXTERNAL_STOAGE permission not granted) + // if none is configured or specified + String defaultDirectory = String.format("%s/Download", getInternalStorageDir(context)); + + SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + String downloadDirectory = pref.getString(SP_DOWNLOAD_DIR_KEY, defaultDirectory); + return downloadDirectory; + } + public static void saveApiSecret(String secret, Context context, KeyStore keyStore) { try { SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); diff --git a/src/main/java/io/lbry/browser/reactmodules/UtilityModule.java b/src/main/java/io/lbry/browser/reactmodules/UtilityModule.java index f107c98..126c9be 100644 --- a/src/main/java/io/lbry/browser/reactmodules/UtilityModule.java +++ b/src/main/java/io/lbry/browser/reactmodules/UtilityModule.java @@ -207,6 +207,19 @@ public class UtilityModule extends ReactContextBaseJavaModule { } } + @ReactMethod + public void canReadWriteStorage(final Promise promise) { + promise.resolve(MainActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, MainActivity.getActivity())); + } + + @ReactMethod + public void requestStoragePermission() { + MainActivity activity = (MainActivity) MainActivity.getActivity(); + if (activity != null) { + MainActivity.checkStoragePermission(activity); + } + } + @ReactMethod public void shareLogFile(Callback errorCallback) { String logFileName = "lbrynet.log"; @@ -378,6 +391,7 @@ public class UtilityModule extends ReactContextBaseJavaModule { Intent intent = new Intent(); intent.setAction(LbrynetService.ACTION_QUEUE_DOWNLOAD); intent.putExtra("outpoint", outpoint); + if (context != null) { context.sendBroadcast(intent); } @@ -450,4 +464,10 @@ public class UtilityModule extends ReactContextBaseJavaModule { promise.resolve(null); } + + @ReactMethod + public void getDownloadDirectory(Promise promise) { + // This obtains a public default download directory after the storage permission has been granted + promise.resolve(Utils.getConfiguredDownloadDirectory(context)); + } } diff --git a/src/main/python/lbrynetservice.py b/src/main/python/lbrynetservice.py index d18b5cc..d40940b 100644 --- a/src/main/python/lbrynetservice.py +++ b/src/main/python/lbrynetservice.py @@ -73,10 +73,12 @@ def configure_logging(conf): def start(): keyring.set_keyring(LbryAndroidKeyring()) private_storage_dir = lbrynet_android_utils.getAppInternalStorageDir(service.getApplicationContext()) + configured_download_dir = lbrynet_android_utils.getConfiguredDownloadDirectory(service.getApplicationContext()) + print('configured_download_dir={}'.format(configured_download_dir)) conf = Config( data_dir=f'{private_storage_dir}/lbrynet', wallet_dir=f'{private_storage_dir}/lbryum', - download_dir=f'{lbrynet_android_utils.getInternalStorageDir(service.getApplicationContext())}/Download', + download_dir=configured_download_dir, blob_lru_cache_size=32, components_to_skip=[DHT_COMPONENT, HASH_ANNOUNCER_COMPONENT, PEER_PROTOCOL_SERVER_COMPONENT], save_blobs=False,