diff --git a/RNPermissions.podspec b/RNPermissions.podspec index 9a464651..dba080d7 100644 --- a/RNPermissions.podspec +++ b/RNPermissions.podspec @@ -1,9 +1,10 @@ require 'json' package = JSON.parse(File.read('./package.json')) +fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1' + Pod::Spec.new do |s| s.name = "RNPermissions" - s.dependency "React-Core" s.version = package["version"] s.license = package["license"] @@ -16,5 +17,29 @@ Pod::Spec.new do |s| s.requires_arc = true s.source = { :git => package["repository"]["url"], :tag => s.version } - s.source_files = "ios/*.{h,m,mm}" + s.source_files = "ios/*.{h,m,mm}" + + if fabric_enabled + folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + + s.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly"', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + } + s.platforms = { ios: '11.0', tvos: '11.0' } + s.compiler_flags = folly_compiler_flags + ' -DRCT_NEW_ARCH_ENABLED' + + s.dependency "React" + s.dependency "React-RCTFabric" # This is for fabric component + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + else + s.platforms = { :ios => "9.0", :tvos => "9.0" } + + s.dependency "React-Core" + end + end diff --git a/android/build.gradle b/android/build.gradle index c886c057..4b669c2a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -19,7 +19,57 @@ buildscript { apply plugin: "com.android.library" +def resolveReactNativeDirectory() { + // monorepo workaround + // react-native can be hoisted or in project's own node_modules + def reactNativeFromProjectNodeModules = file("${rootProject.projectDir}/../node_modules/react-native") + if (reactNativeFromProjectNodeModules.exists()) { + return reactNativeFromProjectNodeModules + } + + def reactNativeFromNodeModulesWithLibrary = file("${projectDir}/../../react-native") + if (reactNativeFromNodeModulesWithLibrary.exists()) { + return reactNativeFromNodeModulesWithLibrary + } + + throw new Exception( + "[react-native-clipboard] Unable to resolve react-native location in " + + "node_modules. You should add project extension property (in app/build.gradle) " + + "`REACT_NATIVE_NODE_MODULES_DIR` with path to react-native." + ) +} + +def REACT_NATIVE_DIR = resolveReactNativeDirectory() + +def reactProperties = new Properties() +file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } + +def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME") +def REACT_NATIVE_MINOR_VERSION = REACT_NATIVE_VERSION.startsWith("0.0.0-") ? 1000 : REACT_NATIVE_VERSION.split("\\.")[1].toInteger() + +def isNewArchitectureEnabled() { + // To opt-in for the New Architecture, you can either: + // - Set `newArchEnabled` to true inside the `gradle.properties` file + // - Invoke gradle with `-newArchEnabled=true` + // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + android { + + // Used to override the NDK path/version on internal CI or by allowing + // users to customize the NDK path/version from their root project (e.g. for M1 support) + if (rootProject.hasProperty("ndkPath")) { + ndkPath rootProject.ext.ndkPath + } + if (rootProject.hasProperty("ndkVersion")) { + ndkVersion rootProject.ext.ndkVersion + } + compileSdkVersion safeExtGet("compileSdkVersion", 33) buildToolsVersion safeExtGet("buildToolsVersion", "33.0.0") defaultConfig { @@ -29,6 +79,15 @@ android { lintOptions { abortOnError false } + + + sourceSets.main { + java { + if (!isNewArchitectureEnabled()) { + srcDirs += 'src/paper/java' + } + } + } } repositories { @@ -46,6 +105,10 @@ repositories { } dependencies { - //noinspection GradleDynamicVersion - implementation "com.facebook.react:react-native:+" // From node_modules + if (isNewArchitectureEnabled() && REACT_NATIVE_MINOR_VERSION < 71) { + implementation project(":ReactAndroid") + } else { + //noinspection GradleDynamicVersion + implementation 'com.facebook.react:react-native:+' + } } diff --git a/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsModule.java b/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsModule.java index 2171fa4f..f4f9f8b6 100644 --- a/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsModule.java +++ b/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsModule.java @@ -18,7 +18,6 @@ import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableMap; @@ -28,12 +27,14 @@ import com.facebook.react.modules.core.PermissionListener; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; -@ReactModule(name = RNPermissionsModule.MODULE_NAME) -public class RNPermissionsModule extends ReactContextBaseJavaModule implements PermissionListener { +@ReactModule(name = RNPermissionsModule.NAME) +public class RNPermissionsModule extends NativePermissionsModuleSpec implements PermissionListener { private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY"; - public static final String MODULE_NAME = "RNPermissions"; + public static final String NAME = "RNPermissionsModule"; private final SparseArray mCallbacks; private int mRequestCode = 0; @@ -49,7 +50,7 @@ public RNPermissionsModule(ReactApplicationContext reactContext) { @Override public String getName() { - return MODULE_NAME; + return NAME; } private @Nullable String getFieldName(final String permission) { @@ -373,6 +374,41 @@ public void invoke(Object... args) { } } + @Override + protected Map getTypedExportedConstants() { + return new HashMap<>(); + } + + @Override + public void check(String permission, Promise promise) { + promise.reject("Permissions:check", "check is not supported on Android"); + } + + @Override + public void checkLocationAccuracy(Promise promise) { + promise.reject("Permissions:checkLocationAccuracy", "checkLocationAccuracy is not supported on Android"); + } + + @Override + public void request(String permission, Promise promise) { + promise.reject("Permissions:request", "request is not supported on Android"); + } + + @Override + public void requestLocationAccuracy(String purposeKey, Promise promise) { + promise.reject("Permissions:requestLocationAccuracy", "requestLocationAccuracy is not supported on Android"); + } + + @Override + public void requestNotifications(ReadableArray options, Promise promise) { + promise.reject("Permissions:requestNotifications", "requestNotifications is not supported on Android"); + } + + @Override + public void openLimitedPhotoLibraryPicker(Promise promise) { + promise.reject("Permissions:openLimitedPhotoLibraryPicker", "openLimitedPhotoLibraryPicker is not supported on Android"); + } + @Override public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { mCallbacks.get(requestCode).invoke(grantResults, getPermissionAwareActivity()); diff --git a/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsPackage.java b/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsPackage.java index 2f216a96..1fbd38de 100644 --- a/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsPackage.java +++ b/android/src/main/java/com/zoontek/rnpermissions/RNPermissionsPackage.java @@ -1,14 +1,102 @@ package com.zoontek.rnpermissions; -import com.facebook.react.ReactPackage; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.facebook.react.TurboReactPackage; +import com.facebook.react.ViewManagerOnDemandReactPackage; +import com.facebook.react.bridge.ModuleSpec; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.module.annotations.ReactModuleList; +import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.module.model.ReactModuleInfoProvider; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; import com.facebook.react.uimanager.ViewManager; -import java.util.Collections; -import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@ReactModuleList( + nativeModules = { + RNPermissionsModule.class, + }) +public class RNPermissionsPackage extends TurboReactPackage implements ViewManagerOnDemandReactPackage { + + /** {@inheritDoc} */ + @Override + public List getViewManagerNames(ReactApplicationContext reactContext) { + return null; + } + + @Override + protected List getViewManagers(ReactApplicationContext reactContext) { + return null; + } -public class RNPermissionsPackage implements ReactPackage { + /** {@inheritDoc} */ + @Override + public @Nullable + ViewManager createViewManager( + ReactApplicationContext reactContext, String viewManagerName) { + return null; + } + + @Override + public NativeModule getModule(String name, @Nonnull ReactApplicationContext reactContext) { + switch (name) { + case RNPermissionsModule.NAME: + return new RNPermissionsModule(reactContext); + default: + return null; + } + } + + @Override + public ReactModuleInfoProvider getReactModuleInfoProvider() { + try { + Class reactModuleInfoProviderClass = + Class.forName("com.zoontek.rnpermissions.RNPermissionsPackage$$ReactModuleInfoProvider"); + return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance(); + } catch (ClassNotFoundException e) { + // ReactModuleSpecProcessor does not run at build-time. Create this ReactModuleInfoProvider by + // hand. + return new ReactModuleInfoProvider() { + @Override + public Map getReactModuleInfos() { + final Map reactModuleInfoMap = new HashMap<>(); + + Class[] moduleList = + new Class[] { + RNPermissionsModule.class, + }; + + for (Class moduleClass : moduleList) { + ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class); + + reactModuleInfoMap.put( + reactModule.name(), + new ReactModuleInfo( + reactModule.name(), + moduleClass.getName(), + reactModule.canOverrideExistingModule(), + reactModule.needsEagerInit(), + reactModule.hasConstants(), + reactModule.isCxxModule(), + TurboModule.class.isAssignableFrom(moduleClass))); + } + + return reactModuleInfoMap; + } + }; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException( + "No ReactModuleInfoProvider for com.zoontek.rnpermissions.RNPermissionsPackage$$ReactModuleInfoProvider", e); + } + } @Override public List createNativeModules(ReactApplicationContext reactContext) { diff --git a/android/src/paper/java/com/zoontek/rnpermissions/NativePermissionsModuleSpec.java b/android/src/paper/java/com/zoontek/rnpermissions/NativePermissionsModuleSpec.java new file mode 100644 index 00000000..0e7e5776 --- /dev/null +++ b/android/src/paper/java/com/zoontek/rnpermissions/NativePermissionsModuleSpec.java @@ -0,0 +1,112 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateModuleJavaSpec.js + * + * @nolint + */ + +package com.zoontek.rnpermissions; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +public abstract class NativePermissionsModuleSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { + public NativePermissionsModuleSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod + @DoNotStrip + public abstract void openSettings(Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void checkNotifications(Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void checkPermission(String permission, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void shouldShowRequestPermissionRationale(String permission, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void requestPermission(String permission, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void checkMultiplePermissions(ReadableArray permissions, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void requestMultiplePermissions(ReadableArray permissions, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void check(String permission, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void checkLocationAccuracy(Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void request(String permission, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void requestLocationAccuracy(String purposeKey, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void requestNotifications(ReadableArray options, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void openLimitedPhotoLibraryPicker(Promise promise); + + protected abstract Map getTypedExportedConstants(); + + @Override + @DoNotStrip + public final @Nullable Map getConstants() { + Map constants = getTypedExportedConstants(); + if (ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD) { + Set obligatoryFlowConstants = new HashSet<>(); + Set optionalFlowConstants = new HashSet<>(Arrays.asList( + "available" + )); + Set undeclaredConstants = new HashSet<>(constants.keySet()); + undeclaredConstants.removeAll(obligatoryFlowConstants); + undeclaredConstants.removeAll(optionalFlowConstants); + if (!undeclaredConstants.isEmpty()) { + throw new IllegalStateException(String.format("Native Module Flow doesn't declare constants: %s", undeclaredConstants)); + } + undeclaredConstants = obligatoryFlowConstants; + undeclaredConstants.removeAll(constants.keySet()); + if (!undeclaredConstants.isEmpty()) { + throw new IllegalStateException(String.format("Native Module doesn't fill in constants: %s", undeclaredConstants)); + } + } + return constants; + } +} diff --git a/ios/AppTrackingTransparency/RNPermissionHandlerAppTrackingTransparency.h b/ios/AppTrackingTransparency/RNPermissionHandlerAppTrackingTransparency.h index 541e51e0..69f8999d 100644 --- a/ios/AppTrackingTransparency/RNPermissionHandlerAppTrackingTransparency.h +++ b/ios/AppTrackingTransparency/RNPermissionHandlerAppTrackingTransparency.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerAppTrackingTransparency : NSObject diff --git a/ios/BluetoothPeripheral/RNPermissionHandlerBluetoothPeripheral.h b/ios/BluetoothPeripheral/RNPermissionHandlerBluetoothPeripheral.h index 5136c00d..b1a2aa85 100644 --- a/ios/BluetoothPeripheral/RNPermissionHandlerBluetoothPeripheral.h +++ b/ios/BluetoothPeripheral/RNPermissionHandlerBluetoothPeripheral.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerBluetoothPeripheral : NSObject diff --git a/ios/Calendars/RNPermissionHandlerCalendars.h b/ios/Calendars/RNPermissionHandlerCalendars.h index f72e4499..b299d11a 100644 --- a/ios/Calendars/RNPermissionHandlerCalendars.h +++ b/ios/Calendars/RNPermissionHandlerCalendars.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerCalendars : NSObject diff --git a/ios/Camera/RNPermissionHandlerCamera.h b/ios/Camera/RNPermissionHandlerCamera.h index ad6c95ee..3fa1bbba 100644 --- a/ios/Camera/RNPermissionHandlerCamera.h +++ b/ios/Camera/RNPermissionHandlerCamera.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerCamera : NSObject diff --git a/ios/Contacts/RNPermissionHandlerContacts.h b/ios/Contacts/RNPermissionHandlerContacts.h index 02ea8efa..ae726719 100644 --- a/ios/Contacts/RNPermissionHandlerContacts.h +++ b/ios/Contacts/RNPermissionHandlerContacts.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerContacts : NSObject diff --git a/ios/FaceID/RNPermissionHandlerFaceID.h b/ios/FaceID/RNPermissionHandlerFaceID.h index 62106b94..aeaaad57 100644 --- a/ios/FaceID/RNPermissionHandlerFaceID.h +++ b/ios/FaceID/RNPermissionHandlerFaceID.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerFaceID : NSObject diff --git a/ios/FaceID/RNPermissionHandlerFaceID.m b/ios/FaceID/RNPermissionHandlerFaceID.m index 19984fcb..cf54d284 100644 --- a/ios/FaceID/RNPermissionHandlerFaceID.m +++ b/ios/FaceID/RNPermissionHandlerFaceID.m @@ -41,7 +41,7 @@ - (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve return resolve(RNPermissionStatusNotAvailable); } - if (![RNPermissions isFlaggedAsRequested:[[self class] handlerUniqueId]]) { + if (![RNPermissionsHelper isFlaggedAsRequested:[[self class] handlerUniqueId]]) { return resolve(RNPermissionStatusNotDetermined); } @@ -100,7 +100,7 @@ - (void)onApplicationDidBecomeActive:(__unused NSNotification *)notification { name:UIApplicationDidBecomeActiveNotification object:nil]; - [RNPermissions flagAsRequested:[[self class] handlerUniqueId]]; + [RNPermissionsHelper flagAsRequested:[[self class] handlerUniqueId]]; [self checkWithResolver:_resolve rejecter:_reject]; } diff --git a/ios/LocationAccuracy/RNPermissionHandlerLocationAccuracy.h b/ios/LocationAccuracy/RNPermissionHandlerLocationAccuracy.h index 508039c0..fe003a21 100644 --- a/ios/LocationAccuracy/RNPermissionHandlerLocationAccuracy.h +++ b/ios/LocationAccuracy/RNPermissionHandlerLocationAccuracy.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerLocationAccuracy : NSObject diff --git a/ios/LocationAlways/RNPermissionHandlerLocationAlways.h b/ios/LocationAlways/RNPermissionHandlerLocationAlways.h index 2a557717..3cdb0b56 100644 --- a/ios/LocationAlways/RNPermissionHandlerLocationAlways.h +++ b/ios/LocationAlways/RNPermissionHandlerLocationAlways.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerLocationAlways : NSObject diff --git a/ios/LocationWhenInUse/RNPermissionHandlerLocationWhenInUse.h b/ios/LocationWhenInUse/RNPermissionHandlerLocationWhenInUse.h index 98badee1..d31f5a56 100644 --- a/ios/LocationWhenInUse/RNPermissionHandlerLocationWhenInUse.h +++ b/ios/LocationWhenInUse/RNPermissionHandlerLocationWhenInUse.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerLocationWhenInUse : NSObject diff --git a/ios/MediaLibrary/RNPermissionHandlerMediaLibrary.h b/ios/MediaLibrary/RNPermissionHandlerMediaLibrary.h index 443204e0..498e9a6d 100644 --- a/ios/MediaLibrary/RNPermissionHandlerMediaLibrary.h +++ b/ios/MediaLibrary/RNPermissionHandlerMediaLibrary.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerMediaLibrary : NSObject diff --git a/ios/Microphone/RNPermissionHandlerMicrophone.h b/ios/Microphone/RNPermissionHandlerMicrophone.h index afa38adf..80e9783b 100644 --- a/ios/Microphone/RNPermissionHandlerMicrophone.h +++ b/ios/Microphone/RNPermissionHandlerMicrophone.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerMicrophone : NSObject diff --git a/ios/Motion/RNPermissionHandlerMotion.h b/ios/Motion/RNPermissionHandlerMotion.h index ce4dbeed..65f3813a 100644 --- a/ios/Motion/RNPermissionHandlerMotion.h +++ b/ios/Motion/RNPermissionHandlerMotion.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerMotion : NSObject diff --git a/ios/Motion/RNPermissionHandlerMotion.m b/ios/Motion/RNPermissionHandlerMotion.m index 5de95eaa..fd120848 100644 --- a/ios/Motion/RNPermissionHandlerMotion.m +++ b/ios/Motion/RNPermissionHandlerMotion.m @@ -38,7 +38,7 @@ - (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve } } - if (![RNPermissions isFlaggedAsRequested:[[self class] handlerUniqueId]]) { + if (![RNPermissionsHelper isFlaggedAsRequested:[[self class] handlerUniqueId]]) { return resolve(RNPermissionStatusNotDetermined); } @@ -65,7 +65,7 @@ - (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve return reject(error); } - [RNPermissions flagAsRequested:[[self class] handlerUniqueId]]; + [RNPermissionsHelper flagAsRequested:[[self class] handlerUniqueId]]; if (error != nil) { return resolve(RNPermissionStatusDenied); diff --git a/ios/Notifications/RNPermissionHandlerNotifications.h b/ios/Notifications/RNPermissionHandlerNotifications.h index 6601123a..5a5ee0e8 100644 --- a/ios/Notifications/RNPermissionHandlerNotifications.h +++ b/ios/Notifications/RNPermissionHandlerNotifications.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerNotifications : NSObject diff --git a/ios/PhotoLibrary/RNPermissionHandlerPhotoLibrary.h b/ios/PhotoLibrary/RNPermissionHandlerPhotoLibrary.h index 9778fc56..1155f2b6 100644 --- a/ios/PhotoLibrary/RNPermissionHandlerPhotoLibrary.h +++ b/ios/PhotoLibrary/RNPermissionHandlerPhotoLibrary.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerPhotoLibrary : NSObject diff --git a/ios/PhotoLibraryAddOnly/RNPermissionHandlerPhotoLibraryAddOnly.h b/ios/PhotoLibraryAddOnly/RNPermissionHandlerPhotoLibraryAddOnly.h index b598882d..7440a990 100644 --- a/ios/PhotoLibraryAddOnly/RNPermissionHandlerPhotoLibraryAddOnly.h +++ b/ios/PhotoLibraryAddOnly/RNPermissionHandlerPhotoLibraryAddOnly.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerPhotoLibraryAddOnly : NSObject diff --git a/ios/RNPermissions.h b/ios/RNPermissionsHelper.h similarity index 94% rename from ios/RNPermissions.h rename to ios/RNPermissionsHelper.h index a6ff0241..f45c3dcf 100644 --- a/ios/RNPermissions.h +++ b/ios/RNPermissionsHelper.h @@ -1,5 +1,4 @@ -#import -#import +#import typedef NS_ENUM(NSInteger, RNPermission) { RNPermissionUnknown = 0, @@ -56,9 +55,6 @@ typedef NS_ENUM(NSInteger, RNPermission) { #endif }; -@interface RCTConvert (RNPermission) -@end - typedef enum { RNPermissionStatusNotAvailable = 0, RNPermissionStatusNotDetermined = 1, @@ -84,7 +80,8 @@ typedef enum { @end -@interface RNPermissions : NSObject + +@interface RNPermissionsHelper : NSObject + (bool)isFlaggedAsRequested:(NSString * _Nonnull)handlerId; diff --git a/ios/RNPermissionsHelper.m b/ios/RNPermissionsHelper.m new file mode 100644 index 00000000..e786321c --- /dev/null +++ b/ios/RNPermissionsHelper.m @@ -0,0 +1,29 @@ +#import "RNPermissionsHelper.h" + +@implementation RNPermissionsHelper + +static NSString* SETTING_KEY = @"@RNPermissions:Requested"; + + + ++ (bool)isFlaggedAsRequested:(NSString * _Nonnull)handlerId { + NSArray *requested = [[NSUserDefaults standardUserDefaults] arrayForKey:SETTING_KEY]; + return requested == nil ? false : [requested containsObject:handlerId]; +} + ++ (void)flagAsRequested:(NSString * _Nonnull)handlerId { + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSMutableArray *requested = [[userDefaults arrayForKey:SETTING_KEY] mutableCopy]; + + if (requested == nil) { + requested = [NSMutableArray new]; + } + + if (![requested containsObject:handlerId]) { + [requested addObject:handlerId]; + [userDefaults setObject:requested forKey:SETTING_KEY]; + [userDefaults synchronize]; + } +} + +@end diff --git a/ios/RNPermissionsModule.h b/ios/RNPermissionsModule.h new file mode 100644 index 00000000..4abc067e --- /dev/null +++ b/ios/RNPermissionsModule.h @@ -0,0 +1,19 @@ +#ifdef RCT_NEW_ARCH_ENABLED +#import +#else +#import +#endif +#import +#import "RNPermissionsHelper.h" + +@interface RCTConvert (RNPermission) +@end + +@interface RNPermissionsModule : NSObject +#ifdef RCT_NEW_ARCH_ENABLED + +#else + +#endif + +@end diff --git a/ios/RNPermissions.m b/ios/RNPermissionsModule.mm similarity index 81% rename from ios/RNPermissions.m rename to ios/RNPermissionsModule.mm index 4e5bf483..252de2e1 100644 --- a/ios/RNPermissions.m +++ b/ios/RNPermissionsModule.mm @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsModule.h" #import #if __has_include("RNPermissionHandlerBluetoothPeripheral.h") @@ -59,8 +59,6 @@ #import "RNPermissionHandlerLocationAccuracy.h" #endif -static NSString* SETTING_KEY = @"@RNPermissions:Requested"; - @implementation RCTConvert(RNPermission) RCT_ENUM_CONVERTER(RNPermission, (@{ @@ -119,13 +117,13 @@ @implementation RCTConvert(RNPermission) @end -@interface RNPermissions() +@interface RNPermissionsModule() @property (nonatomic, strong) NSMutableDictionary> *_Nonnull handlers; @end -@implementation RNPermissions +@implementation RNPermissionsModule RCT_EXPORT_MODULE(); @@ -354,29 +352,8 @@ - (void)unlockHandler:(NSString * _Nonnull)lockId { } } -+ (bool)isFlaggedAsRequested:(NSString * _Nonnull)handlerId { - NSArray *requested = [[NSUserDefaults standardUserDefaults] arrayForKey:SETTING_KEY]; - return requested == nil ? false : [requested containsObject:handlerId]; -} - -+ (void)flagAsRequested:(NSString * _Nonnull)handlerId { - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - NSMutableArray *requested = [[userDefaults arrayForKey:SETTING_KEY] mutableCopy]; - - if (requested == nil) { - requested = [NSMutableArray new]; - } - - if (![requested containsObject:handlerId]) { - [requested addObject:handlerId]; - [userDefaults setObject:requested forKey:SETTING_KEY]; - [userDefaults synchronize]; - } -} - -RCT_REMAP_METHOD(openSettings, - openSettingsWithResolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(openSettings:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { UIApplication *sharedApplication = [UIApplication sharedApplication]; NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; @@ -389,11 +366,22 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { }]; } -RCT_REMAP_METHOD(check, - checkWithPermission:(RNPermission)permission - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { - id handler = [self handlerForPermission:permission]; +RCT_EXPORT_METHOD(check: +#ifdef RCT_NEW_ARCH_ENABLED + (NSString *) +#else + (RNPermission) +#endif + permission + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + id handler = [self handlerForPermission: +#ifdef RCT_NEW_ARCH_ENABLED + [RCTConvert RNPermission:permission] +#else + permission +#endif + ]; NSString *lockId = [self lockHandler:handler]; [handler checkWithResolver:^(RNPermissionStatus status) { @@ -405,11 +393,22 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { }]; } -RCT_REMAP_METHOD(request, - requestWithPermission:(RNPermission)permission - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { - id handler = [self handlerForPermission:permission]; +RCT_EXPORT_METHOD(request: +#ifdef RCT_NEW_ARCH_ENABLED + (NSString *) +#else + (RNPermission) +#endif + permission + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + id handler = [self handlerForPermission: + #ifdef RCT_NEW_ARCH_ENABLED + [RCTConvert RNPermission:permission] + #else + permission + #endif + ]; NSString *lockId = [self lockHandler:handler]; [handler requestWithResolver:^(RNPermissionStatus status) { @@ -421,9 +420,8 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { }]; } -RCT_REMAP_METHOD(checkNotifications, - checkNotificationsWithResolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(checkNotifications:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { #if __has_include("RNPermissionHandlerNotifications.h") RNPermissionHandlerNotifications *handler = [RNPermissionHandlerNotifications new]; NSString *lockId = [self lockHandler:(id)handler]; @@ -440,10 +438,9 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { #endif } -RCT_REMAP_METHOD(requestNotifications, - requestNotificationsWithOptions:(NSArray * _Nonnull)options - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(requestNotifications:(NSArray * _Nonnull)options + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { #if __has_include("RNPermissionHandlerNotifications.h") RNPermissionHandlerNotifications *handler = [RNPermissionHandlerNotifications new]; NSString *lockId = [self lockHandler:(id)handler]; @@ -460,9 +457,8 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { #endif } -RCT_REMAP_METHOD(openLimitedPhotoLibraryPicker, - openLimitedPhotoLibraryPickerWithResolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(openLimitedPhotoLibraryPicker:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { #if __has_include("RNPermissionHandlerPhotoLibrary.h") RNPermissionHandlerPhotoLibrary *handler = [RNPermissionHandlerPhotoLibrary new]; [handler openLimitedPhotoLibraryPickerWithResolver:resolve rejecter:reject]; @@ -471,9 +467,8 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { #endif } -RCT_REMAP_METHOD(checkLocationAccuracy, - checkLocationAccuracyWithResolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(checkLocationAccuracy:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { #if __has_include("RNPermissionHandlerLocationAccuracy.h") [self checkUsageDescriptionKeys:[RNPermissionHandlerLocationAccuracy usageDescriptionKeys]]; @@ -484,10 +479,9 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { #endif } -RCT_REMAP_METHOD(requestLocationAccuracy, - requestLocationAccuracyWithPurposeKey:(NSString * _Nonnull)purposeKey - resolver:(RCTPromiseResolveBlock)resolve - rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(requestLocationAccuracy:(NSString * _Nonnull)purposeKey + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { #if __has_include("RNPermissionHandlerLocationAccuracy.h") [self checkUsageDescriptionKeys:[RNPermissionHandlerLocationAccuracy usageDescriptionKeys]]; @@ -498,4 +492,38 @@ + (void)flagAsRequested:(NSString * _Nonnull)handlerId { #endif } +- (void)checkMultiplePermissions:(NSArray *)permissions resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + reject(@"RNPermissions:checkMultiplePermissions", @"checkMultiplePermissions is not supported on iOS", nil); +} + +- (void)checkPermission:(NSString *)permission resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + reject(@"RNPermissions:checkPermission", @"checkPermission is not supported on iOS", nil); +} + +- (void)requestMultiplePermissions:(NSArray *)permissions resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + reject(@"RNPermissions:requestMultiplePermissions", @"requestMultiplePermissions is not supported on iOS", nil); +} + +- (void)requestPermission:(NSString *)permission resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + reject(@"RNPermissions:requestPermission", @"requestPermission is not supported on iOS", nil); +} + +- (void)shouldShowRequestPermissionRationale:(NSString *)permission resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + reject(@"RNPermissions:shouldShowRequestPermissionRationale", @"shouldShowRequestPermissionRationale is not supported on iOS", nil); +} + + +#if RCT_NEW_ARCH_ENABLED + +- (facebook::react::ModuleConstants)getConstants { + return [self constantsToExport]; +} + +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return std::make_shared(params); +} +#endif + @end diff --git a/ios/Reminders/RNPermissionHandlerReminders.h b/ios/Reminders/RNPermissionHandlerReminders.h index 35da1c5a..a168043c 100644 --- a/ios/Reminders/RNPermissionHandlerReminders.h +++ b/ios/Reminders/RNPermissionHandlerReminders.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerReminders : NSObject diff --git a/ios/Siri/RNPermissionHandlerSiri.h b/ios/Siri/RNPermissionHandlerSiri.h index 5adf78cb..3d912985 100644 --- a/ios/Siri/RNPermissionHandlerSiri.h +++ b/ios/Siri/RNPermissionHandlerSiri.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerSiri : NSObject diff --git a/ios/Siri/RNPermissionHandlerSiri.m b/ios/Siri/RNPermissionHandlerSiri.mm similarity index 100% rename from ios/Siri/RNPermissionHandlerSiri.m rename to ios/Siri/RNPermissionHandlerSiri.mm diff --git a/ios/SpeechRecognition/RNPermissionHandlerSpeechRecognition.h b/ios/SpeechRecognition/RNPermissionHandlerSpeechRecognition.h index 9e9a304b..8a390535 100644 --- a/ios/SpeechRecognition/RNPermissionHandlerSpeechRecognition.h +++ b/ios/SpeechRecognition/RNPermissionHandlerSpeechRecognition.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerSpeechRecognition : NSObject diff --git a/ios/StoreKit/RNPermissionHandlerStoreKit.h b/ios/StoreKit/RNPermissionHandlerStoreKit.h index d30b534e..2af97eb7 100644 --- a/ios/StoreKit/RNPermissionHandlerStoreKit.h +++ b/ios/StoreKit/RNPermissionHandlerStoreKit.h @@ -1,4 +1,4 @@ -#import "RNPermissions.h" +#import "RNPermissionsHelper.h" @interface RNPermissionHandlerStoreKit : NSObject diff --git a/package.json b/package.json index 524e3446..7c2a47e6 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "types": "dist/typescript/index.d.ts", "main": "dist/commonjs/index.js", "module": "dist/module/index.js", + "react-native": "src/index.ts", "files": [ "/android", "!/android/build", @@ -78,5 +79,13 @@ "react-native": "^0.71.3", "react-native-builder-bob": "^0.20.3", "typescript": "^4.9.5" + }, + "codegenConfig": { + "name": "rnpermissions", + "type": "modules", + "jsSrcsDir": "./src", + "android": { + "javaPackageName": "com.zoontek.rnpermissions" + } } } diff --git a/src/NativePermissionsModule.ts b/src/NativePermissionsModule.ts new file mode 100644 index 00000000..66af2211 --- /dev/null +++ b/src/NativePermissionsModule.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/ban-types */ +// we use Object type because methods on the native side use NSDictionary and ReadableMap +// and we want to stay compatible with those +import {TurboModule, TurboModuleRegistry} from 'react-native'; + +type NotificationsResponse = { + status: Object; + settings: Object; +}; + +export interface Spec extends TurboModule { + openSettings(): Promise; + checkNotifications(): Promise; + + // Android only part + checkPermission(permission: string): Promise; + shouldShowRequestPermissionRationale(permission: string): Promise; + requestPermission(permission: string): Promise; + checkMultiplePermissions(permissions: string[]): Promise; + requestMultiplePermissions(permissions: string[]): Promise; + + // iOS only part + check(permission: string): Promise; // TODO: should be number prolly + checkLocationAccuracy(): Promise; + request(permission: string): Promise; // TODO: should be number prolly + requestLocationAccuracy(purposeKey: string): Promise; + requestNotifications(options: string[]): Promise; + openLimitedPhotoLibraryPicker(): Promise; + getConstants(): {available?: string[]}; +} + +export default TurboModuleRegistry.getEnforcing('RNPermissionsModule'); diff --git a/src/index.ts b/src/index.ts index e85878cc..9409a7b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,7 @@ -import {NativeModules} from 'react-native'; import {methods} from './methods'; import {PERMISSIONS} from './permissions'; import {RESULTS} from './results'; -if (NativeModules.RNPermissions == null) { - throw new Error(`react-native-permissions: NativeModule.RNPermissions is null. To fix this issue try these steps: -• 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. -• 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/zoontek/react-native-permissions/blob/master/mock.js -If none of these fix the issue, please open an issue on the Github repository: https://github.com/zoontek/react-native-permissions`); -} - export {PERMISSIONS} from './permissions'; export {RESULTS} from './results'; export * from './types'; diff --git a/src/methods.android.ts b/src/methods.android.ts index 7c629efd..b1f82c43 100644 --- a/src/methods.android.ts +++ b/src/methods.android.ts @@ -1,5 +1,6 @@ -import {Alert, AlertButton, NativeModules} from 'react-native'; +import {Alert, AlertButton} from 'react-native'; import type {Contract} from './contract'; +import NativeModule from './NativePermissionsModule'; import type {NotificationsResponse, Permission, PermissionStatus, Rationale} from './types'; import { checkLocationAccuracy, @@ -7,30 +8,14 @@ import { requestLocationAccuracy, } from './unsupportedPlatformMethods'; import {platformVersion, uniq} from './utils'; - const TIRAMISU_VERSION_CODE = 33; -const NativeModule: { - checkPermission: (permission: Permission) => Promise; - requestPermission: (permission: Permission) => Promise; - checkNotifications: () => Promise; - openSettings: () => Promise; - shouldShowRequestPermissionRationale: (permission: Permission) => Promise; - - checkMultiplePermissions: ( - permissions: Permission[], - ) => Promise>; - requestMultiplePermissions: ( - permissions: Permission[], - ) => Promise>; -} = NativeModules.RNPermissions; - async function openSettings(): Promise { await NativeModule.openSettings(); } function check(permission: Permission): Promise { - return NativeModule.checkPermission(permission); + return NativeModule.checkPermission(permission) as Promise; } async function request(permission: Permission, rationale?: Rationale): Promise { @@ -44,14 +29,16 @@ async function request(permission: Permission, rationale?: Rationale): Promise

