From eb188c8d98fa08686c6b9335e3dcbefe3f524b7e Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Tue, 15 Dec 2015 06:26:51 -0800 Subject: [PATCH] Add deep linking support to IntentAndroid Summary: Add a method to handle URLs registered to the app, ```js IntentAndroid.getInitialURL(url => { if (url) { // do stuff } }); ``` Refer - http://developer.android.com/training/app-indexing/deep-linking.html#adding-filters The API cannot be same as the iOS API (i.e. as a constant), as the activity is not availble at the time of module initialization. Moreover, multiple activties can share the same bridge instance, and the activity itself is not a constant. Hence the initialURL can change. Closes https://github.com/facebook/react-native/pull/4320 Reviewed By: svcscm Differential Revision: D2759667 Pulled By: foghina fb-gh-sync-id: b725231ae1401fa5565d444eee5a30d303e263ae --- .../Intent/IntentAndroid.android.js | 34 ++++++++++++ .../react/modules/intent/IntentModule.java | 54 +++++++++++++++---- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Libraries/Components/Intent/IntentAndroid.android.js b/Libraries/Components/Intent/IntentAndroid.android.js index 75c620ee9..9dad6327b 100644 --- a/Libraries/Components/Intent/IntentAndroid.android.js +++ b/Libraries/Components/Intent/IntentAndroid.android.js @@ -16,6 +16,26 @@ var invariant = require('invariant'); /** * `IntentAndroid` gives you a general interface to handle external links. * + * ### Basic Usage + * + * #### Handling deep links + * + * If your app was launched from an external url registered to your app you can + * access and handle it from any component you want with + * + * ``` + * componentDidMount() { + * var url = IntentAndroid.getInitialURL(url => { + * if (url) { + * console.log('Initial url is: ' + url); + * } + * }); + * } + * ``` + * + * NOTE: For instructions on how to add support for deep linking, + * refer [Enabling Deep Links for App Content - Add Intent Filters for Your Deep Links](http://developer.android.com/training/app-indexing/deep-linking.html#adding-filters). + * * #### Opening external links * * To start the corresponding activity for a link (web URL, email, contact etc.), call @@ -75,6 +95,20 @@ class IntentAndroid { IntentAndroidModule.canOpenURL(url, callback); } + /** + * If the app launch was triggered by an app link with {@code Intent.ACTION_VIEW}, + * it will give the link url, otherwise it will give `null` + * + * Refer http://developer.android.com/training/app-indexing/deep-linking.html#handling-intents + */ + static getInitialURL(callback: Function) { + invariant( + typeof callback === 'function', + 'A valid callback function is required' + ); + IntentAndroidModule.getInitialURL(callback); + } + static _validateURL(url: string) { invariant( typeof url === 'string', diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java index e8e632224..ce4681264 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java @@ -9,15 +9,15 @@ package com.facebook.react.modules.intent; +import android.app.Activity; import android.content.Intent; import android.net.Uri; import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; /** * Intent module. Launch other activities or open URLs. @@ -33,25 +33,58 @@ public class IntentModule extends ReactContextBaseJavaModule { return "IntentAndroid"; } + /** + * Return the URL the activity was started with + * + * @param callback a callback which is called with the initial URL + */ + @ReactMethod + public void getInitialURL(Callback callback) { + try { + Activity currentActivity = getCurrentActivity(); + String initialURL = null; + + if (currentActivity != null) { + Intent intent = currentActivity.getIntent(); + String action = intent.getAction(); + Uri uri = intent.getData(); + + if (Intent.ACTION_VIEW.equals(action) && uri != null) { + initialURL = uri.toString(); + } + } + + callback.invoke(initialURL); + } catch (Exception e) { + throw new JSApplicationIllegalArgumentException( + "Could not get the initial URL : " + e.getMessage()); + } + } + /** * Starts a corresponding external activity for the given URL. * * For example, if the URL is "https://www.facebook.com", the system browser will be opened, * or the "choose application" dialog will be shown. * - * @param URL the URL to open + * @param url the URL to open */ @ReactMethod public void openURL(String url) { if (url == null || url.isEmpty()) { throw new JSApplicationIllegalArgumentException("Invalid URL: " + url); } + try { + Activity currentActivity = getCurrentActivity(); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - // We need Intent.FLAG_ACTIVITY_NEW_TASK since getReactApplicationContext() returns - // the ApplicationContext instead of the Activity context. - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getReactApplicationContext().startActivity(intent); + + if (currentActivity != null) { + currentActivity.startActivity(intent); + } else { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getReactApplicationContext().startActivity(intent); + } } catch (Exception e) { throw new JSApplicationIllegalArgumentException( "Could not open URL '" + url + "': " + e.getMessage()); @@ -61,21 +94,22 @@ public class IntentModule extends ReactContextBaseJavaModule { /** * Determine whether or not an installed app can handle a given URL. * - * @param URL the URL to open - * @param promise a promise that is always resolved with a boolean argument + * @param url the URL to open + * @param callback a callback that is always called with a boolean argument */ @ReactMethod public void canOpenURL(String url, Callback callback) { if (url == null || url.isEmpty()) { throw new JSApplicationIllegalArgumentException("Invalid URL: " + url); } + try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); // We need Intent.FLAG_ACTIVITY_NEW_TASK since getReactApplicationContext() returns // the ApplicationContext instead of the Activity context. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); boolean canOpen = - intent.resolveActivity(this.getReactApplicationContext().getPackageManager()) != null; + intent.resolveActivity(getReactApplicationContext().getPackageManager()) != null; callback.invoke(canOpen); } catch (Exception e) { throw new JSApplicationIllegalArgumentException(