Android AppState
Summary: Closes https://github.com/facebook/react-native/pull/5152 Reviewed By: svcscm Differential Revision: D2850250 Pulled By: mkonicek fb-gh-sync-id: 0b5063fa7121d4e304a70da8573c9ba1d05a757c
This commit is contained in:
parent
2263cddd02
commit
c2d75d7a65
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule AppStateExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppState,
|
||||
Text,
|
||||
View
|
||||
} = React;
|
||||
|
||||
var AppStateSubscription = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
appState: AppState.currentState,
|
||||
previousAppStates: [],
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
AppState.addEventListener('change', this._handleAppStateChange);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
AppState.removeEventListener('change', this._handleAppStateChange);
|
||||
},
|
||||
_handleAppStateChange: function(appState) {
|
||||
var previousAppStates = this.state.previousAppStates.slice();
|
||||
previousAppStates.push(this.state.appState);
|
||||
this.setState({
|
||||
appState,
|
||||
previousAppStates,
|
||||
});
|
||||
},
|
||||
render() {
|
||||
if (this.props.showCurrentOnly) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.state.appState}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
<Text>{JSON.stringify(this.state.previousAppStates)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
exports.title = 'AppState';
|
||||
exports.description = 'app background status';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'AppState.currentState',
|
||||
description: 'Can be null on app initialization',
|
||||
render() { return <Text>{AppState.currentState}</Text>; }
|
||||
},
|
||||
{
|
||||
title: 'Subscribed AppState:',
|
||||
description: 'This changes according to the current state, so you can only ever see it rendered as "active"',
|
||||
render(): ReactElement { return <AppStateSubscription showCurrentOnly={true} />; }
|
||||
},
|
||||
{
|
||||
title: 'Previous states:',
|
||||
render(): ReactElement { return <AppStateSubscription showCurrentOnly={false} />; }
|
||||
},
|
||||
];
|
|
@ -43,6 +43,7 @@ var COMPONENTS = [
|
|||
var APIS = [
|
||||
require('./AccessibilityAndroidExample.android'),
|
||||
require('./AlertExample').AlertExample,
|
||||
require('./AppStateExample'),
|
||||
require('./BorderExample'),
|
||||
require('./CameraRollExample'),
|
||||
require('./ClipboardExample'),
|
||||
|
|
|
@ -64,6 +64,7 @@ var APIS = [
|
|||
require('./AnimatedExample'),
|
||||
require('./AnimatedGratuitousApp/AnExApp'),
|
||||
require('./AppStateIOSExample'),
|
||||
require('./AppStateExample'),
|
||||
require('./AsyncStorageExample'),
|
||||
require('./BorderExample'),
|
||||
require('./BoxShadowExample'),
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* 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 AppState
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Map = require('Map');
|
||||
var NativeModules = require('NativeModules');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var RCTAppState = NativeModules.AppState;
|
||||
|
||||
var logError = require('logError');
|
||||
var invariant = require('invariant');
|
||||
|
||||
var _eventHandlers = {
|
||||
change: new Map(),
|
||||
memoryWarning: new Map(),
|
||||
};
|
||||
|
||||
/**
|
||||
* `AppState` can tell you if the app is in the foreground or background,
|
||||
* and notify you when the state changes.
|
||||
*
|
||||
* AppState is frequently used to determine the intent and proper behavior when
|
||||
* handling push notifications.
|
||||
*
|
||||
* ### App States
|
||||
*
|
||||
* - `active` - The app is running in the foreground
|
||||
* - `background` - The app is running in the background. The user is either
|
||||
* in another app or on the home screen
|
||||
* - `inactive` - This is a transition state that currently never happens for
|
||||
* typical React Native apps.
|
||||
*
|
||||
* For more information, see
|
||||
* [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html)
|
||||
*
|
||||
* ### Basic Usage
|
||||
*
|
||||
* To see the current state, you can check `AppState.currentState`, which
|
||||
* will be kept up-to-date. However, `currentState` will be null at launch
|
||||
* while `AppState` retrieves it over the bridge.
|
||||
*
|
||||
* ```
|
||||
* getInitialState: function() {
|
||||
* return {
|
||||
* currentAppState: AppState.currentState,
|
||||
* };
|
||||
* },
|
||||
* componentDidMount: function() {
|
||||
* AppState.addEventListener('change', this._handleAppStateChange);
|
||||
* },
|
||||
* componentWillUnmount: function() {
|
||||
* AppState.removeEventListener('change', this._handleAppStateChange);
|
||||
* },
|
||||
* _handleAppStateChange: function(currentAppState) {
|
||||
* this.setState({ currentAppState, });
|
||||
* },
|
||||
* render: function() {
|
||||
* return (
|
||||
* <Text>Current state is: {this.state.currentAppState}</Text>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
*
|
||||
* This example will only ever appear to say "Current state is: active" because
|
||||
* the app is only visible to the user when in the `active` state, and the null
|
||||
* state will happen only momentarily.
|
||||
*/
|
||||
|
||||
var AppState = {
|
||||
|
||||
/**
|
||||
* Add a handler to AppState changes by listening to the `change` event type
|
||||
* and providing the handler
|
||||
*/
|
||||
addEventListener: function(
|
||||
type: string,
|
||||
handler: Function
|
||||
) {
|
||||
invariant(
|
||||
['change', 'memoryWarning'].indexOf(type) !== -1,
|
||||
'Trying to subscribe to unknown event: "%s"', type
|
||||
);
|
||||
if (type === 'change') {
|
||||
_eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener(
|
||||
'appStateDidChange',
|
||||
(appStateData) => {
|
||||
handler(appStateData.app_state);
|
||||
}
|
||||
));
|
||||
} else if (type === 'memoryWarning') {
|
||||
_eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener(
|
||||
'memoryWarning',
|
||||
handler
|
||||
));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a handler by passing the `change` event type and the handler
|
||||
*/
|
||||
removeEventListener: function(
|
||||
type: string,
|
||||
handler: Function
|
||||
) {
|
||||
invariant(
|
||||
['change', 'memoryWarning'].indexOf(type) !== -1,
|
||||
'Trying to remove listener for unknown event: "%s"', type
|
||||
);
|
||||
if (!_eventHandlers[type].has(handler)) {
|
||||
return;
|
||||
}
|
||||
_eventHandlers[type].get(handler).remove();
|
||||
_eventHandlers[type].delete(handler);
|
||||
},
|
||||
|
||||
// TODO: getCurrentAppState callback seems to be called at a really late stage
|
||||
// after app launch. Trying to get currentState when mounting App component
|
||||
// will likely to have the initial value here.
|
||||
// Initialize to 'active' instead of null.
|
||||
currentState: ('active' : ?string),
|
||||
|
||||
};
|
||||
|
||||
RCTDeviceEventEmitter.addListener(
|
||||
'appStateDidChange',
|
||||
(appStateData) => {
|
||||
AppState.currentState = appStateData.app_state;
|
||||
}
|
||||
);
|
||||
|
||||
RCTAppState.getCurrentAppState(
|
||||
(appStateData) => {
|
||||
AppState.currentState = appStateData.app_state;
|
||||
},
|
||||
logError
|
||||
);
|
||||
|
||||
module.exports = AppState;
|
|
@ -60,6 +60,7 @@ var ReactNative = {
|
|||
get AlertIOS() { return require('AlertIOS'); },
|
||||
get Animated() { return require('Animated'); },
|
||||
get AppRegistry() { return require('AppRegistry'); },
|
||||
get AppState() { return require('AppState'); },
|
||||
get AppStateIOS() { return require('AppStateIOS'); },
|
||||
get AsyncStorage() { return require('AsyncStorage'); },
|
||||
get BackAndroid() { return require('BackAndroid'); },
|
||||
|
|
|
@ -72,6 +72,7 @@ var ReactNative = Object.assign(Object.create(require('React')), {
|
|||
AlertIOS: require('AlertIOS'),
|
||||
Animated: require('Animated'),
|
||||
AppRegistry: require('AppRegistry'),
|
||||
AppState: require('AppState'),
|
||||
AppStateIOS: require('AppStateIOS'),
|
||||
AsyncStorage: require('AsyncStorage'),
|
||||
BackAndroid: require('BackAndroid'),
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* 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.appstate;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
public class AppStateModule extends ReactContextBaseJavaModule
|
||||
implements LifecycleEventListener {
|
||||
|
||||
public static final String APP_STATE_ACTIVE = "active";
|
||||
public static final String APP_STATE_BACKGROUND = "background";
|
||||
|
||||
private String mAppState = "uninitialized";
|
||||
|
||||
public AppStateModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "AppState";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
getReactApplicationContext().addLifecycleEventListener(this);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getCurrentAppState(Callback success, Callback error) {
|
||||
success.invoke(createAppStateEventMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
mAppState = APP_STATE_ACTIVE;
|
||||
sendAppStateChangeEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
mAppState = APP_STATE_BACKGROUND;
|
||||
sendAppStateChangeEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
// do not set state to destroyed, do not send an event. By the current implementation, the
|
||||
// catalyst instance is going to be immediately dropped, and all JS calls with it.
|
||||
}
|
||||
|
||||
private WritableMap createAppStateEventMap() {
|
||||
WritableMap appState = Arguments.createMap();
|
||||
appState.putString("app_state", mAppState);
|
||||
return appState;
|
||||
}
|
||||
|
||||
private void sendAppStateChangeEvent() {
|
||||
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
|
||||
.emit("appStateDidChange", createAppStateEventMap());
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import com.facebook.react.modules.netinfo.NetInfoModule;
|
|||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import com.facebook.react.modules.storage.AsyncStorageModule;
|
||||
import com.facebook.react.modules.toast.ToastModule;
|
||||
import com.facebook.react.modules.appstate.AppStateModule;
|
||||
import com.facebook.react.modules.websocket.WebSocketModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.react.views.art.ARTRenderableViewManager;
|
||||
|
@ -59,6 +60,7 @@ public class MainReactPackage implements ReactPackage {
|
|||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return Arrays.<NativeModule>asList(
|
||||
new AppStateModule(reactContext),
|
||||
new AsyncStorageModule(reactContext),
|
||||
new CameraRollManager(reactContext),
|
||||
new ClipboardModule(reactContext),
|
||||
|
@ -68,8 +70,8 @@ public class MainReactPackage implements ReactPackage {
|
|||
new LocationModule(reactContext),
|
||||
new NetworkingModule(reactContext),
|
||||
new NetInfoModule(reactContext),
|
||||
new WebSocketModule(reactContext),
|
||||
new ToastModule(reactContext));
|
||||
new ToastModule(reactContext),
|
||||
new WebSocketModule(reactContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,17 +93,17 @@ public class MainReactPackage implements ReactPackage {
|
|||
new ReactImageManager(),
|
||||
new ReactProgressBarViewManager(),
|
||||
new ReactRawTextManager(),
|
||||
new RecyclerViewBackedScrollViewManager(),
|
||||
new ReactScrollViewManager(),
|
||||
new ReactSwitchManager(),
|
||||
new ReactTextInlineImageViewManager(),
|
||||
new ReactTextInputManager(),
|
||||
new ReactTextViewManager(),
|
||||
new ReactToolbarManager(),
|
||||
new ReactViewManager(),
|
||||
new ReactViewPagerManager(),
|
||||
new ReactTextInlineImageViewManager(),
|
||||
new ReactVirtualTextViewManager(),
|
||||
new SwipeRefreshLayoutManager(),
|
||||
new ReactWebViewManager());
|
||||
new ReactWebViewManager(),
|
||||
new RecyclerViewBackedScrollViewManager(),
|
||||
new SwipeRefreshLayoutManager());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,6 +225,7 @@ var apis = [
|
|||
'../Libraries/Animated/src/AnimatedImplementation.js',
|
||||
'../Libraries/AppRegistry/AppRegistry.js',
|
||||
'../Libraries/AppStateIOS/AppStateIOS.ios.js',
|
||||
'../Libraries/AppState/AppState.js',
|
||||
'../Libraries/Storage/AsyncStorage.js',
|
||||
'../Libraries/Utilities/BackAndroid.android.js',
|
||||
'../Libraries/CameraRoll/CameraRoll.js',
|
||||
|
|
Loading…
Reference in New Issue