Android M 权限:对 shouldShowRequestPermissionRationale() 函数的用法感到困惑
Android M Permissions : Confused on the usage of shouldShowRequestPermissionRationale() function
我正在阅读有关 Android M 中新权限模型的官方文档。它讨论了 shouldShowRequestPermissionRationale()
函数,如果应用程序 returns true
之前已请求此权限,但用户拒绝了该请求。如果用户过去拒绝了权限请求并选择了“不再询问”选项,则此方法 returns false
。
但是我们如何区分以下两种情况呢?
案例一:应用没有权限,之前也没有向用户请求过权限。在这种情况下,shouldShowRequestPermissionRationale() 将 return false 因为这是我们第一次询问用户。
情况 2:用户拒绝了权限并选择了 "Don't ask again",在这种情况下,shouldShowRequestPermissionRationale() 也会 return false。
我想在案例 2 中将用户发送到应用程序的设置页面。我该如何区分这两种情况?
更新
我相信下面 CanC 的 是正确的,应该遵循。唯一可以确定的方法是使用 shouldShowPermissionRationale 在 onRequestPermissionResult 回调中验证这一点。
==
我原来的回答:
我发现的唯一方法是自行跟踪这是否是第一次(例如使用共享首选项)。如果不是第一次,那就用shouldShowRequestPermissionRationale()
来区分。
另见:
按照我的理解,shouldShowRequestPermissionRationale() 在幕后运行了许多用例,并通知应用程序是否显示对所请求权限的解释。
运行 时间权限背后的想法是,大多数时候,用户会对权限请求说“是”。这样,用户只需单击一下。当然,应该在正确的上下文中使用请求 - 即在按下 "Camera" 按钮时请求相机权限。
如果用户拒绝请求,但过了一段时间后再次按下 "Camera" 按钮,shouldShowRequestPermissionRationale() 将 return 为真,因此应用程序可以显示一些有意义的解释为什么请求许可,以及为什么没有许可应用程序将无法正常运行。通常,您会在该对话框中显示 window 稍后拒绝 again/decide 的按钮,以及授予权限的按钮。基本原理对话框中的授予权限按钮,应再次启动权限请求。这次用户还将有一个 "Never show again" 复选框。如果他决定 select 它,并再次拒绝许可,它会通知 Android 系统用户和应用程序不在同一页面上。该操作会产生两个结果 - shouldShowRequestPermissionRationale() 将始终 return false,并且 requestPermissions() 方法不会显示任何对话框,但会直接 return 拒绝 onRequestPermissionsResult 回调。
但还有另一种可能的情况,其中可以使用 onRequestPermissionsResult。例如,某些设备可能具有禁用相机的设备策略(为 CIA、DARPA 等工作)。在这些设备上,onRequestPermissionsResult 将始终 return false,并且 requestPermissions() 方法将静默拒绝请求。
这是我通过与 Android 框架的产品经理 Ben Poiesz 一起收听播客而收集到的内容。
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html
M Preview 1 后,如果第一次显示对话框,则没有不再询问复选框。
如果用户拒绝权限请求,权限对话框中会有不再询问复选框第二次权限是请求。
所以逻辑应该是这样的:
请求许可:
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 {
//Do the stuff that requires permission...
}
检查权限是否在 onRequestPermissionsResult
中被拒绝或授予。
如果之前权限被拒绝,这次权限对话框中会出现不再询问复选框。
调用shouldShowRequestPermissionRationale
查看用户是否勾选了不再询问。 shouldShowRequestPermissionRationale
方法 returns 仅当用户选择 不再询问 或设备策略禁止应用程序拥有该权限时才为 false:
if (grantResults.length > 0){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//Do the stuff that requires permission...
}else if (grantResults[0] == PackageManager.PERMISSION_DENIED){
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//Show permission explanation dialog...
}else{
//Never ask again selected, or device policy prohibits the app from having that permission.
//So, disable that feature, or fall back to another situation...
}
}
}
因此,如果用户勾选了不再询问,您将不必跟踪。
可能对某人有用:--
我注意到,如果我们在 onRequestPermissionsResult() 回调方法中检查 shouldShowRequestPermissionRationale() 标志,它只显示两种状态。
状态 1:-Return true:-- 任何时候用户单击“拒绝权限”(包括第一次)。
状态 2:-Returns false :- 如果用户选择“不再询问”。
.
我遇到了同样的问题,我想通了。为了让生活更简单,我写了一个 util class 来处理运行时权限。
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();
}
});
Case 1: The app doesn't have a permission and the user has not been
asked for the permission before. In this case,
shouldShowRequestPermissionRationale() will return false because this
is the first time we're asking the user.
Case 2: The user has denied the permission and selected "Don't ask
again", in this case too shouldShowRequestPermissionRationale() will
return false.
I would want to send the user to the App's settings page in Case 2.
How do i go about differentiating these two cases?
情况 1 的 onPermissionAsk 和情况 2 的 onPermissionDisabled 回调。
编码愉快:)
只是 post 另一种选择,如果有人愿意的话。您可以使用由 Google 本身提供的 EasyPermissions,如前所述,"Simplify Android M system permissions".
那你就不用直接处理shouldShowRequestPermissionRationale
了。
检查这个实现。对我来说很好用。基本上,您在传递权限列表的 checkPermissions() 方法中检查权限。您在 onRequestPermissionsResult() 上检查权限请求的结果。当用户选择 "never ask again" 或不选择时,该实现让您解决这两种情况。在这个实现中,如果 se 选择 "never ask again",对话框有一个选项可以将他带到应用程序设置 Activity。
所有这些代码都在我的片段中。我在想创建一个专门的 class 会更好,比如 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);
}
此代码要求用户在运行时请求许可,
如果用户允许,它执行结果方法,
如果用户拒绝,它会再次询问用户拒绝的描述(它会再次询问说明),
但如果用户选择不再询问。
它处理不再询问,显示带有说明的打开设置选项。
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;
}
}
}
这样可以吗?
@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;
}
}
}
如果有人对 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)
}
}
}
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);
}
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()
}
}
我正在阅读有关 Android M 中新权限模型的官方文档。它讨论了 shouldShowRequestPermissionRationale()
函数,如果应用程序 returns true
之前已请求此权限,但用户拒绝了该请求。如果用户过去拒绝了权限请求并选择了“不再询问”选项,则此方法 returns false
。
但是我们如何区分以下两种情况呢?
案例一:应用没有权限,之前也没有向用户请求过权限。在这种情况下,shouldShowRequestPermissionRationale() 将 return false 因为这是我们第一次询问用户。
情况 2:用户拒绝了权限并选择了 "Don't ask again",在这种情况下,shouldShowRequestPermissionRationale() 也会 return false。
我想在案例 2 中将用户发送到应用程序的设置页面。我该如何区分这两种情况?
更新
我相信下面 CanC 的
==
我原来的回答:
我发现的唯一方法是自行跟踪这是否是第一次(例如使用共享首选项)。如果不是第一次,那就用shouldShowRequestPermissionRationale()
来区分。
另见:
按照我的理解,shouldShowRequestPermissionRationale() 在幕后运行了许多用例,并通知应用程序是否显示对所请求权限的解释。
运行 时间权限背后的想法是,大多数时候,用户会对权限请求说“是”。这样,用户只需单击一下。当然,应该在正确的上下文中使用请求 - 即在按下 "Camera" 按钮时请求相机权限。
如果用户拒绝请求,但过了一段时间后再次按下 "Camera" 按钮,shouldShowRequestPermissionRationale() 将 return 为真,因此应用程序可以显示一些有意义的解释为什么请求许可,以及为什么没有许可应用程序将无法正常运行。通常,您会在该对话框中显示 window 稍后拒绝 again/decide 的按钮,以及授予权限的按钮。基本原理对话框中的授予权限按钮,应再次启动权限请求。这次用户还将有一个 "Never show again" 复选框。如果他决定 select 它,并再次拒绝许可,它会通知 Android 系统用户和应用程序不在同一页面上。该操作会产生两个结果 - shouldShowRequestPermissionRationale() 将始终 return false,并且 requestPermissions() 方法不会显示任何对话框,但会直接 return 拒绝 onRequestPermissionsResult 回调。
但还有另一种可能的情况,其中可以使用 onRequestPermissionsResult。例如,某些设备可能具有禁用相机的设备策略(为 CIA、DARPA 等工作)。在这些设备上,onRequestPermissionsResult 将始终 return false,并且 requestPermissions() 方法将静默拒绝请求。
这是我通过与 Android 框架的产品经理 Ben Poiesz 一起收听播客而收集到的内容。
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html
M Preview 1 后,如果第一次显示对话框,则没有不再询问复选框。
如果用户拒绝权限请求,权限对话框中会有不再询问复选框第二次权限是请求。
所以逻辑应该是这样的:
请求许可:
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 { //Do the stuff that requires permission... }
检查权限是否在
onRequestPermissionsResult
中被拒绝或授予。如果之前权限被拒绝,这次权限对话框中会出现不再询问复选框。
调用
shouldShowRequestPermissionRationale
查看用户是否勾选了不再询问。shouldShowRequestPermissionRationale
方法 returns 仅当用户选择 不再询问 或设备策略禁止应用程序拥有该权限时才为 false:if (grantResults.length > 0){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED) { //Do the stuff that requires permission... }else if (grantResults[0] == PackageManager.PERMISSION_DENIED){ // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //Show permission explanation dialog... }else{ //Never ask again selected, or device policy prohibits the app from having that permission. //So, disable that feature, or fall back to another situation... } } }
因此,如果用户勾选了不再询问,您将不必跟踪。
可能对某人有用:--
我注意到,如果我们在 onRequestPermissionsResult() 回调方法中检查 shouldShowRequestPermissionRationale() 标志,它只显示两种状态。
状态 1:-Return true:-- 任何时候用户单击“拒绝权限”(包括第一次)。
状态 2:-Returns false :- 如果用户选择“不再询问”。
我遇到了同样的问题,我想通了。为了让生活更简单,我写了一个 util class 来处理运行时权限。
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();
}
});
Case 1: The app doesn't have a permission and the user has not been asked for the permission before. In this case, shouldShowRequestPermissionRationale() will return false because this is the first time we're asking the user.
Case 2: The user has denied the permission and selected "Don't ask again", in this case too shouldShowRequestPermissionRationale() will return false.
I would want to send the user to the App's settings page in Case 2. How do i go about differentiating these two cases?
情况 1 的 onPermissionAsk 和情况 2 的 onPermissionDisabled 回调。
编码愉快:)
只是 post 另一种选择,如果有人愿意的话。您可以使用由 Google 本身提供的 EasyPermissions,如前所述,"Simplify Android M system permissions".
那你就不用直接处理shouldShowRequestPermissionRationale
了。
检查这个实现。对我来说很好用。基本上,您在传递权限列表的 checkPermissions() 方法中检查权限。您在 onRequestPermissionsResult() 上检查权限请求的结果。当用户选择 "never ask again" 或不选择时,该实现让您解决这两种情况。在这个实现中,如果 se 选择 "never ask again",对话框有一个选项可以将他带到应用程序设置 Activity。
所有这些代码都在我的片段中。我在想创建一个专门的 class 会更好,比如 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);
}
此代码要求用户在运行时请求许可, 如果用户允许,它执行结果方法, 如果用户拒绝,它会再次询问用户拒绝的描述(它会再次询问说明), 但如果用户选择不再询问。 它处理不再询问,显示带有说明的打开设置选项。
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;
}
}
}
这样可以吗?
@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;
}
}
}
如果有人对 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)
}
}
}
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);
}
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()
}
}