Last active
June 22, 2023 23:37
-
-
Save shlomoweb1/8ff6bcc9121c31634cca4887d3c215de to your computer and use it in GitHub Desktop.
Java Android Permission with fallback up to android Q, React Native
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Top-level build file where you can add configuration options common to all sub-projects/modules. | |
| // https://apilevels.com/ | |
| buildscript { | |
| ext { | |
| buildToolsVersion = "33.0.0" | |
| minSdkVersion = 26 | |
| compileSdkVersion = 33 | |
| targetSdkVersion = 26 | |
| // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. | |
| ndkVersion = "23.1.7779620" | |
| } | |
| repositories { | |
| google() | |
| mavenCentral() | |
| } | |
| dependencies { | |
| classpath("com.android.tools.build:gradle:7.3.1") | |
| classpath("com.facebook.react:react-native-gradle-plugin") | |
| classpath("com.google.gms:google-services:4.3.15") | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.myApp; | |
| import android.app.Application; | |
| import com.facebook.react.PackageList; | |
| import com.facebook.react.ReactApplication; | |
| import com.facebook.react.ReactNativeHost; | |
| import com.facebook.react.ReactPackage; | |
| import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; | |
| import com.facebook.react.defaults.DefaultReactNativeHost; | |
| import com.facebook.soloader.SoLoader; | |
| import java.util.List; | |
| import com.myApp.permissions.PermissionsModulePackage; // Import the PermissionsModulePackage | |
| public class MainApplication extends Application implements ReactApplication { | |
| private final ReactNativeHost mReactNativeHost = | |
| new DefaultReactNativeHost(this) { | |
| @Override | |
| public boolean getUseDeveloperSupport() { | |
| return BuildConfig.DEBUG; | |
| } | |
| @Override | |
| protected List<ReactPackage> getPackages() { | |
| @SuppressWarnings("UnnecessaryLocalVariable") | |
| List<ReactPackage> packages = new PackageList(this).getPackages(); | |
| // Add PermissionsModulePackage to the packages list | |
| packages.add(new PermissionsModulePackage()); | |
| return packages; | |
| } | |
| @Override | |
| protected String getJSMainModuleName() { | |
| return "index"; | |
| } | |
| @Override | |
| protected boolean isNewArchEnabled() { | |
| return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; | |
| } | |
| @Override | |
| protected Boolean isHermesEnabled() { | |
| return BuildConfig.IS_HERMES_ENABLED; | |
| } | |
| }; | |
| @Override | |
| public ReactNativeHost getReactNativeHost() { | |
| return mReactNativeHost; | |
| } | |
| @Override | |
| public void onCreate() { | |
| super.onCreate(); | |
| SoLoader.init(this, /* native exopackage */ false); | |
| if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { | |
| // If you opted-in for the New Architecture, we load the native entry point for this app. | |
| DefaultNewArchitectureEntryPoint.load(); | |
| } | |
| ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.myApp.permissions; | |
| import android.Manifest; | |
| import android.content.pm.PackageManager; | |
| import android.os.Build; | |
| import androidx.annotation.NonNull; | |
| import androidx.core.app.ActivityCompat; | |
| import androidx.core.content.ContextCompat; | |
| import com.facebook.react.bridge.Arguments; | |
| import com.facebook.react.bridge.Callback; | |
| 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.WritableArray; | |
| import com.facebook.react.bridge.WritableMap; | |
| import com.facebook.react.bridge.WritableNativeArray; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Set; | |
| public class PermissionsModule extends ReactContextBaseJavaModule { | |
| private static final String MODULE_NAME = "myAppPermission"; | |
| private static final int PERMISSION_REQUEST_CODE = 1; | |
| private Callback permissionCallback; | |
| public PermissionsModule(ReactApplicationContext reactContext) { | |
| super(reactContext); | |
| } | |
| @NonNull | |
| @Override | |
| public String getName() { | |
| return MODULE_NAME; | |
| } | |
| // Method to request permissions | |
| @ReactMethod | |
| public void requestPermissions(ReadableArray permissions, Callback callback) { | |
| permissionCallback = callback; | |
| List<String> pendingPermissions = filterPermissions(convertReadableArrayToStringArray(permissions)); | |
| String[] pendingPermissionsArray = pendingPermissions.toArray(new String[0]); | |
| if (pendingPermissionsArray.length > 0) { | |
| // Request permissions | |
| ActivityCompat.requestPermissions(getCurrentActivity(), pendingPermissionsArray, PERMISSION_REQUEST_CODE); | |
| } else { | |
| // No permissions to request, return results | |
| WritableArray grantedPermissions = new WritableNativeArray(); | |
| WritableArray deniedPermissions = new WritableNativeArray(); | |
| for (String permission : convertReadableArrayToStringArray(permissions)) { | |
| if (ContextCompat.checkSelfPermission(getReactApplicationContext(), permission) == PackageManager.PERMISSION_GRANTED) { | |
| // Permission granted | |
| grantedPermissions.pushString(permission); | |
| } else { | |
| // Permission denied | |
| deniedPermissions.pushString(permission); | |
| } | |
| } | |
| permissionCallback.invoke(grantedPermissions, deniedPermissions); | |
| permissionCallback = null; | |
| } | |
| } | |
| // Helper method to convert ReadableArray to String[] | |
| private String[] convertReadableArrayToStringArray(ReadableArray readableArray) { | |
| String[] stringArray = new String[readableArray.size()]; | |
| for (int i = 0; i < readableArray.size(); i++) { | |
| stringArray[i] = readableArray.getString(i); | |
| } | |
| return stringArray; | |
| } | |
| // Method to check permissions without requesting | |
| @ReactMethod | |
| public void checkPermissions(ReadableArray permissions, Callback callback) { | |
| List<String> filteredPermissions = filterPermissionsByAPIVersion(convertReadableArrayToStringArray(permissions)); | |
| WritableArray grantedPermissions = new WritableNativeArray(); | |
| WritableArray deniedPermissions = new WritableNativeArray(); | |
| for (String permission : filteredPermissions) { | |
| int permissionStatus = ContextCompat.checkSelfPermission(getReactApplicationContext(), permission); | |
| if (permissionStatus == PackageManager.PERMISSION_GRANTED) { | |
| // Permission granted | |
| grantedPermissions.pushString(permission); | |
| } else { | |
| // Permission denied or not granted | |
| WritableMap deniedPermission = Arguments.createMap(); | |
| deniedPermission.putString("permission", permission); | |
| deniedPermission.putString("reason", String.valueOf(permissionStatus)); | |
| deniedPermissions.pushMap(deniedPermission); | |
| } | |
| } | |
| callback.invoke(grantedPermissions, deniedPermissions); | |
| } | |
| private List<String> filterPermissionsByAPIVersion(String[] permissions) { | |
| List<String> filteredPermissions = new ArrayList<>(); | |
| for (String permission : permissions) { | |
| // Apply your filtering logic based on the Android API version | |
| if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)) { | |
| if(isAndroidQOrAbove()){ | |
| filteredPermissions.add(permission); | |
| } | |
| // Skip adding the permission if not on Android Q or above | |
| continue; | |
| } else if (permission.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) { | |
| if(isAndroidROrAbove()){ | |
| filteredPermissions.add(permission); | |
| } | |
| // Skip adding the permission if not on Android R or above | |
| continue; | |
| } else if (permission.equals(Manifest.permission.POST_NOTIFICATIONS)) { | |
| if(isAndroidTiramisuOrAbove()){ | |
| filteredPermissions.add(permission); | |
| } | |
| // Skip adding the permission if not on Android Tiramisu or above | |
| continue; | |
| } else { | |
| // Permission doesn't match any filtering condition, include it in the final result | |
| filteredPermissions.add(permission); | |
| } | |
| } | |
| return filteredPermissions; | |
| } | |
| // Filter out permissions that are already granted or API level to low | |
| private List<String> filterPermissions(String[] permissions) { | |
| Set<String> pendingPermissions = new HashSet<>(); | |
| for (String permission : permissions) { | |
| boolean hasPermission = ContextCompat.checkSelfPermission(getReactApplicationContext(), permission) == PackageManager.PERMISSION_GRANTED; | |
| if (!hasPermission) { | |
| pendingPermissions.add(permission); | |
| } else { | |
| // Remove if already granted | |
| pendingPermissions.remove(permission); | |
| } | |
| } | |
| // Apply additional filtering based on Android API version [remove uneeded perrmision per API version] | |
| List<String> filteredPermissions = filterPermissionsByAPIVersion(pendingPermissions.toArray(new String[0])); | |
| return filteredPermissions; | |
| } | |
| // Handle the result of permission requests | |
| public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { | |
| if (requestCode == PERMISSION_REQUEST_CODE) { | |
| WritableArray grantedPermissions = new WritableNativeArray(); | |
| WritableArray deniedPermissions = new WritableNativeArray(); | |
| for (int i = 0; i < permissions.length; i++) { | |
| String permission = permissions[i]; | |
| int grantResult = grantResults[i]; | |
| if (grantResult == PackageManager.PERMISSION_GRANTED) { | |
| // Permission granted | |
| grantedPermissions.pushString(permission); | |
| } else { | |
| // Permission denied | |
| WritableMap deniedPermission = Arguments.createMap(); | |
| deniedPermission.putString("permission", permission); | |
| deniedPermission.putString("reason", String.valueOf(grantResult)); | |
| deniedPermissions.pushMap(deniedPermission); | |
| } | |
| } | |
| // Invoke the permission callback with the results | |
| permissionCallback.invoke(grantedPermissions, deniedPermissions); | |
| permissionCallback = null; | |
| } | |
| } | |
| // Check API level 29 (Android 10) or above | |
| private boolean isAndroidQOrAbove() { | |
| return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; | |
| } | |
| // Check API level 30 (Android 11) or above | |
| private boolean isAndroidROrAbove() { | |
| return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R; | |
| } | |
| // Check API level 33 (Android 13) or above | |
| private boolean isAndroidTiramisuOrAbove(){ | |
| return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.myApp.permissions; | |
| import com.facebook.react.bridge.NativeModule; | |
| import com.facebook.react.bridge.ReactApplicationContext; | |
| import com.facebook.react.ReactPackage; | |
| import com.facebook.react.uimanager.ViewManager; | |
| import java.util.Arrays; | |
| import java.util.Collections; | |
| import java.util.List; | |
| public class PermissionsModulePackage implements ReactPackage { | |
| @Override | |
| public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { | |
| return Arrays.asList(new NativeModule[]{ | |
| new PermissionsModule(reactContext) | |
| }); | |
| } | |
| @Override | |
| public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { | |
| return Collections.emptyList(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment