Android M的运行时权限中,如何区分从未请求和停止请求?

67
当涉及到M开发者预览运行时权限时,根据Google的说法:
  1. 如果您以前从未请求过某个权限,请直接请求该权限。
  2. 如果您之前已经请求过,并且用户拒绝了请求,而用户现在尝试执行需要被拒绝的权限的操作,则应提示用户解释为什么需要该权限,然后再次请求该权限。
  3. 如果您之前请求了几次,并且用户已经说“不要再问了”(通过运行时权限对话框上的复选框),则应停止打扰(例如,禁用需要该权限的UI)。
然而,我们只有一个方法shouldShowRequestPermissionRationale(),返回一个boolean,而我们有三种状态。我们需要一种方法来区分从未请求的状态和停止请求的状态,因为我们从shouldShowRequestPermissionRationale()中获得false

对于应用程序首次运行时请求权限,这不是一个大问题。有很多方法可以确定这可能是您的应用程序的第一次运行(例如,在SharedPreferences中的boolean值),因此,如果这是您的应用程序的第一次运行,则认为您处于从未询问的状态。

然而,运行时权限的愿景之一是您可能不会一开始就请求所有权限。与边缘功能相关的权限可能只在以后才会请求,当用户点击需要该权限的内容时才会请求。在这种情况下,应用程序可能已经运行了很多次,几个月之久,然后我们突然需要请求另一个权限。

在这些情况下,我们应该跟踪我们是否自己请求了权限吗?还是我错过了Android M API中告诉我们先前是否请求过的某些东西?


这是我所掌握的所有信息,与您刚刚发布的信息相同 https://plus.google.com/+BenjaminWeiss/posts/PFSd7wau4n8 - MLProgrammer-CiM
这个示例应用程序非常微不足道和不完整,它几乎不存在。 - MLProgrammer-CiM
最好的猜测是为每个权限在SharedPreferences或任何类似的工件中存储一个布尔值,这是Google在IO上的建议。 - MLProgrammer-CiM
2
我的担忧是SharedPreferences可能与Android自身存储的权限信息不同步。在运行时权限方面,Android是“记录系统”。它显然拥有这些信息,否则它将无法从shouldShowRequestPermissionRationale()返回true。我只是想看看是否有一些我错过的方法被添加了。 - CommonsWare
了解 Google,他们会在 6.1 中废弃 shouldShowRequestPermissionRationale() 并添加一个返回 int 值的新方法。 - Kevin Krumwiede
@KevinKrumwiede 没有发生 :( - Faustino Gagneten
11个回答

69

我知道我发布得很晚,但详细的示例可能对某些人有用。

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

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

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

这是一个多个权限请求的示例:

应用程序在启动时需要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;
}

如果一个或多个权限未被授予,activityCompat.requestPermissions()将请求权限,并且控制流程转到onRequestPermissionsResult()回调方法。

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

只有两种情况:

情况1:每当用户拒绝权限(包括第一次),它将返回true。因此,当用户拒绝时,我们可以显示更多解释并继续请求。

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

情况1

Case - 1

情况2

Case - 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();
    }

10
问题在于,如果用户已经勾选了“不再询问”,或者之前从未拒绝过权限请求,那么 shouldShowRequestPermissionRationale 方法将返回 false。因此,仅仅检查该方法的返回值并不能确定我们是否应该提前进行教育。 - the_new_mr
我们如何使用这个解决方案?shouldShowRequestPermissionRationale()需要minSdkVersion 23。 - Na Pro
@NaPro 这个功能(运行时权限)只在API23中推出。并且支持向后兼容,所以除非您的compileSDK版本为23或更高版本,否则对您来说不应该是一个问题。 - Nicks
嘿@Nicks,你能告诉我那个for循环在做什么吗?for (int i = 0; i < permissions.length; i++)它的用途是什么? - user55924
@user55924,上述示例是带有多个权限的,因此循环会对它们进行迭代。 - Nicks
显示剩余2条评论

15

根据当前示例: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
        }
    }

使用您的权限代码作为键和上面指示的值,在SharedPreferences中存储布尔值,以指示该偏好是否在之前被拒绝过。

不幸的是,当您的应用程序正在运行时,您可能无法检查已被接受并稍后被拒绝的首选项。最终规范尚不可用,但有可能您的应用程序会在下次启动之前重新启动或获得模拟值。


