Android ActivityResultContracts 请求权限(封装;含android 11权限变更)
文章目录
- 关联博客
- 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权限变更)相关推荐
- Android 网络请求、网络状态及各种权限判断
一.判断网络连接是否可用 public static boolean isNetworkAvailable(Context context) { ConnectivityManager cm = (C ...
- android get请求最长字符,Android OKHTTP3的GET和POST方法(带basic auth)
使用前需要在Gradle Script中的build gradle中引入: compile 'com.squareup.okio:okio:1.13.0' compile 'com.squareup. ...
- android get请求最长字符串,android – Retrofit @GET – 如何显示请求字符串?
我正在使用一个使用Retrofit创建一个休息的客户端的 Android应用程序.为了调试网络调用,我想显示或转储实际被调用的url.有没有办法做到这一点?我已经在下面列出了一些代码,显示了应用程序当 ...
- android 界面跳转封装,【Android】Fragment跳转系列
一.同一个Activity下Fragment之间相互跳转(含带参数) Fragment1跳转到Fragment2中 1.封装一个方法 这样封装是方便一个Activity下有多个Fragment来回跳转 ...
- 如何在Android 11 中正确请求位置权限?以及Android 8 - 11位置权限的变化及适配方法!
由于现在位置信息变为了敏感数据,因此Android限制了它的使用,尤其在APP后台. 在Android 9 之前,位置权限没有按照前后台分离,APP在前台和后台使用相同的资源. 但是,Google开始 ...
- 为什么说在Android中请求权限从来都不是一件简单的事情?
本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新. 周末时间参加了东莞和深圳的两场GDG,因为都是线上参与,所以时间上并不赶,我只需要坐在家里等 ...
- android 6.0权限封装,Android6.0------权限申请管理(单个权限和多个权限申请)
Android开发时,到6.0系统上之后,有的权限就得申请才能用了. Android将权限分为正常权限 和 危险权限 Android系统权限分为几个保护级别.需要了解的两个最重要保护级别是 正常权限 ...
- Android 6.0及以上版本动态申请权限,11权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//安卓11文件权限// 先判断有没有权限if (Environment.isExter ...
- android 权限开启回调,Android M请求onSurfaceTextureAvailable回调权限不在活动
症状:首次启动我的应用程序崩溃java.lang.SecurityException: Lacking privileges to access camera service.我收到了"不幸 ...
最新文章
- 为什么不推荐使用存储过程?
- Nginx常见的错误及解决方法
- 这家获得谷歌投资的企业想把亚马逊挑下神坛
- dot net操作sql服务器大全
- mysql数据库详解(续一)
- 前端又一本升级版图书上市了,听说比第一版还好看
- 一招让你拿下seata分布式事务框架,看这篇文章准没错!
- jquery弹出框样式大全_jQuery中w2ui是什么?-前端问答
- pycharm中的数据库可视化
- vue2 怎么用vite_vite快速入门教程
- PHP微信h5棋牌程序制作session共享方案梳理
- qca9535 tftp32 刷机_【U-Boot】U-Boot 刷机方法大全
- 本机查看文件服务器所有共享的文件,谁访问,谁打开,
- jdbc连接云数据库mysql_java基于jdbc连接mysql数据库功能实例详解
- 用手机访问电脑的本地服务器
- 这可能是最全的天气Api接口 (可在小程序中使用)
- 1070: 小汽车的位置 Python
- 关于.Net与J2EE的比较,到底用微软平台还是Java平台的问题
- 什么是ETL,ETL是什么技术?
- 帝国模板留言板增加自定义字段教程