diff --git a/Examples/UIExplorer/IntentAndroidExample.android.js b/Examples/UIExplorer/IntentAndroidExample.android.js
new file mode 100644
index 000000000..e655bd965
--- /dev/null
+++ b/Examples/UIExplorer/IntentAndroidExample.android.js
@@ -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 (
+
+
+ Open {this.props.url}
+
+
+ );
+ }
+});
+
+var IntentAndroidExample = React.createClass({
+
+ statics: {
+ title: 'IntentAndroid',
+ description: 'Shows how to use Android Intents to open URLs.',
+ },
+
+ render: function() {
+ return (
+
+
+
+
+
+
+ );
+ },
+});
+
+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;
diff --git a/Examples/UIExplorer/UIExplorerList.android.js b/Examples/UIExplorer/UIExplorerList.android.js
index d04c80c6e..7024779a4 100644
--- a/Examples/UIExplorer/UIExplorerList.android.js
+++ b/Examples/UIExplorer/UIExplorerList.android.js
@@ -38,6 +38,7 @@ var COMPONENTS = [
var APIS = [
require('./AccessibilityAndroidExample.android'),
require('./BorderExample'),
+ require('./IntentAndroidExample.android'),
require('./LayoutEventsExample'),
require('./LayoutExample'),
require('./PanResponderExample'),
diff --git a/Libraries/Components/Intent/IntentAndroid.android.js b/Libraries/Components/Intent/IntentAndroid.android.js
new file mode 100644
index 000000000..75c620ee9
--- /dev/null
+++ b/Libraries/Components/Intent/IntentAndroid.android.js
@@ -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;
diff --git a/Libraries/Components/Intent/IntentAndroid.ios.js b/Libraries/Components/Intent/IntentAndroid.ios.js
new file mode 100644
index 000000000..7ae19934d
--- /dev/null
+++ b/Libraries/Components/Intent/IntentAndroid.ios.js
@@ -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');
+ },
+};
diff --git a/Libraries/LinkingIOS/LinkingIOS.js b/Libraries/LinkingIOS/LinkingIOS.js
index 917b04762..0ee8367df 100644
--- a/Libraries/LinkingIOS/LinkingIOS.js
+++ b/Libraries/LinkingIOS/LinkingIOS.js
@@ -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
diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js
index 0cc6afe3a..206b5f785 100644
--- a/Libraries/react-native/react-native.js
+++ b/Libraries/react-native/react-native.js
@@ -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'),
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
new file mode 100644
index 000000000..e8e632224
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.java
@@ -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());
+ }
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
index fb424ffcd..4d6dba8e5 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java
@@ -18,6 +18,7 @@ import com.facebook.react.bridge.JavaScriptModule;
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 @@ public class MainReactPackage implements ReactPackage {
return Arrays.asList(
new AsyncStorageModule(reactContext),
new FrescoModule(reactContext),
+ new IntentModule(reactContext),
new NetworkingModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));