文章目录

  • 关联博客
  • Android 11 权限变更
  • 权限申请
    • BasePermissionExtendFragment.kt
    • PermissionExtend.kt

关联博客

Android ActivityResultContracts 替代 startActivityForResult

Android 11 权限变更

官方文档

  • Beginning with Android 11, ACTION_MANAGE_OVERLAY_PERMISSION intents always bring the user to the top-level Settings screen, where the user can grant or revoke the SYSTEM_ALERT_WINDOW permissions for apps. Any package: data in the intent is ignored.
  • If your app targets Android 11 or higher and needs to access the phone number APIs shown in the following list, you must request the READ_PHONE_NUMBERS permission, instead of the READ_PHONE_STATE permission.

The getLine1Number() method in both the TelephonyManager class and the TelecomManager class.
The unsupported getMsisdn() method in the TelephonyManager class.

<manifest><!-- Grants the READ_PHONE_STATE permission only on devices that runAndroid 10 (API level 29) and lower. --><uses-permission android:name="android.permission.READ_PHONE_STATE"android:maxSdkVersion="29" /><uses-permission android:name="android.permission.Camera" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

权限申请

官方文档
文档中的 流程图:

通过文档与流程图,再结合
Android ActivityResultContracts 替代 startActivityForResult

inline fun SupportFragment.requestPermission(crossinline block: (Boolean) -> Unit): ActivityResultLauncher<String> {return registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->block(result)}
}

对单个权限的请求流程做了封装。

通常一个功能,要请求的权限也很少超过两个。如果超过了,那可能这是一个组合功能,可以拆解成较细粒度的功能。

