Handle android permission request on dart side

This commit is contained in:
Ming Ming 2022-05-07 18:20:47 +08:00
parent 80689fa431
commit 1cf2e565ba
5 changed files with 242 additions and 1 deletions

View file

@ -0,0 +1,18 @@
import 'package:logging/logging.dart';
import 'package:nc_photos/stream_extension.dart';
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
Future<Map<String, int>> requestPermissionsForResult(
List<String> permissions) async {
Map<String, int>? result;
final resultFuture = Permission.stream
.whereType<PermissionRequestResult>()
.first
.then((ev) => result = ev.grantResults);
await Permission.request(permissions);
await resultFuture;
_log.info("[requestPermissionsForResult] Result: $result");
return result!;
}
final _log = Logger("mobile.android.permission_util");

View file

@ -11,7 +11,8 @@ import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
class NcPhotosPlugin : FlutterPlugin, ActivityAware,
PluginRegistry.ActivityResultListener {
PluginRegistry.ActivityResultListener,
PluginRegistry.RequestPermissionsResultListener {
companion object {
const val ACTION_SHOW_IMAGE_PROCESSOR_RESULT =
K.ACTION_SHOW_IMAGE_PROCESSOR_RESULT
@ -81,6 +82,19 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
contentUriMethodChannel.setMethodCallHandler(
ContentUriChannelHandler(flutterPluginBinding.applicationContext)
)
permissionChannelHandler =
PermissionChannelHandler(flutterPluginBinding.applicationContext)
permissionChannel = EventChannel(
flutterPluginBinding.binaryMessenger,
PermissionChannelHandler.EVENT_CHANNEL
)
permissionChannel.setStreamHandler(permissionChannelHandler)
permissionMethodChannel = MethodChannel(
flutterPluginBinding.binaryMessenger,
PermissionChannelHandler.METHOD_CHANNEL
)
permissionMethodChannel.setMethodCallHandler(permissionChannelHandler)
}
override fun onDetachedFromEngine(
@ -94,30 +108,39 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
mediaStoreMethodChannel.setMethodCallHandler(null)
imageProcessorMethodChannel.setMethodCallHandler(null)
contentUriMethodChannel.setMethodCallHandler(null)
permissionMethodChannel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
mediaStoreChannelHandler.onAttachedToActivity(binding)
permissionChannelHandler.onAttachedToActivity(binding)
pluginBinding = binding
binding.addActivityResultListener(this)
binding.addRequestPermissionsResultListener(this)
}
override fun onReattachedToActivityForConfigChanges(
binding: ActivityPluginBinding
) {
mediaStoreChannelHandler.onReattachedToActivityForConfigChanges(binding)
permissionChannelHandler.onReattachedToActivityForConfigChanges(binding)
pluginBinding = binding
binding.addActivityResultListener(this)
binding.addRequestPermissionsResultListener(this)
}
override fun onDetachedFromActivity() {
mediaStoreChannelHandler.onDetachedFromActivity()
permissionChannelHandler.onDetachedFromActivity()
pluginBinding?.removeActivityResultListener(this)
pluginBinding?.removeRequestPermissionsResultListener(this)
}
override fun onDetachedFromActivityForConfigChanges() {
mediaStoreChannelHandler.onDetachedFromActivityForConfigChanges()
permissionChannelHandler.onDetachedFromActivityForConfigChanges()
pluginBinding?.removeActivityResultListener(this)
pluginBinding?.removeRequestPermissionsResultListener(this)
}
override fun onActivityResult(
@ -141,6 +164,27 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
}
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray
): Boolean {
return try {
when (requestCode) {
K.PERMISSION_REQUEST_CODE -> {
permissionChannelHandler.onRequestPermissionsResult(
requestCode, permissions, grantResults
)
}
else -> false
}
} catch (e: Throwable) {
Log.e(
TAG, "Failed while onActivityResult, requestCode=$requestCode"
)
false
}
}
private var pluginBinding: ActivityPluginBinding? = null
private lateinit var lockChannel: MethodChannel
@ -151,7 +195,10 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware,
private lateinit var mediaStoreMethodChannel: MethodChannel
private lateinit var imageProcessorMethodChannel: MethodChannel
private lateinit var contentUriMethodChannel: MethodChannel
private lateinit var permissionChannel: EventChannel
private lateinit var permissionMethodChannel: MethodChannel
private lateinit var lockChannelHandler: LockChannelHandler
private lateinit var mediaStoreChannelHandler: MediaStoreChannelHandler
private lateinit var permissionChannelHandler: PermissionChannelHandler
}

