Skip to content

Commit c2d75d7

Browse files
skv-headlessfacebook-github-bot-3
authored andcommitted
Android AppState
Summary: Closes #5152 Reviewed By: svcscm Differential Revision: D2850250 Pulled By: mkonicek fb-gh-sync-id: 0b5063fa7121d4e304a70da8573c9ba1d05a757c
1 parent 2263cdd commit c2d75d7

File tree

9 files changed

+316
-6
lines changed

9 files changed

+316
-6
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
@@ -43,6 +43,7 @@ var COMPONENTS = [
4343
var APIS = [
4444
require('./AccessibilityAndroidExample.android'),
4545
require('./AlertExample').AlertExample,
46+
require('./AppStateExample'),
4647
require('./BorderExample'),
4748
require('./CameraRollExample'),
4849
require('./ClipboardExample'),

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('./BoxShadowExample'),

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 AlertIOS() { return require('AlertIOS'); },
6161
get Animated() { return require('Animated'); },
6262
get AppRegistry() { return require('AppRegistry'); },
63+
get AppState() { return require('AppState'); },
6364
get AppStateIOS() { return require('AppStateIOS'); },
6465
get AsyncStorage() { return require('AsyncStorage'); },
6566
get BackAndroid() { return require('BackAndroid'); },

Libraries/react-native/react-native.js.flow

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ var ReactNative = Object.assign(Object.create(require('React')), {
7272
AlertIOS: require('AlertIOS'),
7373
Animated: require('Animated'),
7474
AppRegistry: require('AppRegistry'),
75+
AppState: require('AppState'),
7576
AppStateIOS: require('AppStateIOS'),
7677
AsyncStorage: require('AsyncStorage'),
7778
BackAndroid: require('BackAndroid'),
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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.ReactContextBaseJavaModule;
17+
import com.facebook.react.bridge.ReactMethod;
18+
import com.facebook.react.bridge.WritableMap;
19+
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
20+
21+
public class AppStateModule extends ReactContextBaseJavaModule
22+
implements LifecycleEventListener {
23+
24+
public static final String APP_STATE_ACTIVE = "active";
25+
public static final String APP_STATE_BACKGROUND = "background";
26+
27+
private String mAppState = "uninitialized";
28+
29+
public AppStateModule(ReactApplicationContext reactContext) {
30+
super(reactContext);
31+
}
32+
33+
@Override
34+
public String getName() {
35+
return "AppState";
36+
}
37+
38+
@Override
39+
public void initialize() {
40+
getReactApplicationContext().addLifecycleEventListener(this);
41+
}
42+
43+
@ReactMethod
44+
public void getCurrentAppState(Callback success, Callback error) {
45+
success.invoke(createAppStateEventMap());
46+
}
47+
48+
@Override
49+
public void onHostResume() {
50+
mAppState = APP_STATE_ACTIVE;
51+
sendAppStateChangeEvent();
52+
}
53+
54+
@Override
55+
public void onHostPause() {
56+
mAppState = APP_STATE_BACKGROUND;
57+
sendAppStateChangeEvent();
58+
}
59+
60+
@Override
61+
public void onHostDestroy() {
62+
// do not set state to destroyed, do not send an event. By the current implementation, the
63+
// catalyst instance is going to be immediately dropped, and all JS calls with it.
64+
}
65+
66+
private WritableMap createAppStateEventMap() {
67+
WritableMap appState = Arguments.createMap();
68+
appState.putString("app_state", mAppState);
69+
return appState;
70+
}
71+
72+
private void sendAppStateChangeEvent() {
73+
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
74+
.emit("appStateDidChange", createAppStateEventMap());
75+
}
76+
}

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.facebook.react.modules.network.NetworkingModule;
2727
import com.facebook.react.modules.storage.AsyncStorageModule;
2828
import com.facebook.react.modules.toast.ToastModule;
29+
import com.facebook.react.modules.appstate.AppStateModule;
2930
import com.facebook.react.modules.websocket.WebSocketModule;
3031
import com.facebook.react.uimanager.ViewManager;
3132
import com.facebook.react.views.art.ARTRenderableViewManager;
@@ -59,6 +60,7 @@ public class MainReactPackage implements ReactPackage {
5960
@Override
6061
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
6162
return Arrays.<NativeModule>asList(
63+
new AppStateModule(reactContext),
6264
new AsyncStorageModule(reactContext),
6365
new CameraRollManager(reactContext),
6466
new ClipboardModule(reactContext),
@@ -68,8 +70,8 @@ public List<NativeModule> createNativeModules(ReactApplicationContext reactConte
6870
new LocationModule(reactContext),
6971
new NetworkingModule(reactContext),
7072
new NetInfoModule(reactContext),
71-
new WebSocketModule(reactContext),
72-
new ToastModule(reactContext));
73+
new ToastModule(reactContext),
74+
new WebSocketModule(reactContext));
7375
}
7476

7577
@Override
@@ -91,17 +93,17 @@ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext
9193
new ReactImageManager(),
9294
new ReactProgressBarViewManager(),
9395
new ReactRawTextManager(),
94-
new RecyclerViewBackedScrollViewManager(),
9596
new ReactScrollViewManager(),
9697
new ReactSwitchManager(),
98+
new ReactTextInlineImageViewManager(),
9799
new ReactTextInputManager(),
98100
new ReactTextViewManager(),
99101
new ReactToolbarManager(),
100102
new ReactViewManager(),
101103
new ReactViewPagerManager(),
102-
new ReactTextInlineImageViewManager(),
103104
new ReactVirtualTextViewManager(),
104-
new SwipeRefreshLayoutManager(),
105-
new ReactWebViewManager());
105+
new ReactWebViewManager(),
106+
new RecyclerViewBackedScrollViewManager(),
107+
new SwipeRefreshLayoutManager());
106108
}
107109
}

website/server/extractDocs.js

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

0 commit comments

Comments
 (0)