Skip to content

Commit bf49bde

Browse files
committed
[Android] AppState
1 parent cf350a6 commit bf49bde

File tree

8 files changed

+306
-0
lines changed

8 files changed

+306
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* The examples provided by Facebook are for non-commercial testing and
3+
* evaluation purposes only.
4+
*
5+
* Facebook reserves all rights not expressly granted.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
8+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
10+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
11+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
*
14+
* @providesModule AppStateExample
15+
* @flow
16+
*/
17+
'use strict';
18+
19+
var React = require('react-native');
20+
var {
21+
AppState,
22+
Text,
23+
View
24+
} = React;
25+
26+
var AppStateSubscription = React.createClass({
27+
getInitialState() {
28+
return {
29+
appState: AppState.currentState,
30+
previousAppStates: [],
31+
};
32+
},
33+
componentDidMount: function() {
34+
AppState.addEventListener('change', this._handleAppStateChange);
35+
},
36+
componentWillUnmount: function() {
37+
AppState.removeEventListener('change', this._handleAppStateChange);
38+
},
39+
_handleAppStateChange: function(appState) {
40+
var previousAppStates = this.state.previousAppStates.slice();
41+
previousAppStates.push(this.state.appState);
42+
this.setState({
43+
appState,
44+
previousAppStates,
45+
});
46+
},
47+
render() {
48+
if (this.props.showCurrentOnly) {
49+
return (
50+
<View>
51+
<Text>{this.state.appState}</Text>
52+
</View>
53+
);
54+
}
55+
return (
56+
<View>
57+
<Text>{JSON.stringify(this.state.previousAppStates)}</Text>
58+
</View>
59+
);
60+
}
61+
});
62+
63+
exports.title = 'AppState';
64+
exports.description = 'app background status';
65+
exports.examples = [
66+
{
67+
title: 'AppState.currentState',
68+
description: 'Can be null on app initialization',
69+
render() { return <Text>{AppState.currentState}</Text>; }
70+
},
71+
{
72+
title: 'Subscribed AppState:',
73+
description: 'This changes according to the current state, so you can only ever see it rendered as "active"',
74+
render(): ReactElement { return <AppStateSubscription showCurrentOnly={true} />; }
75+
},
76+
{
77+
title: 'Previous states:',
78+
render(): ReactElement { return <AppStateSubscription showCurrentOnly={false} />; }
79+
},
80+
];

