Skip to content

Commit

Permalink
[Android] AppState
Browse files Browse the repository at this point in the history
  • Loading branch information
skv-headless committed Jan 19, 2016
1 parent cf350a6 commit 1d894ae
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 0 deletions.
80 changes: 80 additions & 0 deletions Examples/UIExplorer/AppStateExample.js
Original file line number Diff line number Diff line change
@@ -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} />; }
},
];
1 change: 1 addition & 0 deletions Examples/UIExplorer/UIExplorerList.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var COMPONENTS = [
var APIS = [
require('./AccessibilityAndroidExample.android'),
require('./AlertExample').AlertExample,
require('./AppStateExample'),
require('./BorderExample'),
require('./ClipboardExample'),
require('./GeolocationExample'),
Expand Down
1 change: 1 addition & 0 deletions Examples/UIExplorer/UIExplorerList.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ var APIS = [
require('./AnimatedExample'),
require('./AnimatedGratuitousApp/AnExApp'),
require('./AppStateIOSExample'),
require('./AppStateExample'),
require('./AsyncStorageExample'),
require('./BorderExample'),
require('./CameraRollExample.ios'),
Expand Down
144 changes: 144 additions & 0 deletions Libraries/AppState/AppState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* 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().then((appStateData) => {
AppState.currentState = appStateData.app_state;
}).catch(logError)

module.exports = AppState;
1 change: 1 addition & 0 deletions Libraries/react-native/react-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ var ReactNative = {
get Animated() { return require('Animated'); },
get AppRegistry() { return require('AppRegistry'); },
get AppStateIOS() { return require('AppStateIOS'); },
get AppState() { return require('AppState'); },
get AsyncStorage() { return require('AsyncStorage'); },
get BackAndroid() { return require('BackAndroid'); },
get CameraRoll() { return require('CameraRoll'); },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* 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.Promise;
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 static com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;

public class AppStateModule extends ReactContextBaseJavaModule
implements LifecycleEventListener {

private String mAppState = "active";

public AppStateModule(ReactApplicationContext reactContext) {
super(reactContext);
}

@Override
public String getName() {
return "AppState";
}

@Override
public void initialize() {
getReactApplicationContext().addLifecycleEventListener(this);
}

@ReactMethod
public void getCurrentAppState(Promise promise) {
promise.resolve(createAppStateEventMap());
}

@Override
public void onHostResume() {
mAppState = "active";
sendAppStateEvent();
}

@Override
public void onHostPause() {
mAppState = "background";
sendAppStateEvent();
}

@Override
public void onHostDestroy() {

}

private WritableMap createAppStateEventMap() {
WritableMap appState = Arguments.createMap();
appState.putString("app_state", mAppState);
return appState;
}

private void sendAppStateEvent() {
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class)
.emit("appStateDidChange", createAppStateEventMap());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;
Expand Down Expand Up @@ -64,6 +65,7 @@ public List<NativeModule> createNativeModules(ReactApplicationContext reactConte
new LocationModule(reactContext),
new NetworkingModule(reactContext),
new NetInfoModule(reactContext),
new AppStateModule(reactContext),
new WebSocketModule(reactContext),
new ToastModule(reactContext));
}
Expand Down
1 change: 1 addition & 0 deletions website/server/extractDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,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',
Expand Down

0 comments on commit 1d894ae

Please sign in to comment.