BasePermissionExtendFragment.kt

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.PermissionChecker/*** desc:* author:  stone* email:   aa86799@163.com* time:    2021/6/9 14:40*/
abstract class BasePermissionExtendFragment : BaseExtendFragment() {private var mGrantedCallback: (() -> Unit)? = nullprivate var mDeniedCallback: (() -> Unit)? = nullval mRequestPermissionLauncher =requestPermission { isGranted: Boolean ->if (isGranted) {if (mGrantedCallback != null) {mGrantedCallback?.invoke()return@requestPermission}Toast.makeText(requireContext(), "granted", Toast.LENGTH_SHORT).show()} else {if (mDeniedCallback != null) {mDeniedCallback?.invoke()return@requestPermission}Toast.makeText(requireContext(), "denied", Toast.LENGTH_SHORT).show()AlertDialog.Builder(requireContext()).setTitle("why need it , please go to setting and open it").setPositiveButton("confirm") { dialog, _ ->}.create().show()}}fun checkPermission(permission: String,grantedCallback: (() -> Unit)? = null,rationaleCallback: (() -> Unit)? = null,deniedCallback: (() -> Unit)? = null) {mGrantedCallback = grantedCallbackmDeniedCallback = deniedCallbackval selfPermission = ContextCompat.checkSelfPermission(requireContext(), permission)when {// 已授权selfPermission == PermissionChecker.PERMISSION_GRANTED -> {if (mGrantedCallback != null) {mGrantedCallback?.invoke()return}Toast.makeText(requireContext(), "granted", Toast.LENGTH_SHORT).show()}// 拒绝,但未选中 "不再提醒"; 可能弹窗提示用户,需要此权限的原因ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), permission) -> {if (rationaleCallback != null) {rationaleCallback.invoke()return}// show tips, needs permission why it isAlertDialog.Builder(requireContext()).setTitle("why need it ").setNegativeButton("cancel") { dialog, _ ->}.setPositiveButton("confirm") { dialog, _ ->mRequestPermissionLauncher.launch(permission)}.create().show()}// 拒绝,且不再提醒selfPermission == PermissionChecker.PERMISSION_DENIED -> {mRequestPermissionLauncher.launch(permission)}}}// 打开当前应用的 系统设置界面fun toSelfSetting(context: Context) {Intent().apply {addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)action = Settings.ACTION_APPLICATION_DETAILS_SETTINGSdata = Uri.fromParts("package", context.packageName, null)context.startActivity(this)}}
}

grantedCallback: 授权后的正常操作;
rationaleCallback: 拒绝(可以提醒)的操作;
deniedCallback: 拒绝(不再提醒的)的操作。

PermissionExtend.kt

对 BasePermissionExtendFragment 进行扩展。增加特定权限的统一处理。

/*** desc:* author:  stone* email:   aa86799@163.com* time:    2021/6/9 14:42*//*** 图片等文件读写操作,可能要用到 WRITE_EXTERNAL_STORAGE 外部存储权限。* android 11 及以上 不再提供 WRITE_EXTERNAL_STORAGE 权限,被标记为 已过时。* 为了兼容性,>=6 && <=10 需要 权限检查。*/
fun BasePermissionExtendFragment.storagePermissionHandle(granted: (() -> Unit)? = null) {val permission = Manifest.permission.WRITE_EXTERNAL_STORAGEcheckPermission(permission, grantedCallback = {granted?.invoke()}, rationaleCallback = {alert(getString(R.string.reject_tip), resources.getString(R.string.warning)) {positiveButton(getString(R.string.confirm)) { dialog ->mRequestPermissionLauncher.launch(permission)}}.show()}, deniedCallback = {alert(getString(R.string.reject_tip), resources.getString(R.string.warning)) {positiveButton(getString(R.string.confirm)) { dialog ->toSelfSetting(requireContext())}}.show()})
}/*** 请求相机权限. 通常也需要请求 外部存储权限 (WRITE_EXTERNAL_STORAGE)*/
fun BasePermissionExtendFragment.cameraPermissionHandle(albumPermission: Boolean = true, granted: (() -> Unit)? = null) {val permission = Manifest.permission.CAMERAcheckPermission(permission, grantedCallback = {if (albumPermission)storagePermissionHandle(granted)elsegranted?.invoke()}, rationaleCallback = {alert(getString(R.string.needCameraPermission), resources.getString(R.string.warning)) {positiveButton(getString(R.string.confirm)) { dialog ->mRequestPermissionLauncher.launch(permission)}}.show()}, deniedCallback = {alert(getString(R.string.startCameraPermissionHint), resources.getString(R.string.warning)) {positiveButton(getString(R.string.confirm)) { dialog ->toSelfSetting(requireContext())}}.show()})
}/*** 请求定位权限. 要先检查是否开启了定位服务*/
fun BasePermissionExtendFragment.locationPermissionHandle(granted: (() -> Unit)? = null) {if (!checkLocationService()) returnval permission = Manifest.permission.ACCESS_FINE_LOCATIONcheckPermission(permission, grantedCallback = {granted?.invoke()}, rationaleCallback = {alert(getString(R.string.needLocationPermission), resources.getString(R.string.warning)) {positiveButton(getString(R.string.confirm)) { dialog ->mRequestPermissionLauncher.launch(permission)}}.show()}, deniedCallback = {alert(getString(R.string.location_is_required), resources.getString(R.string.warning)) {positiveButton(getString(R.string.confirm)) {toSelfSetting(requireContext())}}.show()})
}// 如果打开了定位服务,true;  否则 让用户去设置页面打开定位服务
private fun BasePermissionExtendFragment.checkLocationService(): Boolean {return if (CheckLocationService.isLocServiceEnable(ExpressApplication.instance())) {true} else {alert(getString(R.string.if_not_be_able_connect_bluetooth_printer)) {positiveButton(R.string.confirm) {val locationSet = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)startActivity(locationSet)}negativeButton(R.string.cancel) {}}.show()false}
}

想对一个具体权限进行怎样的提示与处理,就定义在这里。
类似 定位权限 的申请麻烦点,它首先要依赖定位服务。

定位服务检测:

import android.content.Context
import android.location.LocationManagerobject CheckLocationService {/*** 手机是否开启位置服务,如果没有开启那么所有app将不能使用定位功能*/fun isLocServiceEnable(context: Context): Boolean {val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManagerval gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)val netWork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)return gps || netWork}
}

最终调用者就舒服了:

