diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ActivityHostApiImpl.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ActivityHostApiImpl.java index 871bdc953..694b6a8d3 100644 --- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ActivityHostApiImpl.java +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ActivityHostApiImpl.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.content.Intent; +import android.os.PowerManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -53,6 +54,8 @@ public class ActivityHostApiImpl implements private final InstanceManager instanceManager; + private final PowerManagerFlutterApiImpl powerManagerFlutterApi; + /** * Callbacks to complete a pending permission request. *

@@ -62,7 +65,7 @@ public class ActivityHostApiImpl implements private final Map> pendingPermissionsRequestMap = new HashMap<>(); /** - * Callback to complete a pending activity-for-result request. + * Callbacks to complete a pending activity-for-result request. *

* These callbacks are set in {@link this#startActivityForResult(String, String, Long, Result)}, * and are completed in {@link this#onActivityResult(int, int, Intent)}. @@ -76,9 +79,11 @@ public class ActivityHostApiImpl implements * @param instanceManager maintains instances stored to communicate with attached Dart objects */ public ActivityHostApiImpl( + @NonNull PowerManagerFlutterApiImpl powerManagerFlutterApi, @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager ) { + this.powerManagerFlutterApi = powerManagerFlutterApi; this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; } @@ -219,4 +224,22 @@ public boolean onActivityResult( return true; } + + @Override + @NonNull public String getSystemService( + @NonNull String instanceId, + @NonNull String name + ) { + final UUID instanceUuid = UUID.fromString(instanceId); + final Activity activity = instanceManager.getInstance(instanceUuid); + + final Object systemService = activity.getSystemService(name); + + if (systemService instanceof PowerManager) { + powerManagerFlutterApi.create((PowerManager) systemService); + } + + final UUID systemServiceUuid = instanceManager.getIdentifierForStrongReference(systemService); + return systemServiceUuid.toString(); + } } diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/BuildVersionHostApiImpl.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/BuildVersionHostApiImpl.java new file mode 100644 index 000000000..8bff93500 --- /dev/null +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/BuildVersionHostApiImpl.java @@ -0,0 +1,21 @@ +package com.baseflow.permissionhandler; + +import android.os.Build; + +import androidx.annotation.NonNull; + +import com.baseflow.permissionhandler.PermissionHandlerPigeon.BuildVersionHostApi; + +/** + * Host API implementation for `Build.VERSION`. + * + *

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 BuildVersionHostApiImpl implements BuildVersionHostApi { + @NonNull + @Override + public Long sdkInt() { + return (long) Build.VERSION.SDK_INT; + } +} diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ContextHostApiImpl.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ContextHostApiImpl.java index 6a96dbb94..c15644750 100644 --- a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ContextHostApiImpl.java +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/ContextHostApiImpl.java @@ -2,6 +2,7 @@ import android.content.Context; import android.content.Intent; +import android.os.PowerManager; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; @@ -26,6 +27,8 @@ public class ContextHostApiImpl implements ContextHostApi { private final InstanceManager instanceManager; + private final PowerManagerFlutterApiImpl powerManagerFlutterApi; + /** * Constructs an {@link ContextHostApiImpl}. * @@ -33,9 +36,11 @@ public class ContextHostApiImpl implements ContextHostApi { * @param instanceManager maintains instances stored to communicate with attached Dart objects */ public ContextHostApiImpl( + @NonNull PowerManagerFlutterApiImpl powerManagerFlutterApi, @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager ) { + this.powerManagerFlutterApi = powerManagerFlutterApi; this.binaryMessenger = binaryMessenger; this.instanceManager = instanceManager; } @@ -74,4 +79,22 @@ public void startActivity( return context.getPackageName(); } + + @Override + @NonNull public String getSystemService( + @NonNull String instanceId, + @NonNull String name + ) { + final UUID instanceUuid = UUID.fromString(instanceId); + final Context context = instanceManager.getInstance(instanceUuid); + + final Object systemService = context.getSystemService(name); + + if (systemService instanceof PowerManager) { + powerManagerFlutterApi.create((PowerManager) systemService); + } + + final UUID systemServiceUuid = instanceManager.getIdentifierForStrongReference(systemService); + return systemServiceUuid.toString(); + } } 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 b402b5f4f..a753a32fa 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 @@ -340,6 +340,17 @@ public interface ActivityHostApi { */ @NonNull String getPackageName(@NonNull String instanceId); + /** + * Return the handle to a system-level service by name. + * + * The class of the returned object varies by the requested name. + * + * Returns the instance ID of the service. + * + * See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + */ + @NonNull + String getSystemService(@NonNull String instanceId, @NonNull String name); /** * Start an activity for which the application would like a result when it finished. * @@ -473,6 +484,31 @@ public void error(Throwable error) { String output = api.getPackageName(instanceIdArg); wrapped.add(0, output); } + catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getSystemService", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + String instanceIdArg = (String) args.get(0); + String nameArg = (String) args.get(1); + try { + String output = api.getSystemService(instanceIdArg, nameArg); + wrapped.add(0, output); + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; @@ -594,6 +630,17 @@ public interface ContextHostApi { */ @NonNull String getPackageName(@NonNull String instanceId); + /** + * Return the handle to a system-level service by name. + * + * The class of the returned object varies by the requested name. + * + * Returns the instance ID of the service. + * + * See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + */ + @NonNull + String getSystemService(@NonNull String instanceId, @NonNull String name); /** The codec used by ContextHostApi. */ static @NonNull MessageCodec getCodec() { @@ -665,6 +712,31 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable ContextHos String output = api.getPackageName(instanceIdArg); wrapped.add(0, output); } + catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.ContextHostApi.getSystemService", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + String instanceIdArg = (String) args.get(0); + String nameArg = (String) args.get(1); + try { + String output = api.getSystemService(instanceIdArg, nameArg); + wrapped.add(0, output); + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; @@ -992,4 +1064,165 @@ static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable IntentHost } } } + /** + * Host API for `PowerManager`. + * + * 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/PowerManager. + * + * Generated interface from Pigeon that represents a handler of messages from Flutter. + */ + public interface PowerManagerHostApi { + /** + * Returns whether the given application package name is on the device's power allowlist. + * + * Apps can be placed on the allowlist through the settings UI invoked by + * [Settings.actionRequestIgnoreBatteryOptimizations]. + * + * Being on the power allowlist means that the system will not apply most + * power saving features to the app. Guardrails for extreme cases may still + * be applied. + * + * See https://developer.android.com/reference/android/os/PowerManager#isIgnoringBatteryOptimizations(java.lang.String). + */ + @NonNull + Boolean isIgnoringBatteryOptimizations(@NonNull String instanceId, @NonNull String packageName); + + /** The codec used by PowerManagerHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /**Sets up an instance of `PowerManagerHostApi` to handle messages through the `binaryMessenger`. */ + static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable PowerManagerHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.PowerManagerHostApi.isIgnoringBatteryOptimizations", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + String instanceIdArg = (String) args.get(0); + String packageNameArg = (String) args.get(1); + try { + Boolean output = api.isIgnoringBatteryOptimizations(instanceIdArg, packageNameArg); + wrapped.add(0, output); + } + catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + /** + * Flutter API for `PowerManager`. + * + * This class may handle instantiating and adding Dart instances that are + * attached to a native instance or receiving callback methods from an + * overridden native class. + * + * See https://developer.android.com/reference/android/os/PowerManager. + * + * Generated class from Pigeon that represents Flutter messages that can be called from Java. + */ + public static class PowerManagerFlutterApi { + private final @NonNull BinaryMessenger binaryMessenger; + + public PowerManagerFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + /** Public interface for sending reply. */ + @SuppressWarnings("UnknownNullness") + public interface Reply { + void reply(T reply); + } + /** The codec used by PowerManagerFlutterApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /** Create a new Dart instance and add it to the `InstanceManager`. */ + public void create(@NonNull String instanceIdArg, @NonNull Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Collections.singletonList(instanceIdArg)), + channelReply -> callback.reply(null)); + } + /** Dispose of the Dart instance and remove it from the `InstanceManager`. */ + public void dispose(@NonNull String instanceIdArg, @NonNull Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.dispose", getCodec()); + channel.send( + new ArrayList(Collections.singletonList(instanceIdArg)), + channelReply -> callback.reply(null)); + } + } + /** + * Host API for `BuildVersion`. + * + * 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/Build.VERSION. + * + * Generated interface from Pigeon that represents a handler of messages from Flutter. + */ + public interface BuildVersionHostApi { + /** + * The SDK version of the software currently running on this hardware device. + * + * This value never changes while a device is booted, but it may increase + * when the hardware manufacturer provides an OTA update. + * + * Possible values are defined in [Build.versionCodes]. + * + * See https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT. + */ + @NonNull + Long sdkInt(); + + /** The codec used by BuildVersionHostApi. */ + static @NonNull MessageCodec getCodec() { + return new StandardMessageCodec(); + } + /**Sets up an instance of `BuildVersionHostApi` to handle messages through the `binaryMessenger`. */ + static void setup(@NonNull BinaryMessenger binaryMessenger, @Nullable BuildVersionHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.permission_handler_android.BuildVersionHostApi.sdkInt", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + Long output = api.sdkInt(); + 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 692a6237b..e8c2ab094 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 @@ -11,8 +11,10 @@ import com.baseflow.instancemanager.InstanceManagerPigeon.InstanceManagerHostApi; import com.baseflow.instancemanager.JavaObjectHostApiImpl; import com.baseflow.permissionhandler.PermissionHandlerPigeon.ActivityHostApi; +import com.baseflow.permissionhandler.PermissionHandlerPigeon.BuildVersionHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.ContextHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.IntentHostApi; +import com.baseflow.permissionhandler.PermissionHandlerPigeon.PowerManagerHostApi; import com.baseflow.permissionhandler.PermissionHandlerPigeon.UriHostApi; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -50,19 +52,34 @@ private void setUp( final JavaObjectHostApi javaObjectHostApi = new JavaObjectHostApiImpl(instanceManager); JavaObjectHostApi.setup(binaryMessenger, javaObjectHostApi); + final UriHostApi uriHostApi = new UriHostApiImpl(binaryMessenger, instanceManager); + UriHostApi.setup(binaryMessenger, uriHostApi); + + final IntentHostApi intentHostApi = new IntentHostApiImpl(binaryMessenger, instanceManager); + IntentHostApi.setup(binaryMessenger, intentHostApi); + + final PowerManagerFlutterApiImpl powerManagerFlutterApi = new PowerManagerFlutterApiImpl(binaryMessenger, instanceManager); + final PowerManagerHostApi powerManagerHostApi = new PowerManagerHostApiImpl(binaryMessenger, instanceManager); + PowerManagerHostApi.setup(binaryMessenger, powerManagerHostApi); + activityFlutterApi = new ActivityFlutterApiImpl(binaryMessenger, instanceManager); - activityHostApi = new ActivityHostApiImpl(binaryMessenger, instanceManager); + activityHostApi = new ActivityHostApiImpl( + powerManagerFlutterApi, + binaryMessenger, + instanceManager + ); ActivityHostApi.setup(binaryMessenger, activityHostApi); contextFlutterApi = new ContextFlutterApiImpl(binaryMessenger, instanceManager); - final ContextHostApiImpl contextHostApi = new ContextHostApiImpl(binaryMessenger, instanceManager); + final ContextHostApiImpl contextHostApi = new ContextHostApiImpl( + powerManagerFlutterApi, + binaryMessenger, + instanceManager + ); ContextHostApi.setup(binaryMessenger, contextHostApi); - final UriHostApi uriHostApi = new UriHostApiImpl(binaryMessenger, instanceManager); - UriHostApi.setup(binaryMessenger, uriHostApi); - - final IntentHostApi intentHostApi = new IntentHostApiImpl(binaryMessenger, instanceManager); - IntentHostApi.setup(binaryMessenger, intentHostApi); + final BuildVersionHostApi buildVersionHostApi = new BuildVersionHostApiImpl(); + BuildVersionHostApi.setup(binaryMessenger, buildVersionHostApi); } @Override diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PowerManagerFlutterApiImpl.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PowerManagerFlutterApiImpl.java new file mode 100644 index 000000000..306f36155 --- /dev/null +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PowerManagerFlutterApiImpl.java @@ -0,0 +1,66 @@ +package com.baseflow.permissionhandler; + +import android.os.PowerManager; + +import androidx.annotation.NonNull; + +import com.baseflow.instancemanager.InstanceManager; +import com.baseflow.permissionhandler.PermissionHandlerPigeon.PowerManagerFlutterApi; + +import java.util.UUID; + +import io.flutter.plugin.common.BinaryMessenger; + +/** + * Flutter API implementation for `PowerManager`. + * + *

This class may handle adding native instances that are attached to a Dart instance or passing + * arguments of callbacks methods to a Dart instance. + */ +public class PowerManagerFlutterApiImpl { + // To ease adding additional methods, this value is added prematurely. + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + private final BinaryMessenger binaryMessenger; + + private final InstanceManager instanceManager; + + private final PowerManagerFlutterApi api; + + /** + * Constructs a {@link PowerManagerFlutterApiImpl}. + * + * @param binaryMessenger used to communicate with Dart over asynchronous messages + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public PowerManagerFlutterApiImpl( + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager + ) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + api = new PowerManagerFlutterApi(binaryMessenger); + } + + /** + * Stores the `PowerManager` instance and notifies Dart to create and store a new `PowerManager` + * instance that is attached to this one. If `instance` has already been added, this method does + * nothing. + */ + public void create(@NonNull PowerManager instance) { + if (!instanceManager.containsInstance(instance)) { + final UUID powerManagerInstanceUuid = instanceManager.addHostCreatedInstance(instance); + api.create(powerManagerInstanceUuid.toString(), reply -> {}); + } + } + + /** + * Disposes of the `PowerManager` instance in the instance manager and notifies Dart to do the + * same. If `instance` was already disposed, this method does nothing. + */ + public void dispose(PowerManager instance) { + final UUID powerManagerInstanceUuid = instanceManager.getIdentifierForStrongReference(instance); + if (powerManagerInstanceUuid != null) { + api.dispose(powerManagerInstanceUuid.toString(), reply -> {}); + } + } +} diff --git a/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PowerManagerHostApiImpl.java b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PowerManagerHostApiImpl.java new file mode 100644 index 000000000..3c6936126 --- /dev/null +++ b/permission_handler_android/android/src/main/java/com/baseflow/permissionhandler/PowerManagerHostApiImpl.java @@ -0,0 +1,52 @@ +package com.baseflow.permissionhandler; + +import android.os.Build; +import android.os.PowerManager; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.baseflow.instancemanager.InstanceManager; +import com.baseflow.permissionhandler.PermissionHandlerPigeon.PowerManagerHostApi; + +import java.util.UUID; + +import io.flutter.plugin.common.BinaryMessenger; + +/** + * Host API implementation for `PowerManager`. + * + *

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 PowerManagerHostApiImpl implements PowerManagerHostApi { + // To ease adding additional methods, this value is added prematurely. + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + private final BinaryMessenger binaryMessenger; + + private final InstanceManager instanceManager; + + /** + * Constructs an {@link PowerManagerHostApiImpl}. + * + * @param binaryMessenger used to communicate with Dart over asynchronous messages + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public PowerManagerHostApiImpl( + @NonNull BinaryMessenger binaryMessenger, + @NonNull InstanceManager instanceManager + ) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + } + + @RequiresApi(api = Build.VERSION_CODES.M) + @NonNull + @Override + public Boolean isIgnoringBatteryOptimizations(@NonNull String instanceId, @NonNull String packageName) { + final UUID instanceUuid = UUID.fromString(instanceId); + final PowerManager powerManager = instanceManager.getInstance(instanceUuid); + + return powerManager.isIgnoringBatteryOptimizations(packageName); + } +} diff --git a/permission_handler_android/lib/permission_handler_android.dart b/permission_handler_android/lib/permission_handler_android.dart index e806932db..efce2d675 100644 --- a/permission_handler_android/lib/permission_handler_android.dart +++ b/permission_handler_android/lib/permission_handler_android.dart @@ -1,13 +1,14 @@ export 'src/android_object_mirrors/activity.dart'; +export 'src/android_object_mirrors/build.dart'; export 'src/android_object_mirrors/context.dart'; export 'src/android_object_mirrors/intent.dart'; export 'src/android_object_mirrors/manifest.dart'; export 'src/android_object_mirrors/package_manager.dart'; +export 'src/android_object_mirrors/power_manager.dart'; export 'src/android_object_mirrors/settings.dart'; export 'src/android_object_mirrors/uri.dart'; export 'src/activity_aware.dart'; export 'src/activity_manager.dart'; export 'src/extensions.dart'; -export 'src/missing_activity_exception.dart'; export 'src/permission_handler_android.dart'; diff --git a/permission_handler_android/lib/src/android_object_mirrors/activity.dart b/permission_handler_android/lib/src/android_object_mirrors/activity.dart index 2b7ac957e..30ecda27a 100644 --- a/permission_handler_android/lib/src/android_object_mirrors/activity.dart +++ b/permission_handler_android/lib/src/android_object_mirrors/activity.dart @@ -116,6 +116,22 @@ class Activity extends JavaObject { requestCode, ); } + + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + Future getSystemService( + String name, + ) { + return _hostApi.getSystemServiceFromInstance( + this, + name, + ); + } } /// Result of an activity-for-result request. diff --git a/permission_handler_android/lib/src/android_object_mirrors/build.dart b/permission_handler_android/lib/src/android_object_mirrors/build.dart new file mode 100644 index 000000000..53ed428d2 --- /dev/null +++ b/permission_handler_android/lib/src/android_object_mirrors/build.dart @@ -0,0 +1,362 @@ +import '../android_permission_handler_api_impls.dart'; + +/// Information about the current build, extracted from system properties. +/// +/// See https://developer.android.com/reference/android/os/Build +class Build { + Build._(); + + /// Various version strings. + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION + static _Version get version => _version; + static const _Version _version = _Version(); + + /// Enumeration of the currently known SDK version codes. + /// + /// These are the values that can be found in VERSION#SDK. Version numbers + /// increment monotonically with each official platform release. + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES + static _VersionCodes get versionCodes => _versionCodes; + static const _VersionCodes _versionCodes = _VersionCodes(); +} + +/// Various version strings. +/// +/// See https://developer.android.com/reference/android/os/Build.VERSION +class _Version { + const _Version(); + + /// The SDK version of the software currently running on this hardware device. + /// + /// This value never changes while a device is booted, but it may increase + /// when the hardware manufacturer provides an OTA update. + /// + /// Possible values are defined in [Build.versionCodes]. + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT. + Future get sdkInt async => await _hostApi.sdkInt(); + + static final BuildVersionHostApiImpl _hostApi = BuildVersionHostApiImpl(); +} + +/// Enumeration of the currently known SDK version codes. +/// +/// These are the values that can be found in VERSION#SDK_INT. Version numbers +/// increment monotonically with each official platform release. +/// +/// See https://developer.android.com/reference/android/os/Build.VERSION_CODES +class _VersionCodes { + const _VersionCodes(); + + /// The original, first, version of Android. Yay! + /// + /// Released publicly as Android 1.0 in September 2008. + /// + /// Constant Value: 1 (0x00000001). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#BASE + int get base => 1; + + /// First Android update. + /// + /// Released publicly as Android 1.1 in February 2009. + /// + /// Constant Value: 2 (0x00000002). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#BASE_1_1 + int get base1_1 => 2; + + /// C. + /// + /// Released publicly as Android 1.5 in April 2009. + /// + /// Constant Value: 3 (0x00000003). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#CUPCAKE + int get cupcake => 3; + + /// CUR_DEVELOPMENT. + /// + /// Magic version number for a current development build, which has not yet + /// turned into an official release. + /// + /// Constant Value: 10000 (0x00002710). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#CUR_DEVELOPMENT + int get curDevelopment => 10000; + + /// D. + /// + /// Released publicly as Android 1.6 in September 2009. + /// + /// Constant Value: 4 (0x00000004). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#DONUT + int get donut => 4; + + /// E. + /// + /// Released publicly as Android 2.0 in October 2009. + /// + /// Constant Value: 5 (0x00000005). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#ECLAIR + int get eclair => 5; + + /// E incremental update. + /// + /// Released publicly as Android 2.0.1 in December 2009. + /// + /// Constant Value: 6 (0x00000006). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#ECLAIR_0_1 + int get eclair0_1 => 6; + + /// E MR1. + /// + /// Released publicly as Android 2.1 in January 2010. + /// + /// Constant Value: 7 (0x00000007). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#ECLAIR_MR1 + int get eclairMR1 => 7; + + /// F. + /// + /// Released publicly as Android 2.2 in May 2010. + /// + /// Constant Value: 8 (0x00000008). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#FROYO + int get froyo => 8; + + /// G. + /// + /// Released publicly as Android 2.3 in December 2010. + /// + /// Constant Value: 9 (0x00000009). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#GINGERBREAD + int get gingerbread => 9; + + /// G MR1. + /// + /// Released publicly as Android 2.3.3 in February 2011. + /// + /// Constant Value: 10 (0x0000000a). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#GINGERBREAD_MR1 + int get gingerbreadMR1 => 10; + + /// H. + /// + /// Released publicly as Android 3.0 in February 2011. + /// + /// Constant Value: 11 (0x0000000b). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#HONEYCOMB + int get honeycomb => 11; + + /// H MR1. + /// + /// Released publicly as Android 3.1 in May 2011. + /// + /// Constant Value: 12 (0x0000000c). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#HONEYCOMB_MR1 + int get honeycombMR1 => 12; + + /// H MR2. + /// + /// Released publicly as Android 3.2 in July 2011. + /// + /// Constant Value: 13 (0x0000000d). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#HONEYCOMB_MR2 + int get honeycombMR2 => 13; + + /// I. + /// + /// Released publicly as Android 4.0 in October 2011. + /// + /// Constant Value: 14 (0x0000000e). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#ICE_CREAM_SANDWICH + int get iceCreamSandwich => 14; + + /// I MR1. + /// + /// Released publicly as Android 4.0.3 in December 2011. + /// + /// Constant Value: 15 (0x0000000f). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1 + int get iceCreamSandwichMR1 => 15; + + /// J. + /// + /// Released publicly as Android 4.1 in July 2012. + /// + /// Constant Value: 16 (0x00000010). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#JELLY_BEAN + int get jellyBean => 16; + + /// J MR1. + /// + /// Released publicly as Android 4.2 in November 2012. + /// + /// Constant Value: 17 (0x00000011). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#JELLY_BEAN_MR1 + int get jellyBeanMR1 => 17; + + /// J MR2. + /// + /// Released publicly as Android 4.3 in July 2013. + /// + /// Constant Value: 18 (0x00000012). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#JELLY_BEAN_MR2 + int get jellyBeanMR2 => 18; + + /// K. + /// + /// Released publicly as Android 4.4 in October 2013. + /// + /// Constant Value: 19 (0x00000013). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#KITKAT + int get kitKat => 19; + + /// K for watches. + /// + /// Released publicly as Android 4.4W in June 2014. + /// + /// Constant Value: 20 (0x00000014). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#KITKAT_WATCH + int get kitKatWatch => 20; + + /// L. + /// + /// Released publicly as Android 5.0 in November 2014. + /// + /// Constant Value: 21 (0x00000015). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#LOLLIPOP + int get lollipop => 21; + + /// L MR1. + /// + /// Released publicly as Android 5.1 in March 2015. + /// + /// Constant Value: 22 (0x00000016). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#LOLLIPOP_MR1 + int get lollipopMR1 => 22; + + /// M. + /// + /// Released publicly as Android 6.0 in October 2015. + /// + /// Constant Value: 23 (0x00000017). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#M + int get m => 23; + + /// N. + /// + /// Released publicly as Android 7.0 in August 2016. + /// + /// Constant Value: 24 (0x00000018). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#N + int get n => 24; + + /// N MR1. + /// + /// Released publicly as Android 7.1 in October 2016. + /// + /// Constant Value: 25 (0x00000019). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#N_MR1 + int get nMR1 => 25; + + /// O. + /// + /// Released publicly as Android 8.0 in August 2017. + /// + /// Constant Value: 26 (0x0000001a). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#O + int get o => 26; + + /// O MR1. + /// + /// Released publicly as Android 8.1 in December 2017. + /// + /// Constant Value: 27 (0x0000001b). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#O_MR1 + int get oMR1 => 27; + + /// P. + /// + /// Released publicly as Android 9 in August 2018. + /// + /// Constant Value: 28 (0x0000001c). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#P + int get p => 28; + + /// Q. + /// + /// Released publicly as Android 10 in September 2019. + /// + /// Constant Value: 29 (0x0000001d). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#Q + int get q => 29; + + /// R. + /// + /// Released publicly as Android 11 in September 2020. + /// + /// Constant Value: 30 (0x0000001e). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#R + int get r => 30; + + /// S. + /// + /// Constant Value: 31 (0x0000001f). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#S + int get s => 31; + + /// S V2. + /// + /// Once more unto the breach, dear friends, once more. + /// + /// Constant Value: 32 (0x00000020). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#S_V2 + int get sV2 => 32; + + /// Tiramisu. + /// + /// Constant Value: 33 (0x00000021). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#TIRAMISU + int get tiramisu => 33; + + /// Upside Down Cake. + /// + /// Constant Value: 34 (0x00000022). + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION_CODES#UPSIDE_DOWN_CAKE + int get upsideDownCake => 34; +} diff --git a/permission_handler_android/lib/src/android_object_mirrors/context.dart b/permission_handler_android/lib/src/android_object_mirrors/context.dart index 2bb318e8b..742c83dd1 100644 --- a/permission_handler_android/lib/src/android_object_mirrors/context.dart +++ b/permission_handler_android/lib/src/android_object_mirrors/context.dart @@ -29,6 +29,11 @@ class Context extends JavaObject { final ContextHostApiImpl _hostApi; + /// Use with [Context.getSystemService] to retrieve a [PowerManager] for + /// controlling power management, including "wake locks," which let you keep + /// the device on while you're running long tasks. + static const String powerService = 'power'; + /// Determine whether the application has been granted a particular permission. /// /// See https://developer.android.com/reference/android/content/Context#checkSelfPermission(java.lang.String). @@ -61,4 +66,18 @@ class Context extends JavaObject { this, ); } + + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + Future getSystemService( + String name, + ) { + return _hostApi.getSystemServiceFromInstance( + this, + name, + ); + } } diff --git a/permission_handler_android/lib/src/android_object_mirrors/power_manager.dart b/permission_handler_android/lib/src/android_object_mirrors/power_manager.dart new file mode 100644 index 000000000..e4c74ea87 --- /dev/null +++ b/permission_handler_android/lib/src/android_object_mirrors/power_manager.dart @@ -0,0 +1,47 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_instance_manager/flutter_instance_manager.dart'; +import 'package:permission_handler_android/src/android_permission_handler_api_impls.dart'; + +import 'build.dart'; + +/// This class lets you query and request control of aspects of the device's power state. +/// +/// See: https://developer.android.com/reference/android/os/PowerManager +class PowerManager extends JavaObject { + /// Instantiates an [PowerManager] without creating and attaching to an + /// instance of the associated native class. + PowerManager.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _hostApi = PowerManagerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super.detached(); + + final PowerManagerHostApiImpl _hostApi; + + /// Returns whether the given application package name is on the device's power allowlist. + /// + /// Only works on Android M and above. Returns false on lower API levels. + /// + /// Apps can be placed on the allowlist through the settings UI invoked by + /// [Settings.actionRequestIgnoreBatteryOptimizations]. + /// + /// Being on the power allowlist means that the system will not apply most + /// power saving features to the app. Guardrails for extreme cases may still + /// be applied. + /// + /// See https://developer.android.com/reference/android/os/PowerManager#isIgnoringBatteryOptimizations(java.lang.String). + Future isIgnoringBatteryOptimizations(String packageName) async { + final int sdkVersion = await Build.version.sdkInt; + if (sdkVersion < Build.versionCodes.m) { + return false; + } + + return _hostApi.isIgnoringBatteryOptimizationsFromInstance( + this, + packageName, + ); + } +} 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 2e80134c6..b75162d03 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 @@ -1,12 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_instance_manager/flutter_instance_manager.dart'; -import 'package:permission_handler_android/src/activity_aware.dart'; - -import 'android_object_mirrors/activity.dart'; -import 'android_object_mirrors/context.dart'; -import 'android_object_mirrors/intent.dart'; -import 'android_object_mirrors/uri.dart'; +import 'package:permission_handler_android/permission_handler_android.dart'; import 'permission_handler.pigeon.dart'; /// Handles initialization of Flutter APIs for the Android permission handler. @@ -15,9 +10,12 @@ class AndroidPermissionHandlerFlutterApis { AndroidPermissionHandlerFlutterApis({ ActivityFlutterApiImpl? activityFlutterApi, ContextFlutterApiImpl? contextFlutterApi, + PowerManagerFlutterApiImpl? powerManagerFlutterApi, }) { this.activityFlutterApi = activityFlutterApi ?? ActivityFlutterApiImpl(); this.contextFlutterApi = contextFlutterApi ?? ContextFlutterApiImpl(); + this.powerManagerFlutterApi = + powerManagerFlutterApi ?? PowerManagerFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -37,11 +35,15 @@ class AndroidPermissionHandlerFlutterApis { /// Flutter API for [Context]. late final ContextFlutterApiImpl contextFlutterApi; + /// Flutter API for [PowerManager]. + late final PowerManagerFlutterApiImpl powerManagerFlutterApi; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { ActivityFlutterApi.setup(activityFlutterApi); ContextFlutterApi.setup(contextFlutterApi); + PowerManagerFlutterApi.setup(powerManagerFlutterApi); _haveBeenSetUp = true; } @@ -176,6 +178,23 @@ class ActivityHostApiImpl extends ActivityHostApi { requestCode: requestCode, ); } + + /// Return the handle to a system-level service by name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + Future getSystemServiceFromInstance( + Activity activity, + String name, + ) async { + final String systemServiceId = await getSystemService( + instanceManager.getIdentifier(activity)!, + name, + ); + + return instanceManager.getInstanceWithWeakReference(systemServiceId); + } } /// Flutter API implementation of Activity. @@ -258,6 +277,21 @@ class ContextHostApiImpl extends ContextHostApi { instanceManager.getIdentifier(context)!, ); } + + /// Return the handle to a system-level service by name. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + Future getSystemServiceFromInstance( + Context context, + String name, + ) async { + final String systemServiceId = await getSystemService( + instanceManager.getIdentifier(context)!, + name, + ); + + return instanceManager.getInstanceWithWeakReference(systemServiceId); + } } /// Flutter API implementation of Context. @@ -413,3 +447,87 @@ class IntentHostApiImpl extends IntentHostApi { ); } } + +/// Host API implementation of PowerManager. +class PowerManagerHostApiImpl extends PowerManagerHostApi { + /// Creates a new instance of [PowerManagerHostApiImpl]. + PowerManagerHostApiImpl({ + 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; + + /// Returns whether the given application package name is on the device's power allowlist. + /// + /// Apps can be placed on the allowlist through the settings UI invoked by + /// [Settings.actionRequestIgnoreBatteryOptimizations]. + /// + /// Being on the power allowlist means that the system will not apply most + /// power saving features to the app. Guardrails for extreme cases may still + /// be applied. + /// + /// See https://developer.android.com/reference/android/os/PowerManager#isIgnoringBatteryOptimizations(java.lang.String). + Future isIgnoringBatteryOptimizationsFromInstance( + PowerManager powerManager, + String packageName, + ) async { + return await Build.version.sdkInt >= Build.versionCodes.m && + await isIgnoringBatteryOptimizations( + instanceManager.getIdentifier(powerManager)!, + packageName, + ); + } +} + +/// Flutter API implementation of PowerManager. +class PowerManagerFlutterApiImpl extends PowerManagerFlutterApi { + /// Constructs a new instance of [PowerManagerFlutterApiImpl]. + PowerManagerFlutterApiImpl({ + InstanceManager? instanceManager, + }) : _instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager _instanceManager; + + @override + void create(String instanceId) { + final PowerManager powerManager = PowerManager.detached(); + _instanceManager.addHostCreatedInstance( + powerManager, + instanceId, + ); + } + + @override + void dispose(String instanceId) { + _instanceManager.remove(instanceId); + } +} + +/// Host API implementation of Build.Version. +class BuildVersionHostApiImpl extends BuildVersionHostApi { + /// Creates a new instance of [BuildVersionHostApiImpl]. + BuildVersionHostApiImpl({ + 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/extensions.dart b/permission_handler_android/lib/src/extensions.dart index da29678f3..2cbb2e1ed 100644 --- a/permission_handler_android/lib/src/extensions.dart +++ b/permission_handler_android/lib/src/extensions.dart @@ -19,6 +19,8 @@ extension PermissionToManifestStrings on Permission { return [Manifest.permission.writeCalendar]; } else if (this == Permission.camera) { return [Manifest.permission.camera]; + } else if (this == Permission.ignoreBatteryOptimizations) { + return [Manifest.permission.requestIgnoreBatteryOptimizations]; } throw UnimplementedError( diff --git a/permission_handler_android/lib/src/missing_activity_exception.dart b/permission_handler_android/lib/src/missing_activity_exception.dart deleted file mode 100644 index 8899289b5..000000000 --- a/permission_handler_android/lib/src/missing_activity_exception.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'android_object_mirrors/activity.dart'; - -/// An exception thrown when no [Activity] is attached. -/// -/// This can happen if the app is running in the background, for example. -class MissingActivityException implements Exception { - /// Creates a new instance of [MissingActivityException]. - const MissingActivityException(); - - @override - String toString() => - 'MissingActivityException: There is no attached activity'; -} diff --git a/permission_handler_android/lib/src/permission_handler.pigeon.dart b/permission_handler_android/lib/src/permission_handler.pigeon.dart index bba1b1c8c..3f6ca1d0e 100644 --- a/permission_handler_android/lib/src/permission_handler.pigeon.dart +++ b/permission_handler_android/lib/src/permission_handler.pigeon.dart @@ -94,9 +94,9 @@ class _ActivityHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return ActivityResultPigeon.decode(readValue(buffer)!); - case 129: + case 129: return PermissionRequestResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -124,14 +124,12 @@ class ActivityHostApi { /// Gets whether the application should show UI with rationale before requesting a permission. /// /// See https://developer.android.com/reference/android/app/Activity#shouldShowRequestPermissionRationale(java.lang.String). - Future shouldShowRequestPermissionRationale( - String arg_instanceId, String arg_permission) async { + Future shouldShowRequestPermissionRationale(String arg_instanceId, String arg_permission) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_permission]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_permission]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -156,14 +154,12 @@ class ActivityHostApi { /// Determine whether the application has been granted a particular permission. /// /// See https://developer.android.com/reference/android/app/Activity#checkSelfPermission(java.lang.String). - Future checkSelfPermission( - String arg_instanceId, String arg_permission) async { + Future checkSelfPermission(String arg_instanceId, String arg_permission) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_permission]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_permission]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -191,15 +187,12 @@ class ActivityHostApi { /// https://developer.android.com/reference/android/app/Activity#requestPermissions(java.lang.String[],%20int) /// and /// https://developer.android.com/reference/android/app/Activity#onRequestPermissionsResult(int,%20java.lang.String[],%20int[]). - Future requestPermissions(String arg_instanceId, - List arg_permissions, int? arg_requestCode) async { + Future requestPermissions(String arg_instanceId, List arg_permissions, int? arg_requestCode) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_permissions, arg_requestCode]) - as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_permissions, arg_requestCode]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -224,15 +217,12 @@ class ActivityHostApi { /// Launch a new activity. /// /// See https://developer.android.com/reference/android/content/Context#startActivity(android.content.Intent). - Future startActivity( - String arg_instanceId, String arg_intentInstanceId) async { + Future startActivity(String arg_instanceId, String arg_intentInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivity', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivity', codec, binaryMessenger: _binaryMessenger); final List? replyList = - await channel.send([arg_instanceId, arg_intentInstanceId]) - as List?; + await channel.send([arg_instanceId, arg_intentInstanceId]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -254,8 +244,7 @@ class ActivityHostApi { /// See https://developer.android.com/reference/android/content/Context#getPackageName(). Future getPackageName(String arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getPackageName', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getPackageName', codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_instanceId]) as List?; @@ -280,18 +269,49 @@ class ActivityHostApi { } } + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + Future getSystemService(String arg_instanceId, String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getSystemService', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_instanceId, arg_name]) 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 String?)!; + } + } + /// Start an activity for which the application would like a result when it finished. /// /// See https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int). - Future startActivityForResult(String arg_instanceId, - String arg_intentInstanceId, int? arg_requestCode) async { + Future startActivityForResult(String arg_instanceId, String arg_intentInstanceId, int? arg_requestCode) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel.send( - [arg_instanceId, arg_intentInstanceId, arg_requestCode]) - as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_intentInstanceId, arg_requestCode]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -330,19 +350,17 @@ abstract class ActivityFlutterApi { /// Dispose of the Dart instance and remove it from the `InstanceManager`. void dispose(String instanceId); - static void setup(ActivityFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(ActivityFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.create', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.create was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.create was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -354,15 +372,14 @@ abstract class ActivityFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.dispose', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityFlutterApi.dispose was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -395,14 +412,12 @@ class ContextHostApi { /// Determine whether the application has been granted a particular permission. /// /// See https://developer.android.com/reference/android/content/Context#checkSelfPermission(java.lang.String). - Future checkSelfPermission( - String arg_instanceId, String arg_permission) async { + Future checkSelfPermission(String arg_instanceId, String arg_permission) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_permission]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_permission]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -427,15 +442,12 @@ class ContextHostApi { /// Launch a new activity. /// /// See https://developer.android.com/reference/android/content/Context#startActivity(android.content.Intent). - Future startActivity( - String arg_instanceId, String arg_intentInstanceId) async { + Future startActivity(String arg_instanceId, String arg_intentInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.startActivity', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.startActivity', codec, binaryMessenger: _binaryMessenger); final List? replyList = - await channel.send([arg_instanceId, arg_intentInstanceId]) - as List?; + await channel.send([arg_instanceId, arg_intentInstanceId]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -457,8 +469,7 @@ class ContextHostApi { /// See https://developer.android.com/reference/android/content/Context#getPackageName(). Future getPackageName(String arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.getPackageName', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.getPackageName', codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_instanceId]) as List?; @@ -482,6 +493,40 @@ class ContextHostApi { return (replyList[0] as String?)!; } } + + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + Future getSystemService(String arg_instanceId, String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.getSystemService', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_instanceId, arg_name]) 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 String?)!; + } + } } /// Flutter API for `Context`. @@ -500,19 +545,17 @@ abstract class ContextFlutterApi { /// Dispose of the Dart instance and remove it from the `InstanceManager`. void dispose(String instanceId); - static void setup(ContextFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(ContextFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.create', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.create was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.create was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -524,15 +567,14 @@ abstract class ContextFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.dispose', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextFlutterApi.dispose was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -571,8 +613,8 @@ class UriHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.permission_handler_android.UriHostApi.parse', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_uriString]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_uriString]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -599,8 +641,7 @@ class UriHostApi { /// See https://developer.android.com/reference/android/net/Uri#toString(). Future toStringAsync(String arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.UriHostApi.toStringAsync', - codec, + 'dev.flutter.pigeon.permission_handler_android.UriHostApi.toStringAsync', codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_instanceId]) as List?; @@ -648,8 +689,7 @@ class IntentHostApi { /// See https://developer.android.com/reference/android/content/Intent#Intent(). Future create(String arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.create', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.create', codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_instanceId]) as List?; @@ -674,11 +714,10 @@ class IntentHostApi { /// See https://developer.android.com/reference/android/content/Intent#setAction(java.lang.String). Future setAction(String arg_instanceId, String arg_action) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setAction', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setAction', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_action]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_action]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -700,11 +739,10 @@ class IntentHostApi { /// See https://developer.android.com/reference/android/content/Intent#setData(android.net.Uri). Future setData(String arg_instanceId, String arg_uriInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setData', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setData', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_uriInstanceId]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_uriInstanceId]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -730,11 +768,10 @@ class IntentHostApi { /// See https://developer.android.com/reference/android/content/Intent#addCategory(java.lang.String). Future addCategory(String arg_instanceId, String arg_category) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addCategory', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addCategory', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_category]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_category]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -756,11 +793,10 @@ class IntentHostApi { /// See https://developer.android.com/reference/android/content/Intent#addFlags(int). Future addFlags(String arg_instanceId, int arg_flags) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addFlags', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addFlags', codec, binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_instanceId, arg_flags]) as List?; + final List? replyList = + await channel.send([arg_instanceId, arg_flags]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -777,3 +813,169 @@ class IntentHostApi { } } } + +/// Host API for `PowerManager`. +/// +/// 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/PowerManager. +class PowerManagerHostApi { + /// Constructor for [PowerManagerHostApi]. 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. + PowerManagerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + /// Returns whether the given application package name is on the device's power allowlist. + /// + /// Apps can be placed on the allowlist through the settings UI invoked by + /// [Settings.actionRequestIgnoreBatteryOptimizations]. + /// + /// Being on the power allowlist means that the system will not apply most + /// power saving features to the app. Guardrails for extreme cases may still + /// be applied. + /// + /// See https://developer.android.com/reference/android/os/PowerManager#isIgnoringBatteryOptimizations(java.lang.String). + Future isIgnoringBatteryOptimizations(String arg_instanceId, String arg_packageName) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.PowerManagerHostApi.isIgnoringBatteryOptimizations', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_instanceId, arg_packageName]) 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?)!; + } + } +} + +/// Flutter API for `PowerManager`. +/// +/// This class may handle instantiating and adding Dart instances that are +/// attached to a native instance or receiving callback methods from an +/// overridden native class. +/// +/// See https://developer.android.com/reference/android/os/PowerManager. +abstract class PowerManagerFlutterApi { + static const MessageCodec codec = StandardMessageCodec(); + + /// Create a new Dart instance and add it to the `InstanceManager`. + void create(String instanceId); + + /// Dispose of the Dart instance and remove it from the `InstanceManager`. + void dispose(String instanceId); + + static void setup(PowerManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.create was null.'); + final List args = (message as List?)!; + final String? arg_instanceId = (args[0] as String?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.create was null, expected non-null String.'); + api.create(arg_instanceId!); + return; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.dispose was null.'); + final List args = (message as List?)!; + final String? arg_instanceId = (args[0] as String?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerFlutterApi.dispose was null, expected non-null String.'); + api.dispose(arg_instanceId!); + return; + }); + } + } + } +} + +/// Host API for `BuildVersion`. +/// +/// 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/Build.VERSION. +class BuildVersionHostApi { + /// Constructor for [BuildVersionHostApi]. 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. + BuildVersionHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + /// The SDK version of the software currently running on this hardware device. + /// + /// This value never changes while a device is booted, but it may increase + /// when the hardware manufacturer provides an OTA update. + /// + /// Possible values are defined in [Build.versionCodes]. + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT. + Future sdkInt() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.BuildVersionHostApi.sdkInt', 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 int?)!; + } + } +} diff --git a/permission_handler_android/lib/src/permission_handler_android.dart b/permission_handler_android/lib/src/permission_handler_android.dart index 874d6ccb2..b1d5bee7b 100644 --- a/permission_handler_android/lib/src/permission_handler_android.dart +++ b/permission_handler_android/lib/src/permission_handler_android.dart @@ -24,9 +24,13 @@ class PermissionHandlerAndroid extends PermissionHandlerPlatform { /// however, only [PermissionStatus.granted] or [PermissionStatus.denied] will /// be returned. /// - /// TODO(jweener): handle special permissions. + /// TODO(jweener): handle all special permissions. @override Future checkPermissionStatus(Permission permission) async { + if (Permission.ignoreBatteryOptimizations == permission) { + return _checkIgnoreBatteryOptimizationStatus(); + } + final Iterable statuses = await Future.wait( permission.manifestStrings.map( (String manifestString) async { @@ -73,9 +77,11 @@ class PermissionHandlerAndroid extends PermissionHandlerPlatform { return shouldShowRationales.any((bool shouldShow) => shouldShow); } - /// TODO(jweener): handle special permissions. + /// TODO(jweener): handle all special permissions. @override - Future requestPermission(Permission permission) async { + Future requestPermission( + Permission permission, + ) async { final Activity? activity = _activityManager.activity; if (activity == null) { debugPrint( @@ -83,8 +89,27 @@ class PermissionHandlerAndroid extends PermissionHandlerPlatform { return PermissionStatus.denied; } + if (Permission.ignoreBatteryOptimizations == permission) { + await _requestSpecialPermission( + action: Settings.actionRequestIgnoreBatteryOptimizations, + ); + return _checkIgnoreBatteryOptimizationStatus(); + } + + return _requestNormalPermission( + activity: activity, + permission: permission, + ); + } + + Future _requestNormalPermission({ + required Activity activity, + required Permission permission, + int? requestCode, + }) async { final PermissionRequestResult result = await activity.requestPermissions( permission.manifestStrings, + requestCode, ); final List permissions = @@ -106,6 +131,22 @@ class PermissionHandlerAndroid extends PermissionHandlerPlatform { return statuses.strictest; } + Future _requestSpecialPermission({ + required String action, + int? requestCode, + }) async { + await _activityManager.activity?.startActivityForResult( + Intent() + ..setAction(action) + ..setData( + Uri.parse( + 'package:${await _activityManager.applicationContext.getPackageName()}', + ), + ), + requestCode, + ); + } + /// TODO(jweener): return false if opening of the settings page fails. @override Future openAppSettings() async { @@ -126,4 +167,18 @@ class PermissionHandlerAndroid extends PermissionHandlerPlatform { return true; } + + Future _checkIgnoreBatteryOptimizationStatus() async { + final Context applicationContext = _activityManager.applicationContext; + final String packageName = await applicationContext.getPackageName(); + final Object? systemService = + await applicationContext.getSystemService(Context.powerService); + assert(systemService is PowerManager, + 'Expected PowerManager, got $systemService'); + final PowerManager powerManager = systemService as PowerManager; + final bool isIgnoring = + await powerManager.isIgnoringBatteryOptimizations(packageName); + + return isIgnoring ? PermissionStatus.granted : PermissionStatus.denied; + } } diff --git a/permission_handler_android/pigeons/android_permission_handler.dart b/permission_handler_android/pigeons/android_permission_handler.dart index d37bd53a5..fda46cac0 100644 --- a/permission_handler_android/pigeons/android_permission_handler.dart +++ b/permission_handler_android/pigeons/android_permission_handler.dart @@ -100,6 +100,18 @@ abstract class ActivityHostApi { String instanceId, ); + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + String getSystemService( + String instanceId, + String name, + ); + /// Start an activity for which the application would like a result when it finished. /// /// See https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int). @@ -158,6 +170,18 @@ abstract class ContextHostApi { String getPackageName( String instanceId, ); + + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + String getSystemService( + String instanceId, + String name, + ); } /// Flutter API for `Context`. @@ -260,3 +284,64 @@ abstract class IntentHostApi { int flags, ); } + +/// Host API for `PowerManager`. +/// +/// 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/PowerManager. +@HostApi(dartHostTestHandler: 'PowerManagerTestHostApi') +abstract class PowerManagerHostApi { + /// Returns whether the given application package name is on the device's power allowlist. + /// + /// Apps can be placed on the allowlist through the settings UI invoked by + /// [Settings.actionRequestIgnoreBatteryOptimizations]. + /// + /// Being on the power allowlist means that the system will not apply most + /// power saving features to the app. Guardrails for extreme cases may still + /// be applied. + /// + /// See https://developer.android.com/reference/android/os/PowerManager#isIgnoringBatteryOptimizations(java.lang.String). + bool isIgnoringBatteryOptimizations( + String instanceId, + String packageName, + ); +} + +/// Flutter API for `PowerManager`. +/// +/// This class may handle instantiating and adding Dart instances that are +/// attached to a native instance or receiving callback methods from an +/// overridden native class. +/// +/// See https://developer.android.com/reference/android/os/PowerManager. +@FlutterApi() +abstract class PowerManagerFlutterApi { + /// Create a new Dart instance and add it to the `InstanceManager`. + void create(String instanceId); + + /// Dispose of the Dart instance and remove it from the `InstanceManager`. + void dispose(String instanceId); +} + +/// Host API for `BuildVersion`. +/// +/// 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/Build.VERSION. +@HostApi(dartHostTestHandler: 'BuildVersionTestHostApi') +abstract class BuildVersionHostApi { + /// The SDK version of the software currently running on this hardware device. + /// + /// This value never changes while a device is booted, but it may increase + /// when the hardware manufacturer provides an OTA update. + /// + /// Possible values are defined in [Build.versionCodes]. + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT. + int sdkInt(); +} diff --git a/permission_handler_android/test/test_permission_handler.pigeon.dart b/permission_handler_android/test/test_permission_handler.pigeon.dart index f701f0971..6749de231 100644 --- a/permission_handler_android/test/test_permission_handler.pigeon.dart +++ b/permission_handler_android/test/test_permission_handler.pigeon.dart @@ -28,9 +28,9 @@ class _ActivityTestHostApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return ActivityResultPigeon.decode(readValue(buffer)!); - case 129: + case 129: return PermissionRequestResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -46,15 +46,13 @@ class _ActivityTestHostApiCodec extends StandardMessageCodec { /// /// See https://developer.android.com/reference/android/app/Activity. abstract class ActivityTestHostApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = _ActivityTestHostApiCodec(); /// Gets whether the application should show UI with rationale before requesting a permission. /// /// See https://developer.android.com/reference/android/app/Activity#shouldShowRequestPermissionRationale(java.lang.String). - bool shouldShowRequestPermissionRationale( - String instanceId, String permission); + bool shouldShowRequestPermissionRationale(String instanceId, String permission); /// Determine whether the application has been granted a particular permission. /// @@ -67,8 +65,7 @@ abstract class ActivityTestHostApi { /// https://developer.android.com/reference/android/app/Activity#requestPermissions(java.lang.String[],%20int) /// and /// https://developer.android.com/reference/android/app/Activity#onRequestPermissionsResult(int,%20java.lang.String[],%20int[]). - Future requestPermissions( - String instanceId, List permissions, int? requestCode); + Future requestPermissions(String instanceId, List permissions, int? requestCode); /// Launch a new activity. /// @@ -80,28 +77,31 @@ abstract class ActivityTestHostApi { /// See https://developer.android.com/reference/android/content/Context#getPackageName(). String getPackageName(String instanceId); + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + String getSystemService(String instanceId, String name); + /// Start an activity for which the application would like a result when it finished. /// /// See https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int). - Future startActivityForResult( - String instanceId, String intentInstanceId, int? requestCode); + Future startActivityForResult(String instanceId, String intentInstanceId, int? requestCode); - static void setup(ActivityTestHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(ActivityTestHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -109,26 +109,21 @@ abstract class ActivityTestHostApi { final String? arg_permission = (args[1] as String?); assert(arg_permission != null, 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.shouldShowRequestPermissionRationale was null, expected non-null String.'); - final bool output = api.shouldShowRequestPermissionRationale( - arg_instanceId!, arg_permission!); + final bool output = api.shouldShowRequestPermissionRationale(arg_instanceId!, arg_permission!); return [output]; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -136,55 +131,44 @@ abstract class ActivityTestHostApi { final String? arg_permission = (args[1] as String?); assert(arg_permission != null, 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.checkSelfPermission was null, expected non-null String.'); - final int output = - api.checkSelfPermission(arg_instanceId!, arg_permission!); + final int output = api.checkSelfPermission(arg_instanceId!, arg_permission!); return [output]; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions was null, expected non-null String.'); - final List? arg_permissions = - (args[1] as List?)?.cast(); + final List? arg_permissions = (args[1] as List?)?.cast(); assert(arg_permissions != null, 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.requestPermissions was null, expected non-null List.'); final int? arg_requestCode = (args[2] as int?); - final PermissionRequestResult output = await api.requestPermissions( - arg_instanceId!, arg_permissions!, arg_requestCode); + final PermissionRequestResult output = await api.requestPermissions(arg_instanceId!, arg_permissions!, arg_requestCode); return [output]; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivity', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivity', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivity was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivity was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -199,18 +183,14 @@ abstract class ActivityTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getPackageName', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getPackageName', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getPackageName was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getPackageName was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -222,18 +202,36 @@ abstract class ActivityTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult', - codec, + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getSystemService', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getSystemService was null.'); + final List args = (message as List?)!; + final String? arg_instanceId = (args[0] as String?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getSystemService was null, expected non-null String.'); + final String? arg_name = (args[1] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.getSystemService was null, expected non-null String.'); + final String output = api.getSystemService(arg_instanceId!, arg_name!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -242,8 +240,7 @@ abstract class ActivityTestHostApi { assert(arg_intentInstanceId != null, 'Argument for dev.flutter.pigeon.permission_handler_android.ActivityHostApi.startActivityForResult was null, expected non-null String.'); final int? arg_requestCode = (args[2] as int?); - final ActivityResultPigeon output = await api.startActivityForResult( - arg_instanceId!, arg_intentInstanceId!, arg_requestCode); + final ActivityResultPigeon output = await api.startActivityForResult(arg_instanceId!, arg_intentInstanceId!, arg_requestCode); return [output]; }); } @@ -259,8 +256,7 @@ abstract class ActivityTestHostApi { /// /// See https://developer.android.com/reference/android/content/Context. abstract class ContextTestHostApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = StandardMessageCodec(); /// Determine whether the application has been granted a particular permission. @@ -278,22 +274,26 @@ abstract class ContextTestHostApi { /// See https://developer.android.com/reference/android/content/Context#getPackageName(). String getPackageName(String instanceId); - static void setup(ContextTestHostApi? api, - {BinaryMessenger? binaryMessenger}) { + /// Return the handle to a system-level service by name. + /// + /// The class of the returned object varies by the requested name. + /// + /// Returns the instance ID of the service. + /// + /// See https://developer.android.com/reference/android/content/Context#getSystemService(java.lang.String). + String getSystemService(String instanceId, String name); + + static void setup(ContextTestHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -301,26 +301,21 @@ abstract class ContextTestHostApi { final String? arg_permission = (args[1] as String?); assert(arg_permission != null, 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.checkSelfPermission was null, expected non-null String.'); - final int output = - api.checkSelfPermission(arg_instanceId!, arg_permission!); + final int output = api.checkSelfPermission(arg_instanceId!, arg_permission!); return [output]; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.startActivity', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.startActivity', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.startActivity was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.startActivity was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -335,18 +330,14 @@ abstract class ContextTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.getPackageName', - codec, + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.getPackageName', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.getPackageName was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.getPackageName was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -356,6 +347,28 @@ abstract class ContextTestHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.ContextHostApi.getSystemService', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.getSystemService was null.'); + final List args = (message as List?)!; + final String? arg_instanceId = (args[0] as String?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.getSystemService was null, expected non-null String.'); + final String? arg_name = (args[1] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.ContextHostApi.getSystemService was null, expected non-null String.'); + final String output = api.getSystemService(arg_instanceId!, arg_name!); + return [output]; + }); + } + } } } @@ -367,8 +380,7 @@ abstract class ContextTestHostApi { /// /// See https://developer.android.com/reference/android/net/Uri. abstract class UriTestHostApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = StandardMessageCodec(); /// Creates a Uri which parses the given encoded URI string. @@ -391,18 +403,14 @@ abstract class UriTestHostApi { static void setup(UriTestHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.UriHostApi.parse', - codec, + 'dev.flutter.pigeon.permission_handler_android.UriHostApi.parse', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.UriHostApi.parse was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.UriHostApi.parse was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -417,18 +425,14 @@ abstract class UriTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.UriHostApi.toStringAsync', - codec, + 'dev.flutter.pigeon.permission_handler_android.UriHostApi.toStringAsync', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.UriHostApi.toStringAsync was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.UriHostApi.toStringAsync was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -449,8 +453,7 @@ abstract class UriTestHostApi { /// /// See https://developer.android.com/reference/android/content/Intent. abstract class IntentTestHostApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = StandardMessageCodec(); /// Creates an empty intent. @@ -482,22 +485,17 @@ abstract class IntentTestHostApi { /// See https://developer.android.com/reference/android/content/Intent#addFlags(int). void addFlags(String instanceId, int flags); - static void setup(IntentTestHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(IntentTestHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.create', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.create was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.create was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -509,18 +507,14 @@ abstract class IntentTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setAction', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setAction', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.setAction was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.setAction was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -535,18 +529,14 @@ abstract class IntentTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setData', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.setData', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.setData was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.setData was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -561,18 +551,14 @@ abstract class IntentTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addCategory', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addCategory', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.addCategory was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.addCategory was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -587,18 +573,14 @@ abstract class IntentTestHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addFlags', - codec, + 'dev.flutter.pigeon.permission_handler_android.IntentHostApi.addFlags', codec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.addFlags was null.'); + 'Argument for dev.flutter.pigeon.permission_handler_android.IntentHostApi.addFlags was null.'); final List args = (message as List?)!; final String? arg_instanceId = (args[0] as String?); assert(arg_instanceId != null, @@ -613,3 +595,91 @@ abstract class IntentTestHostApi { } } } + +/// Host API for `PowerManager`. +/// +/// 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/PowerManager. +abstract class PowerManagerTestHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + /// Returns whether the given application package name is on the device's power allowlist. + /// + /// Apps can be placed on the allowlist through the settings UI invoked by + /// [Settings.actionRequestIgnoreBatteryOptimizations]. + /// + /// Being on the power allowlist means that the system will not apply most + /// power saving features to the app. Guardrails for extreme cases may still + /// be applied. + /// + /// See https://developer.android.com/reference/android/os/PowerManager#isIgnoringBatteryOptimizations(java.lang.String). + bool isIgnoringBatteryOptimizations(String instanceId, String packageName); + + static void setup(PowerManagerTestHostApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.PowerManagerHostApi.isIgnoringBatteryOptimizations', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerHostApi.isIgnoringBatteryOptimizations was null.'); + final List args = (message as List?)!; + final String? arg_instanceId = (args[0] as String?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerHostApi.isIgnoringBatteryOptimizations was null, expected non-null String.'); + final String? arg_packageName = (args[1] as String?); + assert(arg_packageName != null, + 'Argument for dev.flutter.pigeon.permission_handler_android.PowerManagerHostApi.isIgnoringBatteryOptimizations was null, expected non-null String.'); + final bool output = api.isIgnoringBatteryOptimizations(arg_instanceId!, arg_packageName!); + return [output]; + }); + } + } + } +} + +/// Host API for `BuildVersion`. +/// +/// 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/Build.VERSION. +abstract class BuildVersionTestHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + /// The SDK version of the software currently running on this hardware device. + /// + /// This value never changes while a device is booted, but it may increase + /// when the hardware manufacturer provides an OTA update. + /// + /// Possible values are defined in [Build.versionCodes]. + /// + /// See https://developer.android.com/reference/android/os/Build.VERSION#SDK_INT. + int sdkInt(); + + static void setup(BuildVersionTestHostApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.permission_handler_android.BuildVersionHostApi.sdkInt', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + // ignore message + final int output = api.sdkInt(); + return [output]; + }); + } + } + } +}