Skip to content

Commit b7352b4

Browse files
andreicoman11Facebook Github Bot 6
authored andcommitted
Open source permissions
Summary: This moves into open source the PermissionsModule and the activity and listener interfaces necessary to make permissions work. It also moves the PermissionsExample into the UIExplorer. In order to make this work, the device has to be Android M and above, and the target sdk of the app has to be 23+, so I changed the uiexplorer manifest to target that API. This has the unfortunate consequence that people testing on devices with API 23+ will have to enable the `draw over other apps` setting that react needs for RedBoxing. The app will automatically send the user to that screen, so enabling the setting and resuming the app should be trivial. For testing, try requesting permission for a permission that is currently revoked. If a permission is granted, it can be revoked via adb (`adb shell pm revoke com.your.app android.permission.PERMISSION_NAME`), and then requested. Reviewed By: bestander Differential Revision: D3431324 fbshipit-source-id: 8cbaea676d2b5727cb5191cdb77a02e213bf9ba3
1 parent 4fe7a25 commit b7352b4

File tree

10 files changed

+425
-8
lines changed

10 files changed

+425
-8
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Copyright (c) 2013-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+
* The examples provided by Facebook are for non-commercial testing and
10+
* evaluation purposes only.
11+
*
12+
* Facebook reserves all rights not expressly granted.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
17+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*
21+
* @providesModule PermissionsExampleAndroid
22+
* @flow
23+
*/
24+
'use strict';
25+
26+
const React = require('react');
27+
const ReactNative = require('react-native');
28+
const {
29+
StyleSheet,
30+
Text,
31+
TextInput,
32+
TouchableWithoutFeedback,
33+
View,
34+
} = ReactNative;
35+
const DialogManager = require('NativeModules').DialogManagerAndroid;
36+
const Permissions = require('NativeModules').AndroidPermissions;
37+
38+
exports.displayName = (undefined: ?string);
39+
exports.framework = 'React';
40+
exports.title = '<Permissions>';
41+
exports.description = 'Permissions example for API 23+.';
42+
43+
const PermissionsExample = React.createClass({
44+
getInitialState: function() {
45+
return {
46+
permission: 'android.permission.WRITE_EXTERNAL_STORAGE',
47+
hasPermission: 'Not Checked',
48+
};
49+
},
50+
51+
render: function() {
52+
return (
53+
<View style={styles.container}>
54+
<Text style={styles.text}>Permission Name:</Text>
55+
<TextInput
56+
autoFocus={true}
57+
autoCorrect={false}
58+
style={styles.singleLine}
59+
onChange={this._updateText}
60+
defaultValue={this.state.permission}
61+
/>
62+
<TouchableWithoutFeedback onPress={this._checkPermission}>
63+
<View>
64+
<Text style={[styles.touchable, styles.text]}>Check Permission</Text>
65+
</View>
66+
</TouchableWithoutFeedback>
67+
<Text style={styles.text}>Permission Status: {this.state.hasPermission}</Text>
68+
<TouchableWithoutFeedback onPress={this._shouldExplainPermission}>
69+
<View>
70+
<Text style={[styles.touchable, styles.text]}>Request Permission</Text>
71+
</View>
72+
</TouchableWithoutFeedback>
73+
</View>
74+
);
75+
},
76+
77+
_updateText: function(event: Object) {
78+
this.setState({
79+
permission: event.nativeEvent.text,
80+
});
81+
},
82+
83+
_checkPermission: function() {
84+
Permissions.checkPermission(
85+
this.state.permission,
86+
(permission: string, result: boolean) => {
87+
this.setState({
88+
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' + permission,
89+
});
90+
},
91+
this._showError);
92+
},
93+
94+
_shouldExplainPermission: function() {
95+
Permissions.shouldShowRequestPermissionRationale(
96+
this.state.permission,
97+
(permission: string, shouldShow: boolean) => {
98+
if (shouldShow) {
99+
DialogManager.showAlert(
100+
{
101+
title: 'Permission Explanation',
102+
message:
103+
'The app needs the following permission ' + this.state.permission +
104+
' because of reasons. Please approve.'
105+
},
106+
this._showError,
107+
this._requestPermission);
108+
} else {
109+
this._requestPermission();
110+
}
111+
},
112+
this._showError);
113+
},
114+
115+
_requestPermission: function() {
116+
Permissions.requestPermission(
117+
this.state.permission,
118+
(permission: string, result: boolean) => {
119+
this.setState({
120+
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' + permission,
121+
});
122+
},
123+
this._showError);
124+
},
125+
126+
_showError: function() {
127+
DialogManager.showAlert({message: 'Error'}, {}, {});
128+
}
129+
});
130+
131+
exports.examples = [
132+
{
133+
title: 'Permissions Example',
134+
description: 'Short example of how to use the runtime permissions API introduced in Android M.',
135+
render: () => <PermissionsExample />,
136+
},
137+
];
138+
139+
var styles = StyleSheet.create({
140+
container: {
141+
flex: 1,
142+
backgroundColor: 'white',
143+
},
144+
singleLine: {
145+
fontSize: 16,
146+
padding: 4,
147+
},
148+
text: {
149+
margin: 10,
150+
},
151+
touchable: {
152+
color: '#007AFF',
153+
},
154+
});
155+

