据此:http://developer.android.com/preview/features/runtime-permissions.html#coding 应用程序可以检查运行时权限并在尚未授予权限的情况下请求权限。然后将显示以下对话框:
https://i.stack.imgur.com/JiZCa.png
如果用户拒绝重要权限,则应用程序应显示需要权限的解释以及拒绝的影响。该对话框有两个选项:
重试(再次请求权限)拒绝(应用程序将在没有该权限的情况下工作)。
但是,如果用户选中 Never ask again
,则不应显示带有说明的第二个对话框,尤其是在用户之前已经拒绝过一次的情况下。现在的问题是:我的应用程序如何知道用户是否检查了 Never ask again
? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
没有给我这些信息。
第二个问题是:Google 是否有计划在权限对话框中加入一条自定义消息来解释应用为什么需要权限?这样就永远不会有第二个对话,这肯定会带来更好的用户体验。
Developer Preview 2 对应用程序请求权限的方式进行了一些更改(另请参阅 http://developer.android.com/preview/support.html#preview2-notes)。
第一个对话框现在看起来像这样:
https://i.stack.imgur.com/T9lCb.png
没有“不再显示”复选框(与开发者预览版 1 不同)。如果用户拒绝该权限并且该权限对应用程序至关重要,则它可以显示另一个对话框来解释应用程序请求该权限的原因,例如:
https://i.stack.imgur.com/299nH.png
如果用户再次拒绝,应用程序要么在绝对需要该权限时关闭,要么继续以有限的功能运行。如果用户重新考虑(并选择重试),则再次请求权限。这次提示看起来像这样:
https://i.stack.imgur.com/RVmdo.png
第二次显示“不再询问”复选框。如果用户再次拒绝并且勾选了复选框,则不会发生任何其他事情。是否勾选复选框可以通过使用Activity.shouldShowRequestPermissionRationale(String) 来确定,例如这样:
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...
这就是 Android 文档所说的 (https://developer.android.com/training/permissions/requesting.html):
为了帮助您找到需要额外解释的情况,系统提供了 Activity.shouldShowRequestPermissionRationale(String) 方法。如果应用程序之前已请求此权限并且用户拒绝了该请求,则此方法返回 true。这表明您可能应该向用户解释您需要该权限的原因。如果用户过去拒绝了权限请求并在权限请求系统对话框中选择了不再询问选项,则此方法返回 false。如果设备策略禁止应用程序拥有该权限,该方法也会返回 false。
要知道用户是否以“不再询问”被拒绝,您可以在用户未授予权限时再次检查 onRequestPermissionsResult 中的 shouldShowRequestPermissionRationale 方法。
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_PERMISSION) {
// for each permission check if the user granted/denied them
// you may want to group the rationale in a single dialog,
// this is just an example
for (int i = 0, len = permissions.length; i < len; i++) {
String permission = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
// user rejected the permission
boolean showRationale = shouldShowRequestPermissionRationale( permission );
if (! showRationale) {
// user also CHECKED "never ask again"
// you can either enable some fall back,
// disable features of your app
// or open another dialog explaining
// again the permission and directing to
// the app setting
} else if (Manifest.permission.WRITE_CONTACTS.equals(permission)) {
showRationale(permission, R.string.permission_denied_contacts);
// user did NOT check "never ask again"
// this is a good place to explain the user
// why you need the permission and ask if he wants
// to accept it (the rationale)
} else if ( /* possibly check more permissions...*/ ) {
}
}
}
}
}
您可以使用以下代码打开您的应用设置:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
无法将用户直接发送到授权页面。
您可以在 onRequestPermissionsResult()
中检查 shouldShowRequestPermissionRationale()
。
https://i.stack.imgur.com/e67lS.jpg
检查是否在 onRequestPermissionsResult()
中授予了权限。如果不是,则检查 shouldShowRequestPermissionRationale()
。
如果此方法返回 true,则说明为什么需要此特定权限。然后根据用户的选择再次requestPermissions()。如果它返回 false 则显示一条错误消息,指出未授予权限并且应用程序无法继续进行或禁用特定功能。
下面是示例代码。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case STORAGE_PERMISSION_REQUEST:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted :)
downloadFile();
} else {
// permission was not granted
if (getActivity() == null) {
return;
}
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showStoragePermissionRationale();
} else {
Snackbar snackbar = Snackbar.make(getView(), getResources().getString(R.string.message_no_storage_permission_snackbar), Snackbar.LENGTH_LONG);
snackbar.setAction(getResources().getString(R.string.settings), new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getActivity() == null) {
return;
}
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
intent.setData(uri);
OrderDetailFragment.this.startActivity(intent);
}
});
snackbar.show();
}
}
break;
}
}
显然,谷歌地图正是为了获得位置许可而这样做的。
这是一个检查当前权限状态的好方法:
@Retention(RetentionPolicy.SOURCE)
@IntDef({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED })
public @interface PermissionStatus {}
public static final int GRANTED = 0;
public static final int DENIED = 1;
public static final int BLOCKED_OR_NEVER_ASKED = 2;
@PermissionStatus
public static int getPermissionStatus(Activity activity, String androidPermissionName) {
if(ContextCompat.checkSelfPermission(activity, androidPermissionName) != PackageManager.PERMISSION_GRANTED) {
if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName)){
return BLOCKED_OR_NEVER_ASKED;
}
return DENIED;
}
return GRANTED;
}
警告:返回 BLOCKED_OR_NEVER_ASKED 第一个应用程序启动,在用户通过用户提示接受/拒绝权限之前(在 sdk 23+ 设备上)
更新:
Android 支持库现在似乎也有一个非常相似的类 android.support.v4.content.PermissionChecker
,其中包含一个 checkSelfPermission()
,它返回:
public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;
BLOCKED_OR_NEVER_ASKED
。
android.content.pm
已经定义了 PERMISSION_GRANTED = 0
和 PERMISSION_DENIED = -1
。也许设置 BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1
或其他什么?
一旦用户标记了“不再询问”,就不能再次显示该问题。但可以向用户解释,他之前拒绝了权限,必须在设置中授予权限。并参考他的设置,使用以下代码:
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// now, you have permission go ahead
// TODO: something
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.READ_CALL_LOG)) {
// now, user has denied permission (but not permanently!)
} else {
// now, user has denied permission permanently!
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "You have previously declined this permission.\n" +
"You must approve this permission in \"Permissions\" in the app settings on your device.", Snackbar.LENGTH_LONG).setAction("Settings", new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
}
});
View snackbarView = snackbar.getView();
TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
textView.setMaxLines(5); //Or as much as you need
snackbar.show();
}
}
return;
}
您可以通过检查是否在 onRequestPermissionsResult()
回调方法中显示权限 rationale 来确定。如果您发现任何权限设置为不再询问,您可以请求用户从设置中授予权限。
我的完整实现如下所示。它适用于单个或多个权限请求。使用以下或 directly use my library.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(permissions.length == 0){
return;
}
boolean allPermissionsGranted = true;
if(grantResults.length>0){
for(int grantResult: grantResults){
if(grantResult != PackageManager.PERMISSION_GRANTED){
allPermissionsGranted = false;
break;
}
}
}
if(!allPermissionsGranted){
boolean somePermissionsForeverDenied = false;
for(String permission: permissions){
if(ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
//denied
Log.e("denied", permission);
}else{
if(ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED){
//allowed
Log.e("allowed", permission);
} else{
//set to never ask again
Log.e("set to never ask again", permission);
somePermissionsForeverDenied = true;
}
}
}
if(somePermissionsForeverDenied){
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("Permissions Required")
.setMessage("You have forcefully denied some of the required permissions " +
"for this action. Please open settings, go to permissions and allow them.")
.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getPackageName(), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setCancelable(false)
.create()
.show();
}
} else {
switch (requestCode) {
//act according to the request code used while requesting the permission(s).
}
}
}
可能对某人有用:--
我注意到的是,如果我们将 shouldShowRequestPermissionRationale() 标志检查到 onRequestPermissionsResult() 回调方法中,它只显示两种状态。
状态 1:-返回 true:-- 任何时候用户单击拒绝权限(包括第一次)。
状态 2:-返回 false :- 如果用户选择“不再询问”。
Link of detailed working example
onRequestPermissionsResult
中处理这个,而不是在实际请求权限时。
如果您想检测所有“状态”(第一次被拒绝、刚刚被拒绝、刚刚被“不再询问”或永久拒绝),您可以执行以下操作:
创建 2 个布尔值:
private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;
在请求许可之前设置第一个:
beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);
在 onRequestPermissionsResult
方法中设置第二个:
afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);
使用以下“真值表”在 onRequestPermissionsResult()
中执行您需要的任何操作(在检查您仍然没有权限之后):
// before after
// FALSE FALSE = Was denied permanently, still denied permanently --> App Settings
// FALSE TRUE = First time deny, not denied permanently yet --> Nothing
// TRUE FALSE = Just been permanently denied --> Changing my caption to "Go to app settings to edit permissions"
// TRUE TRUE = Wasn't denied permanently, still not denied permanently --> Nothing
// TRUE FALSE
也发生在用户在之前拒绝后允许权限时。
afterClickPermissionRat
为假时,它会被永久拒绝。
shouldShowRequestPermissionRationale()
的未记录行为,并且可能(s/may/will)在未来的 Android 版本中被破坏。
我有同样的问题,我想通了。为了让生活更简单,我编写了一个 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();
}
});
我的应用程序如何知道用户是否选中了“不再询问”?
如果用户选中“不再询问”,您将在 onPermissionDisabled 上收到回调。
快乐编码:)
shouldShowRequestPermissionRationale
返回的第一个错误值的唯一方法,将发送给用户的请求保存到首选项。我有同样的想法并找到了你的答案。干得好人
方法 shouldShowRequestPermissionRationale() 可用于检查用户是否选择了“不再询问”选项并拒绝了权限。有很多代码示例,所以我宁愿解释如何将它用于这样的目的,因为我认为它的名称和它的实现使这比实际上更复杂。
如 Requesting Permissions at Run Time 中所述,如果“不再询问”选项可见,则该方法返回 true,否则返回 false;因此它在第一次显示对话框时返回 false,然后从第二次开始返回 true,并且只有当用户拒绝选择该选项的权限时,它才会再次返回 false。
要检测这种情况,您可以检测序列 false-true-false,或者(更简单)您可以有一个标志来跟踪显示对话框的初始时间。之后,该方法返回 true 或 false,其中 false 将允许您检测何时选择了该选项。
每个许可案例的完整解释
/**
* Case 1: User doesn't have permission
* Case 2: User has permission
*
* Case 3: User has never seen the permission Dialog
* Case 4: User has denied permission once but he din't clicked on "Never Show again" check box
* Case 5: User denied the permission and also clicked on the "Never Show again" check box.
* Case 6: User has allowed the permission
*
*/
public void handlePermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// This is Case 1. Now we need to check further if permission was shown before or not
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// This is Case 4.
} else {
// This is Case 3. Request for permission here
}
} else {
// This is Case 2. You have permission now you can do anything related to it
}
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// This is Case 2 (Permission is now granted)
} else {
// This is Case 1 again as Permission is not granted by user
//Now further we check if used denied permanently or not
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// case 4 User has denied permission but not permanently
} else {
// case 5. Permission denied permanently.
// You can open Permission setting's page from here now.
}
}
}
您可以使用
shouldShowRequestPermissionRationale()
里面
onRequestPermissionsResult()
请参见下面的示例:
检查用户点击按钮时是否有权限:
@Override
public void onClick(View v) {
if (v.getId() == R.id.appCompatBtn_changeProfileCoverPhoto) {
if (Build.VERSION.SDK_INT < 23) { // API < 23 don't need to ask permission
navigateTo(MainActivity.class); // Navigate to activity to change photos
} else {
if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted yet. Ask for permission...
requestWriteExternalPermission();
} else {
// Permission is already granted, good to go :)
navigateTo(MainActivity.class);
}
}
}
}
当用户回答权限对话框时,我们将转到 onRequestPermissionResult:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {
// Case 1. Permission is granted.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
// Before navigating, I still check one more time the permission for good practice.
navigateTo(MainActivity.class);
}
} else { // Case 2. Permission was refused
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Case 2.1. shouldShowRequest... returns true because the
// permission was denied before. If it is the first time the app is running we will
// end up in this part of the code. Because he need to deny at least once to get
// to onRequestPermissionsResult.
Snackbar snackbar = Snackbar.make(findViewById(R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);
snackbar.setAction("VERIFY", new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(SettingsActivity.this
, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
, WRITE_EXTERNAL_PERMISSION_REQUEST_CODE);
}
});
snackbar.show();
} else {
// Case 2.2. Permission was already denied and the user checked "Never ask again".
// Navigate user to settings if he choose to allow this time.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.instructions_to_turn_on_storage_permission)
.setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
settingsIntent.setData(uri);
startActivityForResult(settingsIntent, 7);
}
})
.setNegativeButton(getString(R.string.not_now), null);
Dialog dialog = builder.create();
dialog.show();
}
}
}
}
确定是否已阻止请求任意权限的有用函数(在 Kotlin 中):
private fun isPermissionBlockedFromAsking(activity: Activity, permission: String): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
&& !activity.shouldShowRequestPermissionRationale(permission)
&& PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
}
return false
}
使用此功能需要在您首次请求权限时将共享首选项布尔值设置为 true
,并将您所需权限的名称(例如 android.Manifest.permission.READ_PHONE_STATE
)设置为 true
。
解释:
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
,因为某些代码只能在 API 级别 23+ 上运行。
ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
检查我们是否还没有该权限。
!activity.shouldShowRequestPermissionRationale(permission)
检查用户是否拒绝了应用再次询问。由于 quirks of this function,还需要以下行。
PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
用于区分“从不询问”和“不再询问”状态(以及在第一次权限请求时将值设置为 true),因为上一行不返回此信息。
请不要为了这个解决方案向我扔石头。
这可行,但有点“hacky”。
当您调用 requestPermissions
时,注册当前时间。
mAskedPermissionTime = System.currentTimeMillis();
然后在 onRequestPermissionsResult
如果结果未通过,请再次检查时间。
if (System.currentTimeMillis() - mAskedPermissionTime < 100)
由于用户不可能这么快点击拒绝按钮,我们知道他选择了“不再询问”,因为回调是即时的。
使用风险自负。
我发现了许多冗长而令人困惑的答案,在阅读了一些答案后,我的结论是
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_EXTERNAL_STORAGE))
Toast.makeText(this, "permanently denied", Toast.LENGTH_SHORT).show();
OnRequestPermissionResult-free 和 shouldShowRequestPermissionRationale-free 方法:
public static void requestDangerousPermission(AppCompatActivity activity, String permission) {
if (hasPermission(activity, permission)) return;
requestPermission();
new Handler().postDelayed(() -> {
if (activity.getLifecycle().getCurrentState() == Lifecycle.State.RESUMED) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
}
}, 250);
}
如果没有弹出权限,则在 250 毫秒后打开设备设置(如果选择了“不再询问”,则会出现这种情况。
shouldShowRequestPermissionRationale
根据先前权限请求中的用户偏好返回 true 或 false。
如果用户刚刚拒绝许可(不是永远),shouldShowRequestPermissionRationale
将返回 true
。如果永远拒绝许可,则返回 false
。诀窍是即使用户允许权限,shouldShowRequestPermissionRationale
也会返回 false
。
所以我们可以结合这两个条件来选择never ask again or not。
因此,如果用户不允许权限并且 shouldShowRequestPermissionRationale
返回 false
,则意味着用户选择不再请求权限。
https://stackoverflow.com/a/58114769/5151336
shouldShowRequestPermissionRationale
将返回 false。即使用户没有永久拒绝该权限。
我在 Android M 中编写了权限请求的简写。这段代码还处理了与旧 Android 版本的向后兼容性。
所有丑陋的代码都被提取到一个 Fragment 中,该 Fragment 将自身附加和分离到请求权限的 Activity。您可以使用 PermissionRequestManager
,如下所示:
new PermissionRequestManager()
// We need a AppCompatActivity here, if you are not using support libraries you will have to slightly change
// the PermissionReuqestManager class
.withActivity(this)
// List all permissions you need
.withPermissions(android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_CALENDAR)
// This Runnable is called whenever the request was successfull
.withSuccessHandler(new Runnable() {
@Override
public void run() {
// Do something with your permissions!
// This is called after the user has granted all
// permissions, we are one a older platform where
// the user does not need to grant permissions
// manually, or all permissions are already granted
}
})
// Optional, called when the user did not grant all permissions
.withFailureHandler(new Runnable() {
@Override
public void run() {
// This is called if the user has rejected one or all of the requested permissions
L.e(this.getClass().getSimpleName(), "Unable to request permission");
}
})
// After calling this, the user is prompted to grant the rights
.request();
看看:https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa
相反,当您在 shouldShowRequestPermissionRationale()
的错误条件下再次请求许可时,您将在 onRequestPermissionsResult()
上收到作为 PERMISSION_DENIED 的回调
来自安卓文档:
当系统要求用户授予权限时,用户可以选择告诉系统不要再次请求该权限。在这种情况下,只要应用程序使用 requestPermissions()
再次请求该权限,系统就会立即拒绝该请求。系统调用您的 onRequestPermissionsResult()
回调方法并传递 PERMISSION_DENIED
,就像用户再次明确拒绝您的请求时一样。这意味着当您调用 requestPermissions()
时,您不能假设与用户发生了任何直接交互。
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
if (grantResults.length > 0) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Denied
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// To what you want
} else {
// Bob never checked click
}
}
}
}
}
}
试试这个简单的权限库。它将通过 3 个简单的步骤处理与权限相关的所有操作。它节省了我的时间。您可以在 15 分钟内完成所有与权限相关的工作。
它可以处理拒绝,它可以处理不再询问,它可以调用应用程序设置以获得权限,它可以给出一个 Rational 消息,它可以给出一个拒绝消息,它可以给出一个接受的权限列表,它可以给出一个拒绝列表权限等
https://github.com/ParkSangGwon/TedPermission
第 1 步:添加您的依赖项
dependencies {
compile 'gun0912.ted:tedpermission:2.1.1'
//check the above link for latest libraries
}
Step2:询问权限
TedPermission.with(this)
.setPermissionListener(permissionlistener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
.check();
第三步:处理权限响应
PermissionListener permissionlistener = new PermissionListener() {
@Override
public void onPermissionGranted() {
Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionDenied(ArrayList<String> deniedPermissions) {
Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
}
};
你可以听得很漂亮。
听众
interface PermissionListener {
fun onNeedPermission()
fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
fun onPermissionDisabledPermanently(numberDenyPermission: Int)
fun onPermissionGranted()
}
MainClass 获得许可
class PermissionUtil {
private val PREFS_FILENAME = "permission"
private val TAG = "PermissionUtil"
private fun shouldAskPermission(context: Context, permission: String): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val permissionResult = ActivityCompat.checkSelfPermission(context, permission)
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
return true
}
}
return false
}
fun checkPermission(context: Context, permission: String, listener: PermissionListener) {
Log.i(TAG, "CheckPermission for $permission")
if (shouldAskPermission(context, permission)) {
// Load history permission
val sharedPreference = context.getSharedPreferences(PREFS_FILENAME, 0)
val numberShowPermissionDialog = sharedPreference.getInt(permission, 0)
if (numberShowPermissionDialog == 0) {
(context as? Activity)?.let {
if (ActivityCompat.shouldShowRequestPermissionRationale(it, permission)) {
Log.e(TAG, "User has denied permission but not permanently")
listener.onPermissionPreviouslyDenied(numberShowPermissionDialog)
} else {
Log.e(TAG, "Permission denied permanently.")
listener.onPermissionDisabledPermanently(numberShowPermissionDialog)
}
} ?: kotlin.run {
listener.onNeedPermission()
}
} else {
// Is FirstTime
listener.onNeedPermission()
}
// Save history permission
sharedPreference.edit().putInt(permission, numberShowPermissionDialog + 1).apply()
} else {
listener.onPermissionGranted()
}
}
}
以这种方式使用
PermissionUtil().checkPermission(this, Manifest.permission.ACCESS_FINE_LOCATION,
object : PermissionListener {
override fun onNeedPermission() {
log("---------------------->onNeedPermission")
// ActivityCompat.requestPermissions(this@SplashActivity,
// Array(1) { Manifest.permission.ACCESS_FINE_LOCATION },
// 118)
}
override fun onPermissionPreviouslyDenied(numberDenyPermission: Int) {
log("---------------------->onPermissionPreviouslyDenied")
}
override fun onPermissionDisabledPermanently(numberDenyPermission: Int) {
log("---------------------->onPermissionDisabled")
}
override fun onPermissionGranted() {
log("---------------------->onPermissionGranted")
}
})
在活动或片段网络中覆盖 onRequestPermissionsResult
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (requestCode == 118) {
if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getLastLocationInMap()
}
}
}
为了准确回答这个问题,当用户按下“不再询问”时会发生什么?
被覆盖的方法/函数
onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)
grantResult 数组结果是空的,所以你可以在那里做点什么吗?但不是最佳实践。
如何处理“不再询问”?
我正在使用 Fragment,它需要 READ_EXTERNAL_STORAGE 权限。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
when {
isReadPermissionsGranted() -> {
/**
* Permissions has been Granted
*/
getDirectories()
}
isPermissionDeniedBefore() -> {
/**
* User has denied before, explain why we need the permission and ask again
*/
updateUIForDeniedPermissions()
checkIfPermissionIsGrantedNow()
}
else -> {
/**
* Need to ask For Permissions, First Time
*/
checkIfPermissionIsGrantedNow()
/**
* If user selects, "Dont Ask Again" it will never ask again! so just update the UI for Denied Permissions
*/
updateUIForDeniedPermissions()
}
}
}
其他功能是微不足道的。
// Is Read Write Permissions Granted
fun isReadWritePermissionGranted(context: Context): Boolean {
return (ContextCompat.checkSelfPermission(
context as Activity,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED) and
(ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED)
}
fun isReadPermissionDenied(context: Context) : Boolean {
return ActivityCompat.shouldShowRequestPermissionRationale(
context as Activity,
PermissionsUtils.READ_EXTERNAL_STORAGE_PERMISSIONS)
}
This sample demonstrates how to handle when the user selects "DENY & DON'T ASK AGAIN"
https://i.stack.imgur.com/dsS2B.gif
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerStoragePermission()
registerGalleryLauncher()
registerCameraPermission()
registerCameraLauncher()
}
private fun registerCameraPermission() {
requestCameraPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
Log.d(TAG, "registerCameraPermission - Camera Permission Granted")
openCamera()
} else {
Log.d(TAG, "registerCameraPermission - Camera Permission NOT Granted")
requestCameraPermission()
}
}
}
private fun registerStoragePermission() {
requestStoragePermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
Log.d(TAG, "registerStoragePermission - Storage Permission Granted")
viewGallery()
} else {
Log.d(TAG, "registerStoragePermission - Storage Permission NOT Granted")
requestStoragePermission()
}
}
}
private fun registerCameraLauncher() {
cameraLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
if (data == null) {
return@registerForActivityResult
}
val extras = data.extras
imageBitmap = extras!!["data"] as Bitmap
file = FileUtils.createFile(requireContext(),
getString(R.string.app_name),
"my_profile_image.png"
)
//FileUtils.saveBitmap(imageBitmap, file);
val imageLocalPath = FileUtils.saveImageToInternalStorage(file, imageBitmap)
SharedPreferencesUtils.setProfilePath(requireActivity(), imageLocalPath)
profileFragmentBinding.imageViewCircleNoStroke.setImageBitmap(imageBitmap)
profileFragmentBinding.imageViewCircleNoStroke.setScaleType(ImageView.ScaleType.CENTER_CROP)
}
}
}
private fun registerGalleryLauncher() {
galleryLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
if (data == null) {
return@registerForActivityResult
}
val uri = data.data
var imageLocalPath = File(FileUtils.getPathReal(requireActivity(), uri!!))
file = imageLocalPath.absoluteFile
SharedPreferencesUtils.setProfilePath(requireActivity(), imageLocalPath.absolutePath)
Glide.with(requireActivity()).load(uri)
.into(profileFragmentBinding.imageViewCircleNoStroke)
profileFragmentBinding.imageViewCircleNoStroke.setScaleType(ImageView.ScaleType.CENTER_CROP)
}
}
}
private fun showImageUploadOptions() {
val mDialog = activity.let { Dialog(it!!) }
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
mDialog.setContentView(R.layout.dialog_profile_image_option)
mDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
//val mAlertMessageTv = mDialog.findViewById<View>(R.id.id_alert_tv) as TextView
//mAlertMessageTv.text = message
galleryLl = mDialog.findViewById<View>(R.id.id_gallery_ll) as LinearLayout
cameraLl = mDialog.findViewById<View>(R.id.id_camera_ll) as LinearLayout
removePhotoLl = mDialog.findViewById<View>(R.id.id_remove_photo_ll) as LinearLayout
galleryLl.setOnClickListener {
CallStoragePermission()
mDialog.dismiss()
}
cameraLl.setOnClickListener {
CallCameraPermission()
mDialog.dismiss()
}
removePhotoLl.setOnClickListener {
CallRemovePhoto()
mDialog.dismiss()
}
mDialog.setCancelable(true)
mDialog.show()
val metrics = resources.displayMetrics
val width = metrics.widthPixels
val height = metrics.heightPixels
mDialog.window!!.setLayout(
width,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
fun CallStoragePermission() {
if (!Status_checkReadExternalStoragePermission()) {
requestStoragePermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
} else {
viewGallery()
}
}
private fun Status_checkReadExternalStoragePermission(): Boolean {
val permissionState = ActivityCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.READ_EXTERNAL_STORAGE
)
return permissionState == PackageManager.PERMISSION_GRANTED
}
private fun requestCameraPermission() {
when {
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
Log.d(TAG, "requestCameraPermission - Camera Permission Granted")
openCamera()
// The permission is granted
// you can go with the flow that requires permission here
}
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
// This case means user previously denied the permission
// So here we can display an explanation to the user
// That why exactly we need this permission
Log.d(TAG, "requestCameraPermission - Camera Permission NOT Granted")
showPermissionAlert(
getString(R.string.camera_permission),
getString(R.string.camera_permission_denied),
getString(R.string.ok_caps),
getString(R.string.cancel_caps)
) { requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) }
}
else -> {
// Everything is fine you can simply request the permission
showPermissionAlert(
getString(R.string.camera_permission),
getString(R.string.camera_permission_denied),
getString(R.string.settings_caps),
getString(R.string.cancel_caps)
) {
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package",
BuildConfig.APPLICATION_ID, null
)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
}
}
}
private fun requestStoragePermission() {
when {
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED -> {
Log.d(TAG, "requestStoragePermission - Storage Permission Granted")
viewGallery()
// The permission is granted
// you can go with the flow that requires permission here
}
shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE) -> {
// This case means user previously denied the permission
// So here we can display an explanation to the user
// That why exactly we need this permission
Log.d(TAG, "requestStoragePermission - Storage Permission NOT Granted")
showPermissionAlert(
getString(R.string.read_storage_permission_required),
getString(R.string.storage_permission_denied),
getString(R.string.ok_caps),
getString(R.string.cancel_caps)
) { requestStoragePermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) }
}
else -> {
// Everything is fine you can simply request the permission
showPermissionAlert(
getString(R.string.read_storage_permission_required),
getString(R.string.storage_permission_denied),
getString(R.string.settings_caps),
getString(R.string.cancel_caps)
) {
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package",
BuildConfig.APPLICATION_ID, null
)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
}
}
}
private fun showPermissionAlert(
title: String,
message: String,
ok: String,
cancel: String,
function: () -> Unit
) {
val mDialog = requireActivity().let { Dialog(it) }
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
mDialog.setContentView(R.layout.dialog_permission_alert)
mDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val mTitleTv = mDialog.findViewById<View>(R.id.id_title_tv) as AppCompatTextView
mTitleTv.text = title
val mMessageTv = mDialog.findViewById<View>(R.id.id_message_tv) as AppCompatTextView
mMessageTv.text = message
val mNoBtn = mDialog.findViewById<View>(R.id.no_btn) as AppCompatTextView
mNoBtn.text = cancel
val mYesBtn = mDialog.findViewById<View>(R.id.yes_btn) as AppCompatTextView
mYesBtn.text = ok
mYesBtn.setOnClickListener {
function.invoke()
mDialog.dismiss()
}
mNoBtn.setOnClickListener { mDialog.dismiss() }
mDialog.setCancelable(true)
mDialog.show()
val metrics = resources.displayMetrics
val width = metrics.widthPixels
val height = metrics.heightPixels
mDialog.window!!.setLayout(
width,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
fun viewGallery() {
val intentDocument = Intent(Intent.ACTION_GET_CONTENT)
intentDocument.type = "image/*"
intentDocument.putExtra(
Constants.REQUEST_CODE,
Constants.REQUEST_PHOTO_FROM_GALLERY
)
galleryLauncher.launch(intentDocument)
}
fun openCamera() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePictureIntent.putExtra(
Constants.REQUEST_CODE,
Constants.REQUEST_PERMISSIONS_REQUEST_CODE_CAMERA
)
cameraLauncher.launch(takePictureIntent)
}
fun CallCameraPermission() {
if (!Status_checkCameraPermission()) {
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
} else {
openCamera()
}
}
private fun Status_checkCameraPermission(): Boolean {
val camera = ActivityCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.CAMERA
)
return camera == PackageManager.PERMISSION_GRANTED
}
我想出这一点的方式对我来说有点新。如果用户曾经选择过一个决定,我必须保留参考。这样,如果没有授予权限,我可以告诉用户第一次在那里并且应该提示看到权限弹出,或者用户暂时或永久拒绝它。
伪代码:
if( granted ) {
// you are set
} else if( requiresRationale() ) {
// in the ui let the user know he has to tap and launch permission
button.onSetClickListener { requestPermission() }
} else if( sharedPreferences.getBoolean("permission", false) ) {
// so user has already decided to deny permission, then it is permanent
launchAppSettings()
} else {
// user's first encounter, request permission
requestPermission()
}
demo 以 gif 格式附在自述文件中。 https://github.com/juanmendez/android-sdk-updates/tree/api/android-permissions/single
我还想获取用户是否选择“不再询问”的信息。我已经用一个丑陋的旗帜实现了“几乎解决”,但在我告诉你如何之前,我会告诉你我的动机:
我想最初提供权限引用功能。如果用户使用它并且没有权限,他/她将获得上面的第 1 个对话或第 2 个和第 3 个对话。当用户选择“不再询问”时,我想禁用该功能并以不同的方式显示它。 - 我的动作是由微调文本条目触发的,我还想在显示的标签文本中添加“(权限撤销)”。这向用户显示:“有功能,但由于我的权限设置,我无法使用它。”但是,这似乎是不可能的,因为我无法检查是否选择了“不再询问”。
我找到了一个我可以接受的解决方案,方法是始终通过主动权限检查启用我的功能。如果出现否定响应,我会在 onRequestPermissionsResult() 中显示 Toast 消息,但前提是我没有显示我的自定义理由弹出窗口。因此,如果用户选择了“不再询问”,他只会收到一条祝酒消息。如果用户不愿意选择“不再询问”,他只会得到操作系统弹出的自定义理由和权限请求,而不是 toast,因为连续三个通知会太痛苦。
我必须为相机实现动态权限。出现 3 种可能的情况:1. 允许,2. 拒绝,3. 不再询问。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (String permission : permissions) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permission)) {
//denied
Log.e("denied", permission);
} else {
if (ActivityCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED) {
//allowed
Log.e("allowed", permission);
} else {
//set to never ask again
Log.e("set to never ask again", permission);
//do something here.
}
}
}
if (requestCode != MaterialBarcodeScanner.RC_HANDLE_CAMERA_PERM) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mScannerView.setResultHandler(this);
mScannerView.startCamera(mCameraId);
mScannerView.setFlash(mFlash);
mScannerView.setAutoFocus(mAutoFocus);
return;
} else {
//set to never ask again
Log.e("set to never ask again", permissions[0]);
}
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Error")
.setMessage(R.string.no_camera_permission)
.setPositiveButton(android.R.string.ok, listener)
.show();
}
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.CAMERA);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA},
REQUEST_CODE_ASK_PERMISSIONS);
return;
}
mScannerView.setResultHandler(this);
mScannerView.startCamera(mCameraId);
mScannerView.setFlash(mFlash);
mScannerView.setAutoFocus(mAutoFocus);
}
private int checkSelfPermission(String camera) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
return REQUEST_CODE_ASK_PERMISSIONS;
} else {
return REQUEST_NOT_CODE_ASK_PERMISSIONS;
}
}
您可以使用 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
方法来检测是否选中了 never ask。
更多参考:Check this
要检查多个权限,请使用:
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
showDialogOK("Service Permissions are 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.
finish();
break;
}
}
});
}
//permission is denied (and never ask again is checked)
//shouldShowRequestPermissionRationale will return false
else {
explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
// //proceed with logic by disabling the related features or quit the app.
}
解释()方法
private void explain(String msg){
final android.support.v7.app.AlertDialog.Builder dialog = new android.support.v7.app.AlertDialog.Builder(this);
dialog.setMessage(msg)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
// permissionsclass.requestPermission(type,code);
startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
finish();
}
});
dialog.show();
}
上面的代码还将显示对话框,该对话框会将用户重定向到应用程序设置屏幕,如果已选中不再询问按钮,他可以从该屏幕授予权限。
扩展上面 mVck 的答案,以下逻辑确定是否已针对给定的权限请求检查“不再询问”:
bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
!bStorage && (
_bStorageRationaleBefore == true && _bStorageRationaleAfter == false ||
_bStorageRationaleBefore == false && _bStorageRationaleAfter == false
);
摘自下文(完整示例参见此 answer)
private bool _bStorageRationaleBefore;
private bool _bStorageRationaleAfter;
private const int ANDROID_PERMISSION_REQUEST_CODE__SDCARD = 2;
//private const int ANDROID_PERMISSION_REQUEST_CODE__CAMERA = 1;
private const int ANDROID_PERMISSION_REQUEST_CODE__NONE = 0;
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case ANDROID_PERMISSION_REQUEST_CODE__SDCARD:
_bStorageRationaleAfter = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
!bStorage && (
_bStorageRationaleBefore == true && _bStorageRationaleAfter == false ||
_bStorageRationaleBefore == false && _bStorageRationaleAfter == false
);
break;
}
}
private List<string> GetRequiredPermissions(out int requestCode)
{
// Android v6 requires explicit permission granting from user at runtime for security reasons
requestCode = ANDROID_PERMISSION_REQUEST_CODE__NONE; // 0
List<string> requiredPermissions = new List<string>();
_bStorageRationaleBefore = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
Permission writeExternalStoragePerm = ApplicationContext.CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
//if(extStoragePerm == Permission.Denied)
if (writeExternalStoragePerm != Permission.Granted)
{
requestCode |= ANDROID_PERMISSION_REQUEST_CODE__SDCARD;
requiredPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
}
return requiredPermissions;
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Android v6 requires explicit permission granting from user at runtime for security reasons
int requestCode;
List<string> requiredPermissions = GetRequiredPermissions(out requestCode);
if (requiredPermissions != null && requiredPermissions.Count > 0)
{
if (requestCode >= ANDROID_PERMISSION_REQUEST_CODE__SDCARD)
{
_savedInstanceState = savedInstanceState;
RequestPermissions(requiredPermissions.ToArray(), requestCode);
return;
}
}
}
OnCreate2(savedInstanceState);
}
我有点晚了,我也遇到过类似的问题。解决了这个问题如下
假设您想要位置权限
请求权限启动器
private final ActivityResultLauncher<String> requestPermissionLauncher =
registerForActivityResult(
new RequestPermission(),
isGranted -> {
if (isGranted) {
// Permission is granted go ahead
} else {
shouldShowRequestPermissionRationale();
}
});
权限检查
private boolean hasPermissions() {
if (checkSelfPermission(requireActivity(), ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) {
// Permission is granted go ahead
} else {
requestPermissionLauncher.launch(ACCESS_FINE_LOCATION);
}
}
检查是否需要显示权限理性/自定义对话框来教育用户
private void shouldShowRequestPermissionRationale() {
if (!shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
// need to show permission rational custom dialog.
}
}