View file

@ -0,0 +1,113 @@
package com.nkming.nc_photos.plugin
import android.app.Activity
import android.content.Context
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.PluginRegistry
class PermissionChannelHandler(context: Context) :
MethodChannel.MethodCallHandler, EventChannel.StreamHandler, ActivityAware,
PluginRegistry.RequestPermissionsResultListener {
companion object {
const val EVENT_CHANNEL = "${K.LIB_ID}/permission"
const val METHOD_CHANNEL = "${K.LIB_ID}/permission_method"
private const val TAG = "PermissionChannelHandler"
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onReattachedToActivityForConfigChanges(
binding: ActivityPluginBinding
) {
activity = binding.activity
}
override fun onDetachedFromActivity() {
activity = null
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray
): Boolean {
return if (requestCode == K.PERMISSION_REQUEST_CODE) {
eventSink?.success(buildMap {
put("event", "RequestPermissionsResult")
put(
"grantResults",
permissions.zip(grantResults.toTypedArray()).toMap()
)
})
true
} else {
false
}
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
eventSink = events
}
override fun onCancel(arguments: Any?) {
eventSink = null
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"request" -> {
try {
request(call.argument("permissions")!!, result)
} catch (e: Throwable) {
result.error("systemException", e.toString(), null)
}
}
"hasWriteExternalStorage" -> {
try {
result.success(
PermissionUtil.hasWriteExternalStorage(context)
)
} catch (e: Throwable) {
result.error("systemException", e.toString(), null)
}
}
"hasReadExternalStorage" -> {
try {
result.success(
PermissionUtil.hasReadExternalStorage(context)
)
} catch (e: Throwable) {
result.error("systemException", e.toString(), null)
}
}
else -> result.notImplemented()
}
}
private fun request(
permissions: List<String>, result: MethodChannel.Result
) {
if (activity == null) {
result.error("systemException", "Activity is not ready", null)
return
}
PermissionUtil.request(activity!!, *permissions.toTypedArray())
result.success(null)
}
private val context = context
private var activity: Activity? = null
private var eventSink: EventChannel.EventSink? = null
}

View file

@ -7,3 +7,4 @@ export 'src/lock.dart';
export 'src/media_store.dart';
export 'src/native_event.dart';
export 'src/notification.dart';
export 'src/permission.dart';

View file

@ -0,0 +1,62 @@
// ignore_for_file: constant_identifier_names
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos_plugin/src/k.dart' as k;
class Permission {
static const READ_EXTERNAL_STORAGE =
"android.permission.READ_EXTERNAL_STORAGE";
static const WRITE_EXTERNAL_STORAGE =
"android.permission.WRITE_EXTERNAL_STORAGE";
static Future<void> request(List<String> permissions) =>
_methodChannel.invokeMethod("request", <String, dynamic>{
"permissions": permissions,
});
static Future<bool> hasWriteExternalStorage() async {
return (await _methodChannel
.invokeMethod<bool>("hasWriteExternalStorage"))!;
}
static Future<bool> hasReadExternalStorage() async {
return (await _methodChannel.invokeMethod<bool>("hasReadExternalStorage"))!;
}
static Stream get stream => _eventStream;
static late final _eventStream =
_eventChannel.receiveBroadcastStream().map((event) {
if (event is Map) {
switch (event["event"]) {
case _eventRequestPermissionsResult:
return PermissionRequestResult(
(event["grantResults"] as Map).cast<String, int>());
default:
_log.shout("[_eventStream] Unknown event: ${event["event"]}");
}
} else {
return event;
}
});
static const _eventChannel = EventChannel("${k.libId}/permission");
static const _methodChannel = MethodChannel("${k.libId}/permission_method");
static const _eventRequestPermissionsResult = "RequestPermissionsResult";
static final _log = Logger("plugin.permission.Permission");
}
class PermissionRequestResult {
static const granted = 0;
static const denied = -1;
const PermissionRequestResult(this.grantResults);
final Map<String, int> grantResults;
}