From 7d2e4c1e8c733b2b0fe05df0009a025267eed096 Mon Sep 17 00:00:00 2001 From: akinwale Date: Fri, 25 May 2018 08:13:05 +0100 Subject: [PATCH] Handle lbry:// url scheme, and download notification fixes. (#139) --- app/src/component/AppNavigator.js | 19 ++++++++++++++++++- app/src/page/splash/view.js | 13 ++++++++++++- app/src/redux/actions/file.js | 16 ++++++++++++---- .../build/templates/AndroidManifest.tmpl.xml | 9 ++++++++- .../java/io/lbry/browser/MainActivity.java | 16 ++++++++++++++-- .../reactmodules/DownloadManagerModule.java | 18 ++++++++++++------ 6 files changed, 76 insertions(+), 15 deletions(-) diff --git a/app/src/component/AppNavigator.js b/app/src/component/AppNavigator.js index 83707936..ae672103 100644 --- a/app/src/component/AppNavigator.js +++ b/app/src/component/AppNavigator.js @@ -20,6 +20,7 @@ import { AppState, AsyncStorage, BackHandler, + Linking, NativeModules, TextInput, ToastAndroid @@ -109,7 +110,7 @@ class AppWithNavigationState extends React.Component { componentWillMount() { AppState.addEventListener('change', this._handleAppStateChange); BackHandler.addEventListener('hardwareBackPress', function() { - const { dispatch, navigation, nav } = this.props; + const { dispatch, nav } = this.props; // There should be a better way to check this if (nav.routes.length > 0) { const subRoutes = nav.routes[0].routes[0].routes; @@ -130,9 +131,14 @@ class AppWithNavigationState extends React.Component { }.bind(this)); } + componentDidMount() { + Linking.addEventListener('url', this._handleUrl); + } + componentWillUnmount() { AppState.removeEventListener('change', this._handleAppStateChange); BackHandler.removeEventListener('hardwareBackPress'); + Linking.removeEventListener('url', this._handleUrl); } componentWillUpdate(nextProps) { @@ -174,6 +180,17 @@ class AppWithNavigationState extends React.Component { } } + _handleUrl = (evt) => { + const { dispatch } = this.props; + if (evt.url) { + const navigateAction = NavigationActions.navigate({ + routeName: 'File', + params: { uri: evt.url } + }); + dispatch(navigateAction); + } + } + render() { const { dispatch, nav } = this.props; return ( diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js index 0827092a..0ff86583 100644 --- a/app/src/page/splash/view.js +++ b/app/src/page/splash/view.js @@ -1,6 +1,6 @@ import React from 'react'; import { Lbry } from 'lbry-redux'; -import { View, Text, NativeModules } from 'react-native'; +import { View, Text, Linking, NativeModules } from 'react-native'; import { NavigationActions } from 'react-navigation'; import PropTypes from 'prop-types'; import splashStyle from '../../styles/splash'; @@ -16,6 +16,7 @@ class SplashScreen extends React.PureComponent { message: 'Connecting', isRunning: false, isLagging: false, + launchUrl: null }); } @@ -51,6 +52,10 @@ class SplashScreen extends React.PureComponent { ] }); navigation.dispatch(resetAction); + + if (this.state.launchUrl) { + navigation.navigate('File', { uri: this.state.launchUrl }); + } }); return; } @@ -78,6 +83,12 @@ class SplashScreen extends React.PureComponent { if (NativeModules.Mixpanel) { NativeModules.Mixpanel.track('App Launch', null); } + + Linking.getInitialURL().then((url) => { + if (url) { + this.setState({ launchUrl: url }); + } + }); Lbry .connect() diff --git a/app/src/redux/actions/file.js b/app/src/redux/actions/file.js index c248f173..0a1461c5 100644 --- a/app/src/redux/actions/file.js +++ b/app/src/redux/actions/file.js @@ -34,7 +34,9 @@ export function doUpdateLoadStatus(uri, outpoint) { }, }); - NativeModules.LbryDownloadManager.updateDownload(uri, fileInfo.file_name, 100, writtenBytes, totalBytes); + if (NativeModules.LbryDownloadManager) { + NativeModules.LbryDownloadManager.updateDownload(uri, fileInfo.file_name, 100, writtenBytes, totalBytes); + } /*const notif = new window.Notification('LBRY Download Complete', { body: fileInfo.metadata.stream.metadata.title, @@ -58,7 +60,9 @@ export function doUpdateLoadStatus(uri, outpoint) { }, }); - NativeModules.LbryDownloadManager.updateDownload(uri, fileInfo.file_name, progress, writtenBytes, totalBytes); + if (NativeModules.LbryDownloadManager) { + NativeModules.LbryDownloadManager.updateDownload(uri, fileInfo.file_name, progress, writtenBytes, totalBytes); + } setTimeout(() => { dispatch(doUpdateLoadStatus(uri, outpoint)); @@ -90,7 +94,9 @@ export function doStartDownload(uri, outpoint) { }, }); - NativeModules.LbryDownloadManager.startDownload(uri, fileInfo.file_name); + if (NativeModules.LbryDownloadManager) { + NativeModules.LbryDownloadManager.startDownload(uri, fileInfo.file_name); + } dispatch(doUpdateLoadStatus(uri, outpoint)); }); @@ -114,7 +120,9 @@ export function doStopDownloadingFile(uri, fileInfo) { }); }); - NativeModules.LbryDownloadManager.stopDownload(uri, fileInfo.file_name); + if (NativeModules.LbryDownloadManager) { + NativeModules.LbryDownloadManager.stopDownload(uri, fileInfo.file_name); + } // Should also delete the file after the user stops downloading dispatch(doDeleteFile(fileInfo.outpoint, uri)); diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/templates/AndroidManifest.tmpl.xml b/p4a/pythonforandroid/bootstraps/lbry/build/templates/AndroidManifest.tmpl.xml index 45e132e0..0b93dc0b 100644 --- a/p4a/pythonforandroid/bootstraps/lbry/build/templates/AndroidManifest.tmpl.xml +++ b/p4a/pythonforandroid/bootstraps/lbry/build/templates/AndroidManifest.tmpl.xml @@ -72,8 +72,15 @@ android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}" android:screenOrientation="{{ args.orientation }}" + android:launchMode="singleInstance" > - + + + + + + + {% if args.launcher %} diff --git a/src/main/java/io/lbry/browser/MainActivity.java b/src/main/java/io/lbry/browser/MainActivity.java index c9684213..853056a6 100644 --- a/src/main/java/io/lbry/browser/MainActivity.java +++ b/src/main/java/io/lbry/browser/MainActivity.java @@ -31,7 +31,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand private static final int OVERLAY_PERMISSION_REQ_CODE = 101; private static final int STORAGE_PERMISSION_REQ_CODE = 201; - + private ReactRootView mReactRootView; private ReactInstanceManager mReactInstanceManager; @@ -43,7 +43,11 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand * onResume method. */ private boolean serviceRunning; - + + protected String getMainComponentName() { + return "LBRYApp"; + } + @Override protected void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -174,6 +178,14 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand super.onBackPressed(); } } + + @Override + public void onNewIntent(Intent intent) { + if (mReactInstanceManager != null) { + mReactInstanceManager.onNewIntent(intent); + } + super.onNewIntent(intent); + } private boolean isServiceRunning(Class serviceClass) { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); diff --git a/src/main/java/io/lbry/browser/reactmodules/DownloadManagerModule.java b/src/main/java/io/lbry/browser/reactmodules/DownloadManagerModule.java index ecd97c98..e9563d05 100644 --- a/src/main/java/io/lbry/browser/reactmodules/DownloadManagerModule.java +++ b/src/main/java/io/lbry/browser/reactmodules/DownloadManagerModule.java @@ -4,6 +4,7 @@ import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; @@ -27,7 +28,7 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule { private HashMap downloadIdNotificationIdMap = new HashMap(); - private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##"); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#"); private static final int MAX_PROGRESS = 100; @@ -74,8 +75,8 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule { } } - private PendingIntent getLaunchPendingIntent() { - Intent launchIntent = new Intent(context, MainActivity.class); + private PendingIntent getLaunchPendingIntent(String uri) { + Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri)); launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent intent = PendingIntent.getActivity(context, 0, launchIntent, 0); return intent; @@ -87,7 +88,8 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule { NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); NotificationCompat.Builder builder = new NotificationCompat.Builder(context); - builder.setContentIntent(getLaunchPendingIntent()) + // The file URI is used as the unique ID + builder.setContentIntent(getLaunchPendingIntent(id)) .setContentTitle(String.format("Downloading %s...", fileName)) .setGroup(GROUP_DOWNLOADS) .setPriority(NotificationCompat.PRIORITY_LOW) @@ -115,14 +117,18 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule { createNotificationGroup(); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); NotificationCompat.Builder builder = builders.get(notificationId); - builder.setContentIntent(getLaunchPendingIntent()) + builder.setContentIntent(getLaunchPendingIntent(id)) .setContentText(String.format("%.0f%% (%s / %s)", progress, formatBytes(writtenBytes), formatBytes(totalBytes))) .setGroup(GROUP_DOWNLOADS) .setProgress(MAX_PROGRESS, new Double(progress).intValue(), false); notificationManager.notify(notificationId, builder.build()); if (progress == MAX_PROGRESS) { - builder.setContentTitle(String.format("Downloaded %s.", fileName)); + builder.setContentTitle(String.format("Downloaded %s", fileName)) + .setContentText(String.format("%s", formatBytes(totalBytes))) + .setProgress(0, 0, false); + notificationManager.notify(notificationId, builder.build()); + downloadIdNotificationIdMap.remove(id); builders.remove(notificationId); }