3
在您的应用程序运行时,您可能无法检查已被接受并稍后被拒绝的首选项。我认为在设置中更改权限会导致您的应用程序进程被终止,从而实际上强制您再次检查是否具有权限。如问题评论中所述,我希望在Android本身中存在某种机制,以避免数据重复和风险不同步。谢谢! - CommonsWare
我相信针对<M和>M目标编译的应用程序之间存在不同的行为。您将获得旧版本的模拟值,并从其他版本中获得SecurityExceptions。 - MLProgrammer-CiM
3
我尝试了这个机制,但不幸的是,它并没有覆盖所有情况。如果你在应用程序中允许权限,然后去权限设置将其拒绝,那么存储在共享偏好中的布尔值仍然为真(之前已经授权过),而你需要它为假。如果您不打算事先教育用户,则shouldShowRequestPermissionRationale方法就可以使用,但如果您打算这样做,这个方法就不太适用了。本应该有一个“shouldEducateUpFront”方法来确定用户以前未拒绝权限的情况。特别是由于谷歌在某些情况下建议这样做:https://www.youtube.com/watch?v=iZqDdvhTZj0 - the_new_mr

6
不需要追踪您是否请求了权限,也不需要区分“从未请求”和“停止询问”。
对于应用程序开发人员,状态1和3是相同的:如果需要权限且ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED,则只需通过ActivityCompat.requestPermissions()请求权限,无论您已经请求多少次,每当用户点击需要权限的功能时。用户最终会“授予”或“拒绝”它,并选择“永不再次询问”。该设计并没有阻止您弹出多次权限请求对话框。
但是,请注意,在某个状态2之前向用户解释权限目的是被鼓励的。shouldShowRequestPermissionRationale()不用于确定是否应该请求权限,而是用于确定在请求权限之前是否应该显示解释。
有关状态3的几点说明:
1. 是的,我们应该通过停止显示解释来停止打扰用户,而不是停止请求。这就是为什么他们提供了shouldShowRequestPermissionRationale()的原因。
2. 继续请求权限并不会打扰用户。在用户选择“永不再次询问”后,ActivityCompat.requestPermissions()将不再弹出对话框。
3. 在单个用户会话期间每次发现自己没有权限时禁用相关UI比shouldShowRequestPermissionRationale()返回false后禁用UI更好。

2
“在单用户会话期间,每次发现没有权限时最好禁用相关的UI,而不是在shouldShowRequestPermissionRationale()返回false后禁用UI” - 这是我们分歧的核心。如果我们使用谷歌的方法不禁用UI,而是显示一个Snackbar引导用户到设置页面,那么我们也会遇到类似的问题。我们需要知道何时显示该Snackbar,而直接的解决方案需要我们区分状态3和状态1。” - CommonsWare
7
好消息是,shouldShowRequestPermissionRationale()onRequestPermissionsResult() 中似乎已经返回了预期的值,因此如果用户选择了"不再询问"并拒绝了权限请求,shouldShowRequestPermissionRationale() 确实会返回 false。因此,如果您想要在用户刚刚拒绝权限请求时或之前都显示相同的响应(例如显示 snackbar),您不需要状态 1。但如果您想要不同的响应(例如仅在用户一段时间前拒绝了权限请求时才显示 snackbar,而不仅仅是现在这个时候),则仍需要状态 1。 - CommonsWare
@CommonsWare 这绝对有很大的帮助!尽管这似乎有点hacky。希望在未来的版本中仍然能够起作用。 - AlbAtNf
请注意,即使用户以前从未拒绝过权限,如果您从未显示过权限对话框,shouldShowRequestPermissionRationale()将返回false。所以第一次应该强制显示权限对话框,然后才可以依赖此方法。请记住,这种行为在未来可能会发生变化。 - Pizza

2

我有一种解决你问题的方法,对我来说似乎效果不错。

我使用SharedPreferences区分从未询问和停止询问,以下是我使用该方法的一个示例。

private void requestAccountPermission() {

        SharedPreferences mPreferences = getSharedPreferences("configuration", MODE_PRIVATE);
        boolean firstTimeAccount = mPreferences.getBoolean("firstTimeAccount", true);

        if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.GET_ACCOUNTS)) {
            // 2. Asked before, and the user said "no"
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_CODE_ACCOUNTS);
        }else {
            if(firstTimeAccount) { 
                // 1. first time, never asked 
                SharedPreferences.Editor editor = mPreferences.edit();
                editor.putBoolean("firstTimeAccount", false);
                editor.commit();

                // Account permission has not been granted, request it directly.
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS},REQUEST_CODE_ACCOUNTS);
            }else{
                // 3. If you asked a couple of times before, and the user has said "no, and stop asking"

                // Your code
            }
        }
    }

