我正在翻阅关于Android m中的新权限模型的官方文档。它讨论了shouldShowRequestPermissionRationale()函数,如果应用程序之前请求了此权限,并且用户拒绝了请求,该函数将返回true。如果用户在过去拒绝了权限请求并选择Don't ask again选项,则此方法返回false。

但是我们如何区分以下两种情况呢?

案例1:应用程序没有权限,之前也没有向用户请求过权限。在这种情况下,shouldShowRequestPermissionRationale()将返回false,因为这是我们第一次请求用户。

情况2:用户拒绝了权限并选择了“不要再问了”,在这种情况下shouldShowRequestPermissionRationale()将返回false。

我想把用户发送到案例2中的应用设置页面。我怎么微分这两种情况呢?


当前回答

正确使用shouldShowRequestPermissionRationale是on onRequestPermissionsResult。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
    <androidx.appcompat.widget.LinearLayoutCompat
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity"
        android:gravity="center">
    
        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btn_camera"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Camera"
            android:textAllCaps="false"
            android:background="@color/purple_200"
            android:layout_marginTop="20dp"
            >
        </androidx.appcompat.widget.AppCompatButton>
        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btn_storage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Storage"
            android:textAllCaps="false"
            android:background="@color/purple_200"
            android:layout_marginTop="30dp"
            >
        </androidx.appcompat.widget.AppCompatButton>
    
    </androidx.appcompat.widget.LinearLayoutCompat>
enter code here

MainActivity.kt

package com.example.myapplication
import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
    private val TAG = "MainActivity"
    lateinit var btnCamera: Button
    private val cameraRequestCode = 100
    lateinit var btnStorage: Button
    private val storageRequestCode = 200
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btnCamera = findViewById(R.id.btn_camera)
        btnStorage = findViewById(R.id.btn_storage)
        btnCamera.setOnClickListener {
            checkPermission(android.Manifest.permission.CAMERA, cameraRequestCode)
        }
        btnStorage.setOnClickListener {
            checkPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE, storageRequestCode)
        }
    }

    private fun checkPermission(permissionName: String, requestCode: Int) {
        /**
         * if the permission is given means it will give the  permissionNumber = 0
         * if the permission is not  given means it will give the  permissionNumber =-1
         * It s same as we are checking for PackageManager.PERMISSION_DENIED =-1 & PackageManager.GRANTED=0
         */
        val permissionNumber: Int =
            ContextCompat.checkSelfPermission(this@MainActivity, permissionName)
        if (permissionNumber == PackageManager.PERMISSION_GRANTED) {

        } else if (permissionNumber == PackageManager.PERMISSION_DENIED) {
            askpermission(permissionName, requestCode, permissionNumber)

        }

    }

    private fun askpermission(permissionName: String, permissionCode: Int, permissionNumner: Int) {
        ActivityCompat.requestPermissions(
            this@MainActivity,
            arrayOf(permissionName),
            permissionCode
        )
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == cameraRequestCode) {
            if (permissions.size > 0) {
                if (permissions[0].toString().equals(Manifest.permission.CAMERA, ignoreCase = true)) {
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        Toast.makeText(applicationContext,"Permission Granted",Toast.LENGTH_SHORT).show()

                    }else{
                        if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                           var permissionDeniedStatus= checkDeniedPermissionStatus(permissions[0]);
                            if(permissionDeniedStatus){
                                /**
                                 * Permission Denied
                                 */
                                Toast.makeText(applicationContext,"Permission Denied",Toast.LENGTH_SHORT).show()
                            }else{
                                /**
                                 * Permission Denied and Selected Don t ask again.
                                 */
                                showDialog("Permission Denied","Permission Denied Permanently\nOpen Setting to allow")
                            }
                        }
                    }
                }
            }
        }else  if (requestCode == storageRequestCode) {
            if(permissions[0].toString().equals(Manifest.permission.READ_EXTERNAL_STORAGE, ignoreCase = true)){
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        Toast.makeText(applicationContext,"Permission Granted",Toast.LENGTH_SHORT).show()
                    }
                }else{
                    if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                        var permissionDeniedStatus= checkDeniedPermissionStatus(permissions[0]);
                        if(permissionDeniedStatus){
                            /**
                             * Permission Denied
                             */
                            Toast.makeText(applicationContext,"Permission Denied",Toast.LENGTH_SHORT).show()
                        }else{
                            /**
                             * Permission Denied and Selected Don t ask again.
                             */
                            showDialog("Permission Denied","Permission Denied Permanently\nOpen Setting to allow")
                        }
                    }
                }
            }
        }
    }

    private fun checkDeniedPermissionStatus(permissionName: String) :Boolean{
        val permissionDeniedStatus: Boolean = ActivityCompat.shouldShowRequestPermissionRationale(this@MainActivity, permissionName)
        return permissionDeniedStatus
    }

    private fun showDialog(title: String, message: String) {
        val builder = AlertDialog.Builder(this)
        builder.setTitle(title)
        builder.setMessage(message)
        builder.setPositiveButton(android.R.string.yes) { dialog, which ->

        }

        builder.setNegativeButton(android.R.string.no) { dialog, which ->

        }
        builder.show()
    }
}

