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

Posted 匆忙拥挤repeat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ActivityResultContracts 请求权限(封装;含android 11权限变更)相关的知识,希望对你有一定的参考价值。

文章目录

关联博客

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 run
         Android 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)? = null
    private var mDeniedCallback: (() -> Unit)? = null
    
    val 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 = grantedCallback
        mDeniedCallback = deniedCallback
        val 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 is
                AlertDialog.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_SETTINGS
            data = 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_STORAGE
    checkPermission(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.CAMERA
    checkPermission(permission, grantedCallback = 
        if (albumPermission)
            storagePermissionHandle(granted)
        else
            granted?.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()) return
    val permission = Manifest.permission.ACCESS_FINE_LOCATION
    checkPermission(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.LocationManager

object CheckLocationService 
    /**
     * 手机是否开启位置服务,如果没有开启那么所有app将不能使用定位功能
     */
    fun isLocServiceEnable(context: Context): Boolean 
        val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        val 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 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

android 21 是啥版本

Android逆向-Android基础逆向(2-2)

【Android笔记】android Toast

图解Android - Android核心机制

Android游戏开发大全的目录