Skip to content

Commit

Permalink
Open source IntentAndroid
Browse files Browse the repository at this point in the history
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
Martin Konicek authored and facebook-github-bot-6 committed Nov 12, 2015
1 parent d5209a0 commit ab7b3b2
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 4 deletions.
90 changes: 90 additions & 0 deletions Examples/UIExplorer/IntentAndroidExample.android.js
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions Examples/UIExplorer/UIExplorerList.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var COMPONENTS = [
var APIS = [
require('./AccessibilityAndroidExample.android'),
require('./BorderExample'),
require('./IntentAndroidExample.android'),
require('./LayoutEventsExample'),
require('./LayoutExample'),
require('./PanResponderExample'),
Expand Down
90 changes: 90 additions & 0 deletions Libraries/Components/Intent/IntentAndroid.android.js
Original file line number Diff line number Diff line change
@@ -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;
17 changes: 17 additions & 0 deletions Libraries/Components/Intent/IntentAndroid.ios.js
Original file line number Diff line number Diff line change
@@ -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');
},
};
8 changes: 4 additions & 4 deletions Libraries/LinkingIOS/LinkingIOS.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Libraries/react-native/react-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -47,6 +48,7 @@ public List<NativeModule> createNativeModules(ReactApplicationContext reactConte
return Arrays.<NativeModule>asList(
new AsyncStorageModule(reactContext),
new FrescoModule(reactContext),
new IntentModule(reactContext),
new NetworkingModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));
Expand Down

1 comment on commit ab7b3b2

@marty-wang
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.