storagePermissionHandle { //do sth. }
cameraPermissionHandle { //do sth. }
locationPermissionHandle { //do sth. }

Android ActivityResultContracts 请求权限(封装;含android 11权限变更)相关推荐

  1. Android 网络请求、网络状态及各种权限判断

    一.判断网络连接是否可用 public static boolean isNetworkAvailable(Context context) { ConnectivityManager cm = (C ...

  2. android get请求最长字符,Android OKHTTP3的GET和POST方法(带basic auth)

    使用前需要在Gradle Script中的build gradle中引入: compile 'com.squareup.okio:okio:1.13.0' compile 'com.squareup. ...

  3. android get请求最长字符串,android – Retrofit @GET – 如何显示请求字符串?

    我正在使用一个使用Retrofit创建一个休息的客户端的 Android应用程序.为了调试网络调用,我想显示或转储实际被调用的url.有没有办法做到这一点?我已经在下面列出了一些代码,显示了应用程序当 ...

  4. android 界面跳转封装,【Android】Fragment跳转系列

    一.同一个Activity下Fragment之间相互跳转(含带参数) Fragment1跳转到Fragment2中 1.封装一个方法 这样封装是方便一个Activity下有多个Fragment来回跳转 ...

  5. 如何在Android 11 中正确请求位置权限?以及Android 8 - 11位置权限的变化及适配方法!

    由于现在位置信息变为了敏感数据,因此Android限制了它的使用,尤其在APP后台. 在Android 9 之前,位置权限没有按照前后台分离,APP在前台和后台使用相同的资源. 但是,Google开始 ...

  6. 为什么说在Android中请求权限从来都不是一件简单的事情?

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新. 周末时间参加了东莞和深圳的两场GDG,因为都是线上参与,所以时间上并不赶,我只需要坐在家里等 ...

  7. android 6.0权限封装,Android6.0------权限申请管理(单个权限和多个权限申请)

    Android开发时,到6.0系统上之后,有的权限就得申请才能用了. Android将权限分为正常权限 和 危险权限 Android系统权限分为几个保护级别.需要了解的两个最重要保护级别是 正常权限  ...

  8. Android 6.0及以上版本动态申请权限,11权限

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//安卓11文件权限// 先判断有没有权限if (Environment.isExter ...

  9. android 权限开启回调,Android M请求onSurfaceTextureAvailable回调权限不在活动

    症状:首次启动我的应用程序崩溃java.lang.SecurityException: Lacking privileges to access camera service.我收到了"不幸 ...

最新文章

  1. 为什么不推荐使用存储过程?
  2. Nginx常见的错误及解决方法
  3. 这家获得谷歌投资的企业想把亚马逊挑下神坛
  4. dot net操作sql服务器大全
  5. mysql数据库详解(续一)
  6. 前端又一本升级版图书上市了,听说比第一版还好看
  7. 一招让你拿下seata分布式事务框架,看这篇文章准没错!
  8. jquery弹出框样式大全_jQuery中w2ui是什么?-前端问答
  9. pycharm中的数据库可视化
  10. vue2 怎么用vite_vite快速入门教程
  11. PHP微信h5棋牌程序制作session共享方案梳理
  12. qca9535 tftp32 刷机_【U-Boot】U-Boot 刷机方法大全
  13. 本机查看文件服务器所有共享的文件,谁访问,谁打开,
  14. jdbc连接云数据库mysql_java基于jdbc连接mysql数据库功能实例详解
  15. 用手机访问电脑的本地服务器
  16. 这可能是最全的天气Api接口 (可在小程序中使用)
  17. 1070: 小汽车的位置 Python
  18. 关于.Net与J2EE的比较,到底用微软平台还是Java平台的问题
  19. 什么是ETL,ETL是什么技术?
  20. 帝国模板留言板增加自定义字段教程

热门文章

  1. 3dmax:3dmax三维VR渲染设置实现快速渲染大图参数设置图文教程之详细攻略
  2. MySQL数据库删除数据后自增ID不连续的问题
  3. 【论文笔记】Image Privacy Prediction Using Deep Neural Networks
  4. JDK8和JDK9【新特性】
  5. html网页设计同字,HTML网页设计中的字体设计
  6. 硬盘恢复数据可以百分百恢复吗?怎么提高文件恢复率
  7. Axure8.0教程:自动带出邮箱
  8. 如何做好新进员工培训工作
  9. 深航协国际物流商会2020年第三季度理事会顺利召开
  10. C++多线程thread用法