其他回答

检查这个实现。对我来说还不错。基本上你在checkPermissions()方法中检查权限,传递一个权限列表。在onRequestPermissionsResult()上检查权限请求的结果。实现让你解决两种情况下,当用户选择“永不再问”或不。在这个实现中,如果se选择“never ask again”,对话框有一个选项将他带到应用程序设置活动。

所有这些代码都在我的片段中。我认为最好创建一个专门的类来做这件事,比如PermissionManager,但我不确定。

/**
     * responsible for checking if permissions are granted. In case permissions are not granted, the user will be requested and the method returns false. In case we have all permissions, the method return true.
     * The response of the request for the permissions is going to be handled in the onRequestPermissionsResult() method
     * @param permissions list of permissions to be checked if are granted onRequestPermissionsResult().
     * @param requestCode request code to identify this request in
     * @return true case we already have all permissions. false in case we had to prompt the user for it.
     */
    private boolean checkPermissions(List<String> permissions, int requestCode) {
        List<String> permissionsNotGranted = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(getActivity(), permission) != PackageManager.PERMISSION_GRANTED)
                permissionsNotGranted.add(permission);
        }

        //If there is any permission we don't have (it's going to be in permissionsNotGranted List) , we need to request.
        if (!permissionsNotGranted.isEmpty()) {
            requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()]), requestCode);
            return false;
        }
        return true;
    }

    /**
     * called after permissions are requested to the user. This is called always, either
     * has granted or not the permissions.
     * @param requestCode  int code used to identify the request made. Was passed as parameter in the
     *                     requestPermissions() call.
     * @param permissions  Array containing the permissions asked to the user.
     * @param grantResults Array containing the results of the permissions requested to the user.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case YOUR_REQUEST_CODE: {
                boolean anyPermissionDenied = false;
                boolean neverAskAgainSelected = false;
                // Check if any permission asked has been denied
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        anyPermissionDenied = true;
                        //check if user select "never ask again" when denying any permission
                        if (!shouldShowRequestPermissionRationale(permissions[i])) {
                            neverAskAgainSelected = true;
                        }
                    }
                }
                if (!anyPermissionDenied) {
                    // All Permissions asked were granted! Yey!
                    // DO YOUR STUFF
                } else {
                    // the user has just denied one or all of the permissions
                    // use this message to explain why he needs to grant these permissions in order to proceed
                    String message = "";
                    DialogInterface.OnClickListener listener = null;
                    if (neverAskAgainSelected) {
                        //This message is displayed after the user has checked never ask again checkbox.
                        message = getString(R.string.permission_denied_never_ask_again_dialog_message);
                        listener = new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //this will be executed if User clicks OK button. This is gonna take the user to the App Settings
                                startAppSettingsConfigActivity();
                            }
                        };
                    } else {
                        //This message is displayed while the user hasn't checked never ask again checkbox.
                        message = getString(R.string.permission_denied_dialog_message);
                    }
                    new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme)
                            .setMessage(message)
                            .setPositiveButton(getString(R.string.label_Ok), listener)
                            .setNegativeButton(getString(R.string.label_cancel), null)
                            .create()
                            .show();
                }
            }
            break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    /**
     * start the App Settings Activity so that the user can change
     * settings related to the application such as permissions.
     */
    private void startAppSettingsConfigActivity() {
        final Intent i = new Intent();
        i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        i.addCategory(Intent.CATEGORY_DEFAULT);
        i.setData(Uri.parse("package:" + getActivity().getPackageName()));
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        getActivity().startActivity(i);
    }

