mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
913d89f4a5 | ||
|
|
d9e14f674c | ||
|
|
62dd4aca59 | ||
|
|
7cb64e1655 | ||
|
|
c6ae1a7702 | ||
|
|
0165ae1e55 | ||
|
|
dc971a3cb9 | ||
|
|
ed6f4a5bd6 | ||
|
|
678dfbb5e7 | ||
|
|
bb75d9e81e | ||
|
|
a534d01e78 | ||
|
|
e01f8a2406 | ||
|
|
f4756d95ee | ||
|
|
65ff8a4455 | ||
|
|
b00a61d4ee | ||
|
|
88783d94f1 | ||
|
|
94420109a1 | ||
|
|
4ba09895dc | ||
|
|
40b74e49f2 | ||
|
|
f7af7d267e |
@@ -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,8 @@ var COMPONENTS = [
|
||||
var APIS = [
|
||||
require('./AccessibilityAndroidExample.android'),
|
||||
require('./BorderExample'),
|
||||
require('./GeolocationExample'),
|
||||
require('./IntentAndroidExample.android'),
|
||||
require('./LayoutEventsExample'),
|
||||
require('./LayoutExample'),
|
||||
require('./PanResponderExample'),
|
||||
|
||||
@@ -26,6 +26,8 @@ var {
|
||||
} = React;
|
||||
|
||||
var XHRExampleHeaders = require('./XHRExampleHeaders');
|
||||
var XHRExampleCookies = require('./XHRExampleCookies');
|
||||
|
||||
|
||||
// TODO t7093728 This is a simlified XHRExample.ios.js.
|
||||
// Once we have Camera roll, Toast, Intent (for opening URLs)
|
||||
@@ -280,6 +282,11 @@ exports.examples = [{
|
||||
render() {
|
||||
return <XHRExampleHeaders/>;
|
||||
}
|
||||
}, {
|
||||
title: 'Cookies',
|
||||
render() {
|
||||
return <XHRExampleCookies/>;
|
||||
}
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var RCTNetworking = require('RCTNetworking');
|
||||
|
||||
class XHRExampleCookies extends React.Component {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.cancelled = false;
|
||||
this.state = {
|
||||
status: '',
|
||||
a: 1,
|
||||
b: 2,
|
||||
};
|
||||
}
|
||||
|
||||
setCookie(domain: string) {
|
||||
var {a, b} = this.state;
|
||||
var url = `https://${domain}/cookies/set?a=${a}&b=${b}`;
|
||||
fetch(url).then((response) => {
|
||||
this.setStatus(`Cookies a=${a}, b=${b} set`);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
status: 'Setting cookies...',
|
||||
a: a + 1,
|
||||
b: b + 2,
|
||||
});
|
||||
}
|
||||
|
||||
getCookies(domain: string) {
|
||||
fetch(`https://${domain}/cookies`).then((response) => {
|
||||
return response.json();
|
||||
}).then((data) => {
|
||||
this.setStatus(`Got cookies ${JSON.stringify(data.cookies)} from server`);
|
||||
});
|
||||
|
||||
this.setStatus('Getting cookies...');
|
||||
}
|
||||
|
||||
clearCookies() {
|
||||
RCTNetworking.clearCookies((cleared) => {
|
||||
this.setStatus('Cookies cleared, had cookies=' + cleared);
|
||||
});
|
||||
}
|
||||
|
||||
setStatus(status: string) {
|
||||
this.setState({status});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={this.setCookie.bind(this, 'httpbin.org')}>
|
||||
<View style={styles.button}>
|
||||
<Text>Set cookie</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={this.setCookie.bind(this, 'eu.httpbin.org')}>
|
||||
<View style={styles.button}>
|
||||
<Text>Set cookie (EU)</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={this.getCookies.bind(this, 'httpbin.org')}>
|
||||
<View style={styles.button}>
|
||||
<Text>Get cookies</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={this.getCookies.bind(this, 'eu.httpbin.org')}>
|
||||
<View style={styles.button}>
|
||||
<Text>Get cookies (EU)</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={this.clearCookies.bind(this)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Clear cookies</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<Text>{this.state.status}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
borderRadius: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 8,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = XHRExampleCookies;
|
||||
@@ -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');
|
||||
},
|
||||
};
|
||||
@@ -29,12 +29,19 @@ type GeoOptions = {
|
||||
}
|
||||
|
||||
/**
|
||||
* The Geolocation API follows the web spec:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
|
||||
*
|
||||
* ### iOS
|
||||
* You need to include the `NSLocationWhenInUseUsageDescription` key
|
||||
* in Info.plist to enable geolocation. Geolocation is enabled by default
|
||||
* when you create a project with `react-native init`.
|
||||
*
|
||||
* Geolocation follows the MDN specification:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
|
||||
* ### Android
|
||||
* To request access to location, you need to add the following line to your
|
||||
* app's `AndroidManifest.xml`:
|
||||
*
|
||||
* `<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />`
|
||||
*/
|
||||
var Geolocation = {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,6 +40,10 @@ class RCTNetworking {
|
||||
static abortRequest(requestId) {
|
||||
RCTNetworkingNative.abortRequest(requestId);
|
||||
}
|
||||
|
||||
static clearCookies(callback) {
|
||||
RCTNetworkingNative.clearCookies(callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RCTNetworking;
|
||||
|
||||
+1
@@ -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'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=0.12.0-SNAPSHOT
|
||||
VERSION_NAME=0.15.3
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -16,9 +16,8 @@ import android.os.AsyncTask;
|
||||
* handled by the {@link com.facebook.react.bridge.NativeModuleCallExceptionHandler} registered if
|
||||
* the app is in dev mode.
|
||||
*
|
||||
* This class doesn't allow doInBackground to return a results. This is mostly because when this
|
||||
* class was written that functionality wasn't used and it would require some extra code to make
|
||||
* work correctly with caught exceptions. Don't let that stop you from adding it if you need it :)
|
||||
* This class doesn't allow doInBackground to return a results. If you need this
|
||||
* use GuardedResultAsyncTask instead.
|
||||
*/
|
||||
public abstract class GuardedAsyncTask<Params, Progress>
|
||||
extends AsyncTask<Params, Progress, Void> {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.bridge;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
/**
|
||||
* Abstract base for a AsyncTask with result support that should have any RuntimeExceptions it
|
||||
* throws handled by the {@link com.facebook.react.bridge.NativeModuleCallExceptionHandler}
|
||||
* registered if the app is in dev mode.
|
||||
*/
|
||||
public abstract class GuardedResultAsyncTask<Result>
|
||||
extends AsyncTask<Void, Void, Result> {
|
||||
|
||||
private final ReactContext mReactContext;
|
||||
|
||||
protected GuardedResultAsyncTask(ReactContext reactContext) {
|
||||
mReactContext = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Result doInBackground(Void... params) {
|
||||
try {
|
||||
return doInBackgroundGuarded();
|
||||
} catch (RuntimeException e) {
|
||||
mReactContext.handleException(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void onPostExecute(Result result) {
|
||||
try {
|
||||
onPostExecuteGuarded(result);
|
||||
} catch (RuntimeException e) {
|
||||
mReactContext.handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Result doInBackgroundGuarded();
|
||||
protected abstract void onPostExecuteGuarded(Result result);
|
||||
|
||||
}
|
||||
@@ -80,7 +80,8 @@ public class FrescoModule extends ReactContextBaseJavaModule implements
|
||||
}
|
||||
|
||||
Context context = this.getReactApplicationContext().getApplicationContext();
|
||||
OkHttpClient okHttpClient = OkHttpClientProvider.getOkHttpClient();
|
||||
OkHttpClient okHttpClient =
|
||||
OkHttpClientProvider.getCookieAwareOkHttpClient(getReactApplicationContext());
|
||||
ImagePipelineConfig.Builder builder =
|
||||
OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* 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.location;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationProvider;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
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.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.SystemClock;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
/**
|
||||
* Native module that exposes Geolocation to JS.
|
||||
*/
|
||||
public class LocationModule extends ReactContextBaseJavaModule {
|
||||
|
||||
private @Nullable String mWatchedProvider;
|
||||
|
||||
private final LocationListener mLocationListener = new LocationListener() {
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
|
||||
.emit("geolocationDidChange", locationToMap(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
if (status == LocationProvider.OUT_OF_SERVICE) {
|
||||
emitError("Provider " + provider + " is out of service.");
|
||||
} else if (status == LocationProvider.TEMPORARILY_UNAVAILABLE) {
|
||||
emitError("Provider " + provider + " is temporarily unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) { }
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) { }
|
||||
};
|
||||
|
||||
public LocationModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LocationObserver";
|
||||
}
|
||||
|
||||
private static class LocationOptions {
|
||||
private final long timeout;
|
||||
private final double maximumAge;
|
||||
private final boolean highAccuracy;
|
||||
|
||||
private LocationOptions(long timeout, double maximumAge, boolean highAccuracy) {
|
||||
this.timeout = timeout;
|
||||
this.maximumAge = maximumAge;
|
||||
this.highAccuracy = highAccuracy;
|
||||
}
|
||||
|
||||
private static LocationOptions fromReactMap(ReadableMap map) {
|
||||
// precision might be dropped on timeout (double -> int conversion), but that's OK
|
||||
long timeout =
|
||||
map.hasKey("timeout") ? (long) map.getDouble("timeout") : Long.MAX_VALUE;
|
||||
double maximumAge =
|
||||
map.hasKey("maximumAge") ? map.getDouble("maximumAge") : Double.POSITIVE_INFINITY;
|
||||
boolean highAccuracy =
|
||||
map.hasKey("enableHighAccuracy") && map.getBoolean("enableHighAccuracy");
|
||||
|
||||
return new LocationOptions(timeout, maximumAge, highAccuracy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current position. This can return almost immediately if the location is cached or
|
||||
* request an update, which might take a while.
|
||||
*
|
||||
* @param options map containing optional arguments: timeout (millis), maximumAge (millis) and
|
||||
* highAccuracy (boolean)
|
||||
*/
|
||||
@ReactMethod
|
||||
public void getCurrentPosition(
|
||||
ReadableMap options,
|
||||
final Callback success,
|
||||
Callback error) {
|
||||
LocationOptions locationOptions = LocationOptions.fromReactMap(options);
|
||||
|
||||
try {
|
||||
LocationManager locationManager =
|
||||
(LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE);
|
||||
String provider = getValidProvider(locationManager, locationOptions.highAccuracy);
|
||||
if (provider == null) {
|
||||
error.invoke("No available location provider.");
|
||||
return;
|
||||
}
|
||||
Location location = locationManager.getLastKnownLocation(provider);
|
||||
if (location != null &&
|
||||
SystemClock.currentTimeMillis() - location.getTime() < locationOptions.maximumAge) {
|
||||
success.invoke(locationToMap(location));
|
||||
return;
|
||||
}
|
||||
new SingleUpdateRequest(locationManager, provider, locationOptions.timeout, success, error)
|
||||
.invoke();
|
||||
} catch (SecurityException e) {
|
||||
throwLocationPermissionMissing(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening for location updates. These will be emitted via the
|
||||
* {@link RCTDeviceEventEmitter} as {@code geolocationDidChange} events.
|
||||
*
|
||||
* @param options map containing optional arguments: highAccuracy (boolean)
|
||||
*/
|
||||
@ReactMethod
|
||||
public void startObserving(ReadableMap options) {
|
||||
if (LocationManager.GPS_PROVIDER.equals(mWatchedProvider)) {
|
||||
return;
|
||||
}
|
||||
LocationOptions locationOptions = LocationOptions.fromReactMap(options);
|
||||
|
||||
try {
|
||||
LocationManager locationManager =
|
||||
(LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE);
|
||||
String provider = getValidProvider(locationManager, locationOptions.highAccuracy);
|
||||
if (provider == null) {
|
||||
emitError("No location provider available.");
|
||||
return;
|
||||
}
|
||||
if (!provider.equals(mWatchedProvider)) {
|
||||
locationManager.removeUpdates(mLocationListener);
|
||||
locationManager.requestLocationUpdates(provider, 1000, 0, mLocationListener);
|
||||
}
|
||||
mWatchedProvider = provider;
|
||||
} catch (SecurityException e) {
|
||||
throwLocationPermissionMissing(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening for location updates.
|
||||
*
|
||||
* NB: this is not balanced with {@link #startObserving}: any number of calls to that method will
|
||||
* be canceled by just one call to this one.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void stopObserving() {
|
||||
LocationManager locationManager =
|
||||
(LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE);
|
||||
locationManager.removeUpdates(mLocationListener);
|
||||
mWatchedProvider = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getValidProvider(LocationManager locationManager, boolean highAccuracy) {
|
||||
String provider =
|
||||
highAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER;
|
||||
if (!locationManager.isProviderEnabled(provider)) {
|
||||
provider = provider.equals(LocationManager.GPS_PROVIDER)
|
||||
? LocationManager.NETWORK_PROVIDER
|
||||
: LocationManager.GPS_PROVIDER;
|
||||
if (!locationManager.isProviderEnabled(provider)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
private static WritableMap locationToMap(Location location) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
WritableMap coords = Arguments.createMap();
|
||||
coords.putDouble("latitude", location.getLatitude());
|
||||
coords.putDouble("longitude", location.getLongitude());
|
||||
coords.putDouble("altitude", location.getAltitude());
|
||||
coords.putDouble("accuracy", location.getAccuracy());
|
||||
coords.putDouble("heading", location.getBearing());
|
||||
coords.putDouble("speed", location.getSpeed());
|
||||
map.putMap("coords", coords);
|
||||
map.putDouble("timestamp", location.getTime());
|
||||
return map;
|
||||
}
|
||||
|
||||
private void emitError(String error) {
|
||||
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
|
||||
.emit("geolocationError", error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a clearer exception message than the default one.
|
||||
*/
|
||||
private static void throwLocationPermissionMissing(SecurityException e) {
|
||||
throw new SecurityException(
|
||||
"Looks like the app doesn't have the permission to access location.\n" +
|
||||
"Add the following line to your app's AndroidManifest.xml:\n" +
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />", e);
|
||||
}
|
||||
|
||||
private static class SingleUpdateRequest {
|
||||
|
||||
private final Callback mSuccess;
|
||||
private final Callback mError;
|
||||
private final LocationManager mLocationManager;
|
||||
private final String mProvider;
|
||||
private final long mTimeout;
|
||||
private final Handler mHandler = new Handler();
|
||||
private final Runnable mTimeoutRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (SingleUpdateRequest.this) {
|
||||
if (!mTriggered) {
|
||||
mError.invoke("Location request timed out");
|
||||
mLocationManager.removeUpdates(mLocationListener);
|
||||
mTriggered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
private final LocationListener mLocationListener = new LocationListener() {
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
synchronized (SingleUpdateRequest.this) {
|
||||
if (!mTriggered) {
|
||||
mSuccess.invoke(locationToMap(location));
|
||||
mHandler.removeCallbacks(mTimeoutRunnable);
|
||||
mTriggered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {}
|
||||
};
|
||||
private boolean mTriggered;
|
||||
|
||||
private SingleUpdateRequest(
|
||||
LocationManager locationManager,
|
||||
String provider,
|
||||
long timeout,
|
||||
Callback success,
|
||||
Callback error) {
|
||||
mLocationManager = locationManager;
|
||||
mProvider = provider;
|
||||
mTimeout = timeout;
|
||||
mSuccess = success;
|
||||
mError = error;
|
||||
}
|
||||
|
||||
public void invoke() {
|
||||
mLocationManager.requestSingleUpdate(mProvider, mLocationListener, null);
|
||||
mHandler.postDelayed(mTimeoutRunnable, SystemClock.currentTimeMillis() + mTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.modules.network;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.CookieSyncManager;
|
||||
import android.webkit.ValueCallback;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.GuardedAsyncTask;
|
||||
import com.facebook.react.bridge.GuardedResultAsyncTask;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
|
||||
/**
|
||||
* Cookie handler that forwards all cookies to the WebView CookieManager.
|
||||
*
|
||||
* This class relies on CookieManager to persist cookies to disk so cookies may be lost if the
|
||||
* application is terminated before it syncs.
|
||||
*/
|
||||
public class ForwardingCookieHandler extends CookieHandler {
|
||||
private static final String VERSION_ZERO_HEADER = "Set-cookie";
|
||||
private static final String VERSION_ONE_HEADER = "Set-cookie2";
|
||||
private static final String COOKIE_HEADER = "Cookie";
|
||||
|
||||
// As CookieManager was synchronous before API 21 this class emulates the async behavior on <21.
|
||||
private static final boolean USES_LEGACY_STORE = Build.VERSION.SDK_INT < 21;
|
||||
|
||||
private final CookieSaver mCookieSaver;
|
||||
private final ReactContext mContext;
|
||||
private @Nullable CookieManager mCookieManager;
|
||||
|
||||
public ForwardingCookieHandler(ReactContext context) {
|
||||
mContext = context;
|
||||
mCookieSaver = new CookieSaver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> get(URI uri, Map<String, List<String>> headers)
|
||||
throws IOException {
|
||||
String cookies = getCookieManager().getCookie(uri.toString());
|
||||
if (TextUtils.isEmpty(cookies)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return Collections.singletonMap(COOKIE_HEADER, Collections.singletonList(cookies));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(URI uri, Map<String, List<String>> headers) throws IOException {
|
||||
String url = uri.toString();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key != null && isCookieHeader(key)) {
|
||||
addCookies(url, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCookies(final Callback callback) {
|
||||
if (USES_LEGACY_STORE) {
|
||||
new GuardedResultAsyncTask<Boolean>(mContext) {
|
||||
@Override
|
||||
protected Boolean doInBackgroundGuarded() {
|
||||
getCookieManager().removeAllCookie();
|
||||
mCookieSaver.onCookiesModified();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecuteGuarded(Boolean result) {
|
||||
callback.invoke(result);
|
||||
}
|
||||
}.execute();
|
||||
} else {
|
||||
clearCookiesAsync(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearCookiesAsync(final Callback callback) {
|
||||
getCookieManager().removeAllCookies(
|
||||
new ValueCallback<Boolean>() {
|
||||
@Override
|
||||
public void onReceiveValue(Boolean value) {
|
||||
mCookieSaver.onCookiesModified();
|
||||
callback.invoke(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (USES_LEGACY_STORE) {
|
||||
getCookieManager().removeExpiredCookie();
|
||||
mCookieSaver.persistCookies();
|
||||
}
|
||||
}
|
||||
|
||||
private void addCookies(final String url, final List<String> cookies) {
|
||||
if (USES_LEGACY_STORE) {
|
||||
runInBackground(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (String cookie : cookies) {
|
||||
getCookieManager().setCookie(url, cookie);
|
||||
}
|
||||
mCookieSaver.onCookiesModified();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (String cookie : cookies) {
|
||||
addCookieAsync(url, cookie);
|
||||
}
|
||||
mCookieSaver.onCookiesModified();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private void addCookieAsync(String url, String cookie) {
|
||||
getCookieManager().setCookie(url, cookie, null);
|
||||
}
|
||||
|
||||
private static boolean isCookieHeader(String name) {
|
||||
return name.equalsIgnoreCase(VERSION_ZERO_HEADER) || name.equalsIgnoreCase(VERSION_ONE_HEADER);
|
||||
}
|
||||
|
||||
private void runInBackground(final Runnable runnable) {
|
||||
new GuardedAsyncTask<Void, Void>(mContext) {
|
||||
@Override
|
||||
protected void doInBackgroundGuarded(Void... params) {
|
||||
runnable.run();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiating CookieManager in KitKat+ will load the Chromium task taking a 100ish ms so we
|
||||
* do it lazily to make sure it's done on a background thread as needed.
|
||||
*/
|
||||
private CookieManager getCookieManager() {
|
||||
if (mCookieManager == null) {
|
||||
possiblyWorkaroundSyncManager(mContext);
|
||||
mCookieManager = CookieManager.getInstance();
|
||||
|
||||
if (USES_LEGACY_STORE) {
|
||||
mCookieManager.removeExpiredCookie();
|
||||
}
|
||||
}
|
||||
|
||||
return mCookieManager;
|
||||
}
|
||||
|
||||
private static void possiblyWorkaroundSyncManager(Context context) {
|
||||
if (USES_LEGACY_STORE) {
|
||||
// This is to work around a bug where CookieManager may fail to instantiate if
|
||||
// CookieSyncManager has never been created. Note that the sync() may not be required but is
|
||||
// here of legacy reasons.
|
||||
CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
|
||||
syncManager.sync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for flushing cookies to disk. Flushes to disk with a maximum delay of 30 seconds.
|
||||
* This class is only active if we are on API < 21.
|
||||
*/
|
||||
private class CookieSaver {
|
||||
private static final int MSG_PERSIST_COOKIES = 1;
|
||||
|
||||
private static final int TIMEOUT = 30 * 1000; // 30 seconds
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
public CookieSaver() {
|
||||
mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
if (msg.what == MSG_PERSIST_COOKIES) {
|
||||
persistCookies();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onCookiesModified() {
|
||||
if (USES_LEGACY_STORE) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_PERSIST_COOKIES, TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
public void persistCookies() {
|
||||
mHandler.removeMessages(MSG_PERSIST_COOKIES);
|
||||
runInBackground(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (USES_LEGACY_STORE) {
|
||||
CookieSyncManager syncManager = CookieSyncManager.getInstance();
|
||||
syncManager.sync();
|
||||
} else {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private void flush() {
|
||||
getCookieManager().flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-6
@@ -13,6 +13,8 @@ import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.net.CookieHandler;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
@@ -59,19 +61,19 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext the ReactContext of the application
|
||||
* @param context the ReactContext of the application
|
||||
*/
|
||||
public NetworkingModule(ReactApplicationContext reactContext) {
|
||||
this(reactContext, null, OkHttpClientProvider.getOkHttpClient());
|
||||
public NetworkingModule(final ReactApplicationContext context) {
|
||||
this(context, null, OkHttpClientProvider.getCookieAwareOkHttpClient(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext the ReactContext of the application
|
||||
* @param context the ReactContext of the application
|
||||
* @param defaultUserAgent the User-Agent header that will be set for all requests where the
|
||||
* caller does not provide one explicitly
|
||||
*/
|
||||
public NetworkingModule(ReactApplicationContext reactContext, String defaultUserAgent) {
|
||||
this(reactContext, defaultUserAgent, OkHttpClientProvider.getOkHttpClient());
|
||||
public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
|
||||
this(context, defaultUserAgent, OkHttpClientProvider.getCookieAwareOkHttpClient(context));
|
||||
}
|
||||
|
||||
public NetworkingModule(ReactApplicationContext reactContext, OkHttpClient client) {
|
||||
@@ -87,6 +89,11 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
||||
public void onCatalystInstanceDestroy() {
|
||||
mShuttingDown = true;
|
||||
mClient.cancel(null);
|
||||
|
||||
CookieHandler cookieHandler = mClient.getCookieHandler();
|
||||
if (cookieHandler instanceof ForwardingCookieHandler) {
|
||||
((ForwardingCookieHandler) cookieHandler).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
@@ -225,6 +232,14 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
||||
}.execute();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clearCookies(com.facebook.react.bridge.Callback callback) {
|
||||
CookieHandler cookieHandler = mClient.getCookieHandler();
|
||||
if (cookieHandler instanceof ForwardingCookieHandler) {
|
||||
((ForwardingCookieHandler) cookieHandler).clearCookies(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable MultipartBuilder constructMultipartBody(
|
||||
ReadableArray body,
|
||||
String contentType,
|
||||
|
||||
+28
-8
@@ -9,7 +9,12 @@
|
||||
|
||||
package com.facebook.react.modules.network;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
|
||||
/**
|
||||
@@ -19,18 +24,33 @@ import com.squareup.okhttp.OkHttpClient;
|
||||
public class OkHttpClientProvider {
|
||||
|
||||
// Centralized OkHttpClient for all networking requests.
|
||||
private static OkHttpClient sClient;
|
||||
private static @Nullable OkHttpClient sClient;
|
||||
private static ForwardingCookieHandler sCookieHandler;
|
||||
|
||||
public static OkHttpClient getOkHttpClient() {
|
||||
if (sClient == null) {
|
||||
// TODO: #7108751 plug in stetho
|
||||
sClient = new OkHttpClient();
|
||||
|
||||
// No timeouts by default
|
||||
sClient.setConnectTimeout(0, TimeUnit.MILLISECONDS);
|
||||
sClient.setReadTimeout(0, TimeUnit.MILLISECONDS);
|
||||
sClient.setWriteTimeout(0, TimeUnit.MILLISECONDS);
|
||||
sClient = createClient();
|
||||
}
|
||||
return sClient;
|
||||
}
|
||||
|
||||
public static OkHttpClient getCookieAwareOkHttpClient(ReactContext context) {
|
||||
if (sCookieHandler == null) {
|
||||
sCookieHandler = new ForwardingCookieHandler(context);
|
||||
getOkHttpClient().setCookieHandler(sCookieHandler);
|
||||
}
|
||||
return getOkHttpClient();
|
||||
}
|
||||
|
||||
private static OkHttpClient createClient() {
|
||||
// TODO: #7108751 plug in stetho
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
|
||||
// No timeouts by default
|
||||
client.setConnectTimeout(0, TimeUnit.MILLISECONDS);
|
||||
client.setReadTimeout(0, TimeUnit.MILLISECONDS);
|
||||
client.setWriteTimeout(0, TimeUnit.MILLISECONDS);
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ 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.location.LocationModule;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import com.facebook.react.modules.storage.AsyncStorageModule;
|
||||
import com.facebook.react.modules.toast.ToastModule;
|
||||
@@ -47,6 +49,8 @@ public class MainReactPackage implements ReactPackage {
|
||||
return Arrays.<NativeModule>asList(
|
||||
new AsyncStorageModule(reactContext),
|
||||
new FrescoModule(reactContext),
|
||||
new IntentModule(reactContext),
|
||||
new LocationModule(reactContext),
|
||||
new NetworkingModule(reactContext),
|
||||
new WebSocketModule(reactContext),
|
||||
new ToastModule(reactContext));
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
|
||||
package com.facebook.systrace;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Trace;
|
||||
|
||||
/**
|
||||
* Systrace stub.
|
||||
* Systrace stub that mostly does nothing but delegates to Trace for beginning/ending sections.
|
||||
* The internal version of this file has not been opensourced yet.
|
||||
*/
|
||||
public class Systrace {
|
||||
|
||||
@@ -50,9 +54,15 @@ public class Systrace {
|
||||
}
|
||||
|
||||
public static void beginSection(long tag, final String sectionName) {
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
Trace.beginSection(sectionName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void endSection(long tag) {
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
Trace.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
public static void beginAsyncSection(
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
## 0.15
|
||||
|
||||
None so far.
|
||||
|
||||
## 0.14
|
||||
|
||||
- D2533877: `react-native bundle` API changes:
|
||||
|
||||
+1
-4
@@ -473,16 +473,13 @@ might be helpful if the component that we are updating is deeply nested
|
||||
and hasn't been optimized with `shouldComponentUpdate`.
|
||||
|
||||
```javascript
|
||||
// Outside of our React component
|
||||
var precomputeStyle = require('precomputeStyle');
|
||||
|
||||
// Back inside of the App component, replace the scrollSpring listener
|
||||
// in componentWillMount with this:
|
||||
this._scrollSpring.addListener({
|
||||
onSpringUpdate: () => {
|
||||
if (!this._photo) { return }
|
||||
var v = this._scrollSpring.getCurrentValue();
|
||||
var newProps = precomputeStyle({transform: [{scaleX: v}, {scaleY: v}]});
|
||||
var newProps = {style: {transform: [{scaleX: v}, {scaleY: v}]}};
|
||||
this._photo.setNativeProps(newProps);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -74,5 +74,5 @@ android {
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:23.0.1"
|
||||
compile "com.facebook.react:react-native:0.13.0"
|
||||
compile "com.facebook.react:react-native:0.15.+"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "0.12.0",
|
||||
"version": "0.15.0",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
|
||||
@@ -33,6 +33,15 @@ cd ..
|
||||
set -x
|
||||
DEST=$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH
|
||||
|
||||
# Define NVM_DIR and source the nvm.sh setup script
|
||||
[ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm"
|
||||
|
||||
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
|
||||
. "$HOME/.nvm/nvm.sh"
|
||||
elif [[ -x "$(command -v brew)" && -s "$(brew --prefix nvm)/nvm.sh" ]]; then
|
||||
. "$(brew --prefix nvm)/nvm.sh"
|
||||
fi
|
||||
|
||||
react-native bundle \
|
||||
--entry-file index.ios.js \
|
||||
--platform ios \
|
||||
|
||||
Reference in New Issue
Block a user