调用 ActivityCompat.requestPermissions 后,如果我们选择了“不再询问”,则权限请求框将不会显示,程序会进入else分支。有没有方法使其再次显示?就像Facebook一样。 - K.Sopheak

1
这里有一种方法可以跟踪权限对话框第一次显示的时间,当用户勾选了“不再询问”并直接拒绝权限时,我们需要保留一个标志来判断是否在onRequestPermissionsResult中获取结果之前已经显示了权限理由对话框。需要时调用checkPermission()方法。
public boolean mPermissionRationaleDialogShown = false;

public void checkPermission() {
    if (ContextCompat.checkSelfPermission(this, "PermissionName")
            != PackageManager.PERMISSION_GRANTED) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")) {
            showPermissionRequiredDialog();
        } else {
            askPermission();
        }
    } else {
       // Permission Granted
    }
}

public void askPermission() {
    ActivityCompat.requestPermissions(this,
            new String[]{"PermissionName"}, permissionRequestCode);
}

public void showPermissionRequiredDialog() {
    mPermissionRationaleDialogShown = true;
    // Dialog to show why permission is required
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == PERMISSION_REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission Granted
        } else {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
                    && !mPermissionRationaleDialogShown) {
                // Permission dialog was shown for first time
            } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
                    && mPermissionRationaleDialogShown){
                // User deny permission without Never ask again checked
            } else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, PERMISSION_READ_EXTERNAL)
                    && mPermissionRationaleDialogShown) {
                // User has checked Never ask again during this permission request
            } else {
                // No permission dialog shown to user has user has previously checked Never ask again. Here we can show dialog to open setting screen to change permission
            }
        }
    } else {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

1
几天来,我一直在努力解决这个问题,最终成功了,并且使用了更新的Jetpack Compose API。
为了区分之前从未询问过的状态和永远不应再次询问的状态,您可以跟踪shouldShowRationaleisGranted的值。 如果:
  • shouldShowRationale的当前值为false,之前的值为true
    并且
    isGranted的当前值为false

我们可以使用mutableStateOfLaunchedEffect来跟踪shouldShowRationale的值。

// Boolean state variable to track if shouldShowRationale changed from true to false
var shouldShowRationaleBecameFalseFromTrue by remember { mutableStateOf(false) }

// Remember the previous value of shouldShowRationale
var prevShouldShowRationale by remember { mutableStateOf(bluetoothPermissionState.status.shouldShowRationale) }

// Track changes in shouldShowRationale
LaunchedEffect(bluetoothPermissionState.status.shouldShowRationale) {
    if (prevShouldShowRationale && !bluetoothPermissionState.status.shouldShowRationale) {
        shouldShowRationaleBecameFalseFromTrue = true
    }
    prevShouldShowRationale = bluetoothPermissionState.status.shouldShowRationale
}

// if shouldShowRationale changed from true to false and the permission is not granted,
// then the user denied the permission and checked the "Never ask again" checkbox
val userDeniedPermission =
    shouldShowRationaleBecameFalseFromTrue && !bluetoothPermissionState.status.isGranted

让我们以accompanist库的示例为例:
@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun FeatureThatRequiresCameraPermission() {

    // Camera permission state
    val cameraPermissionState = rememberPermissionState(
        android.Manifest.permission.CAMERA
    )

    if (cameraPermissionState.status.isGranted) {
        Text("Camera permission Granted")
    } else {
        Column {
            val textToShow = if (cameraPermissionState.status.shouldShowRationale) {
                // If the user has denied the permission but the rationale can be shown,
                // then gently explain why the app requires this permission
                "The camera is important for this app. Please grant the permission."
            } else {
                // If it's the first time the user lands on this feature, or the user
                // doesn't want to be asked again for this permission, explain that the
                // permission is required
                "Camera permission required for this feature to be available. " +
                    "Please grant the permission"
            }
            Text(textToShow)
            Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
                Text("Request permission")
            }
        }
    }
}

