From 6fde8d0a20bdf0feabc031cb97bdbda4042e163a Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Tue, 15 Aug 2023 08:43:35 +0800 Subject: [PATCH 01/18] config file for android API 34 --- android/build.gradle | 2 +- example/android/app/build.gradle | 2 +- example/android/app/src/main/AndroidManifest.xml | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 9d553c9d..bbe8fd67 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,7 +29,7 @@ android { namespace 'com.flutterandies.photo_manager' } - compileSdkVersion 33 + compileSdkVersion 34 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 0df2d0b0..98a767a5 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -23,7 +23,7 @@ if (flutterVersionName == null) { def compileVersion = localProperties.getProperty('android.version') if (compileVersion == null) { - compileVersion = "33" + compileVersion = "34" } apply plugin: 'com.android.application' diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index eec86ac2..885ff240 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + From 6f4167523db08dc25ada95860a46035edc99f1e4 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Fri, 1 Sep 2023 11:10:12 +0800 Subject: [PATCH 02/18] feat(android): permission for android14(API34) --- .../photo_manager/constant/Methods.kt | 1 + .../photo_manager/core/PhotoManagerPlugin.kt | 9 +++ .../permission/PermissionsUtils.kt | 56 ++++++++++++++++++- lib/src/internal/plugin.dart | 4 +- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt index 62fd8adc..7ae3d5e1 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt @@ -10,6 +10,7 @@ class Methods { const val releaseMemoryCache = "releaseMemoryCache" const val requestPermissionExtend = "requestPermissionExtend" + const val presentLimited = "presentLimited" const val getThumbnail = "getThumb" const val requestCacheAssetsThumbnail = "requestCacheAssetsThumb" diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index 41dbc0cd..ef07ab7f 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -196,6 +196,15 @@ class PhotoManagerPlugin( } } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + permissionsUtils.addManifestWithPermission34( + applicationContext, + permissions, + call, + resultHandler + ) + } + val utils = permissionsUtils.apply { withActivity(activity) permissionsListener = object : PermissionsListener { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt index 1e445a96..fc1cb99a 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt @@ -71,7 +71,8 @@ class PermissionsUtils { * @return 返回 [PermissionsUtils] 自身,进行链式调用 */ fun getPermissions(requestCode: Int, permissions: List): PermissionsUtils { - return getPermissionsWithTips(requestCode, *permissions.toTypedArray()) + val permissionSet = permissions.toSet() + return getPermissionsWithTips(requestCode, *permissionSet.toTypedArray()) } /** @@ -119,7 +120,9 @@ class PermissionsUtils { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { resetStatus() for (p in permissions) { - if (mActivity!!.checkSelfPermission(p) == PackageManager.PERMISSION_DENIED) { + if (p == Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) { + needToRequestPermissionsList.add(p) + } else if (mActivity!!.checkSelfPermission(p) == PackageManager.PERMISSION_DENIED) { // Add the denied permission to the pending list. needToRequestPermissionsList.add(p) } @@ -143,6 +146,13 @@ class PermissionsUtils { permissions: Array, grantResults: IntArray ): PermissionsUtils { + var userSelectedResult = false + if (permissions.contains(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)) { + // Get it result + val index = permissions.indexOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) + val result = grantResults[index] + userSelectedResult = result == PackageManager.PERMISSION_GRANTED + } if (requestCode == this.requestCode) { for (i in permissions.indices) { LogUtils.info("Returned permissions: " + permissions[i]) @@ -152,6 +162,17 @@ class PermissionsUtils { grantedPermissionsList.add(permissions[i]) } } + + if (userSelectedResult) { + // 如果包含 READ_MEDIA_VISUAL_USER_SELECTED 的允许的权限,则说明可以忽略 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 的拒绝 + deniedPermissionsList.toList().forEach { + if (it == Manifest.permission.READ_MEDIA_IMAGES || it == Manifest.permission.READ_MEDIA_VIDEO) { + deniedPermissionsList.remove(it) + grantedPermissionsList.add(it) + } + } + } + if (deniedPermissionsList.isNotEmpty()) { // 回调用户拒绝监听 permissionsListener!!.onDenied(deniedPermissionsList, grantedPermissionsList) @@ -246,7 +267,11 @@ class PermissionsUtils { val haveVideo = RequestTypeUtils.containsVideo(type) val haveAudio = RequestTypeUtils.containsAudio(type) - fun checkAndAddPermission(requestHavePermission: Boolean, tag: String, manifestPermission: String) { + fun checkAndAddPermission( + requestHavePermission: Boolean, + tag: String, + manifestPermission: String + ) { if (!requestHavePermission) { return } @@ -267,6 +292,30 @@ class PermissionsUtils { } + @RequiresApi(34) + fun addManifestWithPermission34( + context: Context, + permissions: ArrayList, + call: MethodCall, + resultHandler: ResultHandler + ) { + if (havePermissionInManifest( + context, + Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED + ) + ) { + permissions.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) + + if (Methods.presentLimited != call.method) { + permissions.add(Manifest.permission.READ_MEDIA_IMAGES) + permissions.add(Manifest.permission.READ_MEDIA_VIDEO) + } else { + permissions.remove(Manifest.permission.READ_MEDIA_IMAGES) + permissions.remove(Manifest.permission.READ_MEDIA_VIDEO) + } + } + } + fun havePermissionInManifest(context: Context, permission: String): Boolean { val applicationInfo = context.applicationInfo val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -283,4 +332,5 @@ class PermissionsUtils { } return packageInfo.requestedPermissions.contains(permission) } + } diff --git a/lib/src/internal/plugin.dart b/lib/src/internal/plugin.dart index 8fa5c620..668f57be 100644 --- a/lib/src/internal/plugin.dart +++ b/lib/src/internal/plugin.dart @@ -542,8 +542,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } Future presentLimited() async { - assert(Platform.isIOS); - if (Platform.isIOS) { + assert(Platform.isIOS || Platform.isAndroid); + if (Platform.isIOS || Platform.isAndroid) { return _channel.invokeMethod(PMConstants.mPresentLimited); } } From d9dd3d173481a2103848dbff22a09390b312d484 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Fri, 1 Sep 2023 15:41:27 +0800 Subject: [PATCH 03/18] feat: About check presentLimited SDK version --- .../photo_manager/core/PhotoManagerPlugin.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index ef07ab7f..70653d48 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -197,12 +197,22 @@ class PhotoManagerPlugin( } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + if (Methods.presentLimited == call.method) { + resultHandler.replyError("The $Methods.presentLimited must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") + return + } + permissionsUtils.addManifestWithPermission34( applicationContext, permissions, call, resultHandler ) + } else { + if (Methods.presentLimited == call.method) { + resultHandler.replyError("The $Methods.presentLimited must use Android 14(API 34) or higher.") + return + } } val utils = permissionsUtils.apply { @@ -252,6 +262,10 @@ class PhotoManagerPlugin( resultHandler.reply(PermissionResult.Authorized.value) return } + if (call.method == Methods.presentLimited) { + resultHandler.reply(null) + return + } runOnBackground { try { handleMethodResult(call, resultHandler, needLocationPermission) From 5bbf578e0b3a220a46066ce8934c14f2762650b1 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Fri, 1 Sep 2023 15:45:32 +0800 Subject: [PATCH 04/18] log: Fix typo --- .../fluttercandies/photo_manager/core/PhotoManagerPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index 70653d48..4346d474 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -198,7 +198,7 @@ class PhotoManagerPlugin( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { if (Methods.presentLimited == call.method) { - resultHandler.replyError("The $Methods.presentLimited must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") + resultHandler.replyError("The ${Methods.presentLimited} must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") return } @@ -210,7 +210,7 @@ class PhotoManagerPlugin( ) } else { if (Methods.presentLimited == call.method) { - resultHandler.replyError("The $Methods.presentLimited must use Android 14(API 34) or higher.") + resultHandler.replyError("The ${Methods.presentLimited} must use Android 14(API 34) or higher.") return } } From c075dc005767e9340baa9514fa863eb2013406a0 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Fri, 1 Sep 2023 17:57:32 +0800 Subject: [PATCH 05/18] feat: for present limit --- .../photo_manager/core/PhotoManagerPlugin.kt | 10 ++++++++-- .../photo_manager/permission/PermissionsUtils.kt | 3 ++- example/lib/page/home_page.dart | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index 4346d474..0a34fa06 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -198,8 +198,14 @@ class PhotoManagerPlugin( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { if (Methods.presentLimited == call.method) { - resultHandler.replyError("The ${Methods.presentLimited} must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") - return + val havePermissionInManifest = permissionsUtils.havePermissionInManifest( + applicationContext, + Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED + ) + if (!havePermissionInManifest) { + resultHandler.replyError("The ${Methods.presentLimited} must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") + return + } } permissionsUtils.addManifestWithPermission34( diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt index fc1cb99a..938824c5 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt @@ -306,12 +306,13 @@ class PermissionsUtils { ) { permissions.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) - if (Methods.presentLimited != call.method) { + if (Methods.presentLimited == call.method) { permissions.add(Manifest.permission.READ_MEDIA_IMAGES) permissions.add(Manifest.permission.READ_MEDIA_VIDEO) } else { permissions.remove(Manifest.permission.READ_MEDIA_IMAGES) permissions.remove(Manifest.permission.READ_MEDIA_VIDEO) + permissions.remove(Manifest.permission.ACCESS_MEDIA_LOCATION) } } } diff --git a/example/lib/page/home_page.dart b/example/lib/page/home_page.dart index c2e5f417..4c29f2ab 100644 --- a/example/lib/page/home_page.dart +++ b/example/lib/page/home_page.dart @@ -45,7 +45,7 @@ class _NewHomePageState extends State { title: 'Get all gallery list', onPressed: _scanGalleryList, ), - if (Platform.isIOS) + if (Platform.isIOS || Platform.isAndroid) CustomButton( title: 'Change limited photos with PhotosUI', onPressed: _changeLimitPhotos, From 936473085def27596c849697aa92c21298aaa80f Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Fri, 15 Sep 2023 17:46:10 +0800 Subject: [PATCH 06/18] progressing: Permission for android - Refactor the permission management part of the Android part - In progress, not completed --- .../photo_manager/constant/Methods.kt | 74 +++- .../photo_manager/core/PhotoManager.kt | 2 +- .../photo_manager/core/PhotoManagerPlugin.kt | 327 ++++++++++-------- .../permission/MethodPermissionListener.kt | 28 ++ .../permission/PermissionsUtils.kt | 157 ++++++++- .../photo_manager/thumb/ThumbnailUtil.kt | 6 +- .../photo_manager/util/ResultHandler.kt | 11 +- flow_chart/permission.dio | 1 + lib/src/internal/constants.dart | 2 +- lib/src/internal/map_interface.dart | 12 + lib/src/types/types.dart | 44 ++- 11 files changed, 487 insertions(+), 177 deletions(-) create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt create mode 100644 flow_chart/permission.dio create mode 100644 lib/src/internal/map_interface.dart diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt index 7ae3d5e1..c7a2882e 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/constant/Methods.kt @@ -2,16 +2,62 @@ package com.fluttercandies.photo_manager.constant class Methods { companion object { + // Not need permission methods const val log = "log" const val openSetting = "openSetting" const val forceOldAPI = "forceOldApi" const val systemVersion = "systemVersion" const val clearFileCache = "clearFileCache" const val releaseMemoryCache = "releaseMemoryCache" + const val ignorePermissionCheck = "ignorePermissionCheck" + fun isNotNeedPermissionMethod(method: String): Boolean { + return method in arrayOf( + log, + openSetting, + forceOldAPI, + systemVersion, + clearFileCache, + releaseMemoryCache, + ignorePermissionCheck, + ) + } + // Not need permission methods end + + // About permission start const val requestPermissionExtend = "requestPermissionExtend" const val presentLimited = "presentLimited" + fun isPermissionMethod(method: String): Boolean { + return method in arrayOf( + requestPermissionExtend, + presentLimited, + ) + } + // About permission end + + /// Have [requestType] start + const val fetchPathProperties = "fetchPathProperties" + const val getAssetPathList = "getAssetPathList" + const val getAssetListPaged = "getAssetListPaged" + const val getAssetListRange = "getAssetListRange" + const val getAssetCount = "getAssetCount" + const val getAssetsByRange = "getAssetsByRange" + + val haveRequestTypeMethods = arrayOf( + fetchPathProperties, + getAssetPathList, + getAssetListPaged, + getAssetListRange, + getAssetCount, + getAssetsByRange, + ) + + fun isHaveRequestTypeMethod(method: String): Boolean { + return method in haveRequestTypeMethods + } + /// Have [requestType] end + const val getThumbnail = "getThumb" const val requestCacheAssetsThumbnail = "requestCacheAssetsThumb" const val cancelCacheRequests = "cancelCacheRequests" @@ -32,20 +78,22 @@ class Methods { const val removeNoExistsAssets = "removeNoExistsAssets" const val getColumnNames = "getColumnNames" - const val getAssetCount = "getAssetCount" - const val getAssetsByRange = "getAssetsByRange" + val needMediaLocationMethods = arrayOf( + getLatLng, + getFullFile, + getOriginBytes, + ) - /// Below methods have [RequestType] params, thus permissions are required for Android 13. - const val fetchPathProperties = "fetchPathProperties" - const val getAssetPathList = "getAssetPathList" - const val getAssetListPaged = "getAssetListPaged" - const val getAssetListRange = "getAssetListRange" + fun isNeedMediaLocationMethod(method: String): Boolean { + return method in needMediaLocationMethods + } - val android13PermissionMethods = arrayOf( - fetchPathProperties, - getAssetPathList, - getAssetListPaged, - getAssetListRange, - ) + fun otherMethods(method: String): Boolean { + return (isNotNeedPermissionMethod(method) || + isPermissionMethod(method) || + isHaveRequestTypeMethod(method) || + isNeedMediaLocationMethod(method)) + .not() + } } } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt index 3494f649..47bca1d0 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt @@ -101,7 +101,7 @@ class PhotoManager(private val context: Context) { format, quality, frame, - resultHandler.result + resultHandler, ) } catch (e: Exception) { Log.e(LogUtils.TAG, "get $id thumbnail error, width : $width, height: $height", e) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index 0a34fa06..a9db56e1 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -3,9 +3,11 @@ package com.fluttercandies.photo_manager.core import android.Manifest import android.app.Activity import android.content.Context +import android.content.pm.PackageManager import android.os.Build import android.os.Handler import android.os.Looper +import androidx.activity.result.contract.ActivityResultContracts import com.bumptech.glide.Glide import com.fluttercandies.photo_manager.constant.Methods import io.flutter.plugin.common.BinaryMessenger @@ -77,178 +79,219 @@ class PhotoManagerPlugin( override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { val resultHandler = ResultHandler(result, call) + val method = call.method - if (call.method == "ignorePermissionCheck") { - val ignore = call.argument("ignore")!! - ignorePermissionCheck = ignore - resultHandler.reply(ignore) + if (Methods.isNotNeedPermissionMethod(method)) { + handleNotNeedPermissionMethod(resultHandler) return } - val handleResult = when (call.method) { - Methods.releaseMemoryCache -> { - // The plugin will not hold instances cache on Android. - resultHandler.reply(1) - true - } + if (Methods.isPermissionMethod(method)) { + handlePermissionMethod(resultHandler) + return + } + + if (ignorePermissionCheck) { + val haveLocationPermission = permissionsUtils.haveLocationPermission(applicationContext) + + onHandlePermissionResult( + resultHandler, + haveLocationPermission + ) + return + } + +// permissionsUtils.withActivity(activity) +// .handlePermission(resultHandler, this::onHandlePermissionResult) + +// if (permissionsUtils.isRequesting) { +// resultHandler.replyError( +// "PERMISSION_REQUESTING", +// "Another permission request is still ongoing. Please request after the existing one is done.", +// null +// ) +// return +// } +// +// val needWritePermission = +// permissionsUtils.needWriteExternalStorage(call) +// && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q +// && permissionsUtils.havePermissionInManifest( +// applicationContext, +// Manifest.permission.WRITE_EXTERNAL_STORAGE +// ) +// val needReadPermission = +// Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU +// && permissionsUtils.havePermissionInManifest( +// applicationContext, +// Manifest.permission.READ_EXTERNAL_STORAGE +// ) +// val needLocationPermission = +// permissionsUtils.needAccessLocation(call) +// && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q +// && permissionsUtils.havePermissionInManifest( +// applicationContext, +// Manifest.permission.ACCESS_MEDIA_LOCATION +// ) +// val permissions = arrayListOf() +// if (needReadPermission) { +// permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE) +// } +// if (needWritePermission) { +// permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) +// } +// if (needLocationPermission) { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { +// permissions.add(Manifest.permission.ACCESS_MEDIA_LOCATION) +// } +// } +// +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// permissionsUtils.addManifestWithPermission33( +// applicationContext, +// permissions, +// call, +// resultHandler +// ) +// if (resultHandler.isReplied()) { +// return +// } +// } +// +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { +// if (Methods.presentLimited == call.method) { +// val havePermissionInManifest = permissionsUtils.havePermissionInManifest( +// applicationContext, +// Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED +// ) +// if (!havePermissionInManifest) { +// resultHandler.replyError("The ${Methods.presentLimited} must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") +// return +// } +// } +// +// permissionsUtils.addManifestWithPermission34( +// applicationContext, +// permissions, +// call, +// resultHandler +// ) +// } else { +// if (Methods.presentLimited == call.method) { +// resultHandler.replyError("The ${Methods.presentLimited} must use Android 14(API 34) or higher.") +// return +// } +// } +// +// val utils = permissionsUtils.apply { +// withActivity(activity) +// permissionsListener = object : PermissionsListener { +// override fun onGranted() { +// LogUtils.info("onGranted call.method = ${call.method}") +// onHandlePermissionResult(call, resultHandler, needLocationPermission) +// } +// +// override fun onDenied( +// deniedPermissions: MutableList, +// grantedPermissions: MutableList +// ) { +// LogUtils.info("onDenied call.method = ${call.method}") +// if (call.method == Methods.requestPermissionExtend) { +// resultHandler.reply(PermissionResult.Denied.value) +// return +// } +// if (grantedPermissions.containsAll(permissions)) { +// LogUtils.info("onGranted call.method = ${call.method}") +// onHandlePermissionResult(call, resultHandler, needLocationPermission) +// } else { +// replyPermissionError(resultHandler) +// } +// } +// } +// } +// +// utils.getPermissions(3001, permissions) + } + + private fun handlePermissionMethod(resultHandler: ResultHandler) { + val call = resultHandler.call + if (call.method == Methods.requestPermissionExtend) { + val androidPermission = call.argument>("permission")!! + val requestType = androidPermission["type"] as Int + val mediaLocation = androidPermission["mediaLocation"] as Boolean + + + val permissions = arrayListOf() + permissionsUtils.injectNeedPermissions( + applicationContext, + requestType, + mediaLocation, + permissions, + ) + permissionsUtils.withActivity(activity) + .setListener(object : PermissionsListener { + override fun onGranted() { + TODO("Not yet implemented") + } + + override fun onDenied( + deniedPermissions: MutableList, + grantedPermissions: MutableList + ) { + TODO("Not yet implemented") + } + }) + .getPermissions(3001, permissions) + + return + } else if (call.method == Methods.presentLimited) { +// resultHandler.reply(null) + return + } + } + + private fun handleNotNeedPermissionMethod(resultHandler: ResultHandler) { + val call = resultHandler.call + when (call.method) { Methods.log -> { LogUtils.isLog = call.arguments() ?: false resultHandler.reply(1) - true } Methods.openSetting -> { permissionsUtils.getAppDetailSettingIntent(activity) resultHandler.reply(1) - true - } - - Methods.clearFileCache -> { - Glide.get(applicationContext).clearMemory() - runOnBackground { - photoManager.clearFileCache() - resultHandler.reply(1) - } - true } Methods.forceOldAPI -> { photoManager.useOldApi = true resultHandler.reply(1) - true } Methods.systemVersion -> { resultHandler.reply(Build.VERSION.SDK_INT.toString()) - true } - else -> false - } - - if (handleResult) { - return - } - if (ignorePermissionCheck) { - onHandlePermissionResult( - call, - resultHandler, - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - && permissionsUtils.havePermissionInManifest( - applicationContext, - Manifest.permission.ACCESS_MEDIA_LOCATION - ) - ) - return - } - if (permissionsUtils.isRequesting) { - resultHandler.replyError( - "PERMISSION_REQUESTING", - "Another permission request is still ongoing. Please request after the existing one is done.", - null - ) - return - } - - val needWritePermission = - permissionsUtils.needWriteExternalStorage(call) - && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q - && permissionsUtils.havePermissionInManifest( - applicationContext, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - val needReadPermission = - Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU - && permissionsUtils.havePermissionInManifest( - applicationContext, - Manifest.permission.READ_EXTERNAL_STORAGE - ) - val needLocationPermission = - permissionsUtils.needAccessLocation(call) - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - && permissionsUtils.havePermissionInManifest( - applicationContext, - Manifest.permission.ACCESS_MEDIA_LOCATION - ) - val permissions = arrayListOf() - if (needReadPermission) { - permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE) - } - if (needWritePermission) { - permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) - } - if (needLocationPermission) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - permissions.add(Manifest.permission.ACCESS_MEDIA_LOCATION) - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - permissionsUtils.addManifestWithPermission33( - applicationContext, - permissions, - call, - resultHandler - ) - if (resultHandler.isReplied()) { - return - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - if (Methods.presentLimited == call.method) { - val havePermissionInManifest = permissionsUtils.havePermissionInManifest( - applicationContext, - Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED - ) - if (!havePermissionInManifest) { - resultHandler.replyError("The ${Methods.presentLimited} must have READ_MEDIA_VISUAL_USER_SELECTED in manifest.") - return + Methods.clearFileCache -> { + Glide.get(applicationContext).clearMemory() + runOnBackground { + photoManager.clearFileCache() + resultHandler.reply(1) } } - permissionsUtils.addManifestWithPermission34( - applicationContext, - permissions, - call, - resultHandler - ) - } else { - if (Methods.presentLimited == call.method) { - resultHandler.replyError("The ${Methods.presentLimited} must use Android 14(API 34) or higher.") - return + Methods.releaseMemoryCache -> { + // The plugin will not hold instances cache on Android. + resultHandler.reply(1) } - } - - val utils = permissionsUtils.apply { - withActivity(activity) - permissionsListener = object : PermissionsListener { - override fun onGranted() { - LogUtils.info("onGranted call.method = ${call.method}") - onHandlePermissionResult(call, resultHandler, needLocationPermission) - } - override fun onDenied( - deniedPermissions: MutableList, - grantedPermissions: MutableList - ) { - LogUtils.info("onDenied call.method = ${call.method}") - if (call.method == Methods.requestPermissionExtend) { - resultHandler.reply(PermissionResult.Denied.value) - return - } - if (grantedPermissions.containsAll(permissions)) { - LogUtils.info("onGranted call.method = ${call.method}") - onHandlePermissionResult(call, resultHandler, needLocationPermission) - } else { - replyPermissionError(resultHandler) - } - } + Methods.ignorePermissionCheck -> { + val ignore = call.argument("ignore")!! + ignorePermissionCheck = ignore + resultHandler.reply(ignore) } } - - utils.getPermissions(3001, permissions) } private fun replyPermissionError(resultHandler: ResultHandler) { @@ -260,10 +303,10 @@ class PhotoManagerPlugin( } private fun onHandlePermissionResult( - call: MethodCall, resultHandler: ResultHandler, needLocationPermission: Boolean ) { + val call = resultHandler.call if (call.method == Methods.requestPermissionExtend) { resultHandler.reply(PermissionResult.Authorized.value) return diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt new file mode 100644 index 00000000..7d9d06ff --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt @@ -0,0 +1,28 @@ +package com.fluttercandies.photo_manager.permission + +import com.fluttercandies.photo_manager.util.ResultHandler + +class MethodPermissionListener() : PermissionsListener { + +// fun handleMethod( +// resultHandler: ResultHandler, +// onSuccess: ( +// resultHandler: ResultHandler, +// needLocationPermission: Boolean +// ) -> Unit +// ) { +// val method = resultHandler.call.method +// requiredPermissions() +// } + + override fun onGranted() { + TODO("Not yet implemented") + } + + override fun onDenied( + deniedPermissions: MutableList, + grantedPermissions: MutableList + ) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt index 938824c5..b585986e 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt @@ -3,13 +3,17 @@ package com.fluttercandies.photo_manager.permission import android.Manifest import android.annotation.TargetApi import android.app.Activity +import android.app.Application import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build +import android.os.Build.VERSION_CODES.M import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat +import androidx.core.content.PermissionChecker +import androidx.core.content.PermissionChecker.PERMISSION_GRANTED import com.fluttercandies.photo_manager.constant.Methods import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils import com.fluttercandies.photo_manager.util.LogUtils @@ -23,6 +27,8 @@ class PermissionsUtils { /** 需要申请权限的Activity */ private var mActivity: Activity? = null + private var context: Application? = null + /** 是否正在请求权限 */ var isRequesting = false private set @@ -60,6 +66,12 @@ class PermissionsUtils { */ fun withActivity(activity: Activity?): PermissionsUtils { mActivity = activity + context = activity?.application + return this + } + + fun setListener(listener: PermissionsListener?): PermissionsUtils { + permissionsListener = listener return this } @@ -75,6 +87,89 @@ class PermissionsUtils { return getPermissionsWithTips(requestCode, *permissionSet.toTypedArray()) } + private fun haveVideoAndImagePermission(): Boolean { + if (Build.VERSION.SDK_INT < M) { + return true + } + + return false + } + + private fun haveAudioPermission(): Boolean { + if (Build.VERSION.SDK_INT < M) { + return true + } + + return if (Build.VERSION.SDK_INT >= 33) { + checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_AUDIO) + } else { + checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + + fun haveLocationPermission(context: Context): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && havePermissionInManifest( + context, + Manifest.permission.ACCESS_MEDIA_LOCATION + ) + } + +// /** +// * 判断是否有足够的权限 +// */ +// private fun isHavePermissions(resultHandler: ResultHandler): Boolean { +// val method = resultHandler.call.method +// +// var result = true +// +// if (Methods.haveRequestTypeMethods.contains(method)) { +// val type = resultHandler.call.argument("type") +// val haveImage = RequestTypeUtils.containsImage(type!!) +// val haveVideo = RequestTypeUtils.containsVideo(type) +// val haveAudio = RequestTypeUtils.containsAudio(type) +// +// if (haveImage || haveVideo) { +// result = haveVideoAndImagePermission() and result +// } else if (haveAudio) { +// result = haveAudioPermission() && result +// } +// } +// +// if (Methods.needMediaLocationMethods.contains(method)) { +// result = haveLocationPermission(applicationContext) && result +// } +// +// } +// +// /** +// * 处理权限申请的所有逻辑 +// */ +// fun handlePermission( +// resultHandler: ResultHandler, +// onSuccess: ( +// resultHandler: ResultHandler, +// needLocationPermission: Boolean +// ) -> Unit +// ) { +// // 1. 不同的SDK版本来区分权限申请 +// if (Build.VERSION.SDK_INT < M) { +// // 小于6.0的直接回调成功即可 +// onSuccess(resultHandler, true) +// return +// } +// +// // 2. 判断是否已经有足够的权限 +// if (isHavePermissions(resultHandler)) { +// onSuccess(resultHandler, true) +// return +// } +// +// val method = resultHandler.call.method +// val params = resultHandler.call.arguments() +// +// +// } + /** * 进行权限申请,带拒绝弹框提示 * @@ -110,6 +205,50 @@ class PermissionsUtils { return this } + /** + * Wrapper for [PermissionChecker.checkCallingOrSelfPermission] + */ + fun checkCallingOrSelfPermission(permission: String): Boolean { + if (context == null) { + throw NullPointerException("Context for the permission request is not exist.") + } + return PERMISSION_GRANTED == PermissionChecker.checkCallingOrSelfPermission( + context!!, + permission + ) + } + + /** + * 检查对应方法是否需要申请权限 + */ + fun havePermission(videoOrImage: Boolean, audio: Boolean): Boolean { + if (Build.VERSION.SDK_INT < M) { + return true + } + + return false + } + + fun haveImageOrVideoPermission(): Boolean { + if (Build.VERSION.SDK_INT < M) { + return true + } + + return if (Build.VERSION.SDK_INT >= 34) { + checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) + } else if (Build.VERSION.SDK_INT == 33) { + val haveImage = checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) + val haveVideo = checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_VIDEO) + haveImage && haveVideo + } else { + checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + + fun addVideoPermission(permissions: ArrayList) { + + } + /** * 检查所需权限是否已获取 * @@ -120,9 +259,7 @@ class PermissionsUtils { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { resetStatus() for (p in permissions) { - if (p == Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) { - needToRequestPermissionsList.add(p) - } else if (mActivity!!.checkSelfPermission(p) == PackageManager.PERMISSION_DENIED) { + if (mActivity!!.checkSelfPermission(p) == PackageManager.PERMISSION_DENIED) { // Add the denied permission to the pending list. needToRequestPermissionsList.add(p) } @@ -193,6 +330,10 @@ class PermissionsUtils { if (needToRequestPermissionsList.isNotEmpty()) needToRequestPermissionsList.clear() } + /** + * + */ + /** * 跳转到应用的设置界面 * @@ -254,7 +395,7 @@ class PermissionsUtils { permissions.add(Manifest.permission.READ_MEDIA_AUDIO) } return - } else if (!Methods.android13PermissionMethods.contains(method)) { + } else if (!Methods.haveRequestTypeMethods.contains(method)) { return } @@ -325,7 +466,6 @@ class PermissionsUtils { PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()) ) } else { - @Suppress("DEPRECATION") context.packageManager.getPackageInfo( applicationInfo.packageName, PackageManager.GET_PERMISSIONS @@ -334,4 +474,11 @@ class PermissionsUtils { return packageInfo.requestedPermissions.contains(permission) } + fun injectNeedPermissions( + context: Context, + requestType: Int, + mediaLocation: Boolean, + permissions: ArrayList + ) { + } } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/thumb/ThumbnailUtil.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/thumb/ThumbnailUtil.kt index bed45105..76fba7a2 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/thumb/ThumbnailUtil.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/thumb/ThumbnailUtil.kt @@ -9,11 +9,9 @@ import com.bumptech.glide.request.FutureTarget import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.signature.ObjectKey import com.fluttercandies.photo_manager.core.entity.AssetEntity -import io.flutter.plugin.common.MethodChannel import com.fluttercandies.photo_manager.core.entity.ThumbLoadOption import com.fluttercandies.photo_manager.util.ResultHandler import java.io.ByteArrayOutputStream -import java.io.File object ThumbnailUtil { fun getThumbnail( @@ -24,10 +22,8 @@ object ThumbnailUtil { format: Bitmap.CompressFormat, quality: Int, frame: Long, - result: MethodChannel.Result? + resultHandler: ResultHandler ) { - val resultHandler = ResultHandler(result) - try { val resource = Glide.with(context) .asBitmap() diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/util/ResultHandler.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/util/ResultHandler.kt index 2187484d..cc954ea0 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/util/ResultHandler.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/util/ResultHandler.kt @@ -5,7 +5,7 @@ import android.os.Looper import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel -class ResultHandler(var result: MethodChannel.Result?, val call: MethodCall? = null) { +class ResultHandler(var result: MethodChannel.Result, val call: MethodCall) { init { handler.hasMessages(0) // just do it to init handler } @@ -23,10 +23,9 @@ class ResultHandler(var result: MethodChannel.Result?, val call: MethodCall? = n } isReplied = true val result = this.result - this.result = null handler.post { try { - result?.success(any) + result.success(any) } catch (e: IllegalStateException) { // Do nothing } @@ -39,9 +38,8 @@ class ResultHandler(var result: MethodChannel.Result?, val call: MethodCall? = n } isReplied = true val result = this.result - this.result = null handler.post { - result?.error(code, message, obj) + result.error(code, message, obj) } } @@ -51,9 +49,8 @@ class ResultHandler(var result: MethodChannel.Result?, val call: MethodCall? = n } isReplied = true val result = this.result - this.result = null handler.post { - result?.notImplemented() + result.notImplemented() } } diff --git a/flow_chart/permission.dio b/flow_chart/permission.dio new file mode 100644 index 00000000..055046c7 --- /dev/null +++ b/flow_chart/permission.dio @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/src/internal/constants.dart b/lib/src/internal/constants.dart index 53964b1f..81a16c88 100644 --- a/lib/src/internal/constants.dart +++ b/lib/src/internal/constants.dart @@ -17,7 +17,7 @@ class PMConstants { static const String mFetchEntityProperties = 'fetchEntityProperties'; static const String mGetAssetCountFromPath = 'getAssetCountFromPath'; - /// The 4 methods have [RequestType] params, for android-13 + /// The 4 methods have [RequestType] params, for android-13 or higher. static const String mFetchPathProperties = 'fetchPathProperties'; static const String mGetAssetPathList = 'getAssetPathList'; static const String mGetAssetListPaged = 'getAssetListPaged'; diff --git a/lib/src/internal/map_interface.dart b/lib/src/internal/map_interface.dart new file mode 100644 index 00000000..23b8fc90 --- /dev/null +++ b/lib/src/internal/map_interface.dart @@ -0,0 +1,12 @@ +// Copyright 2018 The FlutterCandies author. All rights reserved. +// Use of this source code is governed by an Apache license that can be found +// in the LICENSE file. + +/// Contains an abstract method toMap to indicate that it can be converted into a Map object +mixin IMapMixin{ + + /// Convert current object to a map. + /// + /// Usually for transfer to MethodChannel. + Map toMap(); +} \ No newline at end of file diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index 1308b83d..71401e14 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import '../internal/enums.dart'; +import '../internal/map_interface.dart'; /// The request type when requesting paths. /// @@ -77,22 +78,59 @@ typedef PermisstionRequestOption = PermissionRequestOption; /// See [PermissionState]. @immutable -class PermissionRequestOption { +class PermissionRequestOption with IMapMixin { const PermissionRequestOption({ this.iosAccessLevel = IosAccessLevel.readWrite, + this.androidPermission = const AndroidPermission( + RequestType.common, + false, + ), }); final IosAccessLevel iosAccessLevel; - Map toMap() => { + /// See [AndroidPermission]. + final AndroidPermission androidPermission; + + @override + Map toMap() => + { 'iosAccessLevel': iosAccessLevel.index + 1, + 'androidPermission': androidPermission.toMap(), }; @override bool operator ==(Object other) => other is PermissionRequestOption && - iosAccessLevel == other.iosAccessLevel; + iosAccessLevel == other.iosAccessLevel; @override int get hashCode => iosAccessLevel.hashCode; } + +/// The permission for android. +class AndroidPermission with IMapMixin { + + /// The type of your need. + /// + /// See [RequestType]. + final RequestType type; + + /// Whether you need to access the media location. + /// You must define `` in your AndroidManifest.xml. + /// + /// If you do not define in AndroidManifest, or [mediaLocation] is false, this permission will not be applied for. + /// + /// See it in [android](https://developer.android.com/reference/android/Manifest.permission#ACCESS_MEDIA_LOCATION). + final bool mediaLocation; + + /// The permission for android. + const AndroidPermission(this.type, this.mediaLocation); + + @override + Map toMap() => + { + 'type': type.value, + 'mediaLocation': mediaLocation, + }; +} \ No newline at end of file From 1c1d3e0676cb68c6d41af1acc19158c6b001d344 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Mon, 18 Sep 2023 17:36:55 +0800 Subject: [PATCH 07/18] progressing: Permission for android - refactor the permission code --- .../photo_manager/core/PhotoManagerPlugin.kt | 93 ++-- .../permission/MethodPermissionListener.kt | 28 -- .../permission/PermissionDelegate.kt | 142 ++++++ .../permission/PermissionsListener.kt | 10 +- .../permission/PermissionsUtils.kt | 406 +++--------------- .../permission/impl/PermissionDelegate19.kt | 24 ++ .../permission/impl/PermissionDelegate23.kt | 38 ++ .../permission/impl/PermissionDelegate29.kt | 40 ++ .../permission/impl/PermissionDelegate30.kt | 44 ++ .../permission/impl/PermissionDelegate33.kt | 82 ++++ .../permission/impl/PermissionDelegate34.kt | 116 +++++ 11 files changed, 614 insertions(+), 409 deletions(-) delete mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt create mode 100644 android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index a9db56e1..8f00b452 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -1,13 +1,10 @@ package com.fluttercandies.photo_manager.core -import android.Manifest import android.app.Activity import android.content.Context -import android.content.pm.PackageManager import android.os.Build import android.os.Handler import android.os.Looper -import androidx.activity.result.contract.ActivityResultContracts import com.bumptech.glide.Glide import com.fluttercandies.photo_manager.constant.Methods import io.flutter.plugin.common.BinaryMessenger @@ -49,12 +46,11 @@ class PhotoManagerPlugin( init { permissionsUtils.permissionsListener = object : PermissionsListener { - override fun onGranted() { - } - + override fun onGranted(needPermissions: MutableList) {} override fun onDenied( deniedPermissions: MutableList, - grantedPermissions: MutableList + grantedPermissions: MutableList, + needPermissions: MutableList, ) { } } @@ -82,12 +78,15 @@ class PhotoManagerPlugin( val method = call.method if (Methods.isNotNeedPermissionMethod(method)) { + // The method does not need permission. + // Usually, these methods are used to config the plugin or get some info. handleNotNeedPermissionMethod(resultHandler) return } if (Methods.isPermissionMethod(method)) { + // The method is used to request permission. handlePermissionMethod(resultHandler) return } @@ -216,39 +215,53 @@ class PhotoManagerPlugin( private fun handlePermissionMethod(resultHandler: ResultHandler) { val call = resultHandler.call - if (call.method == Methods.requestPermissionExtend) { - val androidPermission = call.argument>("permission")!! - val requestType = androidPermission["type"] as Int - val mediaLocation = androidPermission["mediaLocation"] as Boolean - - - val permissions = arrayListOf() - permissionsUtils.injectNeedPermissions( - applicationContext, - requestType, - mediaLocation, - permissions, - ) - permissionsUtils.withActivity(activity) - .setListener(object : PermissionsListener { - override fun onGranted() { - TODO("Not yet implemented") - } + when (call.method) { + Methods.requestPermissionExtend -> { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + resultHandler.reply(PermissionResult.Authorized.value) + return + } - override fun onDenied( - deniedPermissions: MutableList, - grantedPermissions: MutableList - ) { - TODO("Not yet implemented") - } - }) - .getPermissions(3001, permissions) + val androidPermission = call.argument>("permission")!! + val requestType = androidPermission["type"] as Int + val mediaLocation = androidPermission["mediaLocation"] as Boolean + + permissionsUtils.withActivity(activity) + .setListener(object : PermissionsListener { + override fun onGranted(needPermissions: MutableList) { + resultHandler.reply(PermissionResult.Authorized.value) + } + + override fun onDenied( + deniedPermissions: MutableList, + grantedPermissions: MutableList, + needPermissions: MutableList + ) { + if (grantedPermissions.containsAll(needPermissions)) { + resultHandler.reply(PermissionResult.Authorized.value) + } else { + resultHandler.reply(PermissionResult.Denied.value) + } + } + }) + .requestPermission( + applicationContext, + requestType, + mediaLocation, + ) + } + + Methods.presentLimited -> { + // resultHandler.reply(null) + } - return - } else if (call.method == Methods.presentLimited) { -// resultHandler.reply(null) - return + Methods.ignorePermissionCheck -> { + val ignore = call.argument("ignore")!! + ignorePermissionCheck = ignore + resultHandler.reply(ignore) + } } + } private fun handleNotNeedPermissionMethod(resultHandler: ResultHandler) { @@ -285,12 +298,6 @@ class PhotoManagerPlugin( // The plugin will not hold instances cache on Android. resultHandler.reply(1) } - - Methods.ignorePermissionCheck -> { - val ignore = call.argument("ignore")!! - ignorePermissionCheck = ignore - resultHandler.reply(ignore) - } } } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt deleted file mode 100644 index 7d9d06ff..00000000 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/MethodPermissionListener.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.fluttercandies.photo_manager.permission - -import com.fluttercandies.photo_manager.util.ResultHandler - -class MethodPermissionListener() : PermissionsListener { - -// fun handleMethod( -// resultHandler: ResultHandler, -// onSuccess: ( -// resultHandler: ResultHandler, -// needLocationPermission: Boolean -// ) -> Unit -// ) { -// val method = resultHandler.call.method -// requiredPermissions() -// } - - override fun onGranted() { - TODO("Not yet implemented") - } - - override fun onDenied( - deniedPermissions: MutableList, - grantedPermissions: MutableList - ) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt new file mode 100644 index 00000000..91be1e16 --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt @@ -0,0 +1,142 @@ +package com.fluttercandies.photo_manager.permission + +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.app.ActivityCompat +import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate19 +import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate23 +import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate29 +import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate30 +import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate33 +import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate34 + +abstract class PermissionDelegate { + + protected fun requestPermission( + permissionsUtils: PermissionsUtils, + permission: MutableList + ) { + val activity = permissionsUtils.getActivity() + ?: throw NullPointerException("Activity for the permission request is not exist.") + + permissionsUtils.setNeedToRequestPermissionsList(permission) + + ActivityCompat.requestPermissions(activity, permission.toTypedArray(), requestCode) + } + + /** + * Check if the permission is in the manifest. + */ + private fun havePermissionInManifest(context: Context, permission: String): Boolean { + val applicationInfo = context.applicationInfo + val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.packageManager.getPackageInfo( + applicationInfo.packageName, + PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()) + ) + } else { + context.packageManager.getPackageInfo( + applicationInfo.packageName, + PackageManager.GET_PERMISSIONS + ) + } + return packageInfo.requestedPermissions.contains(permission) + } + + /** + * Check if the permission is granted for the user. + */ + protected fun havePermissionForUser(context: Context, permission: String): Boolean { + return ActivityCompat.checkSelfPermission( + context, + permission + ) == PackageManager.PERMISSION_GRANTED + } + + /** + * Check if the permission is granted for the user and it is in the manifest. + */ + fun havePermission(context: Context, permission: String): Boolean { + return havePermissionInManifest(context, permission) && havePermissionForUser( + context, + permission + ) + } + + /** + * Check if the [permission] are granted for the user and it is in the manifest. + */ + fun havePermissions(context: Context, vararg permission: String): Boolean { + return permission.all { havePermission(context, it) } + } + + /** + * Request permission. + * + * The [permissionsUtils] is used to get the activity. + * The [context] is used to check the permission is in the manifest. + * The [requestType] is passed from the dart code. + * The [mediaLocation] is passed from the dart code. + */ + abstract fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean, + ) + + /** + * Check if the [requestType] is granted for the user and it is in the manifest. + */ + abstract fun havePermissions(context: Context, requestType: Int): Boolean + + /** + * Check if the [mediaLocation] is granted for the user and it is in the manifest. + */ + abstract fun haveMediaLocation(context: Context): Boolean + + companion object { + const val requestCode = 3001 + + /** + * Create a [PermissionDelegate] by the sdk version. + */ + fun create(): PermissionDelegate { + return when (Build.VERSION.SDK_INT) { + in 1 until 23 -> PermissionDelegate19() + in 23 until 29 -> PermissionDelegate23() + 29 -> PermissionDelegate29() + in 30 until 33 -> PermissionDelegate30() + 33 -> PermissionDelegate33() + in 34 until Int.MAX_VALUE -> { + PermissionDelegate34() + } + + else -> throw UnsupportedOperationException( + "This sdk version is not supported yet." + ) + } + } + } + + open fun isHandlePermissionResult(): Boolean { + return false + } + + open fun handlePermissionResult( + permissionsUtils: PermissionsUtils, + context: Context, + permissions: Array, + grantResults: IntArray, + needToRequestPermissionsList: MutableList, + deniedPermissionsList: MutableList, + grantedPermissionsList: MutableList + ) { + throw UnsupportedOperationException( + "handlePermissionResult is not implemented," + + " please implement it in your delegate." + ) + } + +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsListener.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsListener.kt index 31751dbf..f87a0cf3 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsListener.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsListener.kt @@ -1,6 +1,12 @@ package com.fluttercandies.photo_manager.permission interface PermissionsListener { - fun onGranted() - fun onDenied(deniedPermissions: MutableList, grantedPermissions: MutableList) + + fun onGranted(needPermissions: MutableList) + + fun onDenied( + deniedPermissions: MutableList, + grantedPermissions: MutableList, + needPermissions: MutableList + ) } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt index b585986e..138b1db6 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt @@ -1,27 +1,16 @@ package com.fluttercandies.photo_manager.permission -import android.Manifest -import android.annotation.TargetApi import android.app.Activity import android.app.Application import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri -import android.os.Build -import android.os.Build.VERSION_CODES.M -import androidx.annotation.RequiresApi -import androidx.core.app.ActivityCompat import androidx.core.content.PermissionChecker import androidx.core.content.PermissionChecker.PERMISSION_GRANTED -import com.fluttercandies.photo_manager.constant.Methods -import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils import com.fluttercandies.photo_manager.util.LogUtils -import com.fluttercandies.photo_manager.util.ResultHandler -import io.flutter.plugin.common.MethodCall -import java.lang.IllegalStateException import java.lang.NullPointerException -import java.util.ArrayList +import kotlin.collections.ArrayList class PermissionsUtils { /** 需要申请权限的Activity */ @@ -33,6 +22,8 @@ class PermissionsUtils { var isRequesting = false private set + private val delegate: PermissionDelegate = PermissionDelegate.create() + /** * 需要申请的权限的List */ @@ -48,11 +39,6 @@ class PermissionsUtils { */ private val grantedPermissionsList: MutableList = ArrayList() - /** - * 某次进行权限申请的requestCode - */ - private var requestCode = 0 - /** * 授权监听回调 */ @@ -70,6 +56,10 @@ class PermissionsUtils { return this } + fun getActivity(): Activity? { + return mActivity + } + fun setListener(listener: PermissionsListener?): PermissionsUtils { permissionsListener = listener return this @@ -78,130 +68,47 @@ class PermissionsUtils { /** * 进行权限申请,不带拒绝弹框提示 * - * @param requestCode 指定该次申请的requestCode - * @param permissions 要申请的权限数组 + * @param applicationContext [Application.getApplicationContext] + * @param requestType type of request, see [com.fluttercandies.photo_manager.core.utils.RequestTypeUtils] + * @param permissions A mutable list of permission to request, the method will be modified in the method. + * @param mediaLocation Whether to request media location permission. + * * @return 返回 [PermissionsUtils] 自身,进行链式调用 */ - fun getPermissions(requestCode: Int, permissions: List): PermissionsUtils { - val permissionSet = permissions.toSet() - return getPermissionsWithTips(requestCode, *permissionSet.toTypedArray()) - } - - private fun haveVideoAndImagePermission(): Boolean { - if (Build.VERSION.SDK_INT < M) { - return true - } - - return false - } - - private fun haveAudioPermission(): Boolean { - if (Build.VERSION.SDK_INT < M) { - return true - } - - return if (Build.VERSION.SDK_INT >= 33) { - checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_AUDIO) - } else { - checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) - } - } - - fun haveLocationPermission(context: Context): Boolean { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && havePermissionInManifest( - context, - Manifest.permission.ACCESS_MEDIA_LOCATION + fun requestPermission( + applicationContext: Context, + requestType: Int, + mediaLocation: Boolean, + ): PermissionsUtils { + delegate.requestPermission( + this, + applicationContext, + requestType, + mediaLocation, ) - } -// /** -// * 判断是否有足够的权限 -// */ -// private fun isHavePermissions(resultHandler: ResultHandler): Boolean { -// val method = resultHandler.call.method -// -// var result = true -// -// if (Methods.haveRequestTypeMethods.contains(method)) { -// val type = resultHandler.call.argument("type") -// val haveImage = RequestTypeUtils.containsImage(type!!) -// val haveVideo = RequestTypeUtils.containsVideo(type) -// val haveAudio = RequestTypeUtils.containsAudio(type) -// -// if (haveImage || haveVideo) { -// result = haveVideoAndImagePermission() and result -// } else if (haveAudio) { -// result = haveAudioPermission() && result -// } -// } -// -// if (Methods.needMediaLocationMethods.contains(method)) { -// result = haveLocationPermission(applicationContext) && result +// delegate.requestPermissions(this, context!!, requestCode, false) +// val permissionSet = permissions.toSet() +// if (mActivity == null) { +// throw NullPointerException("Activity for the permission request is not exist.") // } -// -// } -// -// /** -// * 处理权限申请的所有逻辑 -// */ -// fun handlePermission( -// resultHandler: ResultHandler, -// onSuccess: ( -// resultHandler: ResultHandler, -// needLocationPermission: Boolean -// ) -> Unit -// ) { -// // 1. 不同的SDK版本来区分权限申请 -// if (Build.VERSION.SDK_INT < M) { -// // 小于6.0的直接回调成功即可 -// onSuccess(resultHandler, true) -// return -// } -// -// // 2. 判断是否已经有足够的权限 -// if (isHavePermissions(resultHandler)) { -// onSuccess(resultHandler, true) -// return +// check(!isRequesting) { "Another permission request is ongoing." } +// isRequesting = true +// this.requestCode = requestCode +// if (!checkPermissions(*permissions)) { +// // 通过上面的 checkPermissions,可以知道能得到进入到这里面的都是 6.0 的机子 +// ActivityCompat.requestPermissions( +// mActivity!!, +// needToRequestPermissionsList.toTypedArray(), +// requestCode +// ) +// for (i in needToRequestPermissionsList.indices) { +// LogUtils.info("Permissions: " + needToRequestPermissionsList[i]) +// } +// } else if (permissionsListener != null) { +// isRequesting = false +// permissionsListener!!.onGranted() // } -// -// val method = resultHandler.call.method -// val params = resultHandler.call.arguments() -// -// -// } - - /** - * 进行权限申请,带拒绝弹框提示 - * - * @param requestCode 指定该次申请的requestCode - * @param permissions 要申请的权限数组 - * @return 返回 [PermissionsUtils] 自身,进行链式调用 - */ - @TargetApi(23) - private fun getPermissionsWithTips( - requestCode: Int, - vararg permissions: String - ): PermissionsUtils { - if (mActivity == null) { - throw NullPointerException("Activity for the permission request is not exist.") - } - check(!isRequesting) { "Another permission request is ongoing." } - isRequesting = true - this.requestCode = requestCode - if (!checkPermissions(*permissions)) { - // 通过上面的 checkPermissions,可以知道能得到进入到这里面的都是 6.0 的机子 - ActivityCompat.requestPermissions( - mActivity!!, - needToRequestPermissionsList.toTypedArray(), - requestCode - ) - for (i in needToRequestPermissionsList.indices) { - LogUtils.info("Permissions: " + needToRequestPermissionsList[i]) - } - } else if (permissionsListener != null) { - isRequesting = false - permissionsListener!!.onGranted() - } return this } @@ -218,57 +125,6 @@ class PermissionsUtils { ) } - /** - * 检查对应方法是否需要申请权限 - */ - fun havePermission(videoOrImage: Boolean, audio: Boolean): Boolean { - if (Build.VERSION.SDK_INT < M) { - return true - } - - return false - } - - fun haveImageOrVideoPermission(): Boolean { - if (Build.VERSION.SDK_INT < M) { - return true - } - - return if (Build.VERSION.SDK_INT >= 34) { - checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) - } else if (Build.VERSION.SDK_INT == 33) { - val haveImage = checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) - val haveVideo = checkCallingOrSelfPermission(Manifest.permission.READ_MEDIA_VIDEO) - haveImage && haveVideo - } else { - checkCallingOrSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) - } - } - - fun addVideoPermission(permissions: ArrayList) { - - } - - /** - * 检查所需权限是否已获取 - * - * @param permissions 所需权限数组 - * @return 是否全部已获取 - */ - private fun checkPermissions(vararg permissions: String): Boolean { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - resetStatus() - for (p in permissions) { - if (mActivity!!.checkSelfPermission(p) == PackageManager.PERMISSION_DENIED) { - // Add the denied permission to the pending list. - needToRequestPermissionsList.add(p) - } - } - return needToRequestPermissionsList.isEmpty() - } - return true - } - /** * 处理申请权限返回 * 由于某些rom对权限进行了处理,第一次选择了拒绝,则不会出现第二次询问(或者没有不再询问),故拒绝就回调onDenied @@ -283,14 +139,7 @@ class PermissionsUtils { permissions: Array, grantResults: IntArray ): PermissionsUtils { - var userSelectedResult = false - if (permissions.contains(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)) { - // Get it result - val index = permissions.indexOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) - val result = grantResults[index] - userSelectedResult = result == PackageManager.PERMISSION_GRANTED - } - if (requestCode == this.requestCode) { + if (requestCode == PermissionDelegate.requestCode) { for (i in permissions.indices) { LogUtils.info("Returned permissions: " + permissions[i]) if (grantResults[i] == PackageManager.PERMISSION_DENIED) { @@ -300,24 +149,31 @@ class PermissionsUtils { } } - if (userSelectedResult) { - // 如果包含 READ_MEDIA_VISUAL_USER_SELECTED 的允许的权限,则说明可以忽略 READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 的拒绝 - deniedPermissionsList.toList().forEach { - if (it == Manifest.permission.READ_MEDIA_IMAGES || it == Manifest.permission.READ_MEDIA_VIDEO) { - deniedPermissionsList.remove(it) - grantedPermissionsList.add(it) - } - } - } - - if (deniedPermissionsList.isNotEmpty()) { - // 回调用户拒绝监听 - permissionsListener!!.onDenied(deniedPermissionsList, grantedPermissionsList) + if (delegate.isHandlePermissionResult()) { + delegate.handlePermissionResult( + this, + context!!, + permissions, + grantResults, + needToRequestPermissionsList, + deniedPermissionsList, + grantedPermissionsList + ) } else { - // 回调用户同意监听 - permissionsListener!!.onGranted() + if (deniedPermissionsList.isNotEmpty()) { + // 回调用户拒绝监听 + permissionsListener!!.onDenied( + deniedPermissionsList, + grantedPermissionsList, + needToRequestPermissionsList + ) + } else { + // 回调用户同意监听 + permissionsListener!!.onGranted(needToRequestPermissionsList) + } } } + resetStatus() isRequesting = false return this } @@ -350,135 +206,13 @@ class PermissionsUtils { context.startActivity(localIntent) } - fun needWriteExternalStorage(call: MethodCall): Boolean { - return when (call.method) { - Methods.saveImage, - Methods.saveImageWithPath, - Methods.saveVideo, - Methods.copyAsset, - Methods.moveAssetToPath, - Methods.deleteWithIds, - Methods.removeNoExistsAssets -> true - - else -> false - } + fun haveLocationPermission(applicationContext: Context): Boolean { + return delegate.haveMediaLocation(applicationContext) } - fun needAccessLocation(call: MethodCall): Boolean { - return when (call.method) { - Methods.copyAsset, - Methods.getLatLng, - Methods.getOriginBytes -> true - - Methods.getFullFile -> call.argument("isOrigin")!! && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - else -> false - } + fun setNeedToRequestPermissionsList(permission: MutableList) { + needToRequestPermissionsList.clear() + needToRequestPermissionsList.addAll(permission) } - @RequiresApi(33) - fun addManifestWithPermission33( - context: Context, - permissions: ArrayList, - call: MethodCall, - resultHandler: ResultHandler - ) { - val method = call.method - if (method == Methods.requestPermissionExtend) { - // Check all permissions listed in the manifest, regardless the request type. - if (havePermissionInManifest(context, Manifest.permission.READ_MEDIA_IMAGES)) { - permissions.add(Manifest.permission.READ_MEDIA_IMAGES) - } - if (havePermissionInManifest(context, Manifest.permission.READ_MEDIA_VIDEO)) { - permissions.add(Manifest.permission.READ_MEDIA_VIDEO) - } - if (havePermissionInManifest(context, Manifest.permission.READ_MEDIA_AUDIO)) { - permissions.add(Manifest.permission.READ_MEDIA_AUDIO) - } - return - } else if (!Methods.haveRequestTypeMethods.contains(method)) { - return - } - - val type = call.argument("type") - if (type == null) { - resultHandler.replyError("The $method must pass the 'type' params") - return - } - val haveImage = RequestTypeUtils.containsImage(type) - val haveVideo = RequestTypeUtils.containsVideo(type) - val haveAudio = RequestTypeUtils.containsAudio(type) - - fun checkAndAddPermission( - requestHavePermission: Boolean, - tag: String, - manifestPermission: String - ) { - if (!requestHavePermission) { - return - } - - if (!havePermissionInManifest(context, manifestPermission)) { - throw IllegalStateException("Request $tag must have $manifestPermission in manifest.") - } - permissions.add(manifestPermission) - } - - try { - checkAndAddPermission(haveImage, "image", Manifest.permission.READ_MEDIA_IMAGES) - checkAndAddPermission(haveVideo, "video", Manifest.permission.READ_MEDIA_VIDEO) - checkAndAddPermission(haveAudio, "audio", Manifest.permission.READ_MEDIA_AUDIO) - } catch (e: IllegalStateException) { - resultHandler.replyError("Permissions check error", e.message, e) - } - - } - - @RequiresApi(34) - fun addManifestWithPermission34( - context: Context, - permissions: ArrayList, - call: MethodCall, - resultHandler: ResultHandler - ) { - if (havePermissionInManifest( - context, - Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED - ) - ) { - permissions.add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) - - if (Methods.presentLimited == call.method) { - permissions.add(Manifest.permission.READ_MEDIA_IMAGES) - permissions.add(Manifest.permission.READ_MEDIA_VIDEO) - } else { - permissions.remove(Manifest.permission.READ_MEDIA_IMAGES) - permissions.remove(Manifest.permission.READ_MEDIA_VIDEO) - permissions.remove(Manifest.permission.ACCESS_MEDIA_LOCATION) - } - } - } - - fun havePermissionInManifest(context: Context, permission: String): Boolean { - val applicationInfo = context.applicationInfo - val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - context.packageManager.getPackageInfo( - applicationInfo.packageName, - PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong()) - ) - } else { - context.packageManager.getPackageInfo( - applicationInfo.packageName, - PackageManager.GET_PERMISSIONS - ) - } - return packageInfo.requestedPermissions.contains(permission) - } - - fun injectNeedPermissions( - context: Context, - requestType: Int, - mediaLocation: Boolean, - permissions: ArrayList - ) { - } } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt new file mode 100644 index 00000000..575c2c5f --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt @@ -0,0 +1,24 @@ +package com.fluttercandies.photo_manager.permission.impl + +import android.content.Context +import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.permission.PermissionsUtils + +class PermissionDelegate19 : com.fluttercandies.photo_manager.permission.PermissionDelegate() { + override fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean + ) { + permissionsUtils.permissionsListener?.onGranted(mutableListOf()) + } + + override fun havePermissions(context: Context, requestType: Int): Boolean { + return true + } + + override fun haveMediaLocation(context: Context): Boolean { + return true + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt new file mode 100644 index 00000000..87eb70d5 --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt @@ -0,0 +1,38 @@ +package com.fluttercandies.photo_manager.permission.impl + +import android.Manifest +import android.content.Context +import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.permission.PermissionsUtils + +@RequiresApi(23) +open class PermissionDelegate23 : com.fluttercandies.photo_manager.permission.PermissionDelegate() { + + companion object { + private const val readPermission = Manifest.permission.READ_EXTERNAL_STORAGE + private const val writePermission = Manifest.permission.WRITE_EXTERNAL_STORAGE + } + + override fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean + ) { + val permissions = mutableListOf(readPermission, writePermission) + + if (havePermission(context, readPermission) && havePermission(context, writePermission)) { + permissionsUtils.permissionsListener?.onGranted(permissions) + } else { + requestPermission(permissionsUtils, permissions) + } + } + + override fun havePermissions(context: Context, requestType: Int): Boolean { + return havePermission(context, readPermission) && havePermission(context, writePermission) + } + + override fun haveMediaLocation(context: Context): Boolean { + return true + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt new file mode 100644 index 00000000..28697de4 --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt @@ -0,0 +1,40 @@ +package com.fluttercandies.photo_manager.permission.impl + +import android.Manifest +import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.permission.PermissionsUtils + +@RequiresApi(29) +class PermissionDelegate29 : PermissionDelegate23() { + + companion object { + private const val readPermission = Manifest.permission.READ_EXTERNAL_STORAGE + private const val writePermission = Manifest.permission.WRITE_EXTERNAL_STORAGE + private const val mediaLocationPermission = Manifest.permission.ACCESS_MEDIA_LOCATION + } + + override fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean + ) { + val permissions = mutableListOf(readPermission, writePermission) + + if (mediaLocation) { + permissions.add(mediaLocationPermission) + } + + if (havePermissions(context, *permissions.toTypedArray())) { + permissionsUtils.permissionsListener?.onGranted(permissions) + } else { + requestPermission(permissionsUtils, permissions) + } + } + + override fun haveMediaLocation(context: Context): Boolean { + return havePermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt new file mode 100644 index 00000000..445a5f68 --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt @@ -0,0 +1,44 @@ +package com.fluttercandies.photo_manager.permission.impl + +import android.Manifest +import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.permission.PermissionDelegate +import com.fluttercandies.photo_manager.permission.PermissionsUtils + +@RequiresApi(Build.VERSION_CODES.R) +class PermissionDelegate30 : PermissionDelegate() { + + companion object { + private const val readPermission = Manifest.permission.READ_EXTERNAL_STORAGE + private const val mediaLocationPermission = Manifest.permission.ACCESS_MEDIA_LOCATION + } + + override fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean + ) { + val permissions = mutableListOf(readPermission) + + if (mediaLocation) { + permissions.add(mediaLocationPermission) + } + + if (havePermissions(context, *permissions.toTypedArray())) { + permissionsUtils.permissionsListener?.onGranted(permissions) + } else { + requestPermission(permissionsUtils, permissions) + } + } + + override fun havePermissions(context: Context, requestType: Int): Boolean { + return havePermission(context, readPermission) + } + + override fun haveMediaLocation(context: Context): Boolean { + return havePermission(context, mediaLocationPermission) + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt new file mode 100644 index 00000000..cf23bc5e --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt @@ -0,0 +1,82 @@ +package com.fluttercandies.photo_manager.permission.impl + +import android.Manifest +import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils +import com.fluttercandies.photo_manager.permission.PermissionDelegate +import com.fluttercandies.photo_manager.permission.PermissionsUtils + +@RequiresApi(33) +class PermissionDelegate33 : PermissionDelegate() { + + companion object { + private const val mediaVideo = Manifest.permission.READ_MEDIA_VIDEO + private const val mediaImage = Manifest.permission.READ_MEDIA_IMAGES + private const val mediaAudio = Manifest.permission.READ_MEDIA_AUDIO + + private const val mediaLocationPermission = Manifest.permission.ACCESS_MEDIA_LOCATION + } + + override fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean + ) { + val containsVideo = RequestTypeUtils.containsVideo(requestType) + val containsImage = RequestTypeUtils.containsImage(requestType) + val containsAudio = RequestTypeUtils.containsAudio(requestType) + + val permissions = mutableListOf() + + if (containsVideo) { + permissions.add(mediaVideo) + } + + if (containsImage) { + permissions.add(mediaImage) + } + + if (containsAudio) { + permissions.add(mediaAudio) + } + + if (mediaLocation) { + permissions.add(mediaLocationPermission) + } + + if (havePermissions(context, *permissions.toTypedArray())) { + permissionsUtils.permissionsListener?.onGranted(permissions) + } else { + requestPermission(permissionsUtils, permissions) + } + } + + override fun havePermissions(context: Context, requestType: Int): Boolean { + val containsVideo = RequestTypeUtils.containsVideo(requestType) + val containsImage = RequestTypeUtils.containsImage(requestType) + val containsAudio = RequestTypeUtils.containsAudio(requestType) + + var result = true + + if (containsVideo) { + result = result && havePermission(context, mediaVideo) + } + + if (containsImage) { + result = result && havePermission(context, mediaImage) + } + + if (containsAudio) { + result = result && havePermission(context, mediaAudio) + } + + return result + } + + override fun haveMediaLocation(context: Context): Boolean { + return havePermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt new file mode 100644 index 00000000..7d9e8879 --- /dev/null +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt @@ -0,0 +1,116 @@ +package com.fluttercandies.photo_manager.permission.impl + +import android.Manifest +import android.content.Context +import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils +import com.fluttercandies.photo_manager.permission.PermissionDelegate +import com.fluttercandies.photo_manager.permission.PermissionsUtils + +@RequiresApi(34) +class PermissionDelegate34 : PermissionDelegate() { + + companion object { + private const val mediaVideo = Manifest.permission.READ_MEDIA_VIDEO + private const val mediaImage = Manifest.permission.READ_MEDIA_IMAGES + private const val mediaAudio = Manifest.permission.READ_MEDIA_AUDIO + + private const val mediaVisualUserSelected = + Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED + + private const val mediaLocationPermission = Manifest.permission.ACCESS_MEDIA_LOCATION + } + + + override fun requestPermission( + permissionsUtils: PermissionsUtils, + context: Context, + requestType: Int, + mediaLocation: Boolean + ) { + var havePermission = true + + val containsImage = RequestTypeUtils.containsImage(requestType) + val containsVideo = RequestTypeUtils.containsVideo(requestType) + val containsAudio = RequestTypeUtils.containsAudio(requestType) + + val permissions = mutableListOf() + + if (containsVideo || containsImage) { + permissions.add(mediaVisualUserSelected) + + // check have media visual user selected permission, the permission does not need to be defined in the manifest. + val haveMediaVisualUserSelected = + havePermissionForUser(context, mediaVisualUserSelected) + + havePermission = haveMediaVisualUserSelected + + if (mediaLocation) { + permissions.add(mediaLocationPermission) + havePermission = havePermission && havePermission(context, mediaLocationPermission) + } + + if (containsVideo) { + permissions.add(mediaVideo) + } + + if (containsImage) { + permissions.add(mediaImage) + } + } + + if (containsAudio) { + permissions.add(mediaAudio) + havePermission = havePermission && havePermission(context, mediaAudio) + } + + if (havePermission) { + permissionsUtils.permissionsListener?.onGranted(permissions) + } else { + requestPermission(permissionsUtils, permissions) + } + } + + override fun havePermissions(context: Context, requestType: Int): Boolean { + val containsImage = RequestTypeUtils.containsImage(requestType) + val containsVideo = RequestTypeUtils.containsVideo(requestType) + val containsAudio = RequestTypeUtils.containsAudio(requestType) + + var result = true + + if (containsVideo || containsImage) { + result = result && havePermission(context, mediaVisualUserSelected) + } + + if (containsAudio) { + result = result && havePermission(context, mediaAudio) + } + + return result + } + + override fun haveMediaLocation(context: Context): Boolean { + return havePermission(context, mediaLocationPermission) + } + + override fun isHandlePermissionResult() = true + + override fun handlePermissionResult( + permissionsUtils: PermissionsUtils, + context: Context, + permissions: Array, + grantResults: IntArray, + needToRequestPermissionsList: MutableList, + deniedPermissionsList: MutableList, + grantedPermissionsList: MutableList + ) { + val needImage = needToRequestPermissionsList.contains(mediaImage) + val needVideo = needToRequestPermissionsList.contains(mediaVideo) + val needAudio = needToRequestPermissionsList.contains(mediaAudio) + val needMediaLocation = needToRequestPermissionsList.contains(mediaLocationPermission) + val needMediaVisualUserSelected = + needToRequestPermissionsList.contains(mediaVisualUserSelected) + + + } +} \ No newline at end of file From 198645c348ff05a87b0d76254fe11fca5f0af92e Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Tue, 19 Sep 2023 10:56:39 +0800 Subject: [PATCH 08/18] refactor: Permission for android - split android permission for SDK version --- .../photo_manager/core/PhotoManagerPlugin.kt | 77 +++++---- .../core/entity/PermissionResult.kt | 1 + .../permission/PermissionDelegate.kt | 47 +++++- .../permission/PermissionsUtils.kt | 21 ++- .../permission/impl/PermissionDelegate19.kt | 7 +- .../permission/impl/PermissionDelegate23.kt | 10 ++ .../permission/impl/PermissionDelegate29.kt | 14 ++ .../permission/impl/PermissionDelegate30.kt | 10 ++ .../permission/impl/PermissionDelegate33.kt | 11 +- .../permission/impl/PermissionDelegate34.kt | 146 +++++++++++++++++- example/lib/page/home_page.dart | 79 ++++++---- lib/src/internal/plugin.dart | 106 +++++-------- lib/src/managers/photo_manager.dart | 4 +- lib/src/types/types.dart | 6 +- 14 files changed, 382 insertions(+), 157 deletions(-) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index 8f00b452..c03ea131 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -92,15 +92,12 @@ class PhotoManagerPlugin( } if (ignorePermissionCheck) { - val haveLocationPermission = permissionsUtils.haveLocationPermission(applicationContext) - - onHandlePermissionResult( - resultHandler, - haveLocationPermission - ) + handleOtherMethods(resultHandler) return } + handleOtherMethods(resultHandler) + // permissionsUtils.withActivity(activity) // .handlePermission(resultHandler, this::onHandlePermissionResult) @@ -222,7 +219,7 @@ class PhotoManagerPlugin( return } - val androidPermission = call.argument>("permission")!! + val androidPermission = call.argument>("androidPermission")!! val requestType = androidPermission["type"] as Int val mediaLocation = androidPermission["mediaLocation"] as Boolean @@ -237,11 +234,15 @@ class PhotoManagerPlugin( grantedPermissions: MutableList, needPermissions: MutableList ) { - if (grantedPermissions.containsAll(needPermissions)) { - resultHandler.reply(PermissionResult.Authorized.value) - } else { - resultHandler.reply(PermissionResult.Denied.value) - } + val authResult = + permissionsUtils.getAuthValue(requestType, mediaLocation) + resultHandler.reply(authResult.value) +// resultHandler.reply() +// if (grantedPermissions.containsAll(needPermissions)) { +// resultHandler.reply(authResult.value) +// } else { +// resultHandler.reply(PermissionResult.Denied.value) +// } } }) .requestPermission( @@ -252,7 +253,8 @@ class PhotoManagerPlugin( } Methods.presentLimited -> { - // resultHandler.reply(null) + val type = call.argument("type")!! + permissionsUtils.presentLimited(type, resultHandler) } Methods.ignorePermissionCheck -> { @@ -264,6 +266,25 @@ class PhotoManagerPlugin( } + private fun handleOtherMethods(resultHandler: ResultHandler) { + runOnBackground { + try { + val needLocationPermission = + permissionsUtils.haveLocationPermission(applicationContext) + handleMethodResult(resultHandler, needLocationPermission) + } catch (e: Exception) { + val call = resultHandler.call + val method = call.method + val params = call.arguments + resultHandler.replyError( + "The $method method has an error: ${e.message}", + e.stackTraceToString(), + params + ) + } + } + } + private fun handleNotNeedPermissionMethod(resultHandler: ResultHandler) { val call = resultHandler.call when (call.method) { @@ -309,39 +330,11 @@ class PhotoManagerPlugin( ) } - private fun onHandlePermissionResult( - resultHandler: ResultHandler, - needLocationPermission: Boolean - ) { - val call = resultHandler.call - if (call.method == Methods.requestPermissionExtend) { - resultHandler.reply(PermissionResult.Authorized.value) - return - } - if (call.method == Methods.presentLimited) { - resultHandler.reply(null) - return - } - runOnBackground { - try { - handleMethodResult(call, resultHandler, needLocationPermission) - } catch (e: Exception) { - val method = call.method - val params = call.arguments - resultHandler.replyError( - "The $method method has an error: ${e.message}", - e.stackTraceToString(), - params - ) - } - } - } - private fun handleMethodResult( - call: MethodCall, resultHandler: ResultHandler, needLocationPermission: Boolean ) { + val call = resultHandler.call when (call.method) { Methods.getAssetPathList -> { val type = call.argument("type")!! diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/PermissionResult.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/PermissionResult.kt index 4d7391ab..3e8a8274 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/PermissionResult.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/PermissionResult.kt @@ -4,4 +4,5 @@ enum class PermissionResult(val value: Int) { NotDetermined(0), Denied(2), Authorized(3), + Limited(4), } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt index 91be1e16..012f3765 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt @@ -1,18 +1,29 @@ package com.fluttercandies.photo_manager.permission +import android.app.Application import android.content.Context import android.content.pm.PackageManager import android.os.Build import androidx.core.app.ActivityCompat +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate19 import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate23 import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate29 import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate30 import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate33 import com.fluttercandies.photo_manager.permission.impl.PermissionDelegate34 +import com.fluttercandies.photo_manager.util.LogUtils +import com.fluttercandies.photo_manager.util.ResultHandler abstract class PermissionDelegate { + protected var resultHandler: ResultHandler? = null + + private val tag: String + get() { + return this.javaClass.simpleName + } + protected fun requestPermission( permissionsUtils: PermissionsUtils, permission: MutableList @@ -21,8 +32,9 @@ abstract class PermissionDelegate { ?: throw NullPointerException("Activity for the permission request is not exist.") permissionsUtils.setNeedToRequestPermissionsList(permission) - ActivityCompat.requestPermissions(activity, permission.toTypedArray(), requestCode) + + LogUtils.debug("requestPermission: $permission for code $requestCode") } /** @@ -54,6 +66,14 @@ abstract class PermissionDelegate { ) == PackageManager.PERMISSION_GRANTED } + protected fun havePermissionsForUser(context: Context, vararg permissions: String): Boolean { + return permissions.all { havePermissionForUser(context, it) } + } + + protected fun haveAnyPermissionForUser(context: Context, vararg permissions: String): Boolean { + return permissions.any { havePermissionForUser(context, it) } + } + /** * Check if the permission is granted for the user and it is in the manifest. */ @@ -68,7 +88,9 @@ abstract class PermissionDelegate { * Check if the [permission] are granted for the user and it is in the manifest. */ fun havePermissions(context: Context, vararg permission: String): Boolean { - return permission.all { havePermission(context, it) } + val result = permission.all { havePermission(context, it) } + LogUtils.debug("[$tag] havePermissions: ${permission.toList()}, result: $result") + return result } /** @@ -98,6 +120,7 @@ abstract class PermissionDelegate { companion object { const val requestCode = 3001 + const val limitedRequestCode = 3002 /** * Create a [PermissionDelegate] by the sdk version. @@ -109,10 +132,7 @@ abstract class PermissionDelegate { 29 -> PermissionDelegate29() in 30 until 33 -> PermissionDelegate30() 33 -> PermissionDelegate33() - in 34 until Int.MAX_VALUE -> { - PermissionDelegate34() - } - + in 34 until Int.MAX_VALUE -> PermissionDelegate34() else -> throw UnsupportedOperationException( "This sdk version is not supported yet." ) @@ -131,7 +151,8 @@ abstract class PermissionDelegate { grantResults: IntArray, needToRequestPermissionsList: MutableList, deniedPermissionsList: MutableList, - grantedPermissionsList: MutableList + grantedPermissionsList: MutableList, + requestCode: Int ) { throw UnsupportedOperationException( "handlePermissionResult is not implemented," + @@ -139,4 +160,16 @@ abstract class PermissionDelegate { ) } + open fun presentLimited( + permissionsUtils: PermissionsUtils, + context: Application, + type: Int, + resultHandler: ResultHandler + ) { + LogUtils.debug("[$tag] presentLimited is not implemented") + resultHandler.reply(null) + } + + abstract fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult + } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt index 138b1db6..bfb7a8b6 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt @@ -8,7 +8,9 @@ import android.content.pm.PackageManager import android.net.Uri import androidx.core.content.PermissionChecker import androidx.core.content.PermissionChecker.PERMISSION_GRANTED +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.util.LogUtils +import com.fluttercandies.photo_manager.util.ResultHandler import java.lang.NullPointerException import kotlin.collections.ArrayList @@ -139,7 +141,7 @@ class PermissionsUtils { permissions: Array, grantResults: IntArray ): PermissionsUtils { - if (requestCode == PermissionDelegate.requestCode) { + if (requestCode == PermissionDelegate.requestCode || requestCode == PermissionDelegate.limitedRequestCode) { for (i in permissions.indices) { LogUtils.info("Returned permissions: " + permissions[i]) if (grantResults[i] == PackageManager.PERMISSION_DENIED) { @@ -149,6 +151,12 @@ class PermissionsUtils { } } + LogUtils.debug("dealResult: ") + LogUtils.debug(" permissions: $permissions") + LogUtils.debug(" grantResults: $grantResults") + LogUtils.debug(" deniedPermissionsList: $deniedPermissionsList") + LogUtils.debug(" grantedPermissionsList: $grantedPermissionsList") + if (delegate.isHandlePermissionResult()) { delegate.handlePermissionResult( this, @@ -157,7 +165,8 @@ class PermissionsUtils { grantResults, needToRequestPermissionsList, deniedPermissionsList, - grantedPermissionsList + grantedPermissionsList, + requestCode, ) } else { if (deniedPermissionsList.isNotEmpty()) { @@ -215,4 +224,12 @@ class PermissionsUtils { needToRequestPermissionsList.addAll(permission) } + fun presentLimited(type: Int, resultHandler: ResultHandler) { + delegate.presentLimited(this, context!!, type, resultHandler) + } + + fun getAuthValue(requestType: Int, mediaLocation: Boolean): PermissionResult { + return delegate.getAuthValue(context!!, requestType, mediaLocation) + } + } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt index 575c2c5f..b7a73e58 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt @@ -1,7 +1,8 @@ package com.fluttercandies.photo_manager.permission.impl +import android.app.Application import android.content.Context -import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.permission.PermissionsUtils class PermissionDelegate19 : com.fluttercandies.photo_manager.permission.PermissionDelegate() { @@ -21,4 +22,8 @@ class PermissionDelegate19 : com.fluttercandies.photo_manager.permission.Permiss override fun haveMediaLocation(context: Context): Boolean { return true } + + override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + return PermissionResult.Authorized + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt index 87eb70d5..0f90125d 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt @@ -1,8 +1,10 @@ package com.fluttercandies.photo_manager.permission.impl import android.Manifest +import android.app.Application import android.content.Context import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.permission.PermissionsUtils @RequiresApi(23) @@ -35,4 +37,12 @@ open class PermissionDelegate23 : com.fluttercandies.photo_manager.permission.Pe override fun haveMediaLocation(context: Context): Boolean { return true } + + override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + if (havePermissions(context, readPermission, writePermission)) { + return PermissionResult.Authorized + } else { + return PermissionResult.Denied + } + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt index 28697de4..3411fe5d 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt @@ -1,9 +1,11 @@ package com.fluttercandies.photo_manager.permission.impl import android.Manifest +import android.app.Application import android.content.Context import android.os.Build import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.permission.PermissionsUtils @RequiresApi(29) @@ -37,4 +39,16 @@ class PermissionDelegate29 : PermissionDelegate23() { override fun haveMediaLocation(context: Context): Boolean { return havePermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) } + + override fun getAuthValue( + context: Application, + requestType: Int, + mediaLocation: Boolean + ): PermissionResult { + return if (havePermissions(context, readPermission, writePermission)) { + PermissionResult.Authorized + } else { + PermissionResult.Denied + } + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt index 445a5f68..d2565583 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt @@ -1,9 +1,11 @@ package com.fluttercandies.photo_manager.permission.impl import android.Manifest +import android.app.Application import android.content.Context import android.os.Build import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.permission.PermissionDelegate import com.fluttercandies.photo_manager.permission.PermissionsUtils @@ -41,4 +43,12 @@ class PermissionDelegate30 : PermissionDelegate() { override fun haveMediaLocation(context: Context): Boolean { return havePermission(context, mediaLocationPermission) } + + override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + if (havePermissions(context, readPermission)) { + return PermissionResult.Authorized + } else { + return PermissionResult.Denied + } + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt index cf23bc5e..e1b5d18e 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt @@ -1,9 +1,10 @@ package com.fluttercandies.photo_manager.permission.impl import android.Manifest +import android.app.Application import android.content.Context -import android.os.Build import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils import com.fluttercandies.photo_manager.permission.PermissionDelegate import com.fluttercandies.photo_manager.permission.PermissionsUtils @@ -79,4 +80,12 @@ class PermissionDelegate33 : PermissionDelegate() { override fun haveMediaLocation(context: Context): Boolean { return havePermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) } + + override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + return if (havePermissions(context, requestType)) { + PermissionResult.Authorized + } else { + PermissionResult.Denied + } + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt index 7d9e8879..eae71527 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt @@ -1,11 +1,15 @@ package com.fluttercandies.photo_manager.permission.impl import android.Manifest +import android.app.Application import android.content.Context import androidx.annotation.RequiresApi +import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils import com.fluttercandies.photo_manager.permission.PermissionDelegate import com.fluttercandies.photo_manager.permission.PermissionsUtils +import com.fluttercandies.photo_manager.util.LogUtils +import com.fluttercandies.photo_manager.util.ResultHandler @RequiresApi(34) class PermissionDelegate34 : PermissionDelegate() { @@ -28,6 +32,7 @@ class PermissionDelegate34 : PermissionDelegate() { requestType: Int, mediaLocation: Boolean ) { + LogUtils.debug("requestPermission") var havePermission = true val containsImage = RequestTypeUtils.containsImage(requestType) @@ -38,7 +43,6 @@ class PermissionDelegate34 : PermissionDelegate() { if (containsVideo || containsImage) { permissions.add(mediaVisualUserSelected) - // check have media visual user selected permission, the permission does not need to be defined in the manifest. val haveMediaVisualUserSelected = havePermissionForUser(context, mediaVisualUserSelected) @@ -57,6 +61,7 @@ class PermissionDelegate34 : PermissionDelegate() { if (containsImage) { permissions.add(mediaImage) } + } if (containsAudio) { @@ -64,6 +69,9 @@ class PermissionDelegate34 : PermissionDelegate() { havePermission = havePermission && havePermission(context, mediaAudio) } + LogUtils.debug("Current permissions: $permissions") + LogUtils.debug("havePermission: $havePermission") + if (havePermission) { permissionsUtils.permissionsListener?.onGranted(permissions) } else { @@ -102,8 +110,16 @@ class PermissionDelegate34 : PermissionDelegate() { grantResults: IntArray, needToRequestPermissionsList: MutableList, deniedPermissionsList: MutableList, - grantedPermissionsList: MutableList + grantedPermissionsList: MutableList, + requestCode: Int ) { + if (requestCode == limitedRequestCode) { + val handler = resultHandler ?: return + resultHandler = null + handler.reply(1) + return + } + val needImage = needToRequestPermissionsList.contains(mediaImage) val needVideo = needToRequestPermissionsList.contains(mediaVideo) val needAudio = needToRequestPermissionsList.contains(mediaAudio) @@ -111,6 +127,132 @@ class PermissionDelegate34 : PermissionDelegate() { val needMediaVisualUserSelected = needToRequestPermissionsList.contains(mediaVisualUserSelected) + var result = true + + if (needImage || needVideo || needMediaVisualUserSelected) { + val haveVideoOrImagePermission = haveAnyPermissionForUser( + context, + mediaVisualUserSelected, mediaImage, mediaVideo + ) + + result = haveVideoOrImagePermission + } + + if (needAudio) { + result = result && havePermission(context, mediaAudio) + } + + if (needMediaLocation) { + result = result && havePermissionForUser(context, mediaLocationPermission) + } + + val listener = permissionsUtils.permissionsListener ?: return + + if (result) { + listener.onGranted(needToRequestPermissionsList) + } else { + listener.onDenied( + deniedPermissionsList, + grantedPermissionsList, + needToRequestPermissionsList + ) + } + } + + override fun presentLimited( + permissionsUtils: PermissionsUtils, + context: Application, + type: Int, + resultHandler: ResultHandler + ) { + this.resultHandler = resultHandler + + val containsImage = RequestTypeUtils.containsImage(type) + val containsVideo = RequestTypeUtils.containsVideo(type) + + val permissions = mutableListOf() + + if (containsVideo || containsImage) { + permissions.add(mediaVisualUserSelected) + } + + if (containsVideo) { + permissions.add(mediaVideo) + } + if (containsImage) { + permissions.add(mediaImage) + } + + requestPermission(permissionsUtils, permissions) + } + + override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + var result = PermissionResult.NotDetermined + + fun changeResult(newResult: PermissionResult) { + if (result == PermissionResult.NotDetermined) { + result = newResult + return + } + + when (result) { + PermissionResult.Denied -> { + if (newResult == PermissionResult.Limited || newResult == PermissionResult.Authorized) { + result = PermissionResult.Limited + } + } + + PermissionResult.Authorized -> { + if (newResult == PermissionResult.Limited || newResult == PermissionResult.Denied) { + result = PermissionResult.Limited + } + } + + PermissionResult.Limited -> result = PermissionResult.Limited + else -> { + } + } + } + + val containsImage = RequestTypeUtils.containsImage(requestType) + val containsVideo = RequestTypeUtils.containsVideo(requestType) + val containsAudio = RequestTypeUtils.containsAudio(requestType) + + if (containsAudio) { + val audioResult = if (havePermissions(context, mediaAudio)) { + PermissionResult.Authorized + } else { + PermissionResult.Denied + } + + changeResult(audioResult) + } + + if (containsVideo) { + val videoResult = if (havePermissions(context, mediaVideo)) { + PermissionResult.Authorized + } else if (havePermissionForUser(context, mediaVisualUserSelected)) { + PermissionResult.Limited + } else { + PermissionResult.Denied + } + + changeResult(videoResult) + } + + if (containsImage) { + val imageResult = if (havePermissions(context, mediaImage)) { + PermissionResult.Authorized + } else if (havePermissionForUser(context, mediaVisualUserSelected)) { + PermissionResult.Limited + } else { + PermissionResult.Denied + } + + changeResult(imageResult) + } + + return result } } \ No newline at end of file diff --git a/example/lib/page/home_page.dart b/example/lib/page/home_page.dart index 4c29f2ab..a78bdce9 100644 --- a/example/lib/page/home_page.dart +++ b/example/lib/page/home_page.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:oktoast/oktoast.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager_example/widget/nav_button.dart'; import 'package:provider/provider.dart'; @@ -34,43 +35,44 @@ class _NewHomePageState extends State { Widget build(BuildContext context) { return ChangeNotifierBuilder( value: watchProvider, - builder: (_, __) => Scaffold( - appBar: AppBar( - title: const Text('photo manager example'), - ), - body: ListView( - padding: const EdgeInsets.all(8.0), - children: [ - CustomButton( - title: 'Get all gallery list', - onPressed: _scanGalleryList, + builder: (_, __) => + Scaffold( + appBar: AppBar( + title: const Text('photo manager example'), ), - if (Platform.isIOS || Platform.isAndroid) - CustomButton( - title: 'Change limited photos with PhotosUI', - onPressed: _changeLimitPhotos, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, + body: ListView( + padding: const EdgeInsets.all(8.0), children: [ - const Text('scan type'), - Container(width: 10), + CustomButton( + title: 'Get all gallery list', + onPressed: _scanGalleryList, + ), + if (Platform.isIOS || Platform.isAndroid) + CustomButton( + title: 'Change limited photos with PhotosUI', + onPressed: _changeLimitPhotos, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('scan type'), + Container(width: 10), + ], + ), + _buildTypeChecks(watchProvider), + _buildHasAllCheck(), + _buildOnlyAllCheck(), + _buildContainsLivePhotos(), + _buildOnlyLivePhotos(), + _buildPathContainsModifiedDateCheck(), + _buildPngCheck(), + _buildNotifyCheck(), + _buildFilterOption(watchProvider), + if (Platform.isIOS || Platform.isMacOS) + _buildPathFilterOption(), ], ), - _buildTypeChecks(watchProvider), - _buildHasAllCheck(), - _buildOnlyAllCheck(), - _buildContainsLivePhotos(), - _buildOnlyLivePhotos(), - _buildPathContainsModifiedDateCheck(), - _buildPngCheck(), - _buildNotifyCheck(), - _buildFilterOption(watchProvider), - if (Platform.isIOS || Platform.isMacOS) - _buildPathFilterOption(), - ], - ), - ), + ), ); } @@ -118,6 +120,17 @@ class _NewHomePageState extends State { } Future _scanGalleryList() async { + final permissionResult = await PhotoManager.requestPermissionExtend( + requestOption: PermissionRequestOption( + androidPermission: AndroidPermission( + type: readProvider.type, mediaLocation: true), + ) + ); + if (!permissionResult.hasAccess) { + showToast('no permission'); + return; + } + await readProvider.refreshGalleryList(); if (!mounted) { return; diff --git a/lib/src/internal/plugin.dart b/lib/src/internal/plugin.dart index 668f57be..8634a28f 100644 --- a/lib/src/internal/plugin.dart +++ b/lib/src/internal/plugin.dart @@ -47,9 +47,9 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } if (filterOption is FilterOptionGroup) { assert( - type == RequestType.image || !filterOption.onlyLivePhotos, - 'Filtering only Live Photos is only supported ' - 'when the request type contains image.', + type == RequestType.image || !filterOption.onlyLivePhotos, + 'Filtering only Live Photos is only supported ' + 'when the request type contains image.', ); } @@ -74,8 +74,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } Future requestPermissionExtend( - PermissionRequestOption requestOption, - ) async { + PermissionRequestOption requestOption,) async { final int result = await _channel.invokeMethod( PMConstants.mRequestPermissionExtend, requestOption.toMap(), @@ -102,15 +101,14 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } /// Use pagination to get album content. - Future> getAssetListPaged( - String id, { + Future> getAssetListPaged(String id, { required PMFilter optionGroup, int page = 0, int size = 15, RequestType type = RequestType.common, }) async { final Map result = - await _channel.invokeMethod>( + await _channel.invokeMethod>( PMConstants.mGetAssetListPaged, { 'id': id, @@ -124,15 +122,14 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } /// Asset in the specified range. - Future> getAssetListRange( - String id, { + Future> getAssetListRange(String id, { required RequestType type, required int start, required int end, required PMFilter optionGroup, }) async { final Map map = - await _channel.invokeMethod>( + await _channel.invokeMethod>( PMConstants.mGetAssetListRange, { 'id': id, @@ -146,10 +143,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return ConvertUtils.convertToAssetList(map.cast()); } - void _injectParams( - Map params, - PMProgressHandler? progressHandler, - ) { + void _injectParams(Map params, + PMProgressHandler? progressHandler,) { if (progressHandler != null) { params['progressHandler'] = progressHandler.channelIndex; } @@ -169,8 +164,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return _channel.invokeMethod(PMConstants.mGetThumb, params); } - Future getOriginBytes( - String id, { + Future getOriginBytes(String id, { PMProgressHandler? progressHandler, }) { final Map params = {'id': id}; @@ -182,8 +176,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return _channel.invokeMethod(PMConstants.mReleaseMemoryCache); } - Future getFullFile( - String id, { + Future getFullFile(String id, { required bool isOrigin, PMProgressHandler? progressHandler, int subtype = 0, @@ -212,11 +205,9 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future?> fetchPathProperties( - String id, - RequestType type, - PMFilter optionGroup, - ) { + Future?> fetchPathProperties(String id, + RequestType type, + PMFilter optionGroup,) { return _channel.invokeMethod( PMConstants.mFetchPathProperties, { @@ -261,8 +252,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { 'onlyAddPermission': true, }; - Future saveImage( - typed_data.Uint8List data, { + Future saveImage(typed_data.Uint8List data, { required String? title, String? desc, String? relativePath, @@ -286,8 +276,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future saveImageWithPath( - String path, { + Future saveImageWithPath(String path, { required String title, String? desc, String? relativePath, @@ -316,8 +305,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future saveVideo( - File file, { + Future saveVideo(File file, { required String? title, String? desc, String? relativePath, @@ -414,8 +402,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return LatLng(latitude: entity.latitude, longitude: entity.longitude); } - Future getTitleAsync( - AssetEntity entity, { + Future getTitleAsync(AssetEntity entity, { int subtype = 0, }) async { assert(Platform.isAndroid || Platform.isIOS || Platform.isMacOS); @@ -442,10 +429,9 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } Future> getSubPathEntities( - AssetPathEntity pathEntity, - ) async { + AssetPathEntity pathEntity,) async { final Map result = - await _channel.invokeMethod>( + await _channel.invokeMethod>( PMConstants.mGetSubPath, { 'id': pathEntity.id, @@ -462,14 +448,12 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future copyAssetToGallery( - AssetEntity asset, - AssetPathEntity pathEntity, - ) async { + Future copyAssetToGallery(AssetEntity asset, + AssetPathEntity pathEntity,) async { if (pathEntity.isAll) { assert( - pathEntity.isAll, - "You can't copy the asset into the album containing all the pictures.", + pathEntity.isAll, + "You can't copy the asset into the album containing all the pictures.", ); return null; } @@ -527,10 +511,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return _channel.invokeMethod(PMConstants.mCancelCacheRequests); } - Future requestCacheAssetsThumbnail( - List ids, - ThumbnailOption option, - ) { + Future requestCacheAssetsThumbnail(List ids, + ThumbnailOption option,) { assert(ids.isNotEmpty); return _channel.invokeMethod( PMConstants.mRequestCacheAssetsThumb, @@ -541,10 +523,12 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future presentLimited() async { + Future presentLimited(RequestType type) async { assert(Platform.isIOS || Platform.isAndroid); if (Platform.isIOS || Platform.isAndroid) { - return _channel.invokeMethod(PMConstants.mPresentLimited); + return _channel.invokeMethod(PMConstants.mPresentLimited, { + 'type': type.value, + }); } } @@ -607,11 +591,9 @@ mixin IosPlugin on BasePlugin { return result; } - Future iosCreateAlbum( - String name, - bool isRoot, - AssetPathEntity? parent, - ) async { + Future iosCreateAlbum(String name, + bool isRoot, + AssetPathEntity? parent,) async { final Map map = { 'name': name, 'isRoot': isRoot, @@ -632,11 +614,9 @@ mixin IosPlugin on BasePlugin { return AssetPathEntity.fromId(result['id'] as String); } - Future iosCreateFolder( - String name, - bool isRoot, - AssetPathEntity? parent, - ) async { + Future iosCreateFolder(String name, + bool isRoot, + AssetPathEntity? parent,) async { final Map map = { 'name': name, 'isRoot': isRoot, @@ -657,10 +637,8 @@ mixin IosPlugin on BasePlugin { return AssetPathEntity.fromId(result['id'] as String, albumType: 2); } - Future iosRemoveInAlbum( - List entities, - AssetPathEntity path, - ) async { + Future iosRemoveInAlbum(List entities, + AssetPathEntity path,) async { final Map? result = await _channel.invokeMethod( PMConstants.mRemoveInAlbum, { @@ -673,10 +651,8 @@ mixin IosPlugin on BasePlugin { } mixin AndroidPlugin on BasePlugin { - Future androidMoveAssetToPath( - AssetEntity entity, - AssetPathEntity target, - ) async { + Future androidMoveAssetToPath(AssetEntity entity, + AssetPathEntity target,) async { final Map? result = await _channel.invokeMethod( PMConstants.mMoveAssetToPath, {'assetId': entity.id, 'albumId': target.id}, diff --git a/lib/src/managers/photo_manager.dart b/lib/src/managers/photo_manager.dart index 2db89e23..792b002b 100644 --- a/lib/src/managers/photo_manager.dart +++ b/lib/src/managers/photo_manager.dart @@ -68,7 +68,9 @@ class PhotoManager { /// See the documents from Apple: /// * iOS 14: https://developer.apple.com/documentation/photokit/phphotolibrary/3616113-presentlimitedlibrarypickerfromv/ /// * iOS 15: https://developer.apple.com/documentation/photokit/phphotolibrary/3752108-presentlimitedlibrarypickerfromv/ - static Future presentLimited() => plugin.presentLimited(); + static Future presentLimited({ + RequestType type = RequestType.all, +}) => plugin.presentLimited(type); /// Obtain albums/folders list with couple filter options. /// diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index 71401e14..733ca40e 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -82,8 +82,8 @@ class PermissionRequestOption with IMapMixin { const PermissionRequestOption({ this.iosAccessLevel = IosAccessLevel.readWrite, this.androidPermission = const AndroidPermission( - RequestType.common, - false, + type: RequestType.common, + mediaLocation: false, ), }); @@ -125,7 +125,7 @@ class AndroidPermission with IMapMixin { final bool mediaLocation; /// The permission for android. - const AndroidPermission(this.type, this.mediaLocation); + const AndroidPermission({required this.type, required this.mediaLocation,}); @override Map toMap() => From 338738b4e5c36960c7f9f318ec1f8b8f093dde5e Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Tue, 19 Sep 2023 11:19:21 +0800 Subject: [PATCH 09/18] Upgrade version --- CHANGELOG.md | 42 +++++++++++++++++++++++++++++------------- pubspec.yaml | 2 +- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 084945bd..82037668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ that can be found in the LICENSE file. --> # CHANGELOG +## 2.8.0 + +### Feature + +- Support android API 34(Android 14) limit access to photos and videos. +- Because limit permission, we refactor the permission request API. + +***Breaking changes for permission behavior*** + +Now, users need to ensure they have the access permissions through the +following means before using the API to access resources. +Two ways: + +1. `PhotoManager.requestPermissionExtend()` and the result is `PermissionState.authorized` or `PermissionState.limited`. +2. `PhotoManager.setIgnorePermissionCheck(true)` to ignore permission check and use other permission plugin. + ## 2.7.1 ### Fixes @@ -38,9 +54,9 @@ that can be found in the LICENSE file. --> - Support `CustomFilter` for more filter options. (#901) - Add two new static methods for `PhotoManager`: - - `getAssetCount` for getting assets count. - - `getAssetListRange` for getting assets between start and end. - + - `getAssetCount` for getting assets count. + - `getAssetListRange` for getting assets between start and end. + ## 2.5.2 ### Improvements @@ -120,8 +136,8 @@ that can be found in the LICENSE file. --> - Introduce `AssetPathEntity.assetCountAsync` getter, which improves the speed when loading paths mainly on iOS, also: - - Deprecate `AssetPathEntity.assetCount`. - - Remove `FilterOptionGroup.containsEmptyAlbum`. + - Deprecate `AssetPathEntity.assetCount`. + - Remove `FilterOptionGroup.containsEmptyAlbum`. ### Improvements @@ -545,7 +561,7 @@ and applied multiple ### Fixes which make this plugin as the most solid ever. but at the same time user have to bear the risks corresponding to the permission). - Support clean file cache. - Experimental - - Preload image (Use `PhotoCachingManager` api.) + - Preload image (Use `PhotoCachingManager` api.) - Add `OrderOption` as sort condition. The option default value is order by create date desc; - Support icloud asset progress. @@ -608,14 +624,14 @@ and applied multiple ### Fixes which make this plugin as the most solid ever. - Create AssetEntity with id. - Create AssetPathEntity from id. - Only iOS - - Create folder or album. - - Remove assets in album. - - Delete folder or album. - - Favorite asset. + - Create folder or album. + - Remove assets in album. + - Delete folder or album. + - Favorite asset. - Only android - - move asset to another path. - - Remove all non-existing rows. - - add `relativePath` for android. + - move asset to another path. + - Remove all non-existing rows. + - add `relativePath` for android. ### Improvements diff --git a/pubspec.yaml b/pubspec.yaml index c8d0639b..c5cea1ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: photo_manager description: A Flutter plugin that provides assets abstraction management APIs on Android, iOS, and macOS. repository: https://github.com/fluttercandies/flutter_photo_manager -version: 2.7.1 +version: 2.8.0 environment: sdk: ">=2.13.0 <3.0.0" From 7a223d0b4b9da1990ccc5f68267a37f021cb5f2b Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Tue, 19 Sep 2023 11:22:50 +0800 Subject: [PATCH 10/18] docs: Update README --- README-ZH.md | 4 ++-- README.md | 6 +++--- lib/src/types/types.dart | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index a3beda89..b8806496 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -213,8 +213,8 @@ Android 10 引入了 **Scoped Storage**,导致原始资源文件不能通过 ```dart final PermissionState ps = await PhotoManager.requestPermissionExtend(); -if (ps.isAuth) { - // 已获取到权限。 +if (ps.hasAccess) { + // 已获取到权限(哪怕只是有限的访问权限)。 } else { // 权限受限制(iOS)或者被拒绝,使用 `==` 能够更准确的判断是受限还是拒绝。 // 你可以使用 `PhotoManager.openSetting()` 打开系统设置页面进行进一步的逻辑定制。 diff --git a/README.md b/README.md index 23a85f55..a6b24f6d 100644 --- a/README.md +++ b/README.md @@ -222,9 +222,9 @@ It's pretty much the same as the `NSPhotoLibraryUsageDescription`. Most of the APIs can only use with granted permission. ```dart -final PermissionState ps = await PhotoManager.requestPermissionExtend(); -if (ps.isAuth) { - // Granted. +final PermissionState ps = await PhotoManager.requestPermissionExtend(); // the method can use optional param `permission`. +if (ps.hasAccess) { + // Granted or limited. } else { // Limited(iOS) or Rejected, use `==` for more precise judgements. // You can call `PhotoManager.openSetting()` to open settings for further steps. diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index 733ca40e..9a2b529e 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -87,6 +87,7 @@ class PermissionRequestOption with IMapMixin { ), }); + /// See [IosAccessLevel]. final IosAccessLevel iosAccessLevel; /// See [AndroidPermission]. From 49013d0769a46f02ffcf2bbe6d9054e4a53f4f2d Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Tue, 19 Sep 2023 11:35:32 +0800 Subject: [PATCH 11/18] docs: Update README --- README-ZH.md | 17 ++++++++++++++--- README.md | 23 ++++++++++++++++++----- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index b8806496..a91f8631 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -213,8 +213,11 @@ Android 10 引入了 **Scoped Storage**,导致原始资源文件不能通过 ```dart final PermissionState ps = await PhotoManager.requestPermissionExtend(); -if (ps.hasAccess) { +if (ps.isAuth) { + // 已获取到权限 +} else if (ps.hasAccess) { // 已获取到权限(哪怕只是有限的访问权限)。 + // iOS Android 目前都已经有了部分权限的概念。 } else { // 权限受限制(iOS)或者被拒绝,使用 `==` 能够更准确的判断是受限还是拒绝。 // 你可以使用 `PhotoManager.openSetting()` 打开系统设置页面进行进一步的逻辑定制。 @@ -228,14 +231,22 @@ PhotoManager.setIgnorePermissionCheck(true); 对于一些后台操作(应用未启动等)而言,忽略检查是比较合适的做法。 -#### iOS 受限的资源权限 +#### 受限的资源权限 + +##### iOS 受限的资源权限 iOS14 引入了部分资源限制的权限 (`PermissionState.limited`)。 `PhotoManager.requestPermissionExtend()` 会返回当前的权限状态 `PermissionState`。 详情请参阅 [PHAuthorizationStatus][]。 如果你想要重新选择在应用里能够读取到的资源,你可以使用 `PhotoManager.presentLimited()` 重新选择资源, -这个方法仅在 iOS 14 以上的版本生效,其他平台或版本无法调用这个方法。 +这个方法对于 iOS 14 以上的版本生效。 + +##### Android 受限的资源权限 + +和 iOS 类似,安卓 14(API 34) 中也加入了这个概念,dart 端的使用方法也完全一致。 + +但行为上略有不同(基于模拟器),安卓中一旦授予某个资源的访问权限,就无法撤销,即使再次使用 `presentLimited` 时不选中也一样。 ### 获取相簿或图集 (`AssetPathEntity`) diff --git a/README.md b/README.md index a6b24f6d..12f8f5c7 100644 --- a/README.md +++ b/README.md @@ -223,8 +223,11 @@ Most of the APIs can only use with granted permission. ```dart final PermissionState ps = await PhotoManager.requestPermissionExtend(); // the method can use optional param `permission`. -if (ps.hasAccess) { - // Granted or limited. +if (ps.isAuth) { + // Granted + // You can to get assets here. +} else if (ps.hasAccess) { + // Access will continue, but the amount visible depends on the user's selection. } else { // Limited(iOS) or Rejected, use `==` for more precise judgements. // You can call `PhotoManager.openSetting()` to open settings for further steps. @@ -240,7 +243,9 @@ PhotoManager.setIgnorePermissionCheck(true); For background processing (such as when the app is not in the foreground), ignore permissions check would be proper solution. -#### Limited entities access on iOS +#### Limited entities access + +##### Limited entities access on iOS With iOS 14 released, Apple brought a "Limited Photos Library" permission (`PermissionState.limited`) to iOS. @@ -251,8 +256,16 @@ To reselect accessible entities for the app, use `PhotoManager.presentLimited()` to call the modal of accessible entities' management. This method only available for iOS 14+ and when the permission state -is limited (`PermissionState.limited`), -other platform won't make a valid call. +is limited (`PermissionState.limited`). + +##### Limited entities access on android + +Similar to iOS, Android 14 (API 34) has also introduced this concept, and the usage on the Dart side is completely identical. + +However, there is a slight difference in behavior (based on the emulator). + +In Android, once access permission to a certain resource is granted, it cannot be revoked, +even if you do not select it again when using `presentLimited`. ### Get albums/folders (`AssetPathEntity`) From bcd29d61a7b29f65f3ebd07018c21a5a6ce32b02 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Tue, 19 Sep 2023 11:52:21 +0800 Subject: [PATCH 12/18] file: remove a flow chart file --- flow_chart/permission.dio | 1 - 1 file changed, 1 deletion(-) delete mode 100644 flow_chart/permission.dio diff --git a/flow_chart/permission.dio b/flow_chart/permission.dio deleted file mode 100644 index 055046c7..00000000 --- a/flow_chart/permission.dio +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 9552c993e1e5f0ac2febb14a7580ecc93e529be4 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 21 Sep 2023 17:04:42 +0800 Subject: [PATCH 13/18] style: format code --- example/lib/model/photo_provider.dart | 6 +- example/lib/page/filter_option_page.dart | 47 ++++++----- example/lib/page/home_page.dart | 73 ++++++++--------- lib/src/internal/map_interface.dart | 5 +- lib/src/internal/plugin.dart | 100 ++++++++++++++--------- lib/src/managers/notify_manager.dart | 8 +- lib/src/managers/photo_manager.dart | 3 +- lib/src/types/types.dart | 16 ++-- 8 files changed, 142 insertions(+), 116 deletions(-) diff --git a/example/lib/model/photo_provider.dart b/example/lib/model/photo_provider.dart index 81727654..d061ad2e 100644 --- a/example/lib/model/photo_provider.dart +++ b/example/lib/model/photo_provider.dart @@ -249,7 +249,8 @@ class PhotoProvider extends ChangeNotifier { /// For path filter option var _pathFilterOption = const PMPathFilter(); PMPathFilter get pathFilterOption => _pathFilterOption; - List _pathTypeList = PMDarwinAssetCollectionType.values; + List _pathTypeList = + PMDarwinAssetCollectionType.values; List get pathTypeList => _pathTypeList; @@ -258,7 +259,8 @@ class PhotoProvider extends ChangeNotifier { _onChangePathFilter(); } - late List _pathSubTypeList = _pathFilterOption.darwin.subType; + late List _pathSubTypeList = + _pathFilterOption.darwin.subType; List get pathSubTypeList => _pathSubTypeList; diff --git a/example/lib/page/filter_option_page.dart b/example/lib/page/filter_option_page.dart index 082420ea..bc3cab6d 100644 --- a/example/lib/page/filter_option_page.dart +++ b/example/lib/page/filter_option_page.dart @@ -239,29 +239,28 @@ class _DarwinPathFilterPageState extends State { final PhotoProvider provider = context.watch(); return Scaffold( - appBar: AppBar( - title: const Text('Path filter'), - ), - body: ListView( - children: [ - buildGroup( - 'PMDarwinAssetCollectionType', - PMDarwinAssetCollectionType.values, - provider.pathTypeList, - (value) { - provider.pathTypeList = value; - }, - ), - buildGroup( - 'PMDarwinAssetCollectionSubtype', - PMDarwinAssetCollectionSubtype.values, - provider.pathSubTypeList, - (value) { - provider.pathSubTypeList = value; - }, - ), - ], - ) - ); + appBar: AppBar( + title: const Text('Path filter'), + ), + body: ListView( + children: [ + buildGroup( + 'PMDarwinAssetCollectionType', + PMDarwinAssetCollectionType.values, + provider.pathTypeList, + (value) { + provider.pathTypeList = value; + }, + ), + buildGroup( + 'PMDarwinAssetCollectionSubtype', + PMDarwinAssetCollectionSubtype.values, + provider.pathSubTypeList, + (value) { + provider.pathSubTypeList = value; + }, + ), + ], + )); } } diff --git a/example/lib/page/home_page.dart b/example/lib/page/home_page.dart index a78bdce9..b4f92556 100644 --- a/example/lib/page/home_page.dart +++ b/example/lib/page/home_page.dart @@ -35,44 +35,42 @@ class _NewHomePageState extends State { Widget build(BuildContext context) { return ChangeNotifierBuilder( value: watchProvider, - builder: (_, __) => - Scaffold( - appBar: AppBar( - title: const Text('photo manager example'), + builder: (_, __) => Scaffold( + appBar: AppBar( + title: const Text('photo manager example'), + ), + body: ListView( + padding: const EdgeInsets.all(8.0), + children: [ + CustomButton( + title: 'Get all gallery list', + onPressed: _scanGalleryList, ), - body: ListView( - padding: const EdgeInsets.all(8.0), + if (Platform.isIOS || Platform.isAndroid) + CustomButton( + title: 'Change limited photos with PhotosUI', + onPressed: _changeLimitPhotos, + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ - CustomButton( - title: 'Get all gallery list', - onPressed: _scanGalleryList, - ), - if (Platform.isIOS || Platform.isAndroid) - CustomButton( - title: 'Change limited photos with PhotosUI', - onPressed: _changeLimitPhotos, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('scan type'), - Container(width: 10), - ], - ), - _buildTypeChecks(watchProvider), - _buildHasAllCheck(), - _buildOnlyAllCheck(), - _buildContainsLivePhotos(), - _buildOnlyLivePhotos(), - _buildPathContainsModifiedDateCheck(), - _buildPngCheck(), - _buildNotifyCheck(), - _buildFilterOption(watchProvider), - if (Platform.isIOS || Platform.isMacOS) - _buildPathFilterOption(), + const Text('scan type'), + Container(width: 10), ], ), - ), + _buildTypeChecks(watchProvider), + _buildHasAllCheck(), + _buildOnlyAllCheck(), + _buildContainsLivePhotos(), + _buildOnlyLivePhotos(), + _buildPathContainsModifiedDateCheck(), + _buildPngCheck(), + _buildNotifyCheck(), + _buildFilterOption(watchProvider), + if (Platform.isIOS || Platform.isMacOS) _buildPathFilterOption(), + ], + ), + ), ); } @@ -122,10 +120,9 @@ class _NewHomePageState extends State { Future _scanGalleryList() async { final permissionResult = await PhotoManager.requestPermissionExtend( requestOption: PermissionRequestOption( - androidPermission: AndroidPermission( - type: readProvider.type, mediaLocation: true), - ) - ); + androidPermission: + AndroidPermission(type: readProvider.type, mediaLocation: true), + )); if (!permissionResult.hasAccess) { showToast('no permission'); return; diff --git a/lib/src/internal/map_interface.dart b/lib/src/internal/map_interface.dart index 23b8fc90..c115b9ca 100644 --- a/lib/src/internal/map_interface.dart +++ b/lib/src/internal/map_interface.dart @@ -3,10 +3,9 @@ // in the LICENSE file. /// Contains an abstract method toMap to indicate that it can be converted into a Map object -mixin IMapMixin{ - +mixin IMapMixin { /// Convert current object to a map. /// /// Usually for transfer to MethodChannel. Map toMap(); -} \ No newline at end of file +} diff --git a/lib/src/internal/plugin.dart b/lib/src/internal/plugin.dart index 8634a28f..03455719 100644 --- a/lib/src/internal/plugin.dart +++ b/lib/src/internal/plugin.dart @@ -47,9 +47,9 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } if (filterOption is FilterOptionGroup) { assert( - type == RequestType.image || !filterOption.onlyLivePhotos, - 'Filtering only Live Photos is only supported ' - 'when the request type contains image.', + type == RequestType.image || !filterOption.onlyLivePhotos, + 'Filtering only Live Photos is only supported ' + 'when the request type contains image.', ); } @@ -74,7 +74,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } Future requestPermissionExtend( - PermissionRequestOption requestOption,) async { + PermissionRequestOption requestOption, + ) async { final int result = await _channel.invokeMethod( PMConstants.mRequestPermissionExtend, requestOption.toMap(), @@ -101,14 +102,15 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } /// Use pagination to get album content. - Future> getAssetListPaged(String id, { + Future> getAssetListPaged( + String id, { required PMFilter optionGroup, int page = 0, int size = 15, RequestType type = RequestType.common, }) async { final Map result = - await _channel.invokeMethod>( + await _channel.invokeMethod>( PMConstants.mGetAssetListPaged, { 'id': id, @@ -122,14 +124,15 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } /// Asset in the specified range. - Future> getAssetListRange(String id, { + Future> getAssetListRange( + String id, { required RequestType type, required int start, required int end, required PMFilter optionGroup, }) async { final Map map = - await _channel.invokeMethod>( + await _channel.invokeMethod>( PMConstants.mGetAssetListRange, { 'id': id, @@ -143,8 +146,10 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return ConvertUtils.convertToAssetList(map.cast()); } - void _injectParams(Map params, - PMProgressHandler? progressHandler,) { + void _injectParams( + Map params, + PMProgressHandler? progressHandler, + ) { if (progressHandler != null) { params['progressHandler'] = progressHandler.channelIndex; } @@ -164,7 +169,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return _channel.invokeMethod(PMConstants.mGetThumb, params); } - Future getOriginBytes(String id, { + Future getOriginBytes( + String id, { PMProgressHandler? progressHandler, }) { final Map params = {'id': id}; @@ -176,7 +182,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return _channel.invokeMethod(PMConstants.mReleaseMemoryCache); } - Future getFullFile(String id, { + Future getFullFile( + String id, { required bool isOrigin, PMProgressHandler? progressHandler, int subtype = 0, @@ -205,9 +212,11 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future?> fetchPathProperties(String id, - RequestType type, - PMFilter optionGroup,) { + Future?> fetchPathProperties( + String id, + RequestType type, + PMFilter optionGroup, + ) { return _channel.invokeMethod( PMConstants.mFetchPathProperties, { @@ -252,7 +261,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { 'onlyAddPermission': true, }; - Future saveImage(typed_data.Uint8List data, { + Future saveImage( + typed_data.Uint8List data, { required String? title, String? desc, String? relativePath, @@ -276,7 +286,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future saveImageWithPath(String path, { + Future saveImageWithPath( + String path, { required String title, String? desc, String? relativePath, @@ -305,7 +316,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future saveVideo(File file, { + Future saveVideo( + File file, { required String? title, String? desc, String? relativePath, @@ -402,7 +414,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return LatLng(latitude: entity.latitude, longitude: entity.longitude); } - Future getTitleAsync(AssetEntity entity, { + Future getTitleAsync( + AssetEntity entity, { int subtype = 0, }) async { assert(Platform.isAndroid || Platform.isIOS || Platform.isMacOS); @@ -429,9 +442,10 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { } Future> getSubPathEntities( - AssetPathEntity pathEntity,) async { + AssetPathEntity pathEntity, + ) async { final Map result = - await _channel.invokeMethod>( + await _channel.invokeMethod>( PMConstants.mGetSubPath, { 'id': pathEntity.id, @@ -448,12 +462,14 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { ); } - Future copyAssetToGallery(AssetEntity asset, - AssetPathEntity pathEntity,) async { + Future copyAssetToGallery( + AssetEntity asset, + AssetPathEntity pathEntity, + ) async { if (pathEntity.isAll) { assert( - pathEntity.isAll, - "You can't copy the asset into the album containing all the pictures.", + pathEntity.isAll, + "You can't copy the asset into the album containing all the pictures.", ); return null; } @@ -511,8 +527,10 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin { return _channel.invokeMethod(PMConstants.mCancelCacheRequests); } - Future requestCacheAssetsThumbnail(List ids, - ThumbnailOption option,) { + Future requestCacheAssetsThumbnail( + List ids, + ThumbnailOption option, + ) { assert(ids.isNotEmpty); return _channel.invokeMethod( PMConstants.mRequestCacheAssetsThumb, @@ -591,9 +609,11 @@ mixin IosPlugin on BasePlugin { return result; } - Future iosCreateAlbum(String name, - bool isRoot, - AssetPathEntity? parent,) async { + Future iosCreateAlbum( + String name, + bool isRoot, + AssetPathEntity? parent, + ) async { final Map map = { 'name': name, 'isRoot': isRoot, @@ -614,9 +634,11 @@ mixin IosPlugin on BasePlugin { return AssetPathEntity.fromId(result['id'] as String); } - Future iosCreateFolder(String name, - bool isRoot, - AssetPathEntity? parent,) async { + Future iosCreateFolder( + String name, + bool isRoot, + AssetPathEntity? parent, + ) async { final Map map = { 'name': name, 'isRoot': isRoot, @@ -637,8 +659,10 @@ mixin IosPlugin on BasePlugin { return AssetPathEntity.fromId(result['id'] as String, albumType: 2); } - Future iosRemoveInAlbum(List entities, - AssetPathEntity path,) async { + Future iosRemoveInAlbum( + List entities, + AssetPathEntity path, + ) async { final Map? result = await _channel.invokeMethod( PMConstants.mRemoveInAlbum, { @@ -651,8 +675,10 @@ mixin IosPlugin on BasePlugin { } mixin AndroidPlugin on BasePlugin { - Future androidMoveAssetToPath(AssetEntity entity, - AssetPathEntity target,) async { + Future androidMoveAssetToPath( + AssetEntity entity, + AssetPathEntity target, + ) async { final Map? result = await _channel.invokeMethod( PMConstants.mMoveAssetToPath, {'assetId': entity.id, 'albumId': target.id}, diff --git a/lib/src/managers/notify_manager.dart b/lib/src/managers/notify_manager.dart index f0bbee6c..1f8e6b4b 100644 --- a/lib/src/managers/notify_manager.dart +++ b/lib/src/managers/notify_manager.dart @@ -16,12 +16,14 @@ import '../internal/plugin.dart'; class NotifyManager { /// The method channel used to communicate with the native platform. static const _channel = MethodChannel('${PMConstants.channelPrefix}/notify'); -/// Returns a [Stream] that emits a boolean value indicating whether asset change notifications are currently enabled. + + /// Returns a [Stream] that emits a boolean value indicating whether asset change notifications are currently enabled. Stream get notifyStream => _controller.stream; + /// The stream controller used to manage the [notifyStream]. final _controller = StreamController.broadcast(); -/// The list of callback functions to be executed upon asset changes. + /// The list of callback functions to be executed upon asset changes. final _notifyCallback = >[]; /// {@template photo_manager.NotifyManager.addChangeCallback} @@ -55,7 +57,7 @@ class NotifyManager { } /// {@template photo_manager.NotifyManager.stopChangeNotify} - + /// Disables asset change notifications. /// /// Remember to remove all callback functions for changes. diff --git a/lib/src/managers/photo_manager.dart b/lib/src/managers/photo_manager.dart index 792b002b..574f92b1 100644 --- a/lib/src/managers/photo_manager.dart +++ b/lib/src/managers/photo_manager.dart @@ -70,7 +70,8 @@ class PhotoManager { /// * iOS 15: https://developer.apple.com/documentation/photokit/phphotolibrary/3752108-presentlimitedlibrarypickerfromv/ static Future presentLimited({ RequestType type = RequestType.all, -}) => plugin.presentLimited(type); + }) => + plugin.presentLimited(type); /// Obtain albums/folders list with couple filter options. /// diff --git a/lib/src/types/types.dart b/lib/src/types/types.dart index 9a2b529e..52525f82 100644 --- a/lib/src/types/types.dart +++ b/lib/src/types/types.dart @@ -94,8 +94,7 @@ class PermissionRequestOption with IMapMixin { final AndroidPermission androidPermission; @override - Map toMap() => - { + Map toMap() => { 'iosAccessLevel': iosAccessLevel.index + 1, 'androidPermission': androidPermission.toMap(), }; @@ -103,7 +102,7 @@ class PermissionRequestOption with IMapMixin { @override bool operator ==(Object other) => other is PermissionRequestOption && - iosAccessLevel == other.iosAccessLevel; + iosAccessLevel == other.iosAccessLevel; @override int get hashCode => iosAccessLevel.hashCode; @@ -111,7 +110,6 @@ class PermissionRequestOption with IMapMixin { /// The permission for android. class AndroidPermission with IMapMixin { - /// The type of your need. /// /// See [RequestType]. @@ -126,12 +124,14 @@ class AndroidPermission with IMapMixin { final bool mediaLocation; /// The permission for android. - const AndroidPermission({required this.type, required this.mediaLocation,}); + const AndroidPermission({ + required this.type, + required this.mediaLocation, + }); @override - Map toMap() => - { + Map toMap() => { 'type': type.value, 'mediaLocation': mediaLocation, }; -} \ No newline at end of file +} From d07dbf1106d967c60f0e17afe3f21642f5afd211 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 21 Sep 2023 17:07:29 +0800 Subject: [PATCH 14/18] docs: Upgrade toc --- README-ZH.md | 14 ++++++++------ README.md | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index a91f8631..24ed96e1 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -49,18 +49,20 @@ that can be found in the LICENSE file. --> * [原生平台的配置](#原生平台的配置) * [Android 配置准备](#android-配置准备) * [Kotlin, Gradle, AGP](#kotlin-gradle-agp) - * [Android 10 (Q, 29)](#android-10--q-29-) + * [Android 10 (Q, 29)](#android-10-q-29) * [Glide](#glide) * [iOS 配置准备](#ios-配置准备) * [使用方法](#使用方法) * [请求权限](#请求权限) - * [iOS 受限的资源权限](#ios-受限的资源权限) - * [获取相簿或图集 (`AssetPathEntity`)](#获取相簿或图集--assetpathentity-) + * [受限的资源权限](#受限的资源权限) + * [iOS 受限的资源权限](#ios-受限的资源权限) + * [Android 受限的资源权限](#android-受限的资源权限) + * [获取相簿或图集 (`AssetPathEntity`)](#获取相簿或图集-assetpathentity) * [`getAssetPathList` 方法的参数](#getassetpathlist-方法的参数) * [PMPathFilterOption](#pmpathfilteroption) - * [获取资源 (`AssetEntity`)](#获取资源--assetentity-) + * [获取资源 (`AssetEntity`)](#获取资源-assetentity) * [通过 `AssetPathEntity` 获取](#通过-assetpathentity-获取) - * [通过 `PhotoManager` 方法 (2.6.0+) 获取](#通过-photomanager-方法--260--获取) + * [通过 `PhotoManager` 方法 (2.6.0+) 获取](#通过-photomanager-方法-260-获取) * [通过 ID 获取](#通过-id-获取) * [通过原始数据获取](#通过原始数据获取) * [通过 iCloud 获取](#通过-icloud-获取) @@ -85,7 +87,7 @@ that can be found in the LICENSE file. --> * [原生额外配置](#原生额外配置) * [Android 额外配置](#android-额外配置) * [Glide 相关问题](#glide-相关问题) - * [Android 13 (API level 33) 额外配置](#android-13--api-level-33--额外配置) + * [Android 13 (API level 33) 额外配置](#android-13-api-level-33-额外配置) * [iOS 额外配置](#ios-额外配置) * [配置系统相册名称的国际化](#配置系统相册名称的国际化) * [实验性功能](#实验性功能) diff --git a/README.md b/README.md index 12f8f5c7..f576ef61 100644 --- a/README.md +++ b/README.md @@ -50,25 +50,27 @@ see the [migration guide](MIGRATION_GUIDE.md) for detailed info. * [Configure native platforms](#configure-native-platforms) * [Android config preparation](#android-config-preparation) * [Kotlin, Gradle, AGP](#kotlin-gradle-agp) - * [Android 10 (Q, 29)](#android-10--q-29-) + * [Android 10 (Q, 29)](#android-10-q-29) * [Glide](#glide) * [iOS config preparation](#ios-config-preparation) * [Usage](#usage) * [Request for permission](#request-for-permission) - * [Limited entities access on iOS](#limited-entities-access-on-ios) - * [Get albums/folders (`AssetPathEntity`)](#get-albumsfolders--assetpathentity-) + * [Limited entities access](#limited-entities-access) + * [Limited entities access on iOS](#limited-entities-access-on-ios) + * [Limited entities access on android](#limited-entities-access-on-android) + * [Get albums/folders (`AssetPathEntity`)](#get-albumsfolders-assetpathentity) * [Params of `getAssetPathList`](#params-of-getassetpathlist) * [PMPathFilterOption](#pmpathfilteroption) - * [Get assets (`AssetEntity`)](#get-assets--assetentity-) + * [Get assets (`AssetEntity`)](#get-assets-assetentity) * [From `AssetPathEntity`](#from-assetpathentity) - * [From `PhotoManager` (Since 2.6)](#from-photomanager--since-26-) + * [From `PhotoManager` (Since 2.6)](#from-photomanager-since-26) * [From ID](#from-id) * [From raw data](#from-raw-data) * [From iCloud](#from-icloud) * [Display assets](#display-assets) - * [Obtain "Live Photos"](#obtain--live-photos-) - * [Filtering only "Live Photos"](#filtering-only--live-photos-) - * [Obtain the video from "Live Photos"](#obtain-the-video-from--live-photos-) + * [Obtain "Live Photos"](#obtain-live-photos) + * [Filtering only "Live Photos"](#filtering-only-live-photos) + * [Obtain the video from "Live Photos"](#obtain-the-video-from-live-photos) * [Limitations](#limitations) * [Android 10 media location permission](#android-10-media-location-permission) * [Usage of the original data](#usage-of-the-original-data) @@ -86,7 +88,7 @@ see the [migration guide](MIGRATION_GUIDE.md) for detailed info. * [Native extra configs](#native-extra-configs) * [Android extra configs](#android-extra-configs) * [Glide issues](#glide-issues) - * [Android 13 (Api 33) extra configs](#android-13--api-33--extra-configs) + * [Android 13 (Api 33) extra configs](#android-13-api-33-extra-configs) * [iOS extra configs](#ios-extra-configs) * [Localized system albums name](#localized-system-albums-name) * [Experimental features](#experimental-features) From 4bf4c85e048bd9db26e114a34b044c1d24c38094 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 21 Sep 2023 17:33:15 +0800 Subject: [PATCH 15/18] style: format all kotlin codes --- android/gradle.properties | 3 +- .../photo_manager/PhotoManagerPlugin.kt | 2 +- .../photo_manager/core/PhotoManager.kt | 15 ++++++++-- .../core/PhotoManagerDeleteManager.kt | 2 +- .../core/PhotoManagerNotifyChannel.kt | 6 ++-- .../photo_manager/core/PhotoManagerPlugin.kt | 8 ++--- .../photo_manager/core/cache/ScopedCache.kt | 1 - .../core/entity/filter/CommonFilterOption.kt | 1 - .../core/entity/filter/CustomOption.kt | 1 - .../core/entity/filter/FilterOption.kt | 8 +++-- .../core/utils/AndroidQDBUtils.kt | 30 +++++++++---------- .../photo_manager/core/utils/ConvertUtils.kt | 10 +++++-- .../photo_manager/core/utils/DBUtils.kt | 22 +++++++------- .../photo_manager/core/utils/IDBUtils.kt | 16 +++++++++- .../permission/PermissionDelegate.kt | 6 +++- .../permission/PermissionsUtils.kt | 25 ---------------- .../permission/impl/PermissionDelegate19.kt | 4 ++- .../permission/impl/PermissionDelegate23.kt | 6 +++- .../permission/impl/PermissionDelegate29.kt | 1 - .../permission/impl/PermissionDelegate30.kt | 6 +++- .../permission/impl/PermissionDelegate33.kt | 6 +++- .../permission/impl/PermissionDelegate34.kt | 6 +++- .../photo_manager/util/LogUtils.kt | 2 -- example/android/gradle.properties | 1 + 24 files changed, 107 insertions(+), 81 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index f98a4cc2..75584055 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,2 @@ -org.gradle.jvmargs=-Xmx1536M \ No newline at end of file +org.gradle.jvmargs=-Xmx1536M +kotlin.code.style=official \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/PhotoManagerPlugin.kt index d596f96a..fb1f0c81 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/PhotoManagerPlugin.kt @@ -1,5 +1,6 @@ package com.fluttercandies.photo_manager +import com.fluttercandies.photo_manager.permission.PermissionsUtils import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -7,7 +8,6 @@ import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener import com.fluttercandies.photo_manager.core.PhotoManagerPlugin as InnerPhotoManagerPlugin -import com.fluttercandies.photo_manager.permission.PermissionsUtils class PhotoManagerPlugin : FlutterPlugin, ActivityAware { private var plugin: InnerPhotoManagerPlugin? = null diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt index 47bca1d0..55b6e88d 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt @@ -8,10 +8,13 @@ import android.util.Log import com.bumptech.glide.Glide import com.bumptech.glide.request.FutureTarget import com.fluttercandies.photo_manager.core.entity.AssetEntity -import com.fluttercandies.photo_manager.core.entity.filter.FilterOption import com.fluttercandies.photo_manager.core.entity.AssetPathEntity import com.fluttercandies.photo_manager.core.entity.ThumbLoadOption -import com.fluttercandies.photo_manager.core.utils.* +import com.fluttercandies.photo_manager.core.entity.filter.FilterOption +import com.fluttercandies.photo_manager.core.utils.AndroidQDBUtils +import com.fluttercandies.photo_manager.core.utils.ConvertUtils +import com.fluttercandies.photo_manager.core.utils.DBUtils +import com.fluttercandies.photo_manager.core.utils.IDBUtils import com.fluttercandies.photo_manager.thumb.ThumbnailUtil import com.fluttercandies.photo_manager.util.LogUtils import com.fluttercandies.photo_manager.util.ResultHandler @@ -292,7 +295,13 @@ class PhotoManager(private val context: Context) { resultHandler.reply(assetCount) } - fun getAssetsByRange(resultHandler: ResultHandler, option: FilterOption, start: Int, end: Int, requestType: Int) { + fun getAssetsByRange( + resultHandler: ResultHandler, + option: FilterOption, + start: Int, + end: Int, + requestType: Int + ) { val list = dbUtils.getAssetsByRange(context, option, start, end, requestType) resultHandler.reply(ConvertUtils.convertAssets(list)) } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerDeleteManager.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerDeleteManager.kt index 1dcf6bd5..bd956a9e 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerDeleteManager.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerDeleteManager.kt @@ -8,9 +8,9 @@ import android.net.Uri import android.os.Build import android.provider.MediaStore import androidx.annotation.RequiresApi -import io.flutter.plugin.common.PluginRegistry import com.fluttercandies.photo_manager.core.utils.IDBUtils import com.fluttercandies.photo_manager.util.ResultHandler +import io.flutter.plugin.common.PluginRegistry class PhotoManagerDeleteManager(val context: Context, private var activity: Activity?) : PluginRegistry.ActivityResultListener { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerNotifyChannel.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerNotifyChannel.kt index 82f9c4b0..08815ad6 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerNotifyChannel.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerNotifyChannel.kt @@ -10,10 +10,10 @@ import android.os.Looper import android.provider.BaseColumns import android.provider.MediaStore import android.provider.MediaStore.Files.FileColumns.* -import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.MethodChannel import com.fluttercandies.photo_manager.core.utils.IDBUtils import com.fluttercandies.photo_manager.util.LogUtils +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodChannel @Suppress("Range") class PhotoManagerNotifyChannel( @@ -180,6 +180,7 @@ class PhotoManagerNotifyChannel( } } } + type == MEDIA_TYPE_AUDIO -> { val cursor = cr.query( allUri, @@ -203,6 +204,7 @@ class PhotoManagerNotifyChannel( } } } + else -> { val cursor = cr.query( allUri, diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index c03ea131..aa5c61a6 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -7,18 +7,18 @@ import android.os.Handler import android.os.Looper import com.bumptech.glide.Glide import com.fluttercandies.photo_manager.constant.Methods -import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel import com.fluttercandies.photo_manager.core.entity.AssetEntity -import com.fluttercandies.photo_manager.core.entity.filter.FilterOption import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.core.entity.ThumbLoadOption +import com.fluttercandies.photo_manager.core.entity.filter.FilterOption import com.fluttercandies.photo_manager.core.utils.ConvertUtils import com.fluttercandies.photo_manager.permission.PermissionsListener import com.fluttercandies.photo_manager.permission.PermissionsUtils import com.fluttercandies.photo_manager.util.LogUtils import com.fluttercandies.photo_manager.util.ResultHandler +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/cache/ScopedCache.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/cache/ScopedCache.kt index 2997f49d..f97c10eb 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/cache/ScopedCache.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/cache/ScopedCache.kt @@ -9,7 +9,6 @@ import com.fluttercandies.photo_manager.core.utils.AndroidQDBUtils import com.fluttercandies.photo_manager.util.LogUtils import java.io.File import java.io.FileOutputStream -import java.lang.Exception @RequiresApi(Build.VERSION_CODES.Q) class ScopedCache { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CommonFilterOption.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CommonFilterOption.kt index 0aaee94e..86128d51 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CommonFilterOption.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CommonFilterOption.kt @@ -5,7 +5,6 @@ import android.provider.MediaStore import com.fluttercandies.photo_manager.constant.AssetType import com.fluttercandies.photo_manager.core.utils.ConvertUtils import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils -import java.util.ArrayList class CommonFilterOption(map: Map<*, *>) : FilterOption() { private val videoOption = ConvertUtils.getOptionFromType(map, AssetType.Video) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CustomOption.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CustomOption.kt index b3b1dfb6..967b3681 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CustomOption.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/CustomOption.kt @@ -1,7 +1,6 @@ package com.fluttercandies.photo_manager.core.entity.filter import com.fluttercandies.photo_manager.core.utils.RequestTypeUtils -import java.util.ArrayList class CustomOption(private val map: Map<*, *>) : FilterOption() { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/FilterOption.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/FilterOption.kt index 8f2b6d01..798bcb52 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/FilterOption.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/filter/FilterOption.kt @@ -1,11 +1,13 @@ package com.fluttercandies.photo_manager.core.entity.filter -import java.util.ArrayList - abstract class FilterOption { abstract val containsPathModified: Boolean abstract fun orderByCondString(): String? - abstract fun makeWhere(requestType: Int, args: ArrayList, needAnd: Boolean = true): String + abstract fun makeWhere( + requestType: Int, + args: ArrayList, + needAnd: Boolean = true + ): String } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/AndroidQDBUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/AndroidQDBUtils.kt index e4770b98..9b430bc7 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/AndroidQDBUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/AndroidQDBUtils.kt @@ -15,8 +15,8 @@ import androidx.exifinterface.media.ExifInterface import com.fluttercandies.photo_manager.core.PhotoManager import com.fluttercandies.photo_manager.core.cache.ScopedCache import com.fluttercandies.photo_manager.core.entity.AssetEntity -import com.fluttercandies.photo_manager.core.entity.filter.FilterOption import com.fluttercandies.photo_manager.core.entity.AssetPathEntity +import com.fluttercandies.photo_manager.core.entity.filter.FilterOption import com.fluttercandies.photo_manager.util.LogUtils import java.io.ByteArrayOutputStream import java.io.File @@ -155,11 +155,11 @@ object AndroidQDBUtils : IDBUtils { } val sortOrder = getSortOrder(page * size, size, option) val cursor = context.contentResolver.logQuery( - allUri, - keys(), - selection, - args.toTypedArray(), - sortOrder + allUri, + keys(), + selection, + args.toTypedArray(), + sortOrder ) ?: return list cursor.use { cursorWithRange(it, page * size, size) { cursor -> @@ -215,23 +215,23 @@ object AndroidQDBUtils : IDBUtils { override fun keys(): Array { return (IDBUtils.storeImageKeys + IDBUtils.storeVideoKeys + IDBUtils.typeKeys + arrayOf( - RELATIVE_PATH + RELATIVE_PATH )).distinct().toTypedArray() } override fun getAssetEntity( - context: Context, - id: String, - checkIfExists: Boolean + context: Context, + id: String, + checkIfExists: Boolean ): AssetEntity? { val selection = "$_ID = ?" val args = arrayOf(id) val cursor = context.contentResolver.logQuery( - allUri, - keys(), - selection, - args, - null + allUri, + keys(), + selection, + args, + null ) ?: return null cursor.use { return if (it.moveToNext()) it.toAssetEntity(context, checkIfExists) diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/ConvertUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/ConvertUtils.kt index fac3fb1c..a238ca84 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/ConvertUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/ConvertUtils.kt @@ -2,8 +2,14 @@ package com.fluttercandies.photo_manager.core.utils import android.provider.MediaStore import com.fluttercandies.photo_manager.constant.AssetType -import com.fluttercandies.photo_manager.core.entity.* -import com.fluttercandies.photo_manager.core.entity.filter.* +import com.fluttercandies.photo_manager.core.entity.AssetEntity +import com.fluttercandies.photo_manager.core.entity.AssetPathEntity +import com.fluttercandies.photo_manager.core.entity.filter.CommonFilterOption +import com.fluttercandies.photo_manager.core.entity.filter.CustomOption +import com.fluttercandies.photo_manager.core.entity.filter.DateCond +import com.fluttercandies.photo_manager.core.entity.filter.FilterCond +import com.fluttercandies.photo_manager.core.entity.filter.FilterOption +import com.fluttercandies.photo_manager.core.entity.filter.OrderByCond object ConvertUtils { fun convertPaths(list: List): Map { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/DBUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/DBUtils.kt index fcfac83f..ec3a982a 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/DBUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/DBUtils.kt @@ -8,9 +8,9 @@ import android.util.Log import androidx.exifinterface.media.ExifInterface import com.fluttercandies.photo_manager.core.PhotoManager import com.fluttercandies.photo_manager.core.entity.AssetEntity -import com.fluttercandies.photo_manager.core.entity.filter.FilterOption import com.fluttercandies.photo_manager.core.entity.AssetPathEntity -import java.io.* +import com.fluttercandies.photo_manager.core.entity.filter.FilterOption +import java.io.File import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock @@ -18,18 +18,18 @@ import kotlin.concurrent.withLock @Suppress("Deprecation", "InlinedApi") object DBUtils : IDBUtils { private val locationKeys = arrayOf( - MediaStore.Images.ImageColumns.LONGITUDE, - MediaStore.Images.ImageColumns.LATITUDE + MediaStore.Images.ImageColumns.LONGITUDE, + MediaStore.Images.ImageColumns.LATITUDE ) override fun keys(): Array = - (IDBUtils.storeImageKeys + IDBUtils.storeVideoKeys + IDBUtils.typeKeys + locationKeys).distinct() - .toTypedArray() + (IDBUtils.storeImageKeys + IDBUtils.storeVideoKeys + IDBUtils.typeKeys + locationKeys).distinct() + .toTypedArray() override fun getAssetPathList( - context: Context, - requestType: Int, - option: FilterOption + context: Context, + requestType: Int, + option: FilterOption ): List { val list = ArrayList() val args = ArrayList() @@ -196,8 +196,8 @@ object DBUtils : IDBUtils { val pageSize = end - start val sortOrder = getSortOrder(start, pageSize, option) val cursor = context.contentResolver.logQuery( - allUri, - keys, + allUri, + keys, selection, args.toTypedArray(), sortOrder diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt index 059ac9d8..2e3c8472 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt @@ -11,7 +11,21 @@ import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore -import android.provider.MediaStore.MediaColumns.* +import android.provider.MediaStore.MediaColumns.BUCKET_DISPLAY_NAME +import android.provider.MediaStore.MediaColumns.BUCKET_ID +import android.provider.MediaStore.MediaColumns.DATA +import android.provider.MediaStore.MediaColumns.DATE_ADDED +import android.provider.MediaStore.MediaColumns.DATE_MODIFIED +import android.provider.MediaStore.MediaColumns.DATE_TAKEN +import android.provider.MediaStore.MediaColumns.DISPLAY_NAME +import android.provider.MediaStore.MediaColumns.DURATION +import android.provider.MediaStore.MediaColumns.HEIGHT +import android.provider.MediaStore.MediaColumns.MIME_TYPE +import android.provider.MediaStore.MediaColumns.ORIENTATION +import android.provider.MediaStore.MediaColumns.RELATIVE_PATH +import android.provider.MediaStore.MediaColumns.TITLE +import android.provider.MediaStore.MediaColumns.WIDTH +import android.provider.MediaStore.MediaColumns._ID import android.provider.MediaStore.VOLUME_EXTERNAL import androidx.annotation.ChecksSdkIntAtLeast import androidx.exifinterface.media.ExifInterface diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt index 012f3765..dec3f90b 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionDelegate.kt @@ -170,6 +170,10 @@ abstract class PermissionDelegate { resultHandler.reply(null) } - abstract fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult + abstract fun getAuthValue( + context: Application, + requestType: Int, + mediaLocation: Boolean + ): PermissionResult } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt index bfb7a8b6..ddab1055 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/PermissionsUtils.kt @@ -11,8 +11,6 @@ import androidx.core.content.PermissionChecker.PERMISSION_GRANTED import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.util.LogUtils import com.fluttercandies.photo_manager.util.ResultHandler -import java.lang.NullPointerException -import kotlin.collections.ArrayList class PermissionsUtils { /** 需要申请权限的Activity */ @@ -88,29 +86,6 @@ class PermissionsUtils { requestType, mediaLocation, ) - -// delegate.requestPermissions(this, context!!, requestCode, false) -// val permissionSet = permissions.toSet() -// if (mActivity == null) { -// throw NullPointerException("Activity for the permission request is not exist.") -// } -// check(!isRequesting) { "Another permission request is ongoing." } -// isRequesting = true -// this.requestCode = requestCode -// if (!checkPermissions(*permissions)) { -// // 通过上面的 checkPermissions,可以知道能得到进入到这里面的都是 6.0 的机子 -// ActivityCompat.requestPermissions( -// mActivity!!, -// needToRequestPermissionsList.toTypedArray(), -// requestCode -// ) -// for (i in needToRequestPermissionsList.indices) { -// LogUtils.info("Permissions: " + needToRequestPermissionsList[i]) -// } -// } else if (permissionsListener != null) { -// isRequesting = false -// permissionsListener!!.onGranted() -// } return this } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt index b7a73e58..c9575870 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate19.kt @@ -23,7 +23,9 @@ class PermissionDelegate19 : com.fluttercandies.photo_manager.permission.Permiss return true } - override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + override fun getAuthValue( + context: Application, requestType: Int, mediaLocation: Boolean + ): PermissionResult { return PermissionResult.Authorized } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt index 0f90125d..51ddd223 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate23.kt @@ -38,7 +38,11 @@ open class PermissionDelegate23 : com.fluttercandies.photo_manager.permission.Pe return true } - override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + override fun getAuthValue( + context: Application, + requestType: Int, + mediaLocation: Boolean + ): PermissionResult { if (havePermissions(context, readPermission, writePermission)) { return PermissionResult.Authorized } else { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt index 3411fe5d..f55825ab 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate29.kt @@ -3,7 +3,6 @@ package com.fluttercandies.photo_manager.permission.impl import android.Manifest import android.app.Application import android.content.Context -import android.os.Build import androidx.annotation.RequiresApi import com.fluttercandies.photo_manager.core.entity.PermissionResult import com.fluttercandies.photo_manager.permission.PermissionsUtils diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt index d2565583..ec0be3b3 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate30.kt @@ -44,7 +44,11 @@ class PermissionDelegate30 : PermissionDelegate() { return havePermission(context, mediaLocationPermission) } - override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + override fun getAuthValue( + context: Application, + requestType: Int, + mediaLocation: Boolean + ): PermissionResult { if (havePermissions(context, readPermission)) { return PermissionResult.Authorized } else { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt index e1b5d18e..af8acdb0 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate33.kt @@ -81,7 +81,11 @@ class PermissionDelegate33 : PermissionDelegate() { return havePermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) } - override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + override fun getAuthValue( + context: Application, + requestType: Int, + mediaLocation: Boolean + ): PermissionResult { return if (havePermissions(context, requestType)) { PermissionResult.Authorized } else { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt index eae71527..c212f3c8 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/permission/impl/PermissionDelegate34.kt @@ -187,7 +187,11 @@ class PermissionDelegate34 : PermissionDelegate() { requestPermission(permissionsUtils, permissions) } - override fun getAuthValue(context: Application, requestType: Int, mediaLocation: Boolean): PermissionResult { + override fun getAuthValue( + context: Application, + requestType: Int, + mediaLocation: Boolean + ): PermissionResult { var result = PermissionResult.NotDetermined fun changeResult(newResult: PermissionResult) { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/util/LogUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/util/LogUtils.kt index 036db0de..106144d6 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/util/LogUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/util/LogUtils.kt @@ -2,8 +2,6 @@ package com.fluttercandies.photo_manager.util import android.database.Cursor import android.util.Log -import java.lang.Exception -import java.lang.StringBuilder object LogUtils { const val TAG = "PhotoManager" diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a3..530a5662 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +kotlin.code.style=official \ No newline at end of file From b75a1797d9949fc2c2fee07bf7e6ddc6a9efa415 Mon Sep 17 00:00:00 2001 From: Caijinglong Date: Thu, 21 Sep 2023 17:33:26 +0800 Subject: [PATCH 16/18] Apply suggestions from code review Co-authored-by: Alex Li --- README-ZH.md | 7 ++++--- README.md | 9 ++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index a91f8631..8f5721b0 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -244,9 +244,10 @@ iOS14 引入了部分资源限制的权限 (`PermissionState.limited`)。 ##### Android 受限的资源权限 -和 iOS 类似,安卓 14(API 34) 中也加入了这个概念,dart 端的使用方法也完全一致。 - -但行为上略有不同(基于模拟器),安卓中一旦授予某个资源的访问权限,就无法撤销,即使再次使用 `presentLimited` 时不选中也一样。 +与 iOS 类似,Android 14 (API 34) 中也引入了这个概念。 +它们在行为上略有不同(基于模拟器): +在 Android 中一旦授予某个资源的访问权限,就无法撤销, +即使再次使用 `presentLimited` 时不选中也不会撤销对它的访问权限。 ### 获取相簿或图集 (`AssetPathEntity`) diff --git a/README.md b/README.md index 12f8f5c7..89b98673 100644 --- a/README.md +++ b/README.md @@ -260,12 +260,11 @@ is limited (`PermissionState.limited`). ##### Limited entities access on android -Similar to iOS, Android 14 (API 34) has also introduced this concept, and the usage on the Dart side is completely identical. +Android 14 (API 34) has also introduced the concept of limited assets similar to iOS. -However, there is a slight difference in behavior (based on the emulator). - -In Android, once access permission to a certain resource is granted, it cannot be revoked, -even if you do not select it again when using `presentLimited`. +However, there is a slight difference in behavior (based on the emulator): +On Android, the access permission to a certain resource cannot be revoked once it is granted, +even if it hasn't been selected when using `presentLimited` in future actions. ### Get albums/folders (`AssetPathEntity`) From 90c653d6613268b691ffae64b4cfcbdab557b3ea Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 21 Sep 2023 17:35:26 +0800 Subject: [PATCH 17/18] docs: update CHANGELOG.md for breaking change --- CHANGELOG.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82037668..17ed8e0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,12 +13,13 @@ that can be found in the LICENSE file. --> ***Breaking changes for permission behavior*** -Now, users need to ensure they have the access permissions through the -following means before using the API to access resources. -Two ways: +Methods do not implicitly call for permission requests anymore. +User must follow the below methods to ensure permissions were granted: -1. `PhotoManager.requestPermissionExtend()` and the result is `PermissionState.authorized` or `PermissionState.limited`. -2. `PhotoManager.setIgnorePermissionCheck(true)` to ignore permission check and use other permission plugin. +1. `PhotoManager.requestPermissionExtend()`, verify if the result is + `PermissionState.authorized` or `PermissionState.limited`. +2. `PhotoManager.setIgnorePermissionCheck(true)`, ignoring permission checks, + handle permission with other mechanisms. ## 2.7.1 @@ -54,8 +55,8 @@ Two ways: - Support `CustomFilter` for more filter options. (#901) - Add two new static methods for `PhotoManager`: - - `getAssetCount` for getting assets count. - - `getAssetListRange` for getting assets between start and end. + - `getAssetCount` for getting assets count. + - `getAssetListRange` for getting assets between start and end. ## 2.5.2 @@ -136,8 +137,8 @@ Two ways: - Introduce `AssetPathEntity.assetCountAsync` getter, which improves the speed when loading paths mainly on iOS, also: - - Deprecate `AssetPathEntity.assetCount`. - - Remove `FilterOptionGroup.containsEmptyAlbum`. + - Deprecate `AssetPathEntity.assetCount`. + - Remove `FilterOptionGroup.containsEmptyAlbum`. ### Improvements @@ -561,7 +562,7 @@ and applied multiple ### Fixes which make this plugin as the most solid ever. but at the same time user have to bear the risks corresponding to the permission). - Support clean file cache. - Experimental - - Preload image (Use `PhotoCachingManager` api.) + - Preload image (Use `PhotoCachingManager` api.) - Add `OrderOption` as sort condition. The option default value is order by create date desc; - Support icloud asset progress. @@ -624,14 +625,14 @@ and applied multiple ### Fixes which make this plugin as the most solid ever. - Create AssetEntity with id. - Create AssetPathEntity from id. - Only iOS - - Create folder or album. - - Remove assets in album. - - Delete folder or album. - - Favorite asset. + - Create folder or album. + - Remove assets in album. + - Delete folder or album. + - Favorite asset. - Only android - - move asset to another path. - - Remove all non-existing rows. - - add `relativePath` for android. + - move asset to another path. + - Remove all non-existing rows. + - add `relativePath` for android. ### Improvements From 4ebace7cc2eec05f28497b1a0dc176a17ad6afd2 Mon Sep 17 00:00:00 2001 From: CaiJingLong Date: Thu, 21 Sep 2023 17:55:07 +0800 Subject: [PATCH 18/18] docs: for suggestions --- README-ZH.md | 12 ++++++++++++ README.md | 10 ++++++++++ lib/src/internal/constants.dart | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README-ZH.md b/README-ZH.md index dfacb4d8..ccba31f2 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -87,6 +87,7 @@ that can be found in the LICENSE file. --> * [原生额外配置](#原生额外配置) * [Android 额外配置](#android-额外配置) * [Glide 相关问题](#glide-相关问题) + * [Android 14 (API level 34) 额外配置](#android-14-api-level-34-额外配置) * [Android 13 (API level 33) 额外配置](#android-13-api-level-33-额外配置) * [iOS 额外配置](#ios-额外配置) * [配置系统相册名称的国际化](#配置系统相册名称的国际化) @@ -709,6 +710,17 @@ rootProject.allprojects { 如果你想了解如何同时使用 ProGuard 和 Glide,请参阅 [ProGuard for Glide](https://github.com/bumptech/glide#proguard)。 +#### Android 14 (API level 34) 额外配置 + +当应用的 `targetSdkVersion` 为 34 (Android 14) 时, +你需要在清单文件中添加以下额外配置: + +```xml + + + +``` + #### Android 13 (API level 33) 额外配置 当应用的 `targetSdkVersion` 为 33 (Android 13) 时, diff --git a/README.md b/README.md index c696d221..b4c1c645 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ see the [migration guide](MIGRATION_GUIDE.md) for detailed info. * [Native extra configs](#native-extra-configs) * [Android extra configs](#android-extra-configs) * [Glide issues](#glide-issues) + * [Android 14 (Api 34) extra configs](#android-14-api-34-extra-configs) * [Android 13 (Api 33) extra configs](#android-13-api-33-extra-configs) * [iOS extra configs](#ios-extra-configs) * [Localized system albums name](#localized-system-albums-name) @@ -766,6 +767,15 @@ rootProject.allprojects { See [ProGuard for Glide](https://github.com/bumptech/glide#proguard) if you want to know more about using ProGuard and Glide together. +#### Android 14 (Api 34) extra configs + +When targeting Android 14 (API level 34), +the following extra configs needs to be added to the manifest: + +```xml + +``` + #### Android 13 (Api 33) extra configs When targeting Android 13 (API level 33), diff --git a/lib/src/internal/constants.dart b/lib/src/internal/constants.dart index 81a16c88..fe98a4fb 100644 --- a/lib/src/internal/constants.dart +++ b/lib/src/internal/constants.dart @@ -17,7 +17,7 @@ class PMConstants { static const String mFetchEntityProperties = 'fetchEntityProperties'; static const String mGetAssetCountFromPath = 'getAssetCountFromPath'; - /// The 4 methods have [RequestType] params, for android-13 or higher. + /// These methods have [RequestType] params for Android 13+ (33+). static const String mFetchPathProperties = 'fetchPathProperties'; static const String mGetAssetPathList = 'getAssetPathList'; static const String mGetAssetListPaged = 'getAssetListPaged';