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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/220198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动化补丁管理软件

什么是自动化补丁管理 自动补丁管理&#xff08;或自动补丁&#xff09;是指整个补丁管理过程的自动化&#xff0c;从扫描网络中的所有系统到检测缺失的补丁&#xff0c;在一组测试系统上测试补丁&#xff0c;将它们部署到所需的系统&#xff0c;并提供定期更新和补丁部署状态…

国产数据库适配-达梦(DM)

1、通用性 达梦数据库管理系统兼容多种硬件体系&#xff0c;可运行于X86、X64、SPARC、POWER等硬件体系之上。DM各种平台上的数据存储结构和消息通信结构完全一致&#xff0c;使得DM各种组件在不同的硬件平台上具有一致的使用特性。 达梦数据库管理系统产品实现了平台无关性&…

【算法与数据结构】37、LeetCode解数独

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题也是一道困难题&#xff0c;难点在于如何构建数独棋盘&#xff0c;如何检查棋盘的合法性&#xff…

H5开发App应用程序的常见问题以及解决方案

Hello大家好&#xff0c;我是咕噜铁蛋&#xff0c;天冷记得添衣&#xff0c;ok话说回来H5开发成为了一种流行的方式来构建跨平台的移动应用程序。然而&#xff0c;在H5开发App应用程序的过程中&#xff0c;我们常常会遇到一些问题&#xff0c;这些问题可能涉及性能、兼容性、用…

人工智能_机器学习065_SVM支持向量机KKT条件_深度理解KKT条件下的损失函数求解过程_公式详细推导---人工智能工作笔记0105

之前我们已经说了KKT条件,其实就是用来解决 如何实现对,不等式条件下的,目标函数的求解问题,之前我们说的拉格朗日乘数法,是用来对 等式条件下的目标函数进行求解. KKT条件是这样做的,添加了一个阿尔法平方对吧,这个阿尔法平方肯定是大于0的,那么 可以结合下面的文章去看,也…

3、ollvm移植

github: https://github.com/obfuscator-llvm/obfuscator/tree/llvm-4.0 先复制 include Obfuscation: /home/nowind/llvm/ollvm/obfuscator/include/llvm/Transforms/Obfuscation /home/nowind/llvm/llvm-project-9.0.1/llvm/include/llvm/Transforms/Obfuscation lib Ob…

【基于Flask、MySQL和Echarts的热门游戏数据可视化平台设计与实现】

基于Flask、MySQL和Echarts的热门游戏数据可视化平台设计与实现 前言数据获取与清洗数据集数据获取数据清洗 数据分析与可视化数据分析功能可视化功能 创新点结语 前言 随着游戏产业的蓬勃发展&#xff0c;了解游戏销售数据对于游戏从业者和游戏爱好者都至关重要。为了更好地分…

飞致云与上海吉谛达成战略合作,获得Gitea企业版中国大陆地区独家代理权

2023年12月13日&#xff0c;中国领先的开源软件提供商FIT2CLOUD飞致云宣布与上海吉谛科技有限公司&#xff08;以下简称为上海吉谛&#xff09;正式达成战略合作&#xff0c;FIT2CLOUD飞致云获得上海吉谛旗下代码托管平台Gitea企业版中国大陆地区独家代理权。 Gitea项目&…

市场全局复盘 20231213

昨日回顾&#xff1a; SELECT TOP 10000 CODE,成交额排名,净流入排名,代码,名称,DDE大单金额,涨幅 ,主力净额,DDE大单净量,CONVERT(DATETIME, 最后封板, 120) AS 最后封板 FROM dbo.全部&#xff21;股20231213_ALL WHERE 连板天 > 1AND DDE大单净量 > 0AND DDE散户数量…

文件系统理解

先前的博客我写了关于缓冲区的理解&#xff0c;顺便提及了在内存的文件是怎样管理的&#xff0c;本文就来描述在磁盘上的文件是怎么样。但要先了解了解磁盘。 在笔记本上机械磁盘被固态硬盘代替&#xff0c;因为固态硬盘更快&#xff0c;而且方便携带&#xff0c;机械硬盘若是受…

极狐GitLab 镜像仓库使用技巧

极狐GitLab 镜像仓库 众所周知&#xff0c;极狐GitLab 是一个成熟、安全的一体化 DevOps 平台&#xff0c;其自身内置了容器镜像仓库功能&#xff0c;也即极狐GitLab Container Registry&#xff0c;用户可以将自身需要的镜像推送至极狐GitLab 镜像仓库&#xff0c;而无需自建镜…

JS对象笔记

对象声明 对象也只是一种数据类型/字面值。写对象这个字面值有两种写法&#xff0c;一种是普通的对象&#xff0c;这种对象用new 构造函数&#xff08;&#xff09;&#xff0c;另一种是JS内特有的json对象。这个对象是直接{}就代表对象。且也是在堆内。 对象的构成 无论是上…

道路坑洞数据集(坑洞目标检测)VOC+YOLO格式650张

路面坑洞的形成原因是由于设计、施工、养护处理不当、控制不适和受气候、环境、地质、水文等自然因素影响&#xff0c;以及车辆的运行和车辆超载运行导致路面破损&#xff0c;出现坑洞的现象。 路面坑洞的分类&#xff1a; &#xff08;1&#xff09;路面混凝土板中坑洞&…

python自动化测试实战 —— CSDN的Web页面自动化测试

软件测试专栏 感兴趣可看&#xff1a;软件测试专栏 自动化测试学习部分源码 python自动化测试相关知识&#xff1a; 【如何学习Python自动化测试】—— 自动化测试环境搭建 【如何学习python自动化测试】—— 浏览器驱动的安装 以及 如何更…

SpringBoot入门知识

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

前端体系:前端应用

目录 前端体系基础 html&#xff08;超文本标记语言&#xff09; css&#xff08;层叠样式单&#xff09; javascript&#xff08;&#xff09; 一、前端体系概述 二、前端框架 React Vue Angular 三、前端库和工具 lodash Redux Webpack 四、模块化和组件化 ES…

微软AutoGen框架:AI的新时代,你的新机遇

一、引言 在科技日新月异的今天&#xff0c;人工智能已经深入到我们生活的各个角落。无论是智能手机、智能家居还是自动驾驶汽车&#xff0c;人工智能的应用无处不在。而在这个领域中&#xff0c;微软AutoGen框架无疑是一颗璀璨的新星。它以其独特的创新性和实用性&#xff0c…

【网络通信原理之套接字】

目录 概念 分类 数据报套接字&#xff1a;使用传输层UDP协议 流套接字&#xff1a;使用传输层TCP协议 原始套接字 Socket编程注意事项 前言&#xff1a;本文主要介绍了在什么是套接字及在Java中套接字是什么&#xff0c;和在套接字编程的注意事项。 概念 Socket套接…

大数据技术之Hive(超级详细)

第1章 Hive入门 1.1 什么是Hive Hive&#xff1a;由Facebook开源用于解决海量结构化日志的数据统计。 Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能。 本质是&#xff1a;将HQL转化成MapReduce程序 …

WPF实现更加灵活绑定复杂Command(使用Microsoft XAML Behaviors 库)

1、安装NuGet 2、在XAML的命名空间引入&#xff1a; xmlns:i"http://schemas.microsoft.com/xaml/behaviors" 3、使用&#xff1a; <Canvas Background"Aqua"><Rectangle Stroke"Red" Width"{Binding RectModel.RectangleWidth}…