Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android-14-permission #982

Merged
merged 21 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@ that can be found in the LICENSE file. -->

# CHANGELOG

## 2.7.2
## 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***
CaiJingLong marked this conversation as resolved.
Show resolved Hide resolved

Methods do not implicitly call for permission requests anymore.
User must follow the below methods to ensure permissions were granted:

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.

### Fixes

Expand Down Expand Up @@ -52,7 +67,7 @@ that can be found in the LICENSE file. -->
- Add two new static methods for `PhotoManager`:
- `getAssetCount` for getting assets count.
- `getAssetListRange` for getting assets between start and end.

## 2.5.2

### Improvements
Expand Down
44 changes: 35 additions & 9 deletions README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-获取)
Expand All @@ -85,7 +87,8 @@ that can be found in the LICENSE file. -->
* [原生额外配置](#原生额外配置)
* [Android 额外配置](#android-额外配置)
* [Glide 相关问题](#glide-相关问题)
* [Android 13 (API level 33) 额外配置](#android-13--api-level-33--额外配置)
* [Android 14 (API level 34) 额外配置](#android-14-api-level-34-额外配置)
* [Android 13 (API level 33) 额外配置](#android-13-api-level-33-额外配置)
* [iOS 额外配置](#ios-额外配置)
* [配置系统相册名称的国际化](#配置系统相册名称的国际化)
* [实验性功能](#实验性功能)
Expand Down Expand Up @@ -214,7 +217,10 @@ Android 10 引入了 **Scoped Storage**,导致原始资源文件不能通过
```dart
final PermissionState ps = await PhotoManager.requestPermissionExtend();
if (ps.isAuth) {
// 已获取到权限。
// 已获取到权限
} else if (ps.hasAccess) {
// 已获取到权限(哪怕只是有限的访问权限)。
// iOS Android 目前都已经有了部分权限的概念。
} else {
// 权限受限制(iOS)或者被拒绝,使用 `==` 能够更准确的判断是受限还是拒绝。
// 你可以使用 `PhotoManager.openSetting()` 打开系统设置页面进行进一步的逻辑定制。
Expand All @@ -228,14 +234,23 @@ PhotoManager.setIgnorePermissionCheck(true);

对于一些后台操作(应用未启动等)而言,忽略检查是比较合适的做法。

#### iOS 受限的资源权限
#### 受限的资源权限

##### iOS 受限的资源权限

iOS14 引入了部分资源限制的权限 (`PermissionState.limited`)。
`PhotoManager.requestPermissionExtend()` 会返回当前的权限状态 `PermissionState`。
详情请参阅 [PHAuthorizationStatus][]。

如果你想要重新选择在应用里能够读取到的资源,你可以使用 `PhotoManager.presentLimited()` 重新选择资源,
这个方法仅在 iOS 14 以上的版本生效,其他平台或版本无法调用这个方法。
这个方法对于 iOS 14 以上的版本生效。

##### Android 受限的资源权限

与 iOS 类似,Android 14 (API 34) 中也引入了这个概念。
它们在行为上略有不同(基于模拟器):
在 Android 中一旦授予某个资源的访问权限,就无法撤销,
即使再次使用 `presentLimited` 时不选中也不会撤销对它的访问权限。

### 获取相簿或图集 (`AssetPathEntity`)

Expand Down Expand Up @@ -695,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
<manifest>
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /> <!-- 这是一个可选的配置,不指定并不影响在代码中使用它 -->
</manifest>
```

#### Android 13 (API level 33) 额外配置

当应用的 `targetSdkVersion` 为 33 (Android 13) 时,
Expand Down
52 changes: 38 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -86,7 +88,8 @@ 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 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)
* [Experimental features](#experimental-features)
Expand Down Expand Up @@ -222,9 +225,12 @@ 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();
final PermissionState ps = await PhotoManager.requestPermissionExtend(); // the method can use optional param `permission`.
if (ps.isAuth) {
// Granted.
// 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.
Expand All @@ -240,7 +246,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
CaiJingLong marked this conversation as resolved.
Show resolved Hide resolved

##### Limited entities access on iOS

With iOS 14 released, Apple brought a "Limited Photos Library" permission
(`PermissionState.limited`) to iOS.
Expand All @@ -251,8 +259,15 @@ 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

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):
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`)

Expand Down Expand Up @@ -752,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
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /> <!-- If you want to use the limited access feature, it is an optional permission. -->
```

#### Android 13 (Api 33) extra configs

When targeting Android 13 (API level 33),
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ android {
namespace 'com.flutterandies.photo_manager'
}

compileSdkVersion 33
compileSdkVersion 34

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
Expand Down
3 changes: 2 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx1536M
kotlin.code.style=official
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,61 @@ 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"
Expand All @@ -31,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()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -101,7 +104,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)
Expand Down Expand Up @@ -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))
}
Expand Down
Loading
Loading