Permalink
Browse files

Open source IntentAndroid

Summary: Move the code to the github folder, add more docs and improve the example.

We might want to merge this with `LinkingIOS` later (it has the same functionality
plus support for deep links) but want to see how people use the `IntentAndroid`
API first (and what other methods we should add) to have more data points.

public

Reviewed By: lexs

Differential Revision: D2646936

fb-gh-sync-id: 751f35784d387efcd031f9b458821cdfde048a54
  • Loading branch information...
mkonicek authored and facebook-github-bot-6 committed Nov 12, 2015
1 parent d5209a0 commit ab7b3b2dea21d57717fad68129fb2d1059f405ab
@@ -0,0 +1,90 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ IntentAndroid,
+ StyleSheet,
+ Text,
+ TouchableNativeFeedback,
+ View,
+} = React;
+var UIExplorerBlock = require('./UIExplorerBlock');
+
+var OpenURLButton = React.createClass({
+
+ propTypes: {
+ url: React.PropTypes.string,
+ },
+
+ handleClick: function() {
+ IntentAndroid.canOpenURL(this.props.url, (supported) => {
+ if (supported) {
+ IntentAndroid.openURL(this.props.url);
+ } else {
+ console.log('Don\'t know how to open URI: ' + this.props.url);
+ }
+ });
+ },
+
+ render: function() {
+ return (
+ <TouchableNativeFeedback
+ onPress={this.handleClick}>
+ <View style={styles.button}>
+ <Text style={styles.text}>Open {this.props.url}</Text>
+ </View>
+ </TouchableNativeFeedback>
+ );
+ }
+});
+
+var IntentAndroidExample = React.createClass({
+
+ statics: {
+ title: 'IntentAndroid',
+ description: 'Shows how to use Android Intents to open URLs.',
+ },
+
+ render: function() {
+ return (
+ <UIExplorerBlock title="Open external URLs">
+ <OpenURLButton url={'https://www.facebook.com'} />
+ <OpenURLButton url={'http://www.facebook.com'} />
+ <OpenURLButton url={'http://facebook.com'} />
+ <OpenURLButton url={'geo:37.484847,-122.148386'} />
+ </UIExplorerBlock>
+ );
+ },
+});
+
+var styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: 'white',
+ padding: 10,
+ paddingTop: 30,
+ },
+ button: {
+ padding: 10,
+ backgroundColor: '#3B5998',
+ marginBottom: 10,
+ },
+ text: {
+ color: 'white',
+ },
+});
+
+module.exports = IntentAndroidExample;
@@ -38,6 +38,7 @@ var COMPONENTS = [
var APIS = [
require('./AccessibilityAndroidExample.android'),
require('./BorderExample'),
+ require('./IntentAndroidExample.android'),
require('./LayoutEventsExample'),
require('./LayoutExample'),
require('./PanResponderExample'),
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule IntentAndroid
+ */
+'use strict';
+
+var IntentAndroidModule = require('NativeModules').IntentAndroid;
+var invariant = require('invariant');
+
+/**
+ * `IntentAndroid` gives you a general interface to handle external links.
+ *
+ * #### Opening external links
+ *
+ * To start the corresponding activity for a link (web URL, email, contact etc.), call
+ *
+ * ```
+ * IntentAndroid.openURL(url)
+ * ```
+ *
+ * If you want to check if any installed app can handle a given URL beforehand you can call
+ * ```
+ * IntentAndroid.canOpenURL(url, (supported) => {
+ * if (!supported) {
+ * console.log('Can\'t handle url: ' + url);
+ * } else {
+ * IntentAndroid.openURL(url);
+ * }
+ * });
+ * ```
+ */
+class IntentAndroid {
+
+ /**
+ * 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.
+ *
+ * You can use other URLs, like a location (e.g. "geo:37.484847,-122.148386"), a contact,
+ * or any other URL that can be opened with {@code Intent.ACTION_VIEW}.
+ *
+ * NOTE: This method will fail if the system doesn't know how to open the specified URL.
+ * If you're passing in a non-http(s) URL, it's best to check {@code canOpenURL} first.
+ *
+ * NOTE: For web URLs, the protocol ("http://", "https://") must be set accordingly!
+ */
+ static openURL(url: string) {
+ this._validateURL(url);
+ IntentAndroidModule.openURL(url);
+ }
+
+ /**
+ * Determine whether or not an installed app can handle a given URL.
+ *
+ * You can use other URLs, like a location (e.g. "geo:37.484847,-122.148386"), a contact,
+ * or any other URL that can be opened with {@code Intent.ACTION_VIEW}.
+ *
+ * NOTE: For web URLs, the protocol ("http://", "https://") must be set accordingly!
+ *
+ * @param URL the URL to open
+ */
+ static canOpenURL(url: string, callback: Function) {
+ this._validateURL(url);
+ invariant(
+ typeof callback === 'function',
+ 'A valid callback function is required'
+ );
+ IntentAndroidModule.canOpenURL(url, callback);
+ }
+
+ static _validateURL(url: string) {
+ invariant(
+ typeof url === 'string',
+ 'Invalid URL: should be a string. Was: ' + url
+ );
+ invariant(
+ url,
+ 'Invalid URL: cannot be empty'
+ );
+ }
+}
+
+module.exports = IntentAndroid;
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule IntentAndroid
+ */
+'use strict';
+
+module.exports = {
+ openURI: function(url) {
+ console.error('IntentAndroid is not supported on iOS');
+ },
+};
@@ -23,7 +23,7 @@ var _initialURL = RCTLinkingManager &&
var DEVICE_NOTIF_EVENT = 'openURL';
/**
- * `LinkingIOS` gives you a general interface to interact with both, incoming
+ * `LinkingIOS` gives you a general interface to interact with both incoming
* and outgoing app links.
*
* ### Basic Usage
@@ -65,13 +65,13 @@ var DEVICE_NOTIF_EVENT = 'openURL';
*
* #### Triggering App links
*
- * To trigger an app link (browser, email or custom schemas) you call
+ * To trigger an app link (browser, email or custom schemas), call
*
* ```
* LinkingIOS.openURL(url)
* ```
*
- * If you want to check if any installed app can handle a given url beforehand you can call
+ * If you want to check if any installed app can handle a given URL beforehand you can call
* ```
* LinkingIOS.canOpenURL(url, (supported) => {
* if (!supported) {
@@ -127,7 +127,7 @@ class LinkingIOS {
}
/**
- * Determine whether or not an installed app can handle a given `url`
+ * Determine whether or not an installed app can handle a given URL.
* The callback function will be called with `bool supported` as the only argument
*
* NOTE: As of iOS 9, your app needs to provide a `LSApplicationQueriesSchemes` key
@@ -66,6 +66,7 @@ var ReactNative = Object.assign(Object.create(require('React')), {
Dimensions: require('Dimensions'),
Easing: require('Easing'),
ImagePickerIOS: require('ImagePickerIOS'),
+ IntentAndroid: require('IntentAndroid'),
InteractionManager: require('InteractionManager'),
LayoutAnimation: require('LayoutAnimation'),
LinkingIOS: require('LinkingIOS'),
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+package com.facebook.react.modules.intent;
+
+import android.content.Intent;
+import android.net.Uri;
+
+import com.facebook.react.bridge.Callback;
+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.
+ */
+public class IntentModule extends ReactContextBaseJavaModule {
+
+ public IntentModule(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ public String getName() {
+ return "IntentAndroid";
+ }
+
+ /**
+ * 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
+ */
+ @ReactMethod
+ public void openURL(String url) {
+ 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);
+ getReactApplicationContext().startActivity(intent);
+ } catch (Exception e) {
+ throw new JSApplicationIllegalArgumentException(
+ "Could not open URL '" + url + "': " + e.getMessage());
+ }
+ }
+
+ /**
+ * 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
+ */
+ @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;
+ callback.invoke(canOpen);
+ } catch (Exception e) {
+ throw new JSApplicationIllegalArgumentException(
+ "Could not check if URL '" + url + "' can be opened: " + e.getMessage());
+ }
+ }
+}
@@ -18,6 +18,7 @@
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.modules.fresco.FrescoModule;
+import com.facebook.react.modules.intent.IntentModule;
import com.facebook.react.modules.network.NetworkingModule;
import com.facebook.react.modules.storage.AsyncStorageModule;
import com.facebook.react.modules.toast.ToastModule;
@@ -47,6 +48,7 @@
return Arrays.<NativeModule>asList(
new AsyncStorageModule(reactContext),
new FrescoModule(reactContext),
+ new IntentModule(reactContext),
new NetworkingModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));

1 comment on commit ab7b3b2

@marty-wang

This comment has been minimized.

Show comment
Hide comment
@marty-wang

marty-wang Nov 13, 2015

I would suggest not merge. Just leave to developers to decide how to encapsulate the common functionality to their specific needs. It is very easy to do anyway. But more importantly it allows for maximum flexibility and functionality for its own platform.

I would suggest not merge. Just leave to developers to decide how to encapsulate the common functionality to their specific needs. It is very easy to do anyway. But more importantly it allows for maximum flexibility and functionality for its own platform.

Please sign in to comment.