Skip to content

Commit cc51259

Browse files
committed
Merge branch '2.1.0'
2 parents 6145671 + 23a6b22 commit cc51259

File tree

37 files changed

+2598
-2399
lines changed

37 files changed

+2598
-2399
lines changed

README.md

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -103,31 +103,17 @@ Then update your `Info.plist` with wanted permissions usage descriptions:
103103
</plist>
104104
```
105105

106-
#### ⚠️ If you encounter the error `Invalid RNPermission X. Should be one of: ()`
106+
## Workaround for `use_frameworks!` issues
107107

108-
1. Check that you linked **at least one** permission handler.
109-
2. Clean up Xcode stale data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods`
110-
3. If you use `use_frameworks!`, replace it by `use_modular_headers!` - see [this blog post](http://blog.cocoapods.org/CocoaPods-1.5.0) for more details. [Create empty Swift file in XCode](https://stackoverflow.com/questions/52536380/why-linker-link-static-libraries-with-errors-ios/56176956#56176956). Then add ":modular_headers => false" to Pods with build errors:
108+
If you use `use_frameworks!`, add this at the top of your `Podfile`:
111109

112110
```ruby
113-
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec', :modular_headers => false
114-
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec', :modular_headers => false
115-
```
116-
117-
4. If you use `use_frameworks!` but **can't** replace it with `use_modular_headers!`, check the following workaround:
118-
119-
```ruby
120-
# Add this code at the top of Podfile right after platform definition.
121-
# It will make all the dynamic frameworks turning into static libraries.
122-
123111
use_frameworks!
124112

125-
$dynamic_frameworks = ['RxCocoa', 'RxSwift', 'WhatEverSDKName']
126-
113+
# Convert all permissions handlers into static libraries
127114
pre_install do |installer|
128115
installer.pod_targets.each do |pod|
129-
if !$dynamic_frameworks.include?(pod.name)
130-
puts "Link #{pod.name} as static_library"
116+
if pod.name.start_with?('Permission-')
131117
def pod.build_type;
132118
# Uncomment one line depending on your CocoaPods version
133119
# Pod::BuildType.static_library # >= 1.9
@@ -435,7 +421,7 @@ function check(permission: string): Promise<PermissionStatus>;
435421
import {check, PERMISSIONS, RESULTS} from 'react-native-permissions';
436422

437423
check(PERMISSIONS.IOS.LOCATION_ALWAYS)
438-
.then(result => {
424+
.then((result) => {
439425
switch (result) {
440426
case RESULTS.UNAVAILABLE:
441427
console.log(
@@ -455,7 +441,7 @@ check(PERMISSIONS.IOS.LOCATION_ALWAYS)
455441
break;
456442
}
457443
})
458-
.catch(error => {
444+
.catch((error) => {
459445
//
460446
});
461447
```
@@ -484,7 +470,7 @@ function request(
484470
```js
485471
import {request, PERMISSIONS} from 'react-native-permissions';
486472

487-
request(PERMISSIONS.IOS.LOCATION_ALWAYS).then(result => {
473+
request(PERMISSIONS.IOS.LOCATION_ALWAYS).then((result) => {
488474
//
489475
});
490476
```
@@ -568,6 +554,52 @@ requestNotifications(['alert', 'sound']).then(({status, settings}) => {
568554

569555
---
570556

557+
#### checkMultiple
558+
559+
Check multiples permissions in parallel.
560+
561+
```ts
562+
function checkMultiple<P extends Permission[]>(
563+
permissions: P,
564+
): Promise<Record<P[number], PermissionStatus>>;
565+
```
566+
567+
```js
568+
import {checkMultiple, PERMISSIONS} from 'react-native-permissions';
569+
570+
checkMultiple([PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.FACE_ID]).then(
571+
(statuses) => {
572+
console.log('Camera', statuses[PERMISSIONS.IOS.CAMERA]);
573+
console.log('FaceID', statuses[PERMISSIONS.IOS.FACE_ID]);
574+
},
575+
);
576+
```
577+
578+
---
579+
580+
#### requestMultiple
581+
582+
Request multiple permissions in sequence.
583+
584+
```ts
585+
function requestMultiple<P extends Permission[]>(
586+
permissions: P,
587+
): Promise<Record<P[number], PermissionStatus>>;
588+
```
589+
590+
```js
591+
import {requestMultiple, PERMISSIONS} from 'react-native-permissions';
592+
593+
requestMultiple([PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.FACE_ID]).then(
594+
(statuses) => {
595+
console.log('Camera', statuses[PERMISSIONS.IOS.CAMERA]);
596+
console.log('FaceID', statuses[PERMISSIONS.IOS.FACE_ID]);
597+
},
598+
);
599+
```
600+
601+
---
602+
571603
#### openSettings
572604

573605
Open application settings.
@@ -603,37 +635,3 @@ request(
603635
}),
604636
);
605637
```
606-
607-
## Additional recipes
608-
609-
#### Check multiples permissions
610-
611-
```js
612-
import {check, PERMISSIONS} from 'react-native-permissions';
613-
614-
// can be done in parallel
615-
Promise.all([
616-
check(PERMISSIONS.IOS.CAMERA),
617-
check(PERMISSIONS.IOS.CONTACTS),
618-
//
619-
]).then(([cameraStatus, contactsStatus /**/]) => {
620-
console.log({cameraStatus, contactsStatus});
621-
});
622-
```
623-
624-
#### Request multiples permissions
625-
626-
_⚠️  It's a very bad UX pattern, avoid doing it!_
627-
628-
```js
629-
import {request, PERMISSIONS} from 'react-native-permissions';
630-
631-
// should be done in sequence
632-
async function requestAll() {
633-
const cameraStatus = await request(PERMISSIONS.IOS.CAMERA);
634-
const contactsStatus = await request(PERMISSIONS.IOS.CONTACTS);
635-
return {cameraStatus, contactsStatus};
636-
}
637-
638-
requestAll().then(statuses => console.log(statuses));
639-
```

android/src/main/java/com/reactnativecommunity/rnpermissions/RNPermissionsModule.java

Lines changed: 92 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,50 @@
1919
import com.facebook.react.bridge.WritableMap;
2020
import com.facebook.react.module.annotations.ReactModule;
2121

22+
import java.util.HashMap;
2223
import java.util.Map;
2324

25+
import javax.annotation.Nullable;
26+
2427
@ReactModule(name = RNPermissionsModule.MODULE_NAME)
2528
public class RNPermissionsModule extends ReactContextBaseJavaModule {
2629

2730
private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";
2831
public static final String MODULE_NAME = "RNPermissions";
2932
private static final String SETTING_NAME = "@RNPermissions:NonRequestables";
3033

34+
private static final String[][] PERMISSIONS = new String[][] {
35+
{ "ACCEPT_HANDOVER", "android.permission.ACCEPT_HANDOVER" },
36+
{ "ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_BACKGROUND_LOCATION" },
37+
{ "ACCESS_COARSE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION" },
38+
{ "ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION" },
39+
{ "ACTIVITY_RECOGNITION", "android.permission.ACTIVITY_RECOGNITION" },
40+
{ "ADD_VOICEMAIL", "com.android.voicemail.permission.ADD_VOICEMAIL" },
41+
{ "ANSWER_PHONE_CALLS", "android.permission.ANSWER_PHONE_CALLS" },
42+
{ "BODY_SENSORS", "android.permission.BODY_SENSORS" },
43+
{ "CALL_PHONE", "android.permission.CALL_PHONE" },
44+
{ "CAMERA", "android.permission.CAMERA" },
45+
{ "GET_ACCOUNTS", "android.permission.GET_ACCOUNTS" },
46+
{ "PROCESS_OUTGOING_CALLS", "android.permission.PROCESS_OUTGOING_CALLS" },
47+
{ "READ_CALENDAR", "android.permission.READ_CALENDAR" },
48+
{ "READ_CALL_LOG", "android.permission.READ_CALL_LOG" },
49+
{ "READ_CONTACTS", "android.permission.READ_CONTACTS" },
50+
{ "READ_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE" },
51+
{ "READ_PHONE_NUMBERS", "android.permission.READ_PHONE_NUMBERS" },
52+
{ "READ_PHONE_STATE", "android.permission.READ_PHONE_STATE" },
53+
{ "READ_SMS", "android.permission.READ_SMS" },
54+
{ "RECEIVE_MMS", "android.permission.RECEIVE_MMS" },
55+
{ "RECEIVE_SMS", "android.permission.RECEIVE_SMS" },
56+
{ "RECEIVE_WAP_PUSH", "android.permission.RECEIVE_WAP_PUSH" },
57+
{ "RECORD_AUDIO", "android.permission.RECORD_AUDIO" },
58+
{ "SEND_SMS", "android.permission.SEND_SMS" },
59+
{ "USE_SIP", "android.permission.USE_SIP" },
60+
{ "WRITE_CALENDAR", "android.permission.WRITE_CALENDAR" },
61+
{ "WRITE_CALL_LOG", "android.permission.WRITE_CALL_LOG" },
62+
{ "WRITE_CONTACTS", "android.permission.WRITE_CONTACTS" },
63+
{ "WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" },
64+
};
65+
3166
private final SharedPreferences sharedPrefs;
3267

3368
public RNPermissionsModule(ReactApplicationContext reactContext) {
@@ -40,75 +75,92 @@ public String getName() {
4075
return MODULE_NAME;
4176
}
4277

43-
@ReactMethod
44-
public void isAvailable(final String permission, final Promise promise) {
45-
String fieldName = permission.substring(permission.lastIndexOf('.') + 1);
46-
78+
private boolean fieldExists(final String fieldName) {
4779
try {
4880
Manifest.permission.class.getField(fieldName);
49-
promise.resolve(true);
50-
} catch (NoSuchFieldException e) {
51-
promise.resolve(false);
81+
return true;
82+
} catch (NoSuchFieldException ignored) {
83+
return false;
84+
}
85+
}
86+
87+
@Override
88+
public @Nullable Map<String, Object> getConstants() {
89+
HashMap<String, Object> constants = new HashMap<>();
90+
WritableArray available = Arguments.createArray();
91+
92+
for (String[] permission : PERMISSIONS) {
93+
if (fieldExists(permission[0]))
94+
available.pushString(permission[1]);
5295
}
96+
97+
constants.put("available", available);
98+
return constants;
5399
}
54100

55101
@ReactMethod
56-
public void setNonRequestable(final String permission, final Promise promise) {
57-
promise.resolve(sharedPrefs.edit().putBoolean(permission, true).commit());
102+
public void isNonRequestable(final String permission, final Promise promise) {
103+
promise.resolve(sharedPrefs.getBoolean(permission, false));
58104
}
59105

60106
@ReactMethod
61107
public void getNonRequestables(final Promise promise) {
62-
WritableArray result = Arguments.createArray();
108+
WritableArray output = Arguments.createArray();
63109
Map<String, ?> entries = sharedPrefs.getAll();
64110

65-
for (Map.Entry<String, ?> entry : entries.entrySet()) {
66-
result.pushString(entry.getKey());
67-
}
111+
for (Map.Entry<String, ?> entry : entries.entrySet())
112+
output.pushString(entry.getKey());
68113

69-
promise.resolve(result);
114+
promise.resolve(output);
70115
}
71116

72117
@ReactMethod
73-
public void openSettings(final Promise promise) {
74-
try {
75-
final ReactApplicationContext reactContext = getReactApplicationContext();
76-
final Intent intent = new Intent();
118+
public void setNonRequestable(final String permission, final Promise promise) {
119+
promise.resolve(sharedPrefs.edit().putBoolean(permission, true).commit());
120+
}
77121

78-
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
79-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
80-
intent.setData(Uri.fromParts("package", reactContext.getPackageName(), null));
122+
@ReactMethod
123+
public void setNonRequestables(final ReadableArray permissions, final Promise promise) {
124+
SharedPreferences.Editor editor = sharedPrefs.edit();
81125

82-
reactContext.startActivity(intent);
83-
promise.resolve(true);
84-
} catch (Exception e) {
85-
promise.reject(ERROR_INVALID_ACTIVITY, e);
86-
}
126+
for (int i = 0; i < permissions.size(); i++)
127+
editor.putBoolean(permissions.getString(i), true);
128+
129+
promise.resolve(editor.commit());
87130
}
88131

89-
private WritableMap internalCheckNotifications() {
90-
final ReactApplicationContext reactContext = getReactApplicationContext();
91-
final boolean enabled = NotificationManagerCompat.from(reactContext).areNotificationsEnabled();
92-
final WritableMap map = Arguments.createMap();
132+
@ReactMethod
133+
public void checkNotifications(final Promise promise) {
134+
final boolean enabled = NotificationManagerCompat
135+
.from(getReactApplicationContext()).areNotificationsEnabled();
136+
final WritableMap output = Arguments.createMap();
93137
final WritableMap settings = Arguments.createMap();
94138

95139
if (enabled) {
96-
map.putString("status", "granted");
140+
output.putString("status", "granted");
97141
} else {
98-
map.putString("status", "blocked");
142+
output.putString("status", "blocked");
99143
}
100144

101-
map.putMap("settings", settings);
102-
return map;
145+
output.putMap("settings", settings);
146+
promise.resolve(output);
103147
}
104148

105149
@ReactMethod
106-
public void checkNotifications(final Promise promise) {
107-
promise.resolve(internalCheckNotifications());
108-
}
150+
public void openSettings(final Promise promise) {
151+
try {
152+
final ReactApplicationContext reactContext = getReactApplicationContext();
153+
final Intent intent = new Intent();
154+
final String packageName = reactContext.getPackageName();
109155

110-
@ReactMethod
111-
public void requestNotifications(ReadableArray options, final Promise promise) {
112-
promise.resolve(internalCheckNotifications());
156+
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
157+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
158+
intent.setData(Uri.fromParts("package", packageName, null));
159+
160+
reactContext.startActivity(intent);
161+
promise.resolve(true);
162+
} catch (Exception e) {
163+
promise.reject(ERROR_INVALID_ACTIVITY, e);
164+
}
113165
}
114166
}

0 commit comments

Comments
 (0)