在Android M的运行时权限中,我们如何区分永不问及停止请求?

Com*_*are 62 android android-permissions android-6.0-marshmallow

根据Google的说法,当谈到M Developer Preview运行时权限时:

  1. 如果您之前从未要求过某种许可,那就请求它

  2. 如果您之前询问过,并且用户说"不",然后用户尝试执行需要被拒绝权限的操作,则应该在再次请求权限之前提示用户解释您需要权限的原因

  3. 如果您之前曾多次询问,并且用户已经说"不,并且停止询问"(通过运行时权限对话框上的复选框),您应该停止打扰(例如,禁用需要权限的UI)

但是,我们只有一个方法,shouldShowRequestPermissionRationale()返回a boolean,我们有三个状态.我们需要一种方法来区分从未问过的状态和停止询问状态,就像我们falseshouldShowRequestPermissionRationale()两者中得到的那样.

对于首次运行应用程序时请求的权限,这不是一个大问题.有很多方法可以确定这可能是您的应用程序的第一次运行(例如,booleanSharedPreferences),因此您假设如果它是您的应用程序的第一次运行,那么您处于从未问过的状态.

但是,运行时权限的一部分愿景是您可能不会事先要求所有这些权限.当用户点击需要该权限的内容时,您可能仅在稍后要求的边缘功能上绑定权限.在这里,应用程序可能已运行多次,持续数月,之后我们突然需要再次请求权限.

在这些情况下,我们是否应该跟踪我们是否自己要求获得许可?或者Android M API中是否有我遗漏的东西告诉我们之前是否询问过?

Nic*_*cks 65

我知道我发帖很晚,但详细的例子可能对某人有帮助.

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

状态1: - 返回true: - 用户单击拒绝权限(包括第一次).

状态2:-Returns false: - 如果用户选择s"永远不会再问.

以下是具有多个权限请求的示例: -

该应用程序在启动时需要2个权限.SEND_SMS和ACCESS_FINE_LOCATION(在manifest.xml中都提到了).

应用启动后,它会一起请求多个权限.如果两个权限都被授予,则正常流程会进行.

在此输入图像描述

public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(checkAndRequestPermissions()) {
        // carry on the normal flow, as the case of  permissions  granted.
    }
}

private  boolean checkAndRequestPermissions() {
    int permissionSendMessage = ContextCompat.checkSelfPermission(this,
            Manifest.permission.SEND_SMS);
    int locationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
    List<String> listPermissionsNeeded = new ArrayList<>();
    if (locationPermission != PackageManager.PERMISSION_GRANTED) {
        listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
    }
    if (permissionSendMessage != PackageManager.PERMISSION_GRANTED) {
        listPermissionsNeeded.add(Manifest.permission.SEND_SMS);
    }
    if (!listPermissionsNeeded.isEmpty()) {
        ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),REQUEST_ID_MULTIPLE_PERMISSIONS);
        return false;
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

如果未授予一个或多个权限,activityCompat.requestPermissions()将请求权限,控件将转到onRequestPermissionsResult()回调方法.

您应该在onRequestPermissionsResult()回调方法中检查shouldShowRequestPermissionRationale()标志的值.

只有两种情况: -

案例1: - 任何时间用户点击拒绝权限(包括第一次),它将返回true.因此,当用户拒绝时,我们可以显示更多解释并再次询问.

案例2: - 只有当用户选择"永不再问"时,它才会返回false.在这种情况下,我们可以继续使用有限的功能并引导用户从设置中激活权限以获得更多功能,或者我们可以完成设置,如果权限对于应用程序来说是微不足道的.

情况1

情况1

案例-2

案例 -  2

@Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        Log.d(TAG, "Permission callback called-------");
        switch (requestCode) {
            case REQUEST_ID_MULTIPLE_PERMISSIONS: {

                Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.SEND_SMS, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                // Fill with actual results from user
                if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);
                    // Check for both permissions
                    if (perms.get(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                        Log.d(TAG, "sms & location services permission granted");
                        // process the normal flow
                        //else any one or both the permissions are not granted
                    } else {
                            Log.d(TAG, "Some permissions are not granted ask again ");
                            //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
//                        // shouldShowRequestPermissionRationale will return true
                            //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
                            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
                                showDialogOK("SMS and Location Services Permission required for this app",
                                        new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface dialog, int which) {
                                                switch (which) {
                                                    case DialogInterface.BUTTON_POSITIVE:
                                                        checkAndRequestPermissions();
                                                        break;
                                                    case DialogInterface.BUTTON_NEGATIVE:
                                                        // proceed with logic by disabling the related features or quit the app.
                                                        break;
                                                }
                                            }
                                        });
                            }
                            //permission is denied (and never ask again is  checked)
                            //shouldShowRequestPermissionRationale will return false
                            else {
                                Toast.makeText(this, "Go to settings and enable permissions", Toast.LENGTH_LONG)
                                        .show();
    //                            //proceed with logic by disabling the related features or quit the app.
                            }
                    }
                }
            }
        }

    }

    private void showDialogOK(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", okListener)
                .create()
                .show();
    }
Run Code Online (Sandbox Code Playgroud)

  • 问题是shouldShowRequestPermissionRationale如果已经选中"不再询问"或者之前从未拒绝过,则返回false.因此,检查此方法的返回值不足以知道我们是否应该预先进行教育. (7认同)

MLP*_*CiM 13

根据当前示例:https://github.com/googlesamples/android-RuntimePermissions/blob/master/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java#L195

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
        int[] grantResults) {
    if (requestCode == REQUEST_CAMERA) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            doThing();
            //STORE FALSE IN SHAREDPREFERENCES
        } else {
            //STORE TRUE IN SHAREDPREFERENCES
        }
    }
Run Code Online (Sandbox Code Playgroud)

在SharedPreferences中存储一个布尔值,使用key作为您的权限代码和值,如上所示,以指示之前是否已拒绝该首选项.

遗憾的是,您可能无法检查已在应用程序运行时已被接受并稍后被拒绝的首选项.最终规范不可用,但您的应用程序有可能重新启动或获取模拟值,直到下次启动.

  • "你可能无法检查已经接受并在你的应用程序运行时被拒绝的偏好" - 我的印象是,设置中权限的更改会导致应用程序的进程被终止,从而实际上迫使你检查您是否再次拥有权限.正如对该问题的评论所述,我希望在Android中有一些适当的机制,以避免重复数据和不同步的风险.谢谢! (3认同)
  • 我试过这个机制,但唉,它没有涵盖所有场景.如果你在app中允许权限然后转到权限设置并拒绝它,那么存储在共享首选项中的布尔值仍然是真的(从之前),你需要它是假的.如果你不打算预先教育,那么shouldShowRequestPermissionRationale是好的,但如果你这样做,那就没有好处.应该有一个"shouldEducateUpFront"方法来确定用户之前没有拒绝权限的情况.特别是因为Google建议在某些情况下这样做:https://www.youtube.com/watch?v = iZqDdvhTZj0 (3认同)

ed9*_*9er 6

不,您不需要跟踪是否要求获得许可,并且您不需要区分Never-Asked和Stop-Asking.

对于应用程序开发人员,状态1和3是相同的:您需要权限ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED,然后ActivityCompat.requestPermissions()只要用户点击需要权限的功能,无论您请求了多少次,您都只需要通过权限.用户最终将"授予"它,或者"拒绝"它,并且"再也不要再问".该设计不会阻止您多次弹出权限请求对话框.

但是,该设计确实鼓励您在某些时候解释许可的目的 - 您的状态2. shouldShowRequestPermissionRationale()不用于确定您是否应该请求许可,它用于确定您是否应该在您请求许可之前显示解释.

关于州3的更多解释:

  1. 是的,我们应该通过停止显示解释而不是停止请求来停止打扰用户.这就是为什么他们提供了shouldShowRequestPermissionRationale().
  2. 保持许可请求并不困难.用户选择"永不再问"后,ActivityCompat.requestPermissions()将不再弹出对话框.
  3. 每次我们在单个用户会话期间发现我们没有权限时,最好禁用相关的UI.而不是在shouldShowRequestPermissionRationale()返回false 后禁用UI .

  • 好消息是`shouldShowRequestPermissionRationale()`似乎已经在`onRequestPermissionsResult()`中返回了预期的值,所以如果用户再次拒绝了,那么`shouldShowRequestPermissionRationale()`确实会返回`false` .因此,如果您想要获得相同的响应(例如,显示小吃栏),无论用户是否仅仅拒绝了再次询问或之前这样做,您就不需要状态1.如果您想要不同的响应(例如, ,如果用户在前一段时间再次拒绝,而不仅仅是现在,只显示一个小吃吧,你仍然需要状态1. (7认同)
  • "在单个用户会话期间,每当我们发现我们没有权限时,最好禁用相关的UI.而不是在shouldShowRequestPermissionRationale()之后禁用UI返回false" - 这是我们分歧的核心.如果我们使用谷歌的方法不禁用用户界面,而是显示引导用户使用设置的快餐栏,我们也会遇到类似的问题.不知怎的,我们需要知道显示小吃吧,而直接的解决方案需要我们区分状态3和状态1. (2认同)