Examples/UIExplorer/UIExplorerList.android.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var COMPONENTS = [
4242
var APIS = [
4343
require('./AccessibilityAndroidExample.android'),
4444
require('./AlertExample').AlertExample,
45+
require('./AppStateExample'),
4546
require('./BorderExample'),
4647
require('./ClipboardExample'),
4748
require('./GeolocationExample'),

Examples/UIExplorer/UIExplorerList.ios.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ var APIS = [
6464
require('./AnimatedExample'),
6565
require('./AnimatedGratuitousApp/AnExApp'),
6666
require('./AppStateIOSExample'),
67+
require('./AppStateExample'),
6768
require('./AsyncStorageExample'),
6869
require('./BorderExample'),
6970
require('./CameraRollExample.ios'),

Libraries/AppState/AppState.js

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @providesModule AppState
10+
* @flow
11+
*/
12+
'use strict';
13+
14+
var Map = require('Map');
15+
var NativeModules = require('NativeModules');
16+
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
17+
var RCTAppState = NativeModules.AppState;
18+
19+
var logError = require('logError');
20+
var invariant = require('invariant');
21+
22+
var _eventHandlers = {
23+
change: new Map(),
24+
memoryWarning: new Map(),
25+
};
26+
27+
/**
28+
* `AppState` can tell you if the app is in the foreground or background,
29+
* and notify you when the state changes.
30+
*
31+
* AppState is frequently used to determine the intent and proper behavior when
32+
* handling push notifications.
33+
*
34+
* ### App States
35+
*
36+
* - `active` - The app is running in the foreground
37+
* - `background` - The app is running in the background. The user is either
38+
* in another app or on the home screen
39+
* - `inactive` - This is a transition state that currently never happens for
40+
* typical React Native apps.
41+
*
42+
* For more information, see
43+
* [Apple's documentation](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html)
44+
*
45+
* ### Basic Usage
46+
*
47+
* To see the current state, you can check `AppState.currentState`, which
48+
* will be kept up-to-date. However, `currentState` will be null at launch
49+
* while `AppState` retrieves it over the bridge.
50+
*
51+
* ```
52+
* getInitialState: function() {
53+
* return {
54+
* currentAppState: AppState.currentState,
55+
* };
56+
* },
57+
* componentDidMount: function() {
58+
* AppState.addEventListener('change', this._handleAppStateChange);
59+
* },
60+
* componentWillUnmount: function() {
61+
* AppState.removeEventListener('change', this._handleAppStateChange);
62+
* },
63+
* _handleAppStateChange: function(currentAppState) {
64+
* this.setState({ currentAppState, });
65+
* },
66+
* render: function() {
67+
* return (
68+
* <Text>Current state is: {this.state.currentAppState}</Text>
69+
* );
70+
* },
71+
* ```
72+
*
73+
* This example will only ever appear to say "Current state is: active" because
74+
* the app is only visible to the user when in the `active` state, and the null
75+
* state will happen only momentarily.
76+
*/
77+
78+
var AppState = {
79+
80+
/**
81+
* Add a handler to AppState changes by listening to the `change` event type
82+
* and providing the handler
83+
*/
84+
addEventListener: function(
85+
type: string,
86+
handler: Function
87+
) {
88+
invariant(
89+
['change', 'memoryWarning'].indexOf(type) !== -1,
90+
'Trying to subscribe to unknown event: "%s"', type
91+
);
92+
if (type === 'change') {
93+
_eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener(
94+
'appStateDidChange',
95+
(appStateData) => {
96+
handler(appStateData.app_state);
97+
}
98+
));
99+
} else if (type === 'memoryWarning') {
100+
_eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener(
101+
'memoryWarning',
102+
handler
103+
));
104+
}
105+
},
106+
107+
/**
108+
* Remove a handler by passing the `change` event type and the handler
109+
*/
110+
removeEventListener: function(
111+
type: string,
112+
handler: Function
113+
) {
114+
invariant(
115+
['change', 'memoryWarning'].indexOf(type) !== -1,
116+
'Trying to remove listener for unknown event: "%s"', type
117+
);
118+
if (!_eventHandlers[type].has(handler)) {
119+
return;
120+
}
121+
_eventHandlers[type].get(handler).remove();
122+
_eventHandlers[type].delete(handler);
123+
},
124+
125+
// TODO: getCurrentAppState callback seems to be called at a really late stage
126+
// after app launch. Trying to get currentState when mounting App component
127+
// will likely to have the initial value here.
128+
// Initialize to 'active' instead of null.
129+
currentState: ('active' : ?string),
130+
131+
};
132+
133+
RCTDeviceEventEmitter.addListener(
134+
'appStateDidChange',
135+
(appStateData) => {
136+
AppState.currentState = appStateData.app_state;
137+
}
138+
);
139+
140+
RCTAppState.getCurrentAppState(
141+
(appStateData) => {
142+
AppState.currentState = appStateData.app_state;
143+
},
144+
logError
145+
);
146+
147+
module.exports = AppState;

Libraries/react-native/react-native.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ var ReactNative = {
6060
get Animated() { return require('Animated'); },
6161
get AppRegistry() { return require('AppRegistry'); },
6262
get AppStateIOS() { return require('AppStateIOS'); },
63+
get AppState() { return require('AppState'); },
6364
get AsyncStorage() { return require('AsyncStorage'); },
6465
get BackAndroid() { return require('BackAndroid'); },
6566
get CameraRoll() { return require('CameraRoll'); },
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
package com.facebook.react.modules.appstate;
11+
12+
import com.facebook.react.bridge.Arguments;
13+
import com.facebook.react.bridge.Callback;
14+
import com.facebook.react.bridge.LifecycleEventListener;
15+
import com.facebook.react.bridge.ReactApplicationContext;
16+
import com.facebook.react.bridge.ReactContext;
17+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
18+
import com.facebook.react.bridge.ReactMethod;
19+
import com.facebook.react.bridge.WritableMap;
20+
21+
import static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
22+
23+
public class AppStateModule extends ReactContextBaseJavaModule
24+
implements LifecycleEventListener {
25+
26+
private ReactContext mReactContext;
27+
private String mCurrentState;
28+
29+
public AppStateModule(ReactApplicationContext context) {
30+
super(context);
31+
mReactContext = context;
32+
}
33+
34+
@Override
35+
public String getName() {
36+
return "AppState";
37+
}
38+
39+
@Override
40+
public void initialize() {
41+
getReactApplicationContext().addLifecycleEventListener(this);
42+
}
43+
44+
@ReactMethod
45+
public void getCurrentAppState(Callback success, Callback error) {
46+
success.invoke(createAppStateEventMap());
47+
}
48+
49+
@Override
50+
public void onHostResume() {
51+
mCurrentState = "active";
52+
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
53+
.emit("appStateDidChange", createAppStateEventMap());
54+
}
55+
56+
@Override
57+
public void onHostPause() {
58+
mCurrentState = "background";
59+
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
60+
.emit("appStateDidChange", createAppStateEventMap());
61+
}
62+
63+
@Override
64+
public void onHostDestroy() {
65+
66+
}
67+
68+
private WritableMap createAppStateEventMap() {
69+
WritableMap appState = Arguments.createMap();
70+
appState.putString("app_state", mCurrentState);
71+
return appState;
72+
}
73+
}

ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.facebook.react.modules.network.NetworkingModule;
2626
import com.facebook.react.modules.storage.AsyncStorageModule;
2727
import com.facebook.react.modules.toast.ToastModule;
28+
import com.facebook.react.modules.appstate.AppStateModule;
2829
import com.facebook.react.modules.websocket.WebSocketModule;
2930
import com.facebook.react.uimanager.ViewManager;
3031
import com.facebook.react.views.art.ARTRenderableViewManager;
@@ -64,6 +65,7 @@ public List<NativeModule> createNativeModules(ReactApplicationContext reactConte
6465
new LocationModule(reactContext),
6566
new NetworkingModule(reactContext),
6667
new NetInfoModule(reactContext),
68+
new AppStateModule(reactContext),
6769
new WebSocketModule(reactContext),
6870
new ToastModule(reactContext));
6971
}

website/server/extractDocs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ var apis = [
224224
'../Libraries/Animated/src/AnimatedImplementation.js',
225225
'../Libraries/AppRegistry/AppRegistry.js',
226226
'../Libraries/AppStateIOS/AppStateIOS.ios.js',
227+
'../Libraries/AppState/AppState.js',
227228
'../Libraries/Storage/AsyncStorage.js',
228229
'../Libraries/Utilities/BackAndroid.android.js',
229230
'../Libraries/CameraRoll/CameraRoll.js',

0 commit comments

Comments
 (0)