resolve(NativeModule.checkPermission(permission)); + const onPress = () => + resolve(NativeModule.checkPermission(permission) as Promise); buttonNeutral && buttons.push({text: buttonNeutral, onPress}); buttons.push({text: buttonNegative, onPress}); } buttons.push({ text: buttonPositive, - onPress: () => resolve(NativeModule.requestPermission(permission)), + onPress: () => + resolve(NativeModule.requestPermission(permission) as Promise), }); Alert.alert(title, message, buttons, {cancelable: false}); @@ -59,12 +46,12 @@ async function request(permission: Permission, rationale?: Rationale): Promise

; } async function checkNotifications(): Promise { if (platformVersion < TIRAMISU_VERSION_CODE) { - return NativeModule.checkNotifications(); + return NativeModule.checkNotifications() as Promise; } const status = await check('android.permission.POST_NOTIFICATIONS'); @@ -73,7 +60,7 @@ async function checkNotifications(): Promise { async function requestNotifications(): Promise { if (platformVersion < TIRAMISU_VERSION_CODE) { - return NativeModule.checkNotifications(); + return NativeModule.checkNotifications() as Promise; } const status = await request('android.permission.POST_NOTIFICATIONS'); @@ -84,14 +71,18 @@ function checkMultiple

( permissions: P, ): Promise> { const dedup = uniq(permissions); - return NativeModule.checkMultiplePermissions(dedup); + return NativeModule.checkMultiplePermissions(dedup) as Promise< + Record + >; } function requestMultiple

( permissions: P, ): Promise> { const dedup = uniq(permissions); - return NativeModule.requestMultiplePermissions(dedup); + return NativeModule.requestMultiplePermissions(dedup) as Promise< + Record + >; } export const methods: Contract = { diff --git a/src/methods.ios.ts b/src/methods.ios.ts index e3b57ea6..931837ca 100644 --- a/src/methods.ios.ts +++ b/src/methods.ios.ts @@ -1,5 +1,5 @@ -import {NativeModules} from 'react-native'; import type {Contract} from './contract'; +import NativeModule from './NativePermissionsModule'; import {RESULTS} from './results'; import type { LocationAccuracy, @@ -11,18 +11,14 @@ import type { } from './types'; import {uniq} from './utils'; -const NativeModule: { - available: Permission[]; +let available: string[] | undefined = undefined; - check: (permission: Permission) => Promise; - checkLocationAccuracy: () => Promise; - checkNotifications: () => Promise; - openLimitedPhotoLibraryPicker: () => Promise; - openSettings: () => Promise; - request: (permission: Permission, options?: object) => Promise; - requestLocationAccuracy: (purposeKey: string) => Promise; - requestNotifications: (options: NotificationOption[]) => Promise; -} = NativeModules.RNPermissions; +function getAvailable() { + if (available === undefined) { + available = NativeModule.getConstants().available; + } + return available; +} async function openLimitedPhotoLibraryPicker(): Promise { await NativeModule.openLimitedPhotoLibraryPicker(); @@ -33,33 +29,33 @@ async function openSettings(): Promise { } async function check(permission: Permission): Promise { - return NativeModule.available.includes(permission) - ? NativeModule.check(permission) + return getAvailable()?.includes(permission) + ? (NativeModule.check(permission) as Promise) : RESULTS.UNAVAILABLE; } async function request(permission: Permission): Promise { - return NativeModule.available.includes(permission) - ? NativeModule.request(permission) + return getAvailable()?.includes(permission) + ? (NativeModule.request(permission) as Promise) : RESULTS.UNAVAILABLE; } function checkLocationAccuracy(): Promise { - return NativeModule.checkLocationAccuracy(); + return NativeModule.checkLocationAccuracy() as Promise; } function requestLocationAccuracy(options: LocationAccuracyOptions): Promise { - return NativeModule.requestLocationAccuracy(options.purposeKey); + return NativeModule.requestLocationAccuracy(options.purposeKey) as Promise; } export function checkNotifications(): Promise { - return NativeModule.checkNotifications(); + return NativeModule.checkNotifications() as Promise; } export function requestNotifications( options: NotificationOption[], ): Promise { - return NativeModule.requestNotifications(options); + return NativeModule.requestNotifications(options) as Promise; } async function checkMultiple

(