Commit 49acc864 by Jan-Derk

Added MANAGE_EXTERNAL_STORAGE permission

Registered Listeners on plugin level
parent 14368466
## 7.0.0
This release contains the following **breaking changes**:
* Updated compile SDK version to 30 in the build.gradle for handling the MANAGE_EXTERNAL_STORAGE permission;
* Added the MANAGE_EXTERNAL_STORAGE permission for Android R and up;
* Registered listeners on the plugin level to prevent memory leaks or unwanted behaviour.
## 6.1.3
* Implement equality operator on the `Permission` class;
......
......@@ -28,7 +28,7 @@ project.getTasks().withType(JavaCompile){
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
compileSdkVersion 30
defaultConfig {
minSdkVersion 16
......
......@@ -6,12 +6,9 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import com.baseflow.permissionhandler.PermissionManager.ActivityRegistry;
import com.baseflow.permissionhandler.PermissionManager.PermissionRegistry;
import java.util.List;
......@@ -35,26 +32,10 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
@Nullable
private Activity activity;
@Nullable
private ActivityRegistry activityRegistry;
@Nullable
private PermissionRegistry permissionRegistry;
public void setActivity(@Nullable Activity activity) {
this.activity = activity;
}
public void setActivityRegistry(
@Nullable ActivityRegistry activityRegistry) {
this.activityRegistry = activityRegistry;
}
public void setPermissionRegistry(
@Nullable PermissionRegistry permissionRegistry) {
this.permissionRegistry = permissionRegistry;
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
{
......@@ -91,8 +72,6 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
permissionManager.requestPermissions(
permissions,
activity,
activityRegistry,
permissionRegistry,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
......
......@@ -8,7 +8,8 @@ import java.lang.annotation.RetentionPolicy;
final class PermissionConstants {
static final String LOG_TAG = "permissions_handler";
static final int PERMISSION_CODE = 24;
static final int PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS = 5672353;
static final int PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS = 209;
static final int PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE = 210;
//PERMISSION_GROUP
static final int PERMISSION_GROUP_CALENDAR = 0;
......@@ -33,6 +34,7 @@ final class PermissionConstants {
static final int PERMISSION_GROUP_ACTIVITY_RECOGNITION = 19;
static final int PERMISSION_GROUP_UNKNOWN = 20;
static final int PERMISSION_GROUP_BLUETOOTH = 21;
static final int PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE = 22;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
......@@ -57,6 +59,7 @@ final class PermissionConstants {
PERMISSION_GROUP_ACTIVITY_RECOGNITION,
PERMISSION_GROUP_UNKNOWN,
PERMISSION_GROUP_BLUETOOTH,
PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE
})
@interface PermissionGroup {
}
......@@ -74,7 +77,7 @@ final class PermissionConstants {
PERMISSION_STATUS_GRANTED,
PERMISSION_STATUS_RESTRICTED,
PERMISSION_STATUS_LIMITED,
PERMISSION_STATUS_NEVER_ASK_AGAIN,
PERMISSION_STATUS_NEVER_ASK_AGAIN
})
@interface PermissionStatus {
}
......
......@@ -4,8 +4,6 @@ import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.baseflow.permissionhandler.PermissionManager.ActivityRegistry;
import com.baseflow.permissionhandler.PermissionManager.PermissionRegistry;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
......@@ -23,11 +21,21 @@ import io.flutter.plugin.common.MethodChannel;
*/
public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAware {
private final PermissionManager permissionManager;
private MethodChannel methodChannel;
@SuppressWarnings("deprecation")
@Nullable private io.flutter.plugin.common.PluginRegistry.Registrar pluginRegistrar;
@Nullable private ActivityPluginBinding pluginBinding;
@Nullable
private MethodCallHandlerImpl methodCallHandler;
public PermissionHandlerPlugin() {
this.permissionManager = new PermissionManager();
}
/**
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
* package.
......@@ -38,13 +46,15 @@ public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAwa
@SuppressWarnings("deprecation")
public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
final PermissionHandlerPlugin plugin = new PermissionHandlerPlugin();
plugin.pluginRegistrar = registrar;
plugin.registerListeners();
plugin.startListening(registrar.context(), registrar.messenger());
if (registrar.activeContext() instanceof Activity) {
plugin.startListeningToActivity(
registrar.activity(),
registrar::addActivityResultListener,
registrar::addRequestPermissionsResultListener
registrar.activity()
);
}
}
......@@ -65,10 +75,11 @@ public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAwa
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
startListeningToActivity(
binding.getActivity(),
binding::addActivityResultListener,
binding::addRequestPermissionsResultListener
binding.getActivity()
);
this.pluginBinding = binding;
registerListeners();
}
@Override
......@@ -79,6 +90,8 @@ public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAwa
@Override
public void onDetachedFromActivity() {
stopListeningToActivity();
deregisterListeners();
}
@Override
......@@ -95,7 +108,7 @@ public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAwa
methodCallHandler = new MethodCallHandlerImpl(
applicationContext,
new AppSettingsManager(),
new PermissionManager(),
this.permissionManager,
new ServiceManager()
);
......@@ -109,22 +122,33 @@ public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAwa
}
private void startListeningToActivity(
Activity activity,
ActivityRegistry activityRegistry,
PermissionRegistry permissionRegistry
Activity activity
) {
if (methodCallHandler != null) {
methodCallHandler.setActivity(activity);
methodCallHandler.setActivityRegistry(activityRegistry);
methodCallHandler.setPermissionRegistry(permissionRegistry);
}
}
private void stopListeningToActivity() {
if (methodCallHandler != null) {
methodCallHandler.setActivity(null);
methodCallHandler.setActivityRegistry(null);
methodCallHandler.setPermissionRegistry(null);
}
}
private void registerListeners() {
if (this.pluginRegistrar != null) {
this.pluginRegistrar.addActivityResultListener(this.permissionManager);
this.pluginRegistrar.addRequestPermissionsResultListener(this.permissionManager);
} else if (pluginBinding != null) {
this.pluginBinding.addActivityResultListener(this.permissionManager);
this.pluginBinding.addRequestPermissionsResultListener(this.permissionManager);
}
}
private void deregisterListeners() {
if (this.pluginBinding != null) {
this.pluginBinding.removeActivityResultListener(this.permissionManager);
this.pluginBinding.removeRequestPermissionsResultListener(this.permissionManager);
}
}
}
......@@ -6,12 +6,12 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
......@@ -23,15 +23,115 @@ import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
final class PermissionManager {
@FunctionalInterface
interface ActivityRegistry {
void addListener(PluginRegistry.ActivityResultListener handler);
final class PermissionManager implements PluginRegistry.ActivityResultListener, PluginRegistry.RequestPermissionsResultListener {
@Nullable
private ErrorCallback errorCallback;
@Nullable
private RequestPermissionsSuccessCallback successCallback;
@Nullable
private Activity activity;
private Map<Integer, Integer> requestResults;
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS && requestCode != PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE) {
return false;
}
@FunctionalInterface
interface PermissionRegistry {
void addListener(PluginRegistry.RequestPermissionsResultListener handler);
int status = resultCode == Activity.RESULT_OK
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
int permission;
if (requestCode == PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS) {
permission = PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS;
} else if (requestCode == PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE) {
status = Environment.isExternalStorageManager()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
permission = PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE;
} else {
return false;
}
HashMap<Integer, Integer> results = new HashMap<>();
results.put(permission, status);
successCallback.onSuccess(results);
return true;
}
@Override
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode != PermissionConstants.PERMISSION_CODE) {
ongoing = false;
return false;
}
for (int i = 0; i < permissions.length; i++) {
final String permissionName = permissions[i];
@PermissionConstants.PermissionGroup final int permission =
PermissionUtils.parseManifestName(permissionName);
if (permission == PermissionConstants.PERMISSION_GROUP_UNKNOWN)
continue;
final int result = grantResults[i];
if (permission == PermissionConstants.PERMISSION_GROUP_MICROPHONE) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_MICROPHONE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_MICROPHONE,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_SPEECH)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_SPEECH,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS,
permissionStatus);
}
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
permissionStatus);
}
requestResults.put(permission, permissionStatus);
} else if (!requestResults.containsKey(permission)) {
requestResults.put(
permission,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission);
}
this.successCallback.onSuccess(requestResults);
ongoing = false;
return true;
}
@FunctionalInterface
......@@ -67,8 +167,6 @@ final class PermissionManager {
void requestPermissions(
List<Integer> permissions,
Activity activity,
ActivityRegistry activityRegistry,
PermissionRegistry permissionRegistry,
RequestPermissionsSuccessCallback successCallback,
ErrorCallback errorCallback) {
if (ongoing) {
......@@ -87,7 +185,11 @@ final class PermissionManager {
return;
}
Map<Integer, Integer> requestResults = new HashMap<>();
this.errorCallback = errorCallback;
this.successCallback = successCallback;
this.activity = activity;
this.requestResults = new HashMap<>();
ArrayList<String> permissionsToRequest = new ArrayList<>();
for (Integer permission : permissions) {
@PermissionConstants.PermissionStatus final int permissionStatus = determinePermissionStatus(permission, activity, activity);
......@@ -111,21 +213,30 @@ final class PermissionManager {
} else {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_DENIED);
}
// On Android below R, the android.permission.MANAGE_EXTERNAL_STORAGE flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
if (permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_RESTRICTED);
} else {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_DENIED);
}
}
continue;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
activityRegistry.addListener(
new ActivityResultListener(successCallback)
);
String packageName = activity.getPackageName();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
activity.startActivityForResult(intent, PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
String packageName = activity.getPackageName();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:" + packageName));
activity.startActivityForResult(intent, PermissionConstants.PERMISSION_CODE_MANAGE_EXTERNAL_STORAGE);
} else {
permissionsToRequest.addAll(names);
}
......@@ -133,16 +244,6 @@ final class PermissionManager {
final String[] requestPermissions = permissionsToRequest.toArray(new String[0]);
if (permissionsToRequest.size() > 0) {
permissionRegistry.addListener(
new RequestPermissionsListener(
activity,
requestResults,
(Map<Integer, Integer> results) -> {
ongoing = false;
successCallback.onSuccess(results);
})
);
ongoing = true;
ActivityCompat.requestPermissions(
......@@ -166,7 +267,7 @@ final class PermissionManager {
if (permission == PermissionConstants.PERMISSION_GROUP_NOTIFICATION) {
return checkNotificationPermissionStatus(context);
}
if(permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH){
if (permission == PermissionConstants.PERMISSION_GROUP_BLUETOOTH) {
return checkBluetoothPermissionStatus(context);
}
......@@ -190,6 +291,14 @@ final class PermissionManager {
}
}
// On Android below R, the android.permission.MANAGE_EXTERNAL_STORAGE flag in AndroidManifest.xml
// may be ignored and not visible to the App as it's a new permission setting as a whole.
if (permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
}
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
......@@ -212,6 +321,17 @@ final class PermissionManager {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
}
if (permission == PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
return Environment.isExternalStorageManager()
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
final int permissionStatus = ContextCompat.checkSelfPermission(context, name);
if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PermissionConstants.PERMISSION_STATUS_DENIED;
......@@ -265,139 +385,10 @@ final class PermissionManager {
private int checkBluetoothPermissionStatus(Context context) {
List<String> names = PermissionUtils.getManifestNames(context, PermissionConstants.PERMISSION_GROUP_BLUETOOTH);
boolean missingInManifest = names == null || names.isEmpty();
if(missingInManifest) {
if (missingInManifest) {
Log.d(PermissionConstants.LOG_TAG, "Bluetooth permission missing in manifest");
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
@VisibleForTesting
static final class ActivityResultListener
implements PluginRegistry.ActivityResultListener {
// There's no way to unregister permission listeners in the v1 embedding, so we'll be called
// duplicate times in cases where the user denies and then grants a permission. Keep track of if
// we've responded before and bail out of handling the callback manually if this is a repeat
// call.
boolean alreadyCalled = false;
final RequestPermissionsSuccessCallback callback;
@VisibleForTesting
ActivityResultListener(RequestPermissionsSuccessCallback callback) {
this.callback = callback;
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (alreadyCalled || requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS) {
return false;
}
alreadyCalled = true;
final int status = resultCode == Activity.RESULT_OK
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
HashMap<Integer, Integer> results = new HashMap<>();
results.put(PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS, status);
callback.onSuccess(results);
return true;
}
}
@VisibleForTesting
static final class RequestPermissionsListener
implements PluginRegistry.RequestPermissionsResultListener {
// There's no way to unregister permission listeners in the v1 embedding, so we'll be called
// duplicate times in cases where the user denies and then grants a permission. Keep track of if
// we've responded before and bail out of handling the callback manually if this is a repeat
// call.
boolean alreadyCalled = false;
final Activity activity;
final RequestPermissionsSuccessCallback callback;
final Map<Integer, Integer> requestResults;
@VisibleForTesting
RequestPermissionsListener(
Activity activity,
Map<Integer, Integer> requestResults,
RequestPermissionsSuccessCallback callback) {
this.activity = activity;
this.callback = callback;
this.requestResults = requestResults;
}
@Override
public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) {
if (alreadyCalled || id != PermissionConstants.PERMISSION_CODE) {
return false;
}
alreadyCalled = true;
for (int i = 0; i < permissions.length; i++) {
final String permissionName = permissions[i];
@PermissionConstants.PermissionGroup final int permission =
PermissionUtils.parseManifestName(permissionName);
if (permission == PermissionConstants.PERMISSION_GROUP_UNKNOWN)
continue;
final int result = grantResults[i];
if (permission == PermissionConstants.PERMISSION_GROUP_MICROPHONE) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_MICROPHONE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_MICROPHONE,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_SPEECH)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_SPEECH,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permissionName, result);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS,
permissionStatus);
}
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
permissionStatus);
}
requestResults.put(permission, permissionStatus);
} else if (!requestResults.containsKey(permission)) {
requestResults.put(
permission,
PermissionUtils.toPermissionStatus(this.activity, permissionName, result));
}
PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission);
}
this.callback.onSuccess(requestResults);
return true;
}
}
}
......@@ -6,6 +6,7 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.RequiresApi;
......@@ -60,6 +61,8 @@ public class PermissionUtils {
return PermissionConstants.PERMISSION_GROUP_ACCESS_MEDIA_LOCATION;
case Manifest.permission.ACTIVITY_RECOGNITION:
return PermissionConstants.PERMISSION_GROUP_ACTIVITY_RECOGNITION;
case Manifest.permission.MANAGE_EXTERNAL_STORAGE:
return PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE;
default:
return PermissionConstants.PERMISSION_GROUP_UNKNOWN;
}
......@@ -177,9 +180,12 @@ public class PermissionUtils {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q && Environment.isExternalStorageLegacy())) {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
break;
}
break;
case PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))
......@@ -197,7 +203,7 @@ public class PermissionUtils {
break;
case PermissionConstants.PERMISSION_GROUP_ACTIVITY_RECOGNITION:
// The ACCESS_MEDIA_LOCATION permission is introduced in Android Q, meaning we should
// The ACTIVITY_RECOGNITION permission is introduced in Android Q, meaning we should
// not handle permissions on pre Android Q devices.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
return null;
......@@ -210,6 +216,14 @@ public class PermissionUtils {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.BLUETOOTH))
permissionNames.add(Manifest.permission.BLUETOOTH);
break;
case PermissionConstants.PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE:
// The MANAGE_EXTERNAL_STORAGE permission is introduced in Android R, meaning we should
// not handle permissions on pre Android R devices.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasPermissionInManifest(context, permissionNames, Manifest.permission.MANAGE_EXTERNAL_STORAGE ))
permissionNames.add(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
break;
case PermissionConstants.PERMISSION_GROUP_NOTIFICATION:
case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY:
case PermissionConstants.PERMISSION_GROUP_PHOTOS:
......
......@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
compileSdkVersion 30
lintOptions {
disable 'InvalidPackage'
......
......@@ -63,6 +63,9 @@
<!-- Permissions options for the `bluetooth` group -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- Permissions options for the `manage external storage` group -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher"
......
......@@ -20,7 +20,8 @@ class PermissionList extends StatelessWidget {
permission != Permission.storage &&
permission != Permission.ignoreBatteryOptimizations &&
permission != Permission.accessMediaLocation &&
permission != Permission.activityRecognition;
permission != Permission.activityRecognition &&
permission != Permission.manageExternalStorage;
} else {
return permission != Permission.unknown &&
permission != Permission.mediaLibrary &&
......
......@@ -118,6 +118,7 @@ typedef NS_ENUM(int, PermissionGroup) {
PermissionGroupActivityRecognition,
PermissionGroupUnknown,
PermissionGroupBluetooth,
PermissionGroupManageExternalStorage
};
typedef NS_ENUM(int, PermissionStatus) {
......
name: permission_handler
description: Permission plugin for Flutter. This plugin provides a cross-platform (iOS, Android) API to request and check permissions.
version: 6.1.3
version: 7.0.0
homepage: https://github.com/baseflowit/flutter-permission-handler
flutter:
......@@ -16,7 +16,7 @@ dependencies:
flutter:
sdk: flutter
meta: ^1.3.0
permission_handler_platform_interface: ^3.1.3
permission_handler_platform_interface: ^3.2.0
dev_dependencies:
flutter_test:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment