ChatGPT解决这个技术问题 Extra ChatGPT

Android M 权限:对 shouldShowRequestPermissionRationale() 函数的用法感到困惑

我正在阅读有关 Android M 中新权限模型的官方文档。它讨论了 shouldShowRequestPermissionRationale() 函数,如果应用程序先前已请求此权限并且用户拒绝了该请求,该函数将返回 true。如果用户过去拒绝了权限请求并选择了不再询问选项,则此方法返回 false

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

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

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

我想在案例 2 中将用户发送到应用程序的设置页面。我该如何区分这两种情况?

接受的答案很好。作为替代方案,您还可以使用共享首选项来了解应用程序之前是否请求过权限。只是把它扔在那里,以防它更适用于其他人的情况。
还有一种情况 3:用户已被请求并授予/拒绝权限,但已使用权限设置恢复为“每次都询问”。测试表明 shouldShowRequestPermissionRationale() 在这种情况下返回 false,这将损害任何依赖“我之前问过”标志的代码。
这是一个 google 示例,展示了 Android 上 permissions 中的最佳做法。 github.com/android/permissions-samples
@itabdullah Google 的示例代码毫无用处,因为他们甚至没有考虑“用户上次是否永久拒绝许可”这种极有可能的用例。 :-/ 典型的

T
Tim

在 M Preview 1 之后,如果第一次显示对话框,则没有 Never ask again 复选框。

如果用户拒绝权限请求,则在第二次请求权限时,权限对话框中将出现“不再询问”复选框。

所以逻辑应该是这样的:

请求权限: if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); } else { //做需要权限的事情... } 检查onRequestPermissionsResult中的权限是被拒绝还是被授予。如果之前拒绝了该权限,这一次将在权限对话框中出现“不再询问”复选框。调用 shouldShowRequestPermissionRationale 以查看用户是否选中了 Never ask again。 shouldShowRequestPermissionRationale 方法仅在用户选择 Never ask again 或设备策略禁止应用程序拥有该权限时返回 false: if (grantResults.length > 0){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED) { //Do the stuff这需要权限... }else if (grantResults[0] == PackageManager.PERMISSION_DENIED){ // 我们应该给出解释吗? if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //显示权限说明对话框... }else{ //选择不再询问,或者设备政策禁止应用拥有该权限。 //所以,禁用该功能,或者退回到另一种情况... } } }

因此,您不必跟踪用户是否选中了“不再询问”。


澄清一点,shouldShowRequestPermissionRationale() 如果用户从未被请求许可(即第一次运行应用程序)也将返回 false。如果您遵循所提供示例的逻辑,您将不会遇到这种情况。但是,低于 2 的措辞有点误导。
我不确定,这似乎有缺陷。我们应该如何知道这是否是第一次询问用户?我必须跟踪用户是否被问到,如果他这样做了,那么我必须颠倒逻辑。对我来说没有任何意义。
我认为值得注意的是,您在 ActivityCompat.shouldShowRequestPermissionRationale(...) 中传递 context 的位置实际上是 Activity 类型。可能不会影响你们所有人,但在我的情况下会影响。
这个android逻辑太愚蠢了!它迫使我在回调中调用 should 并将其计数器值保存在 NVM 中,只是为了知道我是否需要在下次打开应用程序时再次提示请求! ......哇(facepalm)......只打一个电话返回一个状态枚举是否太难了?
我认为这是谷歌的一大失败。官方文档指出,应该在检查权限之前调用 shouldShowRequestPermissionRationale()(参见 developer.android.com/training/permissions/requesting#explain),但 StackOverflow 中的所有答案都会在 onRequestPermissionResult() 中调用它,以区分用户是否单击了“不再询问”。
m
muthuraj

我有同样的问题,我想通了。为了让生活更简单,我编写了一个 util 类来处理运行时权限。

public class PermissionUtil {
    /*
    * Check if version is marshmallow and above.
    * Used in deciding to ask runtime permission
    * */
    public static boolean shouldAskPermission() {
        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
    }
private static boolean shouldAskPermission(Context context, String permission){
        if (shouldAskPermission()) {
            int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true;
            }
        }
        return false;
    }
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
        * If permission is not granted
        * */
        if (shouldAskPermission(context, permission)){
/*
            * If permission denied previously
            * */
            if (((Activity) context).shouldShowRequestPermissionRationale(permission)) {
                listener.onPermissionPreviouslyDenied();
            } else {
                /*
                * Permission denied or first time requested
                * */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
                    PreferencesUtil.firstTimeAskingPermission(context, permission, false);
                    listener.onPermissionAsk();
                } else {
                    /*
                    * Handle the feature without permission or ask user to manually allow permission
                    * */
                    listener.onPermissionDisabled();
                }
            }
        } else {
            listener.onPermissionGranted();
        }
    }
/*
    * Callback on various cases on checking permission
    *
    * 1.  Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
    *     If permission is already granted, onPermissionGranted() would be called.
    *
    * 2.  Above M, if the permission is being asked first time onPermissionAsk() would be called.
    *
    * 3.  Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
    *     would be called.
    *
    * 4.  Above M, if the permission is disabled by device policy or the user checked "Never ask again"
    *     check box on previous request permission, onPermissionDisabled() would be called.
    * */
    public interface PermissionAskListener {
/*
        * Callback to ask permission
        * */
        void onPermissionAsk();
/*
        * Callback on permission denied
        * */
        void onPermissionPreviouslyDenied();
/*
        * Callback on permission "Never show again" checked and denied
        * */
        void onPermissionDisabled();
/*
        * Callback on permission granted
        * */
        void onPermissionGranted();
    }
}

PreferenceUtil 方法如下。

public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
 sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
 }
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}

现在,您只需要使用带有适当参数的方法 checkPermission。

这是一个例子,

PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    new PermissionUtil.PermissionAskListener() {
                        @Override
                        public void onPermissionAsk() {
                            ActivityCompat.requestPermissions(
                                    thisActivity,
              new String[]{Manifest.permission.READ_CONTACTS},
                            REQUEST_EXTERNAL_STORAGE
                            );
                        }
@Override
                        public void onPermissionPreviouslyDenied() {
                       //show a dialog explaining permission and then request permission
                        }
@Override
                        public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
                        }
@Override
                        public void onPermissionGranted() {
                            readContacts();
                        }
                    });

案例1:应用没有权限,用户之前没有被请求权限。在这种情况下, shouldShowRequestPermissionRationale() 将返回 false,因为这是我们第一次询问用户。情况 2:用户拒绝了权限并选择了“不再询问”,在这种情况下 shouldShowRequestPermissionRationale() 也将返回 false。我想在案例 2 中将用户发送到应用程序的设置页面。我该如何区分这两种情况?

对于案例 1,您将收到 onPermissionAsk 回调,对于案例 2,您将收到 onPermissionDisabled 回调。

快乐编码:)


很好的解释兄弟。遵循您完全相同的程序。 :)
我要为这个活动填写什么? public void onPermissionAsk() { ActivityCompat.requestPermissions( thisActivity, ...
@Mardymar thisActivity 只不过是 YourActivity.this
如何处理多个权限以及如何将此代码集成到片段中。
谢谢 - 在所有答案中(包括接受/最受好评的答案),您是唯一真正回答问题的人
S
Shumoapp

我理解它的方式, shouldShowRequestPermissionRationale() 在后台运行了许多用例,并通知应用程序是否显示所请求权限的解释。

运行时权限背后的想法是,大多数时候,用户会对权限请求说“是”。这样,用户只需单击一下即可。当然,该请求应该在正确的上下文中使用 - 即在按下“相机”按钮时请求相机权限。

如果用户拒绝了请求,但一段时间后再次按下“相机”按钮,shouldShowRequestPermissionRationale() 将返回 true,因此应用程序可以显示一些有意义的解释为什么请求权限,以及为什么应用程序不会没有它可以正常工作。通常,您会在该对话框窗口中显示一个按钮来再次拒绝/稍后决定,以及一个按钮来授予权限。基本原理对话框中的授予权限按钮应再次启动权限请求。这次用户还将有一个“不再显示”复选框。如果他决定选择它并再次拒绝该权限,它将通知 Android 系统用户和应用程序不在同一页面上。该操作将产生两个后果 - shouldShowRequestPermissionRationale() 将始终返回 false,并且 requestPermissions() 方法将不显示任何对话框,但将直接向 onRequestPermissionsResult 回调返回拒绝。

但是还有另一种可能的情况,可以使用 onRequestPermissionsResult。例如,某些设备可能具有禁用相机的设备策略(适用于 CIA、DARPA 等)。在这些设备上,onRequestPermissionsResult 将始终返回 false,并且 requestPermissions() 方法将默默地拒绝请求。

这就是我通过听 Android 框架产品经理 Ben Poiesz 的播客收集到的信息。
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html


C
Community

更新

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

==

我原来的答案:

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

另请参阅:Android M - check runtime permission - how to determine if the user checked "Never ask again"?


是的,即使我同意 CanC 的方法是应该遵循的方法。我将其标记为已接受的答案。
W
Wei WANG

如果有人愿意,请发布另一个选项。您可以使用 Google 自己提供的 EasyPermissions 来“简化 Android M 系统权限”。

那么您不必直接处理 shouldShowRequestPermissionRationale


为什么我以前没有看到这个项目:)
EasyPermissions 的问题几乎保持不变。在从未请求用户授予权限的情况下,在内部询问 permissionPermanentlyDenied 只会调用 shouldShowPermissionsRationale 并返回 true
b
bmjohns

如果有人对 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)
    }
}

权限处理程序

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)
                    }
                }
            }

不要在 PermissionHandler.checkPermission() 中传递上下文,然后稍后将其转换为活动。上下文并不总是一个活动
T
Thiago Saraiva

检查此实现。对我来说效果很好。基本上,您在传递权限列表的 checkPermissions() 方法中检查权限。您在 onRequestPermissionsResult() 上检查权限请求的结果。当用户选择“不再询问”或不选择时,该实现让您解决这两种情况。在这个实现中,如果 se 选择“不再询问”,对话框有一个选项可以将他带到 App Settings Activity。

所有这些代码都在我的片段中。我在想创建一个专门的类来做到这一点会更好,比如 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);
    }

D
Dr. aNdRO

我们可以通过这种方式做到吗?

@Retention(RetentionPolicy.SOURCE)
@IntDef({GRANTED, DENIED, NEVER})
public @interface PermissionStatus {
}

public static final int GRANTED = 0;
public static final int DENIED = 1;
public static final int NEVER = 2;

@PermissionStatus
public static int getPermissionStatus(Activity activity, String permission) {
    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
        return DENIED;
    } else {
        if (ActivityCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED) {
            return GRANTED;
        } else {
            return NEVER;
        }
    }
}

不幸的是,这段代码没有区分以前从未请求过许可的情况和检查过“不再请求”的情况。
您应该使用 this + 权限助手类的组合来检查权限是否被授予。
C
Community

可能对某人有用:--

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

状态 1:-返回 true:-- 任何时候用户单击拒绝权限(包括第一次)。

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

Link for detailed working example


它第一次返回 false。不对
是的,这就是我提到的,如果您检查 onRequestPermissionsResult() 回调方法中的标志,它将只有两种状态,特别是在此回调中。
不幸的是, shouldShowRequestPermissionRationale 总是返回 false - 无论用户是否曾经拒绝过权限。
E
Eva
public void requestPermission(View view){
        if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.ACCESS_FINE_LOCATION)){

//The Alert Dialog before asking for the second time to help the user understand  why he needs to give permission.

                AlertDialog alert = new AlertDialog.Builder(this).setMessage("Without the permissions you cannot run the application")
                        .setCancelable(false)
                        .setPositiveButton("Okay, I understand", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION);
                            }
                        }).setNegativeButton("No, Exit the App", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        System.exit(2);
                    }
                }).create();
                alert.setTitle("ALERTY");
                alert.show();
//End of the alert Dialog
            }
            else{
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION);
            }
        }
        else {
            textView.setText("Permission Is Already Granted");
        }
    }
    /*
     The shouldShowRequestPermissionRationale() function returns true if the app has requested this permission
     previously and the user denied the request.If the user turned down the permission request in the past and chose
     the Don't ask again option, this method returns false.
      */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if(requestCode == REQUEST_FINE_LOCATION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                textView.setText("Hooray! on Request Permissions Granted");
            }
            else{
                //Since the user has chosen the don't ask again option,
                if(!ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)){

//Alert Dialog that will take user to settings where he can manually give the permissions
                    AlertDialog alert = new AlertDialog.Builder(this).setMessage("You have permanently disabled the permission ")
                            .setPositiveButton("Go to Settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                             openSettings();
                        }
                    }).setNegativeButton("Don't Go",null).setCancelable(false).create();
                  alert.setTitle("Give permission manually");
                  alert.show();
 // End of the Alert Dialog
                }
                else{
                    textView.setText("Permission has been denied the 1st time");
                }
            }
        }
    }

这是 openSettings 方法。

    public void openSettings(){
        Intent intent = new Intent();

        Uri uri = Uri.fromParts("package",this.getPackageName(),null);
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
        startActivity(intent);
    }

我已经创建了意图,这样如果用户之前检查了“不再显示”按钮,他将被重定向到设置页面,他可以在其中手动授予权限。
v
vinay shetty

shouldShowRequestPermissionRationale 的正确用法是 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()
    }
}

E
Eric Aya

此代码要求用户在运行时询问权限,如果用户允许,则执行结果方法,如果用户拒绝,则再次询问,并带有用户拒绝的描述(它再次询问指令),但如果用户选择不再询问。它处理不再询问,显示带有说明的打开设置选项。

public String storagePermissions = Manifest.permission.READ_EXTERNAL_STORAGE;   
private static final int REQUEST_ACCESS =101;  

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      if(checkSelfPermission(storagePermissions)== PackageManager.PERMISSION_GRANTED){
          result();    // result  is your block of code 
      }else {
          requestPermissions(new String[]{storagePermissions},REQUEST_ACCESS);
      }

    }
    else{
        result();    //so if user is lower than api verison M, no permission is requested
    } 

}

 private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(MainActivity.this)
            .setMessage(message)
            .setTitle("Hi User..")
            .setPositiveButton("Ok", okListener)
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {        //idea calling showMessage funtion again
                    Snackbar mySnackbar = Snackbar.make( findViewById(R.id.coordinatorlayout),"You Press Cancel.. ", Snackbar.LENGTH_INDEFINITE);
                    mySnackbar.setAction("Exit", new cancelButton());
                    mySnackbar.show();

                }
            })
            .create()
            .show();
}


private void result(){
          //your code
}

    @RequiresApi(api = Build.VERSION_CODES.M)
public class NeverAskAgain implements View.OnClickListener{
    @Override
    public void onClick(View view)
    {
        goToSettings();
    }
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void goToSettings() {
    Intent myAppSettings = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
    finish();
    myAppSettings.addCategory(Intent.CATEGORY_DEFAULT);
    myAppSettings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivityForResult(myAppSettings, REQUEST_APP_SETTINGS);
}
public class cancelButton implements View.OnClickListener{
    @Override
    public void onClick(View view){
        Toast.makeText(MainActivity.this,"To use this app , you must grant storage permission",Toast.LENGTH_SHORT);
        finish();
    }
    }


 @Override
@RequiresApi(api = Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode,permissions,grantResults);

    switch(requestCode) {
        case REQUEST_ACCESS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission is granted
                    result();
                    break;
                }
                else if (!shouldShowRequestPermissionRationale(permissions[0])){
                    showMessageOKCancel("You choose Never Ask Again,option",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Snackbar mySnackbar = Snackbar.make(findViewById(R.id.coordinatorlayout), "Permission=>Storage=>On", Snackbar.LENGTH_INDEFINITE);
                        mySnackbar.setAction("Settings", new NeverAskAgain());
                        mySnackbar.show();
                    }
                     });
                    break;
                }
                else {
                    showMessageOKCancel("You Denid permission Request..",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            requestPermissions(new String[]{storagePermissions}, REQUEST_ACCESS);
                        }
                    });
                    break;
                }
        }
}