mirror of
https://github.com/status-im/react-native.git
synced 2025-01-27 01:40:08 +00:00
Add JS library for requesting Android M Permissions
Summary:
Explain the **motivation** for making this change. What existing problem does the pull request solve?
The Android permissions native module was open sourced recently (b7352b4667
) but it is currently undocumented and requires directly interfacing with the native module.
This provides a JS wrapper to make it easier to use the permissions module and documents it.
This could be cleaner if the native code used Promise blocks instead of callbacks, but I didn't want to change the native code without a thumbs up since I'm guessing this is used in one of facebook's apps. Happy to do that if it makes sense
I also tried to make the `PERMISSIONS` object a class property - it works in the actual code but not in the documentation (think it's a jsdocs problem), so decided to initialize in the constructor.
**Test plan (required)**
If the API looks good, I will change the UIExplorer example to use this.
cc andreicoman11
Closes https://github.com/facebook/react-native/pull/9292
Differential Revision: D3716303
Pulled By: andreicoman11
fbshipit-source-id: cd40b8757fdf70ea8faecfb58caa00e99a99789e
This commit is contained in:
parent
1a683fa5e8
commit
0fb2ccfcc3
@ -26,23 +26,22 @@
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {
|
||||
PermissionsAndroid,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} = ReactNative;
|
||||
const DialogManager = require('NativeModules').DialogManagerAndroid;
|
||||
const Permissions = require('NativeModules').AndroidPermissions;
|
||||
|
||||
exports.displayName = (undefined: ?string);
|
||||
exports.framework = 'React';
|
||||
exports.title = '<Permissions>';
|
||||
exports.title = 'PermissionsAndroid';
|
||||
exports.description = 'Permissions example for API 23+.';
|
||||
|
||||
class PermissionsExample extends React.Component {
|
||||
state = {
|
||||
permission: 'android.permission.WRITE_EXTERNAL_STORAGE',
|
||||
permission: PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
||||
hasPermission: 'Not Checked',
|
||||
};
|
||||
|
||||
@ -63,7 +62,7 @@ class PermissionsExample extends React.Component {
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<Text style={styles.text}>Permission Status: {this.state.hasPermission}</Text>
|
||||
<TouchableWithoutFeedback onPress={this._shouldExplainPermission}>
|
||||
<TouchableWithoutFeedback onPress={this._requestPermission}>
|
||||
<View>
|
||||
<Text style={[styles.touchable, styles.text]}>Request Permission</Text>
|
||||
</View>
|
||||
@ -78,51 +77,28 @@ class PermissionsExample extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
_checkPermission = () => {
|
||||
Permissions.checkPermission(
|
||||
this.state.permission,
|
||||
(permission: string, result: boolean) => {
|
||||
this.setState({
|
||||
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' + permission,
|
||||
});
|
||||
},
|
||||
this._showError);
|
||||
_checkPermission = async () => {
|
||||
let result = await PermissionsAndroid.checkPermission(this.state.permission);
|
||||
this.setState({
|
||||
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' +
|
||||
this.state.permission,
|
||||
});
|
||||
};
|
||||
|
||||
_shouldExplainPermission = () => {
|
||||
Permissions.shouldShowRequestPermissionRationale(
|
||||
_requestPermission = async () => {
|
||||
let result = await PermissionsAndroid.requestPermission(
|
||||
this.state.permission,
|
||||
(permission: string, shouldShow: boolean) => {
|
||||
if (shouldShow) {
|
||||
DialogManager.showAlert(
|
||||
{
|
||||
title: 'Permission Explanation',
|
||||
message:
|
||||
'The app needs the following permission ' + this.state.permission +
|
||||
' because of reasons. Please approve.'
|
||||
},
|
||||
this._showError,
|
||||
this._requestPermission);
|
||||
} else {
|
||||
this._requestPermission();
|
||||
}
|
||||
{
|
||||
title: 'Permission Explanation',
|
||||
message:
|
||||
'The app needs the following permission ' + this.state.permission +
|
||||
' because of reasons. Please approve.'
|
||||
},
|
||||
this._showError);
|
||||
};
|
||||
|
||||
_requestPermission = () => {
|
||||
Permissions.requestPermission(
|
||||
this.state.permission,
|
||||
(permission: string, result: boolean) => {
|
||||
this.setState({
|
||||
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' + permission,
|
||||
});
|
||||
},
|
||||
this._showError);
|
||||
};
|
||||
|
||||
_showError = () => {
|
||||
DialogManager.showAlert({message: 'Error'}, {}, {});
|
||||
);
|
||||
this.setState({
|
||||
hasPermission: (result ? 'Granted' : 'Revoked') + ' for ' +
|
||||
this.state.permission,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -150,4 +126,3 @@ var styles = StyleSheet.create({
|
||||
color: '#007AFF',
|
||||
},
|
||||
});
|
||||
|
||||
|
161
Libraries/PermissionsAndroid/PermissionsAndroid.js
Normal file
161
Libraries/PermissionsAndroid/PermissionsAndroid.js
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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 PermissionsAndroid
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const DialogManagerAndroid = require('NativeModules').DialogManagerAndroid;
|
||||
const AndroidPermissions = require('NativeModules').AndroidPermissions;
|
||||
|
||||
type Rationale = {
|
||||
title: string,
|
||||
message: string,
|
||||
}
|
||||
|
||||
/**
|
||||
* `PermissionsAndroid` provides access to Android M's new permissions model.
|
||||
* Some permissions are granted by default when the application is installed
|
||||
* so long as they appear in `AndroidManifest.xml`. However, "dangerous"
|
||||
* permissions require a dialog prompt. You should use this module for those
|
||||
* permissions.
|
||||
*
|
||||
* On devices before SDK version 23, the permissions are automatically granted
|
||||
* if they appear in the manifest, so `checkPermission` and `requestPermission`
|
||||
* should always be true.
|
||||
*
|
||||
* If a user has previously turned off a permission that you prompt for, the OS
|
||||
* will advise your app to show a rationale for needing the permission. The
|
||||
* optional `rationale` argument will show a dialog prompt only if
|
||||
* necessary - otherwise the normal permission prompt will appear.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* async function requestCameraPermission() {
|
||||
* try {
|
||||
* const granted = await AndroidPermissions.requestPermission(
|
||||
* AndroidPermissions.PERMISSIONS.CAMERA,
|
||||
* {
|
||||
* 'title': 'Cool Photo App Camera Permission',
|
||||
* 'message': 'Cool Photo App needs access to your camera ' +
|
||||
* 'so you can take awesome pictures.'
|
||||
* }
|
||||
* )
|
||||
* if (granted) {
|
||||
* console.log("You can use the camera")
|
||||
* } else {
|
||||
* console.log("Camera permission denied")
|
||||
* }
|
||||
* } catch (err) {
|
||||
* console.warn(err)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
class PermissionsAndroid {
|
||||
PERMISSIONS: Object;
|
||||
|
||||
constructor() {
|
||||
/**
|
||||
* A list of specified "dangerous" permissions that require prompting the user
|
||||
*/
|
||||
this.PERMISSIONS = {
|
||||
READ_CALENDAR: 'android.permission.READ_CALENDAR',
|
||||
WRITE_CALENDAR: 'android.permission.WRITE_CALENDAR',
|
||||
CAMERA: 'android.permission.CAMERA',
|
||||
READ_CONTACTS: 'android.permission.READ_CONTACTS',
|
||||
WRITE_CONTACTS: 'android.permission.WRITE_CONTACTS',
|
||||
GET_ACCOUNTS: 'android.permission.GET_ACCOUNTS',
|
||||
ACCESS_FINE_LOCATION: 'android.permission.ACCESS_FINE_LOCATION',
|
||||
ACCESS_COARSE_LOCATION: 'android.permission.ACCESS_COARSE_LOCATION',
|
||||
RECORD_AUDIO: 'android.permission.RECORD_AUDIO',
|
||||
READ_PHONE_STATE: 'android.permission.READ_PHONE_STATE',
|
||||
CALL_PHONE: 'android.permission.CALL_PHONE',
|
||||
READ_CALL_LOG: 'android.permission.READ_CALL_LOG',
|
||||
WRITE_CALL_LOG: 'android.permission.WRITE_CALL_LOG',
|
||||
ADD_VOICEMAIL: 'com.android.voicemail.permission.ADD_VOICEMAIL',
|
||||
USE_SIP: 'android.permission.USE_SIP',
|
||||
PROCESS_OUTGOING_CALLS: 'android.permission.PROCESS_OUTGOING_CALLS',
|
||||
BODY_SENSORS: 'android.permission.BODY_SENSORS',
|
||||
SEND_SMS: 'android.permission.SEND_SMS',
|
||||
RECEIVE_SMS: 'android.permission.RECEIVE_SMS',
|
||||
READ_SMS: 'android.permission.READ_SMS',
|
||||
RECEIVE_WAP_PUSH: 'android.permission.RECEIVE_WAP_PUSH',
|
||||
RECEIVE_MMS: 'android.permission.RECEIVE_MMS',
|
||||
READ_EXTERNAL_STORAGE: 'android.permission.READ_EXTERNAL_STORAGE',
|
||||
WRITE_EXTERNAL_STORAGE: 'android.permission.WRITE_EXTERNAL_STORAGE',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise resolving to a boolean value as to whether the specified
|
||||
* permissions has been granted
|
||||
*/
|
||||
checkPermission(permission: string) : Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
AndroidPermissions.checkPermission(
|
||||
permission,
|
||||
function(perm: string, result: boolean) { resolve(result); },
|
||||
function(error: string) { reject(error); },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enable a permission and returns a promise resolving to a
|
||||
* boolean value indicating whether the user allowed or denied the request
|
||||
*
|
||||
* If the optional rationale argument is included (which is an object with a
|
||||
* `title` and `message`), this function checks with the OS whether it is
|
||||
* necessary to show a dialog explaining why the permission is needed
|
||||
* (https://developer.android.com/training/permissions/requesting.html#explain)
|
||||
* and then shows the system permission dialog
|
||||
*/
|
||||
requestPermission(permission: string, rationale?: Rationale) : Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestPermission = () => {
|
||||
AndroidPermissions.requestPermission(
|
||||
permission,
|
||||
function(perm: string, result: boolean) { resolve(result); },
|
||||
function(error: string) { reject(error); },
|
||||
);
|
||||
};
|
||||
|
||||
if (rationale) {
|
||||
AndroidPermissions.shouldShowRequestPermissionRationale(
|
||||
permission,
|
||||
function(perm: string, shouldShowRationale: boolean) {
|
||||
if (shouldShowRationale) {
|
||||
DialogManagerAndroid.showAlert(
|
||||
rationale,
|
||||
() => {
|
||||
DialogManagerAndroid.showAlert({
|
||||
message: 'Error Requesting Permissions'
|
||||
}, {}, {});
|
||||
reject();
|
||||
},
|
||||
requestPermission
|
||||
);
|
||||
} else {
|
||||
requestPermission();
|
||||
}
|
||||
},
|
||||
function(error: string) { reject(error); },
|
||||
);
|
||||
} else {
|
||||
requestPermission();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PermissionsAndroid = new PermissionsAndroid();
|
||||
|
||||
module.exports = PermissionsAndroid;
|
1
Libraries/react-native/react-native.js
vendored
1
Libraries/react-native/react-native.js
vendored
@ -98,6 +98,7 @@ const ReactNative = {
|
||||
get NavigationExperimental() { return require('NavigationExperimental'); },
|
||||
get NetInfo() { return require('NetInfo'); },
|
||||
get PanResponder() { return require('PanResponder'); },
|
||||
get PermissionsAndroid() { return require('PermissionsAndroid'); },
|
||||
get PixelRatio() { return require('PixelRatio'); },
|
||||
get PushNotificationIOS() { return require('PushNotificationIOS'); },
|
||||
get Settings() { return require('Settings'); },
|
||||
|
@ -110,6 +110,7 @@ var ReactNative = {
|
||||
NavigationExperimental: require('NavigationExperimental'),
|
||||
NetInfo: require('NetInfo'),
|
||||
PanResponder: require('PanResponder'),
|
||||
PermissionsAndroid: require('PermissionsAndroid'),
|
||||
PixelRatio: require('PixelRatio'),
|
||||
PushNotificationIOS: require('PushNotificationIOS'),
|
||||
Settings: require('Settings'),
|
||||
|
@ -550,6 +550,7 @@ const apis = [
|
||||
'../Libraries/BatchedBridge/BatchedBridgedModules/NativeModules.js',
|
||||
'../Libraries/Network/NetInfo.js',
|
||||
'../Libraries/Interaction/PanResponder.js',
|
||||
'../Libraries/PermissionsAndroid/PermissionsAndroid.js',
|
||||
'../Libraries/Utilities/PixelRatio.js',
|
||||
'../Libraries/PushNotificationIOS/PushNotificationIOS.js',
|
||||
'../Libraries/Settings/Settings.ios.js',
|
||||
|
Loading…
x
Reference in New Issue
Block a user