让我们用我们的实现来修改它:
@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun FeatureThatRequiresBluetoothPermission() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return

    // Bluetooth permission state
    val bluetoothPermissionState = rememberPermissionState(
        Manifest.permission.BLUETOOTH_CONNECT
    )

    // Boolean state variable to track if shouldShowRationale changed from true to false
    var shouldShowRationaleBecameFalseFromTrue by remember { mutableStateOf(false) }

    // Remember the previous value of shouldShowRationale
    var prevShouldShowRationale by remember { mutableStateOf(bluetoothPermissionState.status.shouldShowRationale) }

    // Track changes in shouldShowRationale
    LaunchedEffect(bluetoothPermissionState.status.shouldShowRationale) {
        if (prevShouldShowRationale && !bluetoothPermissionState.status.shouldShowRationale) {
            shouldShowRationaleBecameFalseFromTrue = true
        }
        prevShouldShowRationale = bluetoothPermissionState.status.shouldShowRationale
    }

    // if shouldShowRationale changed from true to false and the permission is not granted,
    // then the user denied the permission and checked the "Never ask again" checkbox
    val userDeniedPermission =
        shouldShowRationaleBecameFalseFromTrue && !bluetoothPermissionState.status.isGranted


    if (userDeniedPermission) {
        Text(
            "You denied the permission, in order for the app to work properly you need to grant the permission manually." +
                    "Open the app settings and grant the permission manually."
        )
        return
    }

    if (bluetoothPermissionState.status.isGranted) {
        Text("Bluetooth permission Granted")
    } else {
        Column {
            val textToShow = if (bluetoothPermissionState.status.shouldShowRationale) {
                // If the user has denied the permission but the rationale can be shown,
                // then gently explain why the app requires this permission
                "The bluetooth is important for this app. Please grant the permission."
            } else {
                // If it's the first time the user lands on this feature, or the user
                // doesn't want to be asked again for this permission, explain that the
                // permission is required
                "Bluetooth permission required for this feature to be available. " +
                        "Please grant the permission"
            }
            Text(textToShow)
            Button(onClick = { bluetoothPermissionState.launchPermissionRequest() }) {
                Text("Request permission")
            }
        }
    }
}

并且所有这些功能都包含在一个可组合的组件中,该组件能够请求权限、监听权限变化并对其变化做出反应。工作得非常出色。

enter image description here

现在,如果用户多次拒绝权限,系统会提示用户前往设置手动更改权限。

1
关于MLProgrammer-CiM的回答,我有一个解决方案,可以解决用户在SharedPrefrences中存储的布尔值为true后撤销权限的情况。
简单地创建另一个常量布尔值,如果第一个被称为例如:Constant.FIRST_TIME_REQUEST(其默认状态将为true),则第二个将被称为Constant.PERMISSION_ALREADY_GRANTED(其默认状态将为false)。
onRequestPermissionsResult中,如果授予权限,则将其值更改为true。
现在,在您想要进行预先解释的权限请求部分,编写类似以下内容的内容:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
   SharedPreferences sp = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
   boolean isPermissionGranted = sp.getBoolean(Constant.PERMISSION_ALREADY_GRANTED, false);
   if (isPermissionGranted) {
      sp.putBoolean(Constant.PERMISSION_ALREADY_GRANTED, false);
      sp.putBoolean(Constant.FIRST_TIME_REQUEST, true);
   }

   if (ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName) || sp.getBoolean(Constant.FIRST_TIME_REQUEST, true) ) {
   showDialogExplanation();
}
}

那样即使用户取消权限,布尔值也会再次设置为false。祝好运,希望这能有所帮助。Shlo

1

在尝试了所有答案以及互联网上的一些其他帖子后,我知道我必须使用一个共享首选项 isLocationPermissionDialogShown(默认为false),并且一切都按预期工作。

  1. 如果第一次请求权限。在这种情况下,shouldShowRequestPermissionRationale 返回 falseisLocationPermissionDialogShown 也返回 false
  2. 第二次 shouldShowRequestPermissionRationale 返回 true,并在显示对话框时将 isLocationPermissionDialogShown 设置为 true。当我们检查条件时,两者都将是 true
  3. 每次直到选择“不再询问” shouldShowRequestPermissionRationale 返回 trueisLocationPermissionDialogShown 返回 true
  4. 如果选择了“不再询问” shouldShowRequestPermissionRationale 返回 falseisLocationPermissionDialogShown 返回 true。这正是我们需要的。

请检查工作示例。