可能对某人有用:——

我注意到的是,如果我们在onRequestPermissionsResult()回调方法中检查shouldshowrequestpermissionration理()标志,它只显示两种状态。

状态1:-返回true:——任何时候用户单击Deny permissions(包括第一次)。

状态2:-返回false:-如果用户选择“永不再问”。

链接到详细的工作示例。

如果有人喜欢,就发布另一种选择。你可以使用EasyPermissions,这是由谷歌本身提供的,如前所述,“简化Android M系统权限”。

然后你不必直接处理shouldShowRequestPermissionRationale。

如果有人对Kotlin解决方案感兴趣,我将@muthuraj的答案重构为Kotlin。它也现代化了一点,有一个完成块,而不是监听器。

PermissionUtil

object PermissionUtil {
    private val PREFS_FILE_NAME = "preference"

    fun firstTimeAskingPermission(context: Context, permission: String, isFirstTime: Boolean) {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        sharedPreference.preferences.edit().putBoolean(permission,
                isFirstTime).apply()
    }

    fun isFirstTimeAskingPermission(context: Context, permission: String): Boolean {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        return sharedPreference.preferences.getBoolean(permission,
                true)
    }
}

PermissionHandler

enum class CheckPermissionResult {
    PermissionAsk,
    PermissionPreviouslyDenied,
    PermissionDisabled,
    PermissionGranted
}

typealias PermissionCheckCompletion = (CheckPermissionResult) -> Unit


object PermissionHandler {

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        return ContextCompat.checkSelfPermission(context,
                permission) != PackageManager.PERMISSION_GRANTED
    }

    fun checkPermission(context: Context, permission: String, completion: PermissionCheckCompletion) {
        // If permission is not granted
        if (shouldAskPermission(context, permission)) {
            //If permission denied previously
            if ((context as Activity).shouldShowRequestPermissionRationale(permission)) {
                completion(CheckPermissionResult.PermissionPreviouslyDenied)
            } else {
                // Permission denied or first time requested
                if (PermissionUtil.isFirstTimeAskingPermission(context,
                                permission)) {
                    PermissionUtil.firstTimeAskingPermission(context,
                            permission,
                            false)
                    completion(CheckPermissionResult.PermissionAsk)
                } else {
                    // Handle the feature without permission or ask user to manually allow permission
                    completion(CheckPermissionResult.PermissionDisabled)
                }
            }
        } else {
            completion(CheckPermissionResult.PermissionGranted)
        }
    }
}

实现

PermissionHandler.checkPermission(activity,
                    Manifest.permission.CAMERA) { result ->
                when (result) {
                    CheckPermissionResult.PermissionGranted -> {
                        // openCamera()
                    }
                    CheckPermissionResult.PermissionDisabled -> {
                        // displayAlert(noPermissionAlert)
                    }
                    CheckPermissionResult.PermissionAsk -> {
                        // requestCameraPermissions()
                    }
                    CheckPermissionResult.PermissionPreviouslyDenied -> {
                        // displayAlert(permissionRequestAlert)
                    }
                }
            }

更新

我相信下面CanC的答案是正确的,应该遵循。唯一确定的方法是在onRequestPermissionResult回调中使用shouldShowPermissionRationale来验证这一点。

==

我最初的回答是:

我发现的唯一方法是自己跟踪这是否是第一次(例如使用共享的偏好)。如果不是第一次,则使用shouldShowRequestPermissionRationale()进行区分。

另见:Android M -检查运行时权限-如何确定用户是否勾选了“永不再问”?