Android13適配所有文件管理權限
前言:
很早之前在Android11上面就適配過所有文件管理權限,這次是海外版升級到Android13,由于選擇相冊用的是第三方庫,組內(nèi)的同事沒有上架Google的經(jīng)驗直接就提交代碼,雖然功能沒有問題,但是上架的時候被打回了,于是記錄一下適配工作.
1.簡介:
絕大多數(shù)需要共享存儲空間訪問權限的應用都可以遵循共享媒體文件和共享非媒體文件方面的最佳做法。然而,某些應用的核心用例需要廣泛訪問設備上的文件,但無法采用注重隱私保護的存儲最佳實踐高效地訪問這些文件。對于這些情況,Android 提供了一種名為“所有文件訪問權”的特殊應用訪問權限。
例如,防病毒應用的主要用例可能需要定期掃描不同目錄中的許多文件。如果此掃描需要反復的用戶交互,讓其使用系統(tǒng)文件選擇器選擇目錄,就會帶來糟糕的用戶體驗。其他用例(如文件管理器應用、備份和恢復應用以及文檔管理應用)也需要考慮類似情況。
2.Google Play通知:
此部分為在 Google Play 上發(fā)布應用的開發(fā)者提供通知。
為了限制對共享存儲的廣泛訪問,Google Play 商店已更新其政策,用來評估以 Android 11(API 級別 30)或更高版本為目標平臺且通過 MANAGE_EXTERNAL_STORAGE
權限請求“所有文件訪問權”的應用。此政策自 2021 年 5 月起生效。
當應用以 Android 11 或更高版本為目標平臺并聲明了 MANAGE_EXTERNAL_STORAGE
權限時,Android Studio 會顯示圖 1 中所示的 lint 警告。此警告會提醒您:“Google Play 商店的一項政策限制了對該權限的使用”。
圖 1. Android Studio 中的 Lint 警告,提醒開發(fā)者有關 MANAGE_EXTERNAL_STORAGE
權限的 Google Play 政策。
僅當您的應用無法有效利用更有利于保護隱私的 API(如存儲訪問框架或 Media Store API)時,您才能請求 MANAGE_EXTERNAL_STORAGE
權限。您的應用對該權限的使用必須在允許的使用情形范圍內(nèi),并且必須與應用的核心功能直接相關。如果您的應用包含與以下任一項類似的用例,可能會請求 MANAGE_EXTERNAL_STORAGE
權限:
- 文件管理器
- 備份和恢復應用
- 防病毒應用
- 文檔管理應用
- 設備上的文件搜索
- 磁盤和文件加密
- 設備到設備數(shù)據(jù)遷移
3.選擇相冊圖片:
由于使用的是io.github.lucksiege:pictureselector這個庫,版本為v3.10.9:
PictureSelector.create(this)
.openGallery(SelectMimeType.ofImage())
.setImageEngine(GlideEngine.createGlideEngine())
.forResult(object : OnResultCallbackListener<LocalMedia?> {
override fun onResult(result: ArrayList<LocalMedia?>) {
LogUtils.d("===返回的圖片地址為===", result[0]!!.path)
Glide.with(this@MainActivity).load(result[0]?.path).into(ivBg)
}
override fun onCancel() {}
})
4.使用系統(tǒng)拍照:
PictureSelector.create(this)
.openCamera(SelectMimeType.ofImage())
.forResult(object : OnResultCallbackListener<LocalMedia?> {
override fun onResult(result: ArrayList<LocalMedia?>) {
LogUtils.d("===返回的圖片地址為===", result[0]!!.path)
Glide.with(this@MainActivity).load(result[0]?.path).into(ivCamera)
}
override fun onCancel() {}
})
5.打開系統(tǒng)相冊:
private fun openPhotoAlbum() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
//1.以startActivityForResult方式打開Activity
/* startActivityForResult(
Intent.createChooser(intent, "File Browser"), Constants.FILE_CHOOSER_RESULT_CODE)*/
//2.以launch方式打開相冊
photoLaunch.launch("image/*")
}
6.未適配前的效果如下:
7.去掉本項目的所有文件管理權限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" android:maxSdkVersion="32"/>
8.去掉后權限后再次運行:
還是提示申請所有文件管理權限,去github查看圖片庫的版本,發(fā)現(xiàn)新版本已經(jīng)適配了Android13和所有文件管理權限,于是更新一下圖片庫的依賴版本.
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation("io.github.lucksiege:pictureselector:v3.11.1")
implementation("com.github.bumptech.glide:glide:4.15.1")
annotationProcessor("com.github.bumptech.glide:compiler:4.15.1")
implementation("com.blankj:utilcodex:1.31.1")
}
9.新版本ImageEngine:
/**
* 加載圖片
*
* @param context 上下文
* @param url 資源url
* @param imageView 圖片承載控件
*/
@Override
public void loadImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.into(imageView);
}
@Override
public void loadImageBitmap(@NonNull Context context, @NonNull String url, int maxWidth, int maxHeight, OnCallbackListener<Bitmap> call) {
}
/**
* 加載相冊目錄封面
*
* @param context 上下文
* @param url 圖片路徑
* @param imageView 承載圖片ImageView
*/
@Override
public void loadAlbumCover(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.override(180, 180)
.sizeMultiplier(0.5f)
.transform(new CenterCrop(), new RoundedCorners(8))
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
/**
* 加載圖片列表圖片
*
* @param context 上下文
* @param url 圖片路徑
* @param imageView 承載圖片ImageView
*/
@Override
public void loadGridImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(200, 200)
.centerCrop()
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
@Override
public void pauseRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).pauseRequests();
}
@Override
public void resumeRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).resumeRequests();
}
private MyImageGlideEngine() {
}
private static final class InstanceHolder {
static final MyImageGlideEngine instance = new MyImageGlideEngine();
}
public static MyImageGlideEngine createGlideEngine() {
return InstanceHolder.instance;
}
?
package com.example.allfilemanagerdemo.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.example.allfilemanagerdemo.R;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.interfaces.OnCallbackListener;
import com.luck.picture.lib.utils.ActivityCompatHelper;
/**
* @author:luck
* @date:2019-11-13 17:02
* @describe:Glide加載引擎
*/
public class MyImageGlideEngine implements ImageEngine {
/**
* 加載圖片
*
* @param context 上下文
* @param url 資源url
* @param imageView 圖片承載控件
*/
@Override
public void loadImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.into(imageView);
}
@Override
public void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(maxWidth, maxHeight)
.into(imageView);
}
/**
* 加載相冊目錄封面
*
* @param context 上下文
* @param url 圖片路徑
* @param imageView 承載圖片ImageView
*/
@Override
public void loadAlbumCover(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.override(180, 180)
.sizeMultiplier(0.5f)
.transform(new CenterCrop(), new RoundedCorners(8))
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
/**
* 加載圖片列表圖片
*
* @param context 上下文
* @param url 圖片路徑
* @param imageView 承載圖片ImageView
*/
@Override
public void loadGridImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(200, 200)
.centerCrop()
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
@Override
public void pauseRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).pauseRequests();
}
@Override
public void resumeRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).resumeRequests();
}
private MyImageGlideEngine() {
}
private static final class InstanceHolder {
static final MyImageGlideEngine instance = new MyImageGlideEngine();
}
public static MyImageGlideEngine createGlideEngine() {
return InstanceHolder.instance;
}
}
10.Android13文件讀寫權限適配:
private fun initPermission() {
if (checkPermissions()) {
takePhoto()
} else {
requestPermission()
}
}
private fun requestPermission() {
when {
Build.VERSION.SDK_INT >= 33 -> {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_AUDIO,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.CAMERA,
),
Constants.REQUEST_CODE_PERMISSIONS
)
}
else -> {
ActivityCompat.requestPermissions(
this,
REQUIRED_PERMISSIONS,
Constants.REQUEST_CODE_PERMISSIONS
)
}
}
}
private fun checkPermissions(): Boolean {
when {
Build.VERSION.SDK_INT >= 33 -> {
val permissions = arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_AUDIO,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.CAMERA,
)
for (permission in permissions) {
return Environment.isExternalStorageManager()
}
}
else -> {
for (permission in REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
}
}
return true
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
Constants.REQUEST_CODE_PERMISSIONS -> {
var allPermissionsGranted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false
break
}
}
when {
allPermissionsGranted -> {
// 權限已授予,執(zhí)行文件讀寫操作
takePhoto()
}
else -> {
// 權限被拒絕,處理權限請求失敗的情況
ToastUtils.showShort("請您打開必要權限")
requestPermission()
}
}
}
}
}
11.打開相冊:
這里有兩種方式:
11.1 以startActivityForResult方式打開
private fun openPhotoAlbum() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
//以startActivityForResult方式打開Activity
startActivityForResult(
Intent.createChooser(intent, "File Browser"), Constants.FILE_CHOOSER_RESULT_CODE)
}
11.2 以launch方式打開
private fun openPhotoAlbum() {
//以launch方式打開相冊
photoLaunch.launch("image/*")
}
12.打開相冊結果回調:
12.1 startActivityResult方式結果回調:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != RESULT_OK) {
return
}
when (requestCode) {
Constants.FILE_CHOOSER_RESULT_CODE -> {
if (data?.data == null) {
return
}
val imgStr: String = getImageAbsolutePath(this, data.data).toString()
if (!TextUtils.isEmpty(imgStr)) {
val imgUri: Uri? = FileUtils.getUriFromPath(imgStr, application)
if (imgUri != null) {
Glide.with(this@MainActivity).load(imgUri.toString()).into(ivPhoto)
}
}
}
}
}
}
12.2 launch方式結果回調:
private val photoLaunch =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
Glide.with(this@MainActivity).load(uri.toString()).into(ivPhoto)
}
13.實現(xiàn)的效果如下:
文章來源:http://www.zghlxwxcb.cn/news/detail-815309.html
14.項目源碼如下:
https://gitee.com/jackning_admin/all-file-manager-demo文章來源地址http://www.zghlxwxcb.cn/news/detail-815309.html
到了這里,關于Android13適配所有文件管理權限的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!