public class MainActivity extends AppCompatActivity {
    SharedPreferences sharedPreferences;
    String locationPermission;
    String prefLocationPermissionKey = "isLocationPermissionDialogShown";
    private final int PERMISSION_REQUEST_CODE_LOCATION = 1001;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        locationPermission = Manifest.permission.ACCESS_FINE_LOCATION;
        sharedPreferences = getSharedPreferences("configuration", MODE_PRIVATE);

        //check for android version
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //Check for permission
            if (checkSelfPermission(locationPermission) != PackageManager.PERMISSION_GRANTED) {
                //check if clarification dialog should be shown.
                if (shouldShowRequestPermissionRationale(locationPermission)) {
                    showClarificationDialog(locationPermission, PERMISSION_REQUEST_CODE_LOCATION);
                } else  {
                    requestPermissions(new String[] { locationPermission}, PERMISSION_REQUEST_CODE_LOCATION);
                }
            } else {
                Log.d("nets-debug", "permission already grranted");
            }
        }

    }

    @Override
    @TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            //for location permission
            if (requestCode == PERMISSION_REQUEST_CODE_LOCATION) {
                boolean isLocationPermissionDialogShown = sharedPreferences.getBoolean(prefLocationPermissionKey, false);

                if (!shouldShowRequestPermissionRationale(locationPermission) && isLocationPermissionDialogShown) {
                    // user selected Never Ask Again. do something
                    Log.d("nets-debug", "never ask again");
                } else {
                    // all other conditions like first time asked, previously denied etc are captured here and can be extended if required.
                    Log.d("nets-debug", "all other cases");
                }
            }

        }

    }

    @TargetApi(Build.VERSION_CODES.M)
    public void showClarificationDialog(final String permission, final int requestCode) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Permission Required");
        builder.setMessage("Please grant Location permission to use all features of this app");
        builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putBoolean(prefLocationPermissionKey, true);
                editor.apply();
                requestPermissions(new String[] {permission}, requestCode);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), "This permission required", Toast.LENGTH_LONG).show();
            }
        });
        builder.create().show();
    }

}

希望这能有所帮助。

0

终于到我回答CommonsWare的问题的时间了。

业务流程:

1. 当用户第一次点击“拒绝权限”时,我将显示理性对话框以解释权限的必要性。然后,如果用户在理性对话框上点击“取消”按钮,则会显示一个提示,显示消息“请授予获取位置的权限”。

2. 之后,当用户在权限对话框上点击拒绝权限(不再询问)时,我将显示一条消息“请从应用程序设置中授予位置权限”。请注意,我已添加了“从应用程序设置中”这些词,因为用户已经选中了“不再询问”的复选框。

3. 所以从现在开始,权限对话框将不会显示。同时,理性对话框也不会显示。

因此,关键在于如果权限对话框和理性对话框都没有显示,那么就意味着用户已经选中了“不再询问”复选框。

代码:

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)){
                AlertDialogHelper.showDialogWithYesNoCallback(mContext, getString(R.string.confirm), getString(R.string.please_give_permission_to_get_location), new onItemClickReturnBoolean() {
                    @Override
                    public void onItemClick(Boolean status) {
                        if(status){
                            ActivityCompat.requestPermissions(SplashScreenActivity.this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
                        }
                        else{
                            ShowToast.showShortToast(SplashScreenActivity.this,getString(R.string.please_give_permission_to_get_location));
                            finish();
                        }
                    }
                });
            }
            else{
                ActivityCompat.requestPermissions(this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
            }
        }
        else{
            gettingLocationAfterPermissionGranted();
        }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE){
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                gettingLocationAfterPermissionGranted();
            }
            else{
                if(ActivityCompat.shouldShowRequestPermissionRationale(SplashScreenActivity.this,Manifest.permission.ACCESS_FINE_LOCATION)){
                    ShowToast.showShortToast(this,getString(R.string.please_give_permission_to_get_location));
                }
                else{
                    ShowToast.showShortToast(this,getString(R.string.please_give_location_permission_from_app_settings));
                }
                finish();
            }
        }
    }

请查看此代码库: https://github.com/debChowdhury/PermissionHelperEasy

易如反掌



-1

你可以看这里 - 这里有一个流程图,非常好地解释了这个过程。它还解释了何时应该调用shouldShowRequestPermissionRationale()以及何时返回true。

基本上根据Android的文档,如果你没有权限,你应该总是请求权限(如果用户选择不再询问,Android将自动在回调中返回DENIED),如果用户以前拒绝过你但没有标记“不再询问”选项,则应显示一个简短的消息。


这并没有回答问题。 - CommonsWare

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接