Examples/UIExplorer/UIExplorerList.android.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ const APIExamples = [
169169
key: 'PanResponderExample',
170170
module: require('./PanResponderExample'),
171171
},
172+
{
173+
key: 'PermissionsExampleAndroid',
174+
module: require('./PermissionsExampleAndroid'),
175+
},
172176
{
173177
key: 'PointerEventsExample',
174178
module: require('./PointerEventsExample'),

Examples/UIExplorer/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<uses-sdk
1515
android:minSdkVersion="16"
16-
android:targetSdkVersion="22" />
16+
android:targetSdkVersion="23" />
1717

1818
<application
1919
android:allowBackup="true"

ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,35 @@
99

1010
package com.facebook.react;
1111

12+
import javax.annotation.Nullable;
13+
14+
import java.util.List;
15+
1216
import android.app.Activity;
1317
import android.content.Intent;
1418
import android.os.Build;
1519
import android.os.Bundle;
16-
import android.os.Handler;
1720
import android.provider.Settings;
1821
import android.view.KeyEvent;
19-
import android.widget.EditText;
2022
import android.widget.Toast;
2123

2224
import com.facebook.common.logging.FLog;
2325
import com.facebook.react.common.ReactConstants;
2426
import com.facebook.react.devsupport.DoubleTapReloadRecognizer;
2527
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
26-
27-
import java.util.List;
28-
29-
import javax.annotation.Nullable;
28+
import com.facebook.react.modules.core.PermissionAwareActivity;
29+
import com.facebook.react.modules.core.PermissionListener;
3030

3131
/**
3232
* Base Activity for React Native applications.
3333
*/
34-
public abstract class ReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
34+
public abstract class ReactActivity extends Activity
35+
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
3536

3637
private static final String REDBOX_PERMISSION_MESSAGE =
3738
"Overlay permissions needs to be granted in order for react native apps to run in dev mode";
3839

40+
private @Nullable PermissionListener mPermissionListener;
3941
private @Nullable ReactInstanceManager mReactInstanceManager;
4042
private @Nullable ReactRootView mReactRootView;
4143
private LifecycleState mLifecycleState = LifecycleState.BEFORE_RESUME;
@@ -233,4 +235,24 @@ public void onNewIntent(Intent intent) {
233235
super.onNewIntent(intent);
234236
}
235237
}
238+
239+
@Override
240+
public void requestPermissions(
241+
String[] permissions,
242+
int requestCode,
243+
PermissionListener listener) {
244+
mPermissionListener = listener;
245+
this.requestPermissions(permissions, requestCode);
246+
}
247+
248+
@Override
249+
public void onRequestPermissionsResult(
250+
int requestCode,
251+
String[] permissions,
252+
int[] grantResults) {
253+
if (mPermissionListener != null &&
254+
mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
255+
mPermissionListener = null;
256+
}
257+
}
236258
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.core;
11+
12+
import android.app.Activity;
13+
14+
/**
15+
* Interface used to denote activities that can forward permission requests and call
16+
* {@link PermissionListener}s with the permission request results.
17+
*/
18+
public interface PermissionAwareActivity {
19+
20+
/**
21+
* See {@link Activity#checkPermission}.
22+
*/
23+
int checkPermission(String permission, int pid, int uid);
24+
25+
/**
26+
* See {@link Activity#checkSelfPermission}.
27+
*/
28+
int checkSelfPermission(String permission);
29+
30+
/**
31+
* See {@link Activity#shouldShowRequestPermissionRationale}.
32+
*/
33+
boolean shouldShowRequestPermissionRationale(String permission);
34+
35+
/**
36+
* See {@link Activity#requestPermissions}.
37+
*/
38+
void requestPermissions(String[] permissions, int requestCode, PermissionListener listener);
39+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.core;
11+
12+
import android.app.Activity;
13+
14+
/**
15+
* Interface used by activities to delegate permission request results. Classes implementing this
16+
* class will be notified whenever there's a result for a permission request.
17+
*/
18+
public interface PermissionListener {
19+
20+
/**
21+
* Method called whenever there's a result to a permission request. It is forwarded from
22+
* {@link Activity#onRequestPermissionsResult}.
23+
*
24+
* @return boolean Whether the PermissionListener can be removed.
25+
*/
26+
boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
include_defs('//ReactAndroid/DEFS')
2+
3+
android_library(
4+
name = 'permissions',
5+
srcs = glob(['**/*.java']),
6+
deps = [
7+
react_native_target('java/com/facebook/react/bridge:bridge'),
8+
react_native_target('java/com/facebook/react/common:common'),
9+
react_native_target('java/com/facebook/react/modules/core:core'),
10+
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
11+
react_native_dep('third-party/java/jsr-305:jsr-305'),
12+
],
13+
visibility = [
14+
'PUBLIC',
15+
],
16+
)
17+
18+
project_config(
19+
src_target = ':permissions',
20+
)

0 commit comments

Comments
 (0)