From 40e41a2b5c758a9c72fa41846aa3268a4436b532 Mon Sep 17 00:00:00 2001 From: Jeroen Weener Date: Wed, 15 Nov 2023 09:46:53 +0100 Subject: [PATCH] Port `Environment.isExternalStorageManager` (#1218) --- .../EnvironmentHostApiImpl.java | 49 +++++++++++++++++ .../PermissionHandlerPigeon.java | 54 +++++++++++++++++++ .../PermissionHandlerPlugin.java | 4 ++ .../lib/permission_handler_android.dart | 1 + .../android_object_mirrors/environment.dart | 21 ++++++++ .../android_permission_handler_api_impls.dart | 19 +++++++ .../lib/src/permission_handler.pigeon.dart | 52 ++++++++++++++++++ .../pigeons/android_permission_handler.dart | 19 +++++++ .../test/test_permission_handler.pigeon.dart | 44 +++++++++++++++ 9 files changed, 263 insertions(+) create mode 100644 permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/EnvironmentHostApiImpl.java create mode 100644 permission_handler_android/lib/src/android_object_mirrors/environment.dart diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/EnvironmentHostApiImpl.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/EnvironmentHostApiImpl.java new file mode 100644 index 000000000..4647ebef8 --- /dev/null +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/EnvironmentHostApiImpl.java @@ -0,0 +1,49 @@ +package com.baseflow.permissionhandler; + +import android.os.Build; +import android.os.Environment; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.baseflow.instancemanager.InstanceManager; +import com.baseflow.permissionhandler.PermissionHandlerPigeon.EnvironmentHostApi; + +import io.flutter.plugin.common.BinaryMessenger; + +/** + * Host API implementation for `Environment`. + * + *

This class may handle instantiating and adding native object instances that are attached to a + * Dart instance or handle method calls on the associated native class or an instance of the class. + */ +public class EnvironmentHostApiImpl implements EnvironmentHostApi { + // To ease adding additional methods, this value is added prematurely. + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + private final BinaryMessenger binaryMessenger; + + // To ease adding additional methods, this value is added prematurely. + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + private final InstanceManager instanceManager; + + /** + * Constructs an {@link EnvironmentHostApiImpl}. + * + * @param binaryMessenger used to communicate with Dart over asynchronous messages + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public EnvironmentHostApiImpl( + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager + ) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + } + + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public Boolean isExternalStorageManager() { + return Environment.isExternalStorageManager(); + } +} diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPigeon.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPigeon.java index 10dd63465..804e45563 100644 --- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPigeon.java +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPigeon.java @@ -1516,4 +1516,58 @@ public void dispose(@NonNull String instanceIdArg, @NonNull Reply callback channelReply -> callback.reply(null)); } } + /** + * Host API for `Environment`. + * + * This class may handle instantiating and adding native object instances that + * are attached to a Dart instance or handle method calls on the associated + * native class or an instance of the class. + * + * See https://developer.android.com/reference/android/os/Environment. + * + * Generated interface from Pigeon that represents a handler of messages from Flutter. + */ + public interface EnvironmentHostApi { + /** + * Returns whether the calling app has All Files Access on the primary shared/external storage media. + * + * Declaring the permission [Manifest.permission.manageExternalStorage] is + * not enough to gain the access. To request access, use + * [Settings.actionManageAppAllFilesAccessPermission]. + * + * See https://developer.android.com/reference/android/os/Environment#isExternalStorageManager(). + */ + @NonNull + Boolean isExternalStorageManager(); + + /** The codec used by EnvironmentHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /**Sets up an instance of `EnvironmentHostApi` to handle messages through the `binaryMessenger`. */ + static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable EnvironmentHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.EnvironmentHostApi.isExternalStorageManager", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + Boolean output = api.isExternalStorageManager(); + wrapped.add(0, output); + } + catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } } diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPlugin.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPlugin.java index 64412cfa4..27220d308 100644 --- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPlugin.java +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PermissionHandlerPlugin.java @@ -14,6 +14,7 @@ import com.baseflow.permissionhandler.PermissionHandlerPigeon.AlarmManagerHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.BuildVersionHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.ContextHostApi; +import com.baseflow.permissionhandler.PermissionHandlerPigeon.EnvironmentHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.IntentHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.NotificationManagerHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.PackageManagerHostApi; @@ -81,6 +82,9 @@ private void setUp( final NotificationManagerHostApi notificationManagerHostApi = new NotificationManagerHostApiImpl(binaryMessenger, instanceManager); NotificationManagerHostApi.setup(binaryMessenger, notificationManagerHostApi); + final EnvironmentHostApi environmentHostApi = new EnvironmentHostApiImpl(binaryMessenger, instanceManager); + EnvironmentHostApi.setup(binaryMessenger, environmentHostApi); + activityFlutterApi = new ActivityFlutterApiImpl(binaryMessenger, instanceManager); activityHostApi = new ActivityHostApiImpl( binaryMessenger, diff --git a/permission_handler_android/lib/permission_handler_android.dart b/permission_handler_android/lib/permission_handler_android.dart index 932188147..9f0651d98 100644 --- a/permission_handler_android/lib/permission_handler_android.dart +++ b/permission_handler_android/lib/permission_handler_android.dart @@ -2,6 +2,7 @@ export 'src/android_object_mirrors/activity.dart'; export 'src/android_object_mirrors/alarm_manager.dart'; export 'src/android_object_mirrors/build.dart'; export 'src/android_object_mirrors/context.dart'; +export 'src/android_object_mirrors/environment.dart'; export 'src/android_object_mirrors/intent.dart'; export 'src/android_object_mirrors/manifest.dart'; export 'src/android_object_mirrors/notification_manager.dart'; diff --git a/permission_handler_android/lib/src/android_object_mirrors/environment.dart b/permission_handler_android/lib/src/android_object_mirrors/environment.dart new file mode 100644 index 000000000..c7ea9c676 --- /dev/null +++ b/permission_handler_android/lib/src/android_object_mirrors/environment.dart @@ -0,0 +1,21 @@ +import '../permission_handler.pigeon.dart'; + +/// Provides access to environment variables. +/// +/// See https://developer.android.com/reference/android/os/Environment. +class Environment { + const Environment._(); + + static final EnvironmentHostApi _hostApi = EnvironmentHostApi(); + + /// Returns whether the calling app has All Files Access on the primary shared/external storage media. + /// + /// Declaring the permission [Manifest.permission.manageExternalStorage] is + /// not enough to gain the access. To request access, use + /// [Settings.actionManageAppAllFilesAccessPermission]. + /// + /// See https://developer.android.com/reference/android/os/Environment#isExternalStorageManager(). + static Future isExternalStorageManager() { + return _hostApi.isExternalStorageManager(); + } +} diff --git a/permission_handler_android/lib/src/android_permission_handler_api_impls.dart b/permission_handler_android/lib/src/android_permission_handler_api_impls.dart index 1de90bcaa..4929b80f6 100644 --- a/permission_handler_android/lib/src/android_permission_handler_api_impls.dart +++ b/permission_handler_android/lib/src/android_permission_handler_api_impls.dart @@ -730,3 +730,22 @@ class NotificationManagerFlutterApiImpl extends NotificationManagerFlutterApi { _instanceManager.remove(instanceId); } } + +/// Host API implementation of Environment. +class EnvironmentHostApiImpl extends EnvironmentHostApi { + /// Creates a new instance of [EnvironmentHostApiImpl]. + EnvironmentHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; +} diff --git a/permission_handler_android/lib/src/permission_handler.pigeon.dart b/permission_handler_android/lib/src/permission_handler.pigeon.dart index 443a33d25..3a0de215a 100644 --- a/permission_handler_android/lib/src/permission_handler.pigeon.dart +++ b/permission_handler_android/lib/src/permission_handler.pigeon.dart @@ -1347,3 +1347,55 @@ abstract class NotificationManagerFlutterApi { } } } + +/// Host API for `Environment`. +/// +/// This class may handle instantiating and adding native object instances that +/// are attached to a Dart instance or handle method calls on the associated +/// native class or an instance of the class. +/// +/// See https://developer.android.com/reference/android/os/Environment. +class EnvironmentHostApi { + /// Constructor for [EnvironmentHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + EnvironmentHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + /// Returns whether the calling app has All Files Access on the primary shared/external storage media. + /// + /// Declaring the permission [Manifest.permission.manageExternalStorage] is + /// not enough to gain the access. To request access, use + /// [Settings.actionManageAppAllFilesAccessPermission]. + /// + /// See https://developer.android.com/reference/android/os/Environment#isExternalStorageManager(). + Future isExternalStorageManager() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.EnvironmentHostApi.isExternalStorageManager', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel.send(null) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as bool?)!; + } + } +} diff --git a/permission_handler_android/pigeons/android_permission_handler.dart b/permission_handler_android/pigeons/android_permission_handler.dart index 8c6d1b993..6343a42aa 100644 --- a/permission_handler_android/pigeons/android_permission_handler.dart +++ b/permission_handler_android/pigeons/android_permission_handler.dart @@ -449,3 +449,22 @@ abstract class NotificationManagerFlutterApi { /// Dispose of the Dart instance and remove it from the `InstanceManager`. void dispose(String instanceId); } + +/// Host API for `Environment`. +/// +/// This class may handle instantiating and adding native object instances that +/// are attached to a Dart instance or handle method calls on the associated +/// native class or an instance of the class. +/// +/// See https://developer.android.com/reference/android/os/Environment. +@HostApi(dartHostTestHandler: 'EnvironmentTestHostApi') +abstract class EnvironmentHostApi { + /// Returns whether the calling app has All Files Access on the primary shared/external storage media. + /// + /// Declaring the permission [Manifest.permission.manageExternalStorage] is + /// not enough to gain the access. To request access, use + /// [Settings.actionManageAppAllFilesAccessPermission]. + /// + /// See https://developer.android.com/reference/android/os/Environment#isExternalStorageManager(). + bool isExternalStorageManager(); +} diff --git a/permission_handler_android/test/test_permission_handler.pigeon.dart b/permission_handler_android/test/test_permission_handler.pigeon.dart index ebd3d7d40..e34105983 100644 --- a/permission_handler_android/test/test_permission_handler.pigeon.dart +++ b/permission_handler_android/test/test_permission_handler.pigeon.dart @@ -909,3 +909,47 @@ abstract class NotificationManagerTestHostApi { } } } + +/// Host API for `Environment`. +/// +/// This class may handle instantiating and adding native object instances that +/// are attached to a Dart instance or handle method calls on the associated +/// native class or an instance of the class. +/// +/// See https://developer.android.com/reference/android/os/Environment. +abstract class EnvironmentTestHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + /// Returns whether the calling app has All Files Access on the primary shared/external storage media. + /// + /// Declaring the permission [Manifest.permission.manageExternalStorage] is + /// not enough to gain the access. To request access, use + /// [Settings.actionManageAppAllFilesAccessPermission]. + /// + /// See https://developer.android.com/reference/android/os/Environment#isExternalStorageManager(). + bool isExternalStorageManager(); + + static void setup(EnvironmentTestHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.EnvironmentHostApi.isExternalStorageManager', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + // ignore message + final bool output = api.isExternalStorageManager(); + return [output]; + }); + } + } + } +}