Commit 948ea53f60d711a768a2de63db4b02ce40280691

Authored by cuong
1 parent 6da63a55a2

test android overlay

Showing 6 changed files with 34 additions and 0 deletions Inline Diff

android/src/main/java/com/reactnativecommunity/rnpermissions/RNPermissionsModule.java
1 package com.reactnativecommunity.rnpermissions; 1 package com.reactnativecommunity.rnpermissions;
2 2
3 import android.Manifest; 3 import android.Manifest;
4 import android.app.Activity; 4 import android.app.Activity;
5 import android.content.Context; 5 import android.content.Context;
6 import android.content.Intent; 6 import android.content.Intent;
7 import android.content.SharedPreferences; 7 import android.content.SharedPreferences;
8 import android.net.Uri; 8 import android.net.Uri;
9 import android.provider.Settings; 9 import android.provider.Settings;
10 10
11 import androidx.core.app.NotificationManagerCompat; 11 import androidx.core.app.NotificationManagerCompat;
12 12
13 import com.facebook.react.bridge.ActivityEventListener; 13 import com.facebook.react.bridge.ActivityEventListener;
14 import com.facebook.react.bridge.Arguments; 14 import com.facebook.react.bridge.Arguments;
15 import com.facebook.react.bridge.BaseActivityEventListener; 15 import com.facebook.react.bridge.BaseActivityEventListener;
16 import com.facebook.react.bridge.Promise; 16 import com.facebook.react.bridge.Promise;
17 import com.facebook.react.bridge.ReactApplicationContext; 17 import com.facebook.react.bridge.ReactApplicationContext;
18 import com.facebook.react.bridge.ReactContextBaseJavaModule; 18 import com.facebook.react.bridge.ReactContextBaseJavaModule;
19 import com.facebook.react.bridge.ReactMethod; 19 import com.facebook.react.bridge.ReactMethod;
20 import com.facebook.react.bridge.ReadableArray; 20 import com.facebook.react.bridge.ReadableArray;
21 import com.facebook.react.bridge.WritableArray; 21 import com.facebook.react.bridge.WritableArray;
22 import com.facebook.react.bridge.WritableMap; 22 import com.facebook.react.bridge.WritableMap;
23 import com.facebook.react.module.annotations.ReactModule; 23 import com.facebook.react.module.annotations.ReactModule;
24 24
25 import java.util.HashMap; 25 import java.util.HashMap;
26 import java.util.Map; 26 import java.util.Map;
27 import android.os.Build; 27 import android.os.Build;
28 import android.util.Log; 28 import android.util.Log;
29 29
30 import javax.annotation.Nullable; 30 import javax.annotation.Nullable;
31 31
32 @ReactModule(name = RNPermissionsModule.MODULE_NAME) 32 @ReactModule(name = RNPermissionsModule.MODULE_NAME)
33 public class RNPermissionsModule extends ReactContextBaseJavaModule { 33 public class RNPermissionsModule extends ReactContextBaseJavaModule {
34 34
35 private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY"; 35 private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";
36 public static final String MODULE_NAME = "RNPermissions"; 36 public static final String MODULE_NAME = "RNPermissions";
37 private static final String SETTING_NAME = "@RNPermissions:NonRequestables"; 37 private static final String SETTING_NAME = "@RNPermissions:NonRequestables";
38 private static final int OVERLAY_PERMISSION_CODE = 1009; 38 private static final int OVERLAY_PERMISSION_CODE = 1009;
39 39
40 private Promise mPickerPromise; 40 private Promise mPickerPromise;
41 41
42 private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() { 42 private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
43 @Override 43 @Override
44 public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) { 44 public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
45 if (requestCode == OVERLAY_PERMISSION_CODE) { 45 if (requestCode == OVERLAY_PERMISSION_CODE) {
46 // Log.e("TEAGGGG", "OVERLAY_PERMISSION_CODE: "+ requestCode); 46 // Log.e("TEAGGGG", "OVERLAY_PERMISSION_CODE: "+ requestCode);
47 if (mPickerPromise != null) { 47 if (mPickerPromise != null) {
48 final WritableMap output = Arguments.createMap(); 48 final WritableMap output = Arguments.createMap();
49 final WritableMap settings = Arguments.createMap(); 49 final WritableMap settings = Arguments.createMap();
50 output.putMap("settings", settings); 50 output.putMap("settings", settings);
51 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 51 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
52 final ReactApplicationContext reactContext = getReactApplicationContext(); 52 final ReactApplicationContext reactContext = getReactApplicationContext();
53 if (!Settings.canDrawOverlays(reactContext)) { 53 if (!Settings.canDrawOverlays(reactContext)) {
54 output.putString("status", "blocked"); 54 output.putString("status", "blocked");
55 } else { 55 } else {
56 output.putString("status", "granted"); 56 output.putString("status", "granted");
57 } 57 }
58 } else { 58 } else {
59 if (resultCode == Activity.RESULT_CANCELED) { 59 if (resultCode == Activity.RESULT_CANCELED) {
60 output.putString("status", "blocked"); 60 output.putString("status", "blocked");
61 } else if (resultCode == Activity.RESULT_OK) { 61 } else if (resultCode == Activity.RESULT_OK) {
62 output.putString("status", "granted"); 62 output.putString("status", "granted");
63 } 63 }
64 } 64 }
65 65
66 // Log.e("TEAGGGG", "output: "+ output.toString()); 66 // Log.e("TEAGGGG", "output: "+ output.toString());
67 mPickerPromise.resolve(output); 67 mPickerPromise.resolve(output);
68 } 68 }
69 } 69 }
70 } 70 }
71 }; 71 };
72 72
73 private static final String[][] PERMISSIONS = new String[][] { 73 private static final String[][] PERMISSIONS = new String[][] {
74 { "ACCEPT_HANDOVER", "android.permission.ACCEPT_HANDOVER" }, 74 { "ACCEPT_HANDOVER", "android.permission.ACCEPT_HANDOVER" },
75 { "ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_BACKGROUND_LOCATION" }, 75 { "ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_BACKGROUND_LOCATION" },
76 { "ACCESS_COARSE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION" }, 76 { "ACCESS_COARSE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION" },
77 { "ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION" }, 77 { "ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION" },
78 { "ACTIVITY_RECOGNITION", "android.permission.ACTIVITY_RECOGNITION" }, 78 { "ACTIVITY_RECOGNITION", "android.permission.ACTIVITY_RECOGNITION" },
79 { "ADD_VOICEMAIL", "com.android.voicemail.permission.ADD_VOICEMAIL" }, 79 { "ADD_VOICEMAIL", "com.android.voicemail.permission.ADD_VOICEMAIL" },
80 { "ANSWER_PHONE_CALLS", "android.permission.ANSWER_PHONE_CALLS" }, 80 { "ANSWER_PHONE_CALLS", "android.permission.ANSWER_PHONE_CALLS" },
81 { "BODY_SENSORS", "android.permission.BODY_SENSORS" }, 81 { "BODY_SENSORS", "android.permission.BODY_SENSORS" },
82 { "CALL_PHONE", "android.permission.CALL_PHONE" }, 82 { "CALL_PHONE", "android.permission.CALL_PHONE" },
83 { "CAMERA", "android.permission.CAMERA" }, 83 { "CAMERA", "android.permission.CAMERA" },
84 { "GET_ACCOUNTS", "android.permission.GET_ACCOUNTS" }, 84 { "GET_ACCOUNTS", "android.permission.GET_ACCOUNTS" },
85 { "PROCESS_OUTGOING_CALLS", "android.permission.PROCESS_OUTGOING_CALLS" }, 85 { "PROCESS_OUTGOING_CALLS", "android.permission.PROCESS_OUTGOING_CALLS" },
86 { "READ_CALENDAR", "android.permission.READ_CALENDAR" }, 86 { "READ_CALENDAR", "android.permission.READ_CALENDAR" },
87 { "READ_CALL_LOG", "android.permission.READ_CALL_LOG" }, 87 { "READ_CALL_LOG", "android.permission.READ_CALL_LOG" },
88 { "READ_CONTACTS", "android.permission.READ_CONTACTS" }, 88 { "READ_CONTACTS", "android.permission.READ_CONTACTS" },
89 { "READ_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE" }, 89 { "READ_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE" },
90 { "READ_PHONE_NUMBERS", "android.permission.READ_PHONE_NUMBERS" }, 90 { "READ_PHONE_NUMBERS", "android.permission.READ_PHONE_NUMBERS" },
91 { "READ_PHONE_STATE", "android.permission.READ_PHONE_STATE" }, 91 { "READ_PHONE_STATE", "android.permission.READ_PHONE_STATE" },
92 { "READ_SMS", "android.permission.READ_SMS" }, 92 { "READ_SMS", "android.permission.READ_SMS" },
93 { "RECEIVE_MMS", "android.permission.RECEIVE_MMS" }, 93 { "RECEIVE_MMS", "android.permission.RECEIVE_MMS" },
94 { "RECEIVE_SMS", "android.permission.RECEIVE_SMS" }, 94 { "RECEIVE_SMS", "android.permission.RECEIVE_SMS" },
95 { "RECEIVE_WAP_PUSH", "android.permission.RECEIVE_WAP_PUSH" }, 95 { "RECEIVE_WAP_PUSH", "android.permission.RECEIVE_WAP_PUSH" },
96 { "RECORD_AUDIO", "android.permission.RECORD_AUDIO" }, 96 { "RECORD_AUDIO", "android.permission.RECORD_AUDIO" },
97 { "SEND_SMS", "android.permission.SEND_SMS" }, 97 { "SEND_SMS", "android.permission.SEND_SMS" },
98 { "USE_SIP", "android.permission.USE_SIP" }, 98 { "USE_SIP", "android.permission.USE_SIP" },
99 { "WRITE_CALENDAR", "android.permission.WRITE_CALENDAR" }, 99 { "WRITE_CALENDAR", "android.permission.WRITE_CALENDAR" },
100 { "WRITE_CALL_LOG", "android.permission.WRITE_CALL_LOG" }, 100 { "WRITE_CALL_LOG", "android.permission.WRITE_CALL_LOG" },
101 { "WRITE_CONTACTS", "android.permission.WRITE_CONTACTS" }, 101 { "WRITE_CONTACTS", "android.permission.WRITE_CONTACTS" },
102 { "WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" }, 102 { "WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" },
103 { "MANAGE_OVERLAY_PERMISSION", "android.permission.MANAGE_OVERLAY_PERMISSION" }, 103 { "MANAGE_OVERLAY_PERMISSION", "android.permission.MANAGE_OVERLAY_PERMISSION" },
104 }; 104 };
105 105
106 private final SharedPreferences sharedPrefs; 106 private final SharedPreferences sharedPrefs;
107 107
108 public RNPermissionsModule(ReactApplicationContext reactContext) { 108 public RNPermissionsModule(ReactApplicationContext reactContext) {
109 super(reactContext); 109 super(reactContext);
110 sharedPrefs = reactContext.getSharedPreferences(SETTING_NAME, Context.MODE_PRIVATE); 110 sharedPrefs = reactContext.getSharedPreferences(SETTING_NAME, Context.MODE_PRIVATE);
111 // Add the listener for `onActivityResult` 111 // Add the listener for `onActivityResult`
112 reactContext.addActivityEventListener(mActivityEventListener); 112 reactContext.addActivityEventListener(mActivityEventListener);
113 } 113 }
114 114
115 @Override 115 @Override
116 public String getName() { 116 public String getName() {
117 return MODULE_NAME; 117 return MODULE_NAME;
118 } 118 }
119 119
120 private boolean fieldExists(final String fieldName) { 120 private boolean fieldExists(final String fieldName) {
121 try { 121 try {
122 Manifest.permission.class.getField(fieldName); 122 Manifest.permission.class.getField(fieldName);
123 return true; 123 return true;
124 } catch (NoSuchFieldException ignored) { 124 } catch (NoSuchFieldException ignored) {
125 return false; 125 return false;
126 } 126 }
127 } 127 }
128 128
129 @Override 129 @Override
130 public @Nullable Map<String, Object> getConstants() { 130 public @Nullable Map<String, Object> getConstants() {
131 HashMap<String, Object> constants = new HashMap<>(); 131 HashMap<String, Object> constants = new HashMap<>();
132 WritableArray available = Arguments.createArray(); 132 WritableArray available = Arguments.createArray();
133 133
134 for (String[] permission : PERMISSIONS) { 134 for (String[] permission : PERMISSIONS) {
135 if (fieldExists(permission[0])) 135 if (fieldExists(permission[0]))
136 available.pushString(permission[1]); 136 available.pushString(permission[1]);
137 } 137 }
138 138
139 constants.put("available", available); 139 constants.put("available", available);
140 return constants; 140 return constants;
141 } 141 }
142 142
143 @ReactMethod 143 @ReactMethod
144 public void isNonRequestable(final String permission, final Promise promise) { 144 public void isNonRequestable(final String permission, final Promise promise) {
145 promise.resolve(sharedPrefs.getBoolean(permission, false)); 145 promise.resolve(sharedPrefs.getBoolean(permission, false));
146 } 146 }
147 147
148 @ReactMethod 148 @ReactMethod
149 public void getNonRequestables(final Promise promise) { 149 public void getNonRequestables(final Promise promise) {
150 WritableArray output = Arguments.createArray(); 150 WritableArray output = Arguments.createArray();
151 Map<String, ?> entries = sharedPrefs.getAll(); 151 Map<String, ?> entries = sharedPrefs.getAll();
152 152
153 for (Map.Entry<String, ?> entry : entries.entrySet()) 153 for (Map.Entry<String, ?> entry : entries.entrySet())
154 output.pushString(entry.getKey()); 154 output.pushString(entry.getKey());
155 155
156 promise.resolve(output); 156 promise.resolve(output);
157 } 157 }
158 158
159 @ReactMethod 159 @ReactMethod
160 public void setNonRequestable(final String permission, final Promise promise) { 160 public void setNonRequestable(final String permission, final Promise promise) {
161 promise.resolve(sharedPrefs.edit().putBoolean(permission, true).commit()); 161 promise.resolve(sharedPrefs.edit().putBoolean(permission, true).commit());
162 } 162 }
163 163
164 @ReactMethod 164 @ReactMethod
165 public void setNonRequestables(final ReadableArray permissions, final Promise promise) { 165 public void setNonRequestables(final ReadableArray permissions, final Promise promise) {
166 SharedPreferences.Editor editor = sharedPrefs.edit(); 166 SharedPreferences.Editor editor = sharedPrefs.edit();
167 167
168 for (int i = 0; i < permissions.size(); i++) 168 for (int i = 0; i < permissions.size(); i++)
169 editor.putBoolean(permissions.getString(i), true); 169 editor.putBoolean(permissions.getString(i), true);
170 170
171 promise.resolve(editor.commit()); 171 promise.resolve(editor.commit());
172 } 172 }
173 173
174 @ReactMethod 174 @ReactMethod
175 public void checkNotifications(final Promise promise) { 175 public void checkNotifications(final Promise promise) {
176 final boolean enabled = NotificationManagerCompat 176 final boolean enabled = NotificationManagerCompat
177 .from(getReactApplicationContext()).areNotificationsEnabled(); 177 .from(getReactApplicationContext()).areNotificationsEnabled();
178 final WritableMap output = Arguments.createMap(); 178 final WritableMap output = Arguments.createMap();
179 final WritableMap settings = Arguments.createMap(); 179 final WritableMap settings = Arguments.createMap();
180 180
181 if (enabled) { 181 if (enabled) {
182 output.putString("status", "granted"); 182 output.putString("status", "granted");
183 } else { 183 } else {
184 output.putString("status", "blocked"); 184 output.putString("status", "blocked");
185 } 185 }
186 186
187 output.putMap("settings", settings); 187 output.putMap("settings", settings);
188 promise.resolve(output); 188 promise.resolve(output);
189 } 189 }
190 190
191 @ReactMethod 191 @ReactMethod
192 public void openSettings(final Promise promise) { 192 public void openSettings(final Promise promise) {
193 try { 193 try {
194 final ReactApplicationContext reactContext = getReactApplicationContext(); 194 final ReactApplicationContext reactContext = getReactApplicationContext();
195 final Intent intent = new Intent(); 195 final Intent intent = new Intent();
196 final String packageName = reactContext.getPackageName(); 196 final String packageName = reactContext.getPackageName();
197 197
198 intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 198 intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
199 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 199 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
200 intent.setData(Uri.fromParts("package", packageName, null)); 200 intent.setData(Uri.fromParts("package", packageName, null));
201 201
202 reactContext.startActivity(intent); 202 reactContext.startActivity(intent);
203 promise.resolve(true); 203 promise.resolve(true);
204 } catch (Exception e) { 204 } catch (Exception e) {
205 promise.reject(ERROR_INVALID_ACTIVITY, e); 205 promise.reject(ERROR_INVALID_ACTIVITY, e);
206 } 206 }
207 } 207 }
208 208
209 // 209 //
210 //https://developer.android.com/preview/behavior-changes-all#manage_overlay 210 //https://developer.android.com/preview/behavior-changes-all#manage_overlay
211 @ReactMethod 211 @ReactMethod
212 public void checkOrRequestOverlayPermission(final Promise promise) { 212 public void checkOrRequestOverlayPermission(final Promise promise) {
213 // Check if Android M or higher 213 // Check if Android M or higher
214 final ReactApplicationContext reactContext = getReactApplicationContext(); 214 final ReactApplicationContext reactContext = getReactApplicationContext();
215 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M && !Settings.canDrawOverlays(reactContext)) { 215 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M && !Settings.canDrawOverlays(reactContext)) {
216 // Store the promise to resolve/reject when picker returns data 216 // Store the promise to resolve/reject when picker returns data
217 try { 217 try {
218 Activity currentActivity = getCurrentActivity(); 218 Activity currentActivity = getCurrentActivity();
219 if (currentActivity == null) { 219 if (currentActivity == null) {
220 promise.reject("E_ACTIVITY_DOES_NOT_EXIST", "Activity doesn't exist"); 220 promise.reject("E_ACTIVITY_DOES_NOT_EXIST", "Activity doesn't exist");
221 return; 221 return;
222 } 222 }
223 mPickerPromise = promise; 223 mPickerPromise = promise;
224 final String packageName = reactContext.getPackageName(); 224 final String packageName = reactContext.getPackageName();
225 // Show alert dialog to the user saying a separate permission is needed 225 // Show alert dialog to the user saying a separate permission is needed
226 // Launch the settings activity if the user prefers 226 // Launch the settings activity if the user prefers
227 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, 227 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
228 Uri.parse("package:" + reactContext.getPackageName())); 228 Uri.parse("package:" + reactContext.getPackageName()));
229 currentActivity.startActivityForResult(intent, OVERLAY_PERMISSION_CODE); 229 currentActivity.startActivityForResult(intent, OVERLAY_PERMISSION_CODE);
230 } catch(Exception e) { 230 } catch(Exception e) {
231 mPickerPromise.reject("ACTION_MANAGE_OVERLAY_PERMISSION_ERROR", e); 231 mPickerPromise.reject("ACTION_MANAGE_OVERLAY_PERMISSION_ERROR", e);
232 mPickerPromise = null; 232 mPickerPromise = null;
233 } 233 }
234 } else { 234 } else {
235 //default is granted :D 235 //default is granted :D
236 final WritableMap output = Arguments.createMap(); 236 final WritableMap output = Arguments.createMap();
237 final WritableMap settings = Arguments.createMap(); 237 final WritableMap settings = Arguments.createMap();
238 output.putString("status", "granted"); 238 output.putString("status", "granted");
239 output.putMap("settings", settings); 239 output.putMap("settings", settings);
240 promise.resolve(output); 240 promise.resolve(output);
241 } 241 }
242 } 242 }
243
244 @ReactMethod
245 public void checkOverlayAndroid(final Promise promise) {
246 // Check if Android M or higher
247 final ReactApplicationContext reactContext = getReactApplicationContext();
248 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M && !Settings.canDrawOverlays(reactContext)) {
249 promise.resolve(false);
250 } else {
251 //default is granted :D
252 promise.resolve(true);
253 }
254 }
255
243 } 256 }
244 257
1 import { 1 import {
2 NotificationOption, 2 NotificationOption,
3 NotificationsResponse, 3 NotificationsResponse,
4 Permission, 4 Permission,
5 PermissionStatus, 5 PermissionStatus,
6 Rationale, 6 Rationale,
7 } from './types'; 7 } from './types';
8 8
9 export interface Contract { 9 export interface Contract {
10 openSettings(): Promise<void>; 10 openSettings(): Promise<void>;
11 11
12 check(permission: Permission): Promise<PermissionStatus>; 12 check(permission: Permission): Promise<PermissionStatus>;
13 13
14 checkOverlayAndroid(): Promise<boolean>;
15
14 request( 16 request(
15 permission: Permission, 17 permission: Permission,
16 rationale?: Rationale, 18 rationale?: Rationale,
17 ): Promise<PermissionStatus>; 19 ): Promise<PermissionStatus>;
18 20
19 checkNotifications(): Promise<NotificationsResponse>; 21 checkNotifications(): Promise<NotificationsResponse>;
20 22
21 requestNotifications( 23 requestNotifications(
22 options: NotificationOption[], 24 options: NotificationOption[],
23 ): Promise<NotificationsResponse>; 25 ): Promise<NotificationsResponse>;
24 26
25 checkMultiple<P extends Permission[]>( 27 checkMultiple<P extends Permission[]>(
26 permissions: P, 28 permissions: P,
27 ): Promise<Record<P[number], PermissionStatus>>; 29 ): Promise<Record<P[number], PermissionStatus>>;
28 30
29 requestMultiple<P extends Permission[]>( 31 requestMultiple<P extends Permission[]>(
30 permissions: P, 32 permissions: P,
31 ): Promise<Record<P[number], PermissionStatus>>; 33 ): Promise<Record<P[number], PermissionStatus>>;
32 } 34 }
33 35
1 import {NativeModules} from 'react-native'; 1 import {NativeModules} from 'react-native';
2 import {PERMISSIONS, RESULTS} from './constants'; 2 import {PERMISSIONS, RESULTS} from './constants';
3 import {module} from './module'; 3 import {module} from './module';
4 4
5 if (NativeModules.RNPermissions == null) { 5 if (NativeModules.RNPermissions == null) {
6 throw new Error(`react-native-permissions: NativeModule.RNPermissions is null. To fix this issue try these steps: 6 throw new Error(`react-native-permissions: NativeModule.RNPermissions is null. To fix this issue try these steps:
7 • If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then clean, rebuild and re-run the app. You may also need to re-open Xcode to get the new pods. 7 • If you are using CocoaPods on iOS, run \`pod install\` in the \`ios\` directory and then clean, rebuild and re-run the app. You may also need to re-open Xcode to get the new pods.
8 • If you are getting this error while unit testing you need to mock the native module. You can use this to get started: https://github.com/react-native-community/react-native-permissions/blob/master/mock.js 8 • If you are getting this error while unit testing you need to mock the native module. You can use this to get started: https://github.com/react-native-community/react-native-permissions/blob/master/mock.js
9 If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-community/react-native-permissions`); 9 If none of these fix the issue, please open an issue on the Github repository: https://github.com/react-native-community/react-native-permissions`);
10 } 10 }
11 11
12 export {PERMISSIONS, RESULTS} from './constants'; 12 export {PERMISSIONS, RESULTS} from './constants';
13 export * from './types'; 13 export * from './types';
14 14
15 export const openSettings = module.openSettings; 15 export const openSettings = module.openSettings;
16 export const check = module.check; 16 export const check = module.check;
17 export const checkOverlayAndroid = module.checkOverlayAndroid;
17 export const request = module.request; 18 export const request = module.request;
18 export const checkNotifications = module.checkNotifications; 19 export const checkNotifications = module.checkNotifications;
19 export const requestNotifications = module.requestNotifications; 20 export const requestNotifications = module.requestNotifications;
20 export const checkMultiple = module.checkMultiple; 21 export const checkMultiple = module.checkMultiple;
21 export const requestMultiple = module.requestMultiple; 22 export const requestMultiple = module.requestMultiple;
22 23
23 export default { 24 export default {
24 PERMISSIONS, 25 PERMISSIONS,
25 RESULTS, 26 RESULTS,
26 openSettings, 27 openSettings,
27 check, 28 check,
29 checkOverlayAndroid,
28 request, 30 request,
29 checkNotifications, 31 checkNotifications,
30 requestNotifications, 32 requestNotifications,
31 checkMultiple, 33 checkMultiple,
32 requestMultiple, 34 requestMultiple,
33 }; 35 };
34 36
src/module.android.ts
1 import { 1 import {
2 NativeModules, 2 NativeModules,
3 Permission as CorePermission, 3 Permission as CorePermission,
4 PermissionsAndroid as Core, 4 PermissionsAndroid as Core,
5 PermissionStatus as CoreStatus, 5 PermissionStatus as CoreStatus,
6 Rationale, 6 Rationale,
7 } from 'react-native'; 7 } from 'react-native';
8 import { RESULTS, PERMISSIONS } from './constants'; 8 import { RESULTS, PERMISSIONS } from './constants';
9 import { Contract } from './contract'; 9 import { Contract } from './contract';
10 import { NotificationsResponse, Permission, PermissionStatus } from './types'; 10 import { NotificationsResponse, Permission, PermissionStatus } from './types';
11 import { uniq } from './utils'; 11 import { uniq } from './utils';
12 12
13 const RNP: { 13 const RNP: {
14 available: Permission[]; 14 available: Permission[];
15 15
16 checkNotifications: () => Promise<NotificationsResponse>; 16 checkNotifications: () => Promise<NotificationsResponse>;
17 openSettings: () => Promise<true>; 17 openSettings: () => Promise<true>;
18 checkOrRequestOverlayPermission: () => Promise<true>; 18 checkOrRequestOverlayPermission: () => Promise<true>;
19 checkOverlayAndroid: () => Promise<boolean>;
19 getNonRequestables: () => Promise<Permission[]>; 20 getNonRequestables: () => Promise<Permission[]>;
20 isNonRequestable: (permission: Permission) => Promise<boolean>; 21 isNonRequestable: (permission: Permission) => Promise<boolean>;
21 setNonRequestable: (permission: Permission) => Promise<true>; 22 setNonRequestable: (permission: Permission) => Promise<true>;
22 setNonRequestables: (permissions: Permission[]) => Promise<true>; 23 setNonRequestables: (permissions: Permission[]) => Promise<true>;
23 } = NativeModules.RNPermissions; 24 } = NativeModules.RNPermissions;
24 25
25 function coreStatusToStatus(status: CoreStatus): PermissionStatus { 26 function coreStatusToStatus(status: CoreStatus): PermissionStatus {
26 switch (status) { 27 switch (status) {
27 case 'granted': 28 case 'granted':
28 return RESULTS.GRANTED; 29 return RESULTS.GRANTED;
29 case 'denied': 30 case 'denied':
30 return RESULTS.DENIED; 31 return RESULTS.DENIED;
31 case 'never_ask_again': 32 case 'never_ask_again':
32 return RESULTS.BLOCKED; 33 return RESULTS.BLOCKED;
33 default: 34 default:
34 return RESULTS.UNAVAILABLE; 35 return RESULTS.UNAVAILABLE;
35 } 36 }
36 } 37 }
37 38
38 async function openSettings(): Promise<void> { 39 async function openSettings(): Promise<void> {
39 await RNP.openSettings(); 40 await RNP.openSettings();
40 } 41 }
41 42
42 async function check(permission: Permission): Promise<PermissionStatus> { 43 async function check(permission: Permission): Promise<PermissionStatus> {
43 if (permission == PERMISSIONS.ANDROID.MANAGE_OVERLAY_PERMISSION) { 44 if (permission == PERMISSIONS.ANDROID.MANAGE_OVERLAY_PERMISSION) {
44 console.warn("check MANAGE_OVERLAY_PERMISSION ts") 45 console.warn("check MANAGE_OVERLAY_PERMISSION ts")
45 const output = await RNP.checkOrRequestOverlayPermission() 46 const output = await RNP.checkOrRequestOverlayPermission()
46 if (output.status == RESULTS.GRANTED) { 47 if (output.status == RESULTS.GRANTED) {
47 return RESULTS.GRANTED 48 return RESULTS.GRANTED
48 } else if (output.status == RESULTS.BLOCKED) { 49 } else if (output.status == RESULTS.BLOCKED) {
49 return RESULTS.BLOCKED 50 return RESULTS.BLOCKED
50 } else { 51 } else {
51 return RESULTS.UNAVAILABLE 52 return RESULTS.UNAVAILABLE
52 } 53 }
53 // return (await RNP.checkOrRequestOverlayPermission()) 54 // return (await RNP.checkOrRequestOverlayPermission())
54 // ? RESULTS.BLOCKED 55 // ? RESULTS.BLOCKED
55 // : RESULTS.DENIED; 56 // : RESULTS.DENIED;
56 } else { 57 } else {
57 if (!RNP.available.includes(permission)) { 58 if (!RNP.available.includes(permission)) {
58 return RESULTS.UNAVAILABLE; 59 return RESULTS.UNAVAILABLE;
59 } 60 }
60 61
61 if (await Core.check(permission as CorePermission)) { 62 if (await Core.check(permission as CorePermission)) {
62 return RESULTS.GRANTED; 63 return RESULTS.GRANTED;
63 } 64 }
64 65
65 return (await RNP.isNonRequestable(permission)) 66 return (await RNP.isNonRequestable(permission))
66 ? RESULTS.BLOCKED 67 ? RESULTS.BLOCKED
67 : RESULTS.DENIED; 68 : RESULTS.DENIED;
68 } 69 }
69 } 70 }
70 71
72 async function checkOverlayAndroid(): Promise<boolean> {
73 return await RNP.checkOverlayAndroid();
74 }
75
71 async function request( 76 async function request(
72 permission: Permission, 77 permission: Permission,
73 rationale?: Rationale, 78 rationale?: Rationale,
74 ): Promise<PermissionStatus> { 79 ): Promise<PermissionStatus> {
75 if (!RNP.available.includes(permission)) { 80 if (!RNP.available.includes(permission)) {
76 return RESULTS.UNAVAILABLE; 81 return RESULTS.UNAVAILABLE;
77 } 82 }
78 83
79 const status = coreStatusToStatus( 84 const status = coreStatusToStatus(
80 await Core.request(permission as CorePermission, rationale), 85 await Core.request(permission as CorePermission, rationale),
81 ); 86 );
82 87
83 if (status === RESULTS.BLOCKED) { 88 if (status === RESULTS.BLOCKED) {
84 await RNP.setNonRequestable(permission); 89 await RNP.setNonRequestable(permission);
85 } 90 }
86 91
87 return status; 92 return status;
88 } 93 }
89 94
90 function splitByAvailability<P extends Permission[]>( 95 function splitByAvailability<P extends Permission[]>(
91 permissions: P, 96 permissions: P,
92 ): { 97 ): {
93 unavailable: Partial<Record<P[number], PermissionStatus>>; 98 unavailable: Partial<Record<P[number], PermissionStatus>>;
94 available: P[number][]; 99 available: P[number][];
95 } { 100 } {
96 const unavailable: Partial<Record<P[number], PermissionStatus>> = {}; 101 const unavailable: Partial<Record<P[number], PermissionStatus>> = {};
97 const available: P[number][] = []; 102 const available: P[number][] = [];
98 103
99 for (let index = 0; index < permissions.length; index++) { 104 for (let index = 0; index < permissions.length; index++) {
100 const permission: P[number] = permissions[index]; 105 const permission: P[number] = permissions[index];
101 106
102 if (RNP.available.includes(permission)) { 107 if (RNP.available.includes(permission)) {
103 available.push(permission); 108 available.push(permission);
104 } else { 109 } else {
105 unavailable[permission] = RESULTS.UNAVAILABLE; 110 unavailable[permission] = RESULTS.UNAVAILABLE;
106 } 111 }
107 } 112 }
108 113
109 return { unavailable, available }; 114 return { unavailable, available };
110 } 115 }
111 116
112 function checkNotifications(): Promise<NotificationsResponse> { 117 function checkNotifications(): Promise<NotificationsResponse> {
113 return RNP.checkNotifications(); 118 return RNP.checkNotifications();
114 } 119 }
115 120
116 async function checkMultiple<P extends Permission[]>( 121 async function checkMultiple<P extends Permission[]>(
117 permissions: P, 122 permissions: P,
118 ): Promise<Record<P[number], PermissionStatus>> { 123 ): Promise<Record<P[number], PermissionStatus>> {
119 const dedup = uniq(permissions); 124 const dedup = uniq(permissions);
120 const { unavailable: output, available } = splitByAvailability(dedup); 125 const { unavailable: output, available } = splitByAvailability(dedup);
121 const blocklist = await RNP.getNonRequestables(); 126 const blocklist = await RNP.getNonRequestables();
122 127
123 await Promise.all( 128 await Promise.all(
124 available.map(async (permission: P[number]) => { 129 available.map(async (permission: P[number]) => {
125 const granted = await Core.check(permission as CorePermission); 130 const granted = await Core.check(permission as CorePermission);
126 131
127 output[permission] = granted 132 output[permission] = granted
128 ? RESULTS.GRANTED 133 ? RESULTS.GRANTED
129 : blocklist.includes(permission) 134 : blocklist.includes(permission)
130 ? RESULTS.BLOCKED 135 ? RESULTS.BLOCKED
131 : RESULTS.DENIED; 136 : RESULTS.DENIED;
132 }), 137 }),
133 ); 138 );
134 139
135 return output as Record<P[number], PermissionStatus>; 140 return output as Record<P[number], PermissionStatus>;
136 } 141 }
137 142
138 async function requestMultiple<P extends Permission[]>( 143 async function requestMultiple<P extends Permission[]>(
139 permissions: P, 144 permissions: P,
140 ): Promise<Record<P[number], PermissionStatus>> { 145 ): Promise<Record<P[number], PermissionStatus>> {
141 const toSetAsNonRequestable: Permission[] = []; 146 const toSetAsNonRequestable: Permission[] = [];
142 const dedup = uniq(permissions); 147 const dedup = uniq(permissions);
143 const { unavailable: output, available } = splitByAvailability(dedup); 148 const { unavailable: output, available } = splitByAvailability(dedup);
144 const statuses = await Core.requestMultiple(available as CorePermission[]); 149 const statuses = await Core.requestMultiple(available as CorePermission[]);
145 150
146 for (const permission in statuses) { 151 for (const permission in statuses) {
147 if (statuses.hasOwnProperty(permission)) { 152 if (statuses.hasOwnProperty(permission)) {
148 const status = coreStatusToStatus(statuses[permission as CorePermission]); 153 const status = coreStatusToStatus(statuses[permission as CorePermission]);
149 output[permission as P[number]] = status; 154 output[permission as P[number]] = status;
150 155
151 status === RESULTS.BLOCKED && 156 status === RESULTS.BLOCKED &&
152 toSetAsNonRequestable.push(permission as Permission); 157 toSetAsNonRequestable.push(permission as Permission);
153 } 158 }
154 } 159 }
155 160
156 if (toSetAsNonRequestable.length > 0) { 161 if (toSetAsNonRequestable.length > 0) {
157 await RNP.setNonRequestables(toSetAsNonRequestable); 162 await RNP.setNonRequestables(toSetAsNonRequestable);
158 } 163 }
159 164
160 return output as Record<P[number], PermissionStatus>; 165 return output as Record<P[number], PermissionStatus>;
161 } 166 }
162 167
163 export const module: Contract = { 168 export const module: Contract = {
164 openSettings, 169 openSettings,
165 check, 170 check,
171 checkOverlayAndroid,
166 request, 172 request,
167 checkNotifications, 173 checkNotifications,
168 requestNotifications: checkNotifications, 174 requestNotifications: checkNotifications,
169 checkMultiple, 175 checkMultiple,
170 requestMultiple, 176 requestMultiple,
171 }; 177 };
172 178
1 import {NativeModules} from 'react-native'; 1 import {NativeModules} from 'react-native';
2 import {RESULTS, PERMISSIONS} from './constants'; 2 import {RESULTS, PERMISSIONS} from './constants';
3 import {Contract} from './contract'; 3 import {Contract} from './contract';
4 import { 4 import {
5 NotificationOption, 5 NotificationOption,
6 NotificationsResponse, 6 NotificationsResponse,
7 Permission, 7 Permission,
8 PermissionStatus, 8 PermissionStatus,
9 } from './types'; 9 } from './types';
10 import {uniq} from './utils'; 10 import {uniq} from './utils';
11 11
12 const RNP: { 12 const RNP: {
13 available: Permission[]; 13 available: Permission[];
14 14
15 checkNotifications: () => Promise<NotificationsResponse>; 15 checkNotifications: () => Promise<NotificationsResponse>;
16 requestNotifications: ( 16 requestNotifications: (
17 options: NotificationOption[], 17 options: NotificationOption[],
18 ) => Promise<NotificationsResponse>; 18 ) => Promise<NotificationsResponse>;
19 openSettings: () => Promise<true>; 19 openSettings: () => Promise<true>;
20
20 check: (permission: Permission) => Promise<PermissionStatus>; 21 check: (permission: Permission) => Promise<PermissionStatus>;
21 request: (permission: Permission) => Promise<PermissionStatus>; 22 request: (permission: Permission) => Promise<PermissionStatus>;
22 } = NativeModules.RNPermissions; 23 } = NativeModules.RNPermissions;
23 24
24 async function openSettings(): Promise<void> { 25 async function openSettings(): Promise<void> {
25 await RNP.openSettings(); 26 await RNP.openSettings();
26 } 27 }
27 28
28 async function check(permission: Permission): Promise<PermissionStatus> { 29 async function check(permission: Permission): Promise<PermissionStatus> {
29 return RNP.available.includes(permission) 30 return RNP.available.includes(permission)
30 ? RNP.check(permission) 31 ? RNP.check(permission)
31 : RESULTS.UNAVAILABLE; 32 : RESULTS.UNAVAILABLE;
32 } 33 }
33 34
34 async function request(permission: Permission): Promise<PermissionStatus> { 35 async function request(permission: Permission): Promise<PermissionStatus> {
35 return RNP.available.includes(permission) 36 return RNP.available.includes(permission)
36 ? RNP.request(permission) 37 ? RNP.request(permission)
37 : RESULTS.UNAVAILABLE; 38 : RESULTS.UNAVAILABLE;
38 } 39 }
39 40
40 export function checkNotifications(): Promise<NotificationsResponse> { 41 export function checkNotifications(): Promise<NotificationsResponse> {
41 return RNP.checkNotifications(); 42 return RNP.checkNotifications();
42 } 43 }
43 44
45 async function checkOverlayAndroid(): Promise<boolean> {
46 return true;
47 }
48
44 export function requestNotifications( 49 export function requestNotifications(
45 options: NotificationOption[], 50 options: NotificationOption[],
46 ): Promise<NotificationsResponse> { 51 ): Promise<NotificationsResponse> {
47 return RNP.requestNotifications(options); 52 return RNP.requestNotifications(options);
48 } 53 }
49 54
50 async function checkMultiple<P extends Permission[]>( 55 async function checkMultiple<P extends Permission[]>(
51 permissions: P, 56 permissions: P,
52 ): Promise<Record<P[number], PermissionStatus>> { 57 ): Promise<Record<P[number], PermissionStatus>> {
53 type Output = Record<P[number], PermissionStatus>; 58 type Output = Record<P[number], PermissionStatus>;
54 59
55 const output: Partial<Output> = {}; 60 const output: Partial<Output> = {};
56 const dedup = uniq(permissions); 61 const dedup = uniq(permissions);
57 62
58 await Promise.all( 63 await Promise.all(
59 dedup.map(async (permission: P[number]) => { 64 dedup.map(async (permission: P[number]) => {
60 output[permission] = await check(permission); 65 output[permission] = await check(permission);
61 }), 66 }),
62 ); 67 );
63 68
64 return output as Output; 69 return output as Output;
65 } 70 }
66 71
67 async function requestMultiple<P extends Permission[]>( 72 async function requestMultiple<P extends Permission[]>(
68 permissions: P, 73 permissions: P,
69 ): Promise<Record<P[number], PermissionStatus>> { 74 ): Promise<Record<P[number], PermissionStatus>> {
70 type Output = Record<P[number], PermissionStatus>; 75 type Output = Record<P[number], PermissionStatus>;
71 76
72 const output: Partial<Output> = {}; 77 const output: Partial<Output> = {};
73 const dedup = uniq(permissions); 78 const dedup = uniq(permissions);
74 79
75 for (let index = 0; index < dedup.length; index++) { 80 for (let index = 0; index < dedup.length; index++) {
76 const permission: P[number] = dedup[index]; 81 const permission: P[number] = dedup[index];
77 output[permission] = await request(permission); 82 output[permission] = await request(permission);
78 } 83 }
79 84
80 return output as Output; 85 return output as Output;
81 } 86 }
82 87
83 export const module: Contract = { 88 export const module: Contract = {
84 openSettings, 89 openSettings,
85 check, 90 check,
91 checkOverlayAndroid,
86 request, 92 request,
87 checkNotifications, 93 checkNotifications,
88 requestNotifications, 94 requestNotifications,
89 checkMultiple, 95 checkMultiple,
90 requestMultiple, 96 requestMultiple,
91 }; 97 };
92 98
1 import {RESULTS} from './constants'; 1 import {RESULTS} from './constants';
2 import {Contract} from './contract'; 2 import {Contract} from './contract';
3 import {NotificationsResponse, Permission, PermissionStatus} from './types'; 3 import {NotificationsResponse, Permission, PermissionStatus} from './types';
4 4
5 async function check(): Promise<PermissionStatus> { 5 async function check(): Promise<PermissionStatus> {
6 return RESULTS.UNAVAILABLE; 6 return RESULTS.UNAVAILABLE;
7 } 7 }
8 8
9 async function checkOverlayAndroid(): Promise<PermissionStatus> {
10 return RESULTS.UNAVAILABLE;
11 }
12
9 async function checkNotifications(): Promise<NotificationsResponse> { 13 async function checkNotifications(): Promise<NotificationsResponse> {
10 return {status: RESULTS.UNAVAILABLE, settings: {}}; 14 return {status: RESULTS.UNAVAILABLE, settings: {}};
11 } 15 }
12 16
13 async function checkMultiple<P extends Permission[]>( 17 async function checkMultiple<P extends Permission[]>(
14 permissions: P, 18 permissions: P,
15 ): Promise<Record<P[number], PermissionStatus>> { 19 ): Promise<Record<P[number], PermissionStatus>> {
16 return permissions.reduce((acc, permission: P[number]) => { 20 return permissions.reduce((acc, permission: P[number]) => {
17 acc[permission] = RESULTS.UNAVAILABLE; 21 acc[permission] = RESULTS.UNAVAILABLE;
18 return acc; 22 return acc;
19 }, {} as Record<P[number], PermissionStatus>); 23 }, {} as Record<P[number], PermissionStatus>);
20 } 24 }
21 25
22 export const module: Contract = { 26 export const module: Contract = {
23 openSettings: Promise.reject, 27 openSettings: Promise.reject,
24 check, 28 check,
29 checkOverlayAndroid,
25 request: check, 30 request: check,
26 checkNotifications, 31 checkNotifications,
27 requestNotifications: checkNotifications, 32 requestNotifications: checkNotifications,
28 checkMultiple, 33 checkMultiple,
29 requestMultiple: checkMultiple, 34 requestMultiple: checkMultiple,
30 }; 35 };
31 36