|
@@ -1,4 +1,204 @@
|
1
|
1
|
// @flow
|
2
|
2
|
|
3
|
|
-import Permissions from './lib/permissions';
|
4
|
|
-export default Permissions;
|
|
3
|
+import {NativeModules, PermissionsAndroid, Platform} from 'react-native';
|
|
4
|
+import AsyncStorage from '@react-native-community/async-storage';
|
|
5
|
+const NativeModule = NativeModules.ReactNativePermissions;
|
|
6
|
+
|
|
7
|
+export type PermissionStatus =
|
|
8
|
+ | 'authorized'
|
|
9
|
+ | 'denied'
|
|
10
|
+ | 'restricted'
|
|
11
|
+ | 'undetermined';
|
|
12
|
+
|
|
13
|
+export type Rationale = {
|
|
14
|
+ title: string,
|
|
15
|
+ message: string,
|
|
16
|
+ buttonPositive?: string,
|
|
17
|
+ buttonNegative?: string,
|
|
18
|
+ buttonNeutral?: string,
|
|
19
|
+};
|
|
20
|
+
|
|
21
|
+const ASYNC_STORAGE_KEY = '@RNPermissions:didAskPermission:';
|
|
22
|
+
|
|
23
|
+const PERMISSIONS = Platform.select({
|
|
24
|
+ ios: {
|
|
25
|
+ backgroundRefresh: 'backgroundRefresh',
|
|
26
|
+ bluetooth: 'bluetooth',
|
|
27
|
+ camera: 'camera',
|
|
28
|
+ contacts: 'contacts',
|
|
29
|
+ event: 'event',
|
|
30
|
+ location: 'location',
|
|
31
|
+ mediaLibrary: 'mediaLibrary',
|
|
32
|
+ microphone: 'microphone',
|
|
33
|
+ motion: 'motion',
|
|
34
|
+ notification: 'notification',
|
|
35
|
+ photo: 'photo',
|
|
36
|
+ reminder: 'reminder',
|
|
37
|
+ speechRecognition: 'speechRecognition',
|
|
38
|
+ },
|
|
39
|
+ android: {
|
|
40
|
+ callPhone: PermissionsAndroid.PERMISSIONS.CALL_PHONE,
|
|
41
|
+ camera: PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
42
|
+ coarseLocation: PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
|
|
43
|
+ contacts: PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
|
|
44
|
+ event: PermissionsAndroid.PERMISSIONS.READ_CALENDAR,
|
|
45
|
+ location: PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
|
|
46
|
+ microphone: PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
|
|
47
|
+ photo: PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
48
|
+ readSms: PermissionsAndroid.PERMISSIONS.READ_SMS,
|
|
49
|
+ receiveSms: PermissionsAndroid.PERMISSIONS.RECEIVE_SMS,
|
|
50
|
+ sendSms: PermissionsAndroid.PERMISSIONS.SEND_SMS,
|
|
51
|
+ storage: PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
52
|
+ },
|
|
53
|
+});
|
|
54
|
+
|
|
55
|
+const IOS_DEFAULT_OPTIONS = {
|
|
56
|
+ location: 'whenInUse',
|
|
57
|
+ notification: ['alert', 'badge', 'sound'],
|
|
58
|
+};
|
|
59
|
+
|
|
60
|
+const ANDROID_RESULTS = {
|
|
61
|
+ granted: 'authorized',
|
|
62
|
+ denied: 'denied',
|
|
63
|
+ never_ask_again: 'restricted',
|
|
64
|
+};
|
|
65
|
+
|
|
66
|
+const setDidAskOnce = (permission: string) =>
|
|
67
|
+ AsyncStorage.setItem(ASYNC_STORAGE_KEY + permission, 'true');
|
|
68
|
+
|
|
69
|
+const getDidAskOnce = (permission: string) =>
|
|
70
|
+ AsyncStorage.getItem(ASYNC_STORAGE_KEY + permission).then(item => !!item);
|
|
71
|
+
|
|
72
|
+class ReactNativePermissions {
|
|
73
|
+ canOpenSettings(): Promise<boolean> {
|
|
74
|
+ return Platform.OS === 'ios'
|
|
75
|
+ ? NativeModule.canOpenSettings().then(result => !!result)
|
|
76
|
+ : Promise.resolve(false);
|
|
77
|
+ }
|
|
78
|
+
|
|
79
|
+ openSettings(): Promise<void> {
|
|
80
|
+ return Platform.OS === 'ios'
|
|
81
|
+ ? NativeModule.openSettings()
|
|
82
|
+ : Promise.reject(new Error("'openSettings' is deprecated on android"));
|
|
83
|
+ }
|
|
84
|
+
|
|
85
|
+ getTypes(): string[] {
|
|
86
|
+ return Object.keys(PERMISSIONS);
|
|
87
|
+ }
|
|
88
|
+
|
|
89
|
+ check = (
|
|
90
|
+ permission: string,
|
|
91
|
+ options?: string | {type?: string},
|
|
92
|
+ ): Promise<PermissionStatus> => {
|
|
93
|
+ if (!PERMISSIONS[permission]) {
|
|
94
|
+ return Promise.reject(
|
|
95
|
+ new Error(
|
|
96
|
+ `ReactNativePermissions: ${permission} is not a valid permission type`,
|
|
97
|
+ ),
|
|
98
|
+ );
|
|
99
|
+ }
|
|
100
|
+
|
|
101
|
+ if (Platform.OS === 'ios') {
|
|
102
|
+ let type = IOS_DEFAULT_OPTIONS[permission];
|
|
103
|
+
|
|
104
|
+ if (typeof options === 'string') {
|
|
105
|
+ type = options;
|
|
106
|
+ } else if (options && options.type) {
|
|
107
|
+ type = options.type;
|
|
108
|
+ }
|
|
109
|
+
|
|
110
|
+ return NativeModule.getPermissionStatus(permission, type);
|
|
111
|
+ }
|
|
112
|
+
|
|
113
|
+ if (Platform.OS === 'android') {
|
|
114
|
+ return PermissionsAndroid.check(PERMISSIONS[permission]).then(granted => {
|
|
115
|
+ if (granted) {
|
|
116
|
+ return 'authorized';
|
|
117
|
+ }
|
|
118
|
+
|
|
119
|
+ return getDidAskOnce(permission).then(didAsk => {
|
|
120
|
+ if (didAsk) {
|
|
121
|
+ return NativeModules.PermissionsAndroid.shouldShowRequestPermissionRationale(
|
|
122
|
+ PERMISSIONS[permission],
|
|
123
|
+ ).then(shouldShow => (shouldShow ? 'denied' : 'restricted'));
|
|
124
|
+ }
|
|
125
|
+
|
|
126
|
+ return 'undetermined';
|
|
127
|
+ });
|
|
128
|
+ });
|
|
129
|
+ }
|
|
130
|
+
|
|
131
|
+ return Promise.resolve('restricted');
|
|
132
|
+ };
|
|
133
|
+
|
|
134
|
+ request = (
|
|
135
|
+ permission: string,
|
|
136
|
+ options?: string | {type?: string, rationale?: Rationale},
|
|
137
|
+ ): Promise<PermissionStatus> => {
|
|
138
|
+ if (!PERMISSIONS[permission]) {
|
|
139
|
+ return Promise.reject(
|
|
140
|
+ new Error(
|
|
141
|
+ `ReactNativePermissions: ${permission} is not a valid permission type`,
|
|
142
|
+ ),
|
|
143
|
+ );
|
|
144
|
+ }
|
|
145
|
+
|
|
146
|
+ if (Platform.OS === 'ios') {
|
|
147
|
+ if (permission == 'backgroundRefresh') {
|
|
148
|
+ return Promise.reject(
|
|
149
|
+ new Error(
|
|
150
|
+ 'ReactNativePermissions: You cannot request backgroundRefresh',
|
|
151
|
+ ),
|
|
152
|
+ );
|
|
153
|
+ }
|
|
154
|
+
|
|
155
|
+ let type = IOS_DEFAULT_OPTIONS[permission];
|
|
156
|
+
|
|
157
|
+ if (typeof options === 'string') {
|
|
158
|
+ type = options;
|
|
159
|
+ } else if (options && options.type) {
|
|
160
|
+ type = options.type;
|
|
161
|
+ }
|
|
162
|
+
|
|
163
|
+ return NativeModule.requestPermission(permission, type);
|
|
164
|
+ }
|
|
165
|
+
|
|
166
|
+ if (Platform.OS === 'android') {
|
|
167
|
+ let rationale: Rationale;
|
|
168
|
+
|
|
169
|
+ if (typeof options === 'object' && options.rationale) {
|
|
170
|
+ rationale = options.rationale;
|
|
171
|
+ }
|
|
172
|
+
|
|
173
|
+ return PermissionsAndroid.request(
|
|
174
|
+ PERMISSIONS[permission],
|
|
175
|
+ rationale,
|
|
176
|
+ ).then(result => {
|
|
177
|
+ // PermissionsAndroid.request() to native module resolves to boolean
|
|
178
|
+ // rather than string if running on OS version prior to Android M
|
|
179
|
+ if (typeof result === 'boolean') {
|
|
180
|
+ return result ? 'authorized' : 'denied';
|
|
181
|
+ }
|
|
182
|
+
|
|
183
|
+ return setDidAskOnce(permission).then(() => ANDROID_RESULTS[result]);
|
|
184
|
+ });
|
|
185
|
+ }
|
|
186
|
+
|
|
187
|
+ return Promise.resolve('restricted');
|
|
188
|
+ };
|
|
189
|
+
|
|
190
|
+ checkMultiple = (
|
|
191
|
+ permissions: string[],
|
|
192
|
+ ): Promise<{[permission: string]: PermissionStatus}> => {
|
|
193
|
+ return Promise.all(
|
|
194
|
+ permissions.map(permission => this.check(permission)),
|
|
195
|
+ ).then(result =>
|
|
196
|
+ result.reduce((acc, value, i) => {
|
|
197
|
+ acc[permissions[i]] = value;
|
|
198
|
+ return acc;
|
|
199
|
+ }, {}),
|
|
200
|
+ );
|
|
201
|
+ };
|
|
202
|
+}
|
|
203
|
+
|
|
204
|
+export default new ReactNativePermissions();
|