Android13适配所有文件管理权限

2023-12-13 13:12

本文主要是介绍Android13适配所有文件管理权限,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android13适配所有文件管理权限

前言:

很早之前在Android11上面就适配过所有文件管理权限,这次是海外版升级到Android13,由于选择相册用的是第三方库,组内的同事没有上架Google的经验直接就提交代码,虽然功能没有问题,但是上架的时候被打回了,于是记录一下适配工作.

1.简介:

绝大多数需要共享存储空间访问权限的应用都可以遵循共享媒体文件和共享非媒体文件方面的最佳做法。然而,某些应用的核心用例需要广泛访问设备上的文件,但无法采用注重隐私保护的存储最佳实践高效地访问这些文件。对于这些情况,Android 提供了一种名为“所有文件访问权”的特殊应用访问权限。

例如,防病毒应用的主要用例可能需要定期扫描不同目录中的许多文件。如果此扫描需要反复的用户交互,让其使用系统文件选择器选择目录,就会带来糟糕的用户体验。其他用例(如文件管理器应用、备份和恢复应用以及文档管理应用)也需要考虑类似情况。

2.Google Play通知:

此部分为在 Google Play 上发布应用的开发者提供通知。

为了限制对共享存储的广泛访问,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 警告,提醒开发者有关 MANAGE_EXTERNAL_STORAGE 权限的 Google Play 政策。

仅当您的应用无法有效利用更有利于保护隐私的 API(如存储访问框架或 Media Store API)时,您才能请求 MANAGE_EXTERNAL_STORAGE 权限。您的应用对该权限的使用必须在允许的使用情形范围内,并且必须与应用的核心功能直接相关。如果您的应用包含与以下任一项类似的用例,可能会请求 MANAGE_EXTERNAL_STORAGE 权限:

  • 文件管理器
  • 备份和恢复应用
  • 防病毒应用
  • 文档管理应用
  • 设备上的文件搜索
  • 磁盘和文件加密
  • 设备到设备数据迁移

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.使用系统拍照:

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.打开系统相册:

    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查看图片库的版本,发现新版本已经适配了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 图片承载控件*/@Overridepublic void loadImage(Context context, String url, ImageView imageView) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).load(url).into(imageView);}@Overridepublic 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*/@Overridepublic 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*/@Overridepublic 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);}@Overridepublic void pauseRequests(Context context) {if (!ActivityCompatHelper.assertValidRequest(context)) {return;}Glide.with(context).pauseRequests();}@Overridepublic 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 = truefor (result in grantResults) {if (result != PackageManager.PERMISSION_GRANTED) {allPermissionsGranted = falsebreak}}when {allPermissionsGranted -> {// 权限已授予,执行文件读写操作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方式打开ActivitystartActivityForResult(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.实现的效果如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

14.项目源码如下:

https://gitee.com/jackning_admin/all-file-manager-demo

这篇关于Android13适配所有文件管理权限的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/488593

相关文章

Python实现将MySQL中所有表的数据都导出为CSV文件并压缩

《Python实现将MySQL中所有表的数据都导出为CSV文件并压缩》这篇文章主要为大家详细介绍了如何使用Python将MySQL数据库中所有表的数据都导出为CSV文件到一个目录,并压缩为zip文件到... python将mysql数据库中所有表的数据都导出为CSV文件到一个目录,并压缩为zip文件到另一个

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi

通过C#获取PDF中指定文本或所有文本的字体信息

《通过C#获取PDF中指定文本或所有文本的字体信息》在设计和出版行业中,字体的选择和使用对最终作品的质量有着重要影响,然而,有时我们可能会遇到包含未知字体的PDF文件,这使得我们无法准确地复制或修改文... 目录引言C# 获取PDF中指定文本的字体信息C# 获取PDF文档中用到的所有字体信息引言在设计和出

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE

Android13_SystemUI下拉框新增音量控制条

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 Android13_SystemUI下拉框新增音量控制条 一、必备知识二、源码分析对比1.brightness模块分析对比2.statusbar/phone 对应模块对比对比初始化类声明对比构造方法 三、源码修改四、相关资源 一、必备知识 在Android12 版本上面已经完成了功能的实现,目前是在And

Collection的所有的方法演示

import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;public class TestCollection {/*** @param args* Collection的所有的方法演示* 此程序没有使用泛型,所以可以添加任意类型* 以后如果写到泛型会补充这一方面的内容*/public s

Temu官方宣导务必将所有的点位材料进行检测-RSL资质检测

关于饰品类产品合规问题宣导: 产品法规RSL要求 RSL测试是根据REACH法规及附录17的要求进行测试。REACH法规是欧洲一项重要的法规,其中包含许多对化学物质进行限制的规定和高度关注物质。 为了确保珠宝首饰的安全性,欧盟REACH法规规定,珠宝首饰上架各大电商平台前必须进行RSLReport(欧盟禁限用化学物质检测报告)资质认证,以确保产品不含对人体有害的化学物质。 RSL-铅,