Android M -检查运行时权限-如何确定用户是否检查了“永不再问”?

根据http://developer.android.com/preview/features/runtime-permissions.html#coding,应用程序可以检查运行时权限,如果还没有被授予权限,则可以请求权限。弹出如下对话框:

enter image description here

如果用户拒绝一个重要的权限,在我看来,应用程序应该显示一个解释为什么需要权限和什么影响拒绝。该对话框有两个选项:

  1. 重试(再次请求许可)
  2. 拒绝(应用程序将工作没有该许可)。
但是,如果用户检查了Never ask again,则不应该显示带有解释的第二个对话框,特别是如果用户之前已经拒绝了一次。 现在的问题是:我的应用程序如何知道用户是否选中了Never ask again?在我看来,onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)没有给我这个信息

第二个问题是:谷歌是否计划在权限对话框中包含一个自定义消息,以解释为什么应用程序需要权限?这样就不会出现第二个对话框,这肯定会带来更好的用户体验。

242835 次浏览

开发者预览2对应用程序请求权限的方式进行了一些更改(另见http://developer.android.com/preview/support.html#preview2-notes)。

第一个对话框现在看起来像这样:

enter image description here

没有“永不再次显示”复选框(不像开发人员预览1)。如果用户拒绝权限,如果权限是必不可少的应用程序,它可以显示另一个对话框来解释应用程序请求该权限的原因,例如:

enter image description here

如果用户再次拒绝,应用程序应该关闭(如果它绝对需要该权限)或继续运行有限的功能。如果用户重新考虑(并选择重试),则再次请求权限。这次的提示符是这样的:

enter image description here

第二次“永不再问”复选框显示。如果用户再次拒绝并且勾选复选框,就不会再发生任何事情。 是否勾选复选框可以通过使用Activity.shouldShowRequestPermissionRationale(String)来确定,例如:

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...

这就是Android文档所说的(https://developer.android.com/training/permissions/requesting.html):

帮助你找到需要额外提供的情况 说明,系统提供的 Activity.shouldShowRequestPermissionRationale (String)方法。这 如果应用程序已请求此权限,则方法返回true 之前和用户拒绝了请求。这表明你 可能应该向用户解释为什么需要权限。

如果用户在过去拒绝了权限请求并选择 权限请求系统对话框中的Don't ask again选项, 这个方法返回false。该方法还返回false,如果设备 策略禁止应用程序拥有该权限。< / p >

要知道用户是否拒绝了“never ask again”,你可以在用户没有授予权限时再次检查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);

无法将用户直接发送到授权页面。

我还想知道用户是否选择了“never ask again”。我已经实现了一个“几乎解决方案”与一个丑陋的旗帜,但在我告诉你如何,我将告诉你我的动机:

我想首先提供权限引用功能。如果用户使用它并且没有权限,他/她可以得到上面的第1个对话框或第2和第3个对话框。当用户选择“永不再问”,我想禁用功能,并以不同的方式显示它。-我的操作是由旋转器文本条目触发的,我还想添加“(权限撤销)”到显示的标签文本。这向用户显示:“有功能,但由于我的权限设置,我不能使用它。”然而,这似乎是不可能的,因为我无法检查是否已经选择了“Never ask again”。

我找到了一个可以接受的解决方案,即始终启用活动权限检查的功能。我在onRequestPermissionsResult()中显示一个Toast消息,如果是负面响应,但只有当我没有显示我的自定义原理弹出。所以如果用户选择了“永不再问”,他只会收到一条祝酒信息。如果用户不愿意选择“再也不问”,他只会得到自定义的基本原理和操作系统弹出的权限请求,而不是吐司,因为连续三个通知将会太痛苦了。

你可以在onRequestPermissionsResult()中检查shouldShowRequestPermissionRationale()

shouldShowRequestPermissionRationale https://youtu.be/C8lUdPVSzDk?t=2m23s < / p >

检查在onRequestPermissionsResult()中是否授予了权限。如果,则检查shouldShowRequestPermissionRationale()

  1. 如果此方法返回true,则说明为什么需要此特定权限。然后根据用户的选择再次requestPermissions()
  2. 如果它返回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;
}
}

显然,谷歌maps对位置许可正是这样做的。

这里有一个很好的和简单的方法来检查当前的权限状态:

    @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;

可能对某人有用:——

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

状态1:-返回true:——任何时候用户单击Deny permissions(包括第一次)。

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

详细工作示例链接

我在Android m中写了一个权限请求的简写,这段代码还处理了对旧Android版本的向后兼容性。

所有丑陋的代码都被提取到一个片段中,该片段将自己附加到请求权限的活动上。你可以像下面这样使用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()的false条件下再次请求权限时,你将收到onRequestPermissionsResult()上的回调作为PERMISSION_DENIED

来自Android文档:

当系统要求用户授予权限时,用户可以选择告诉系统不再请求该权限。在这种情况下,任何时候应用程序使用requestPermissions()再次请求该权限,系统立即拒绝该请求。系统调用你的onRequestPermissionsResult()回调方法并传递PERMISSION_DENIED,如果用户再次显式拒绝你的请求,它会以同样的方式传递。这意味着当你调用requestPermissions()时,你不能假设已经发生了与用户的任何直接交互。

我也遇到过同样的问题,但我解决了。为了简化工作,我编写了一个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上得到回调。

快乐编码:)

你可以通过检查权限基本原理是否显示在onRequestPermissionsResult()回调方法中来可以确定它。如果你发现任何权限设置为不要再问了,你可以请求用户从设置中授予权限。

我的完整实现如下所示。它适用于多个权限请求。使用下面的或直接使用我的图书馆。

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

如果你想检测所有的“国家”;(第一次被拒绝,刚刚被拒绝,刚刚被拒绝,“永不再问”;或永久拒绝)你可以做以下事情:

创建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

你可以使用if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)方法来检测never ask是否被检查。

更多参考:检查这个

检查是否使用了多个权限:

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

上面的代码还将显示对话框,这将重定向到用户的应用程序设置屏幕,在那里他可以给予权限,如果选中了“永不再问”按钮。

请不要为这个解决办法向我扔石头。

这是可行的,但有点“俗气”。

当你调用requestPermissions时,注册当前时间。

        mAskedPermissionTime = System.currentTimeMillis();

然后在onRequestPermissionsResult

如果结果不允许,请再次检查时间。

 if (System.currentTimeMillis() - mAskedPermissionTime < 100)

由于用户不可能那么快地点击拒绝按钮,我们知道他选择了“永不再问”,因为回调是即时的。

使用风险自负。

我必须实现相机的动态权限。有三种可能的情况:允许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;
}
}
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
}
}
}
}
}
}
shouldShowRequestPermissionRationale ()方法可用于检查用户是否选择了“never asked again”选项并拒绝权限。 有很多代码示例,所以我宁愿解释如何将其用于这样的目的,因为我认为它的名称和实现使它比实际情况更加复杂

正如在运行时请求权限中解释的那样,如果选项“never ask again”可见,该方法返回true,否则返回false;因此,它在第一次显示对话框时返回false,然后从第二次开始返回true,只有当用户拒绝选择该选项的权限时,它才会再次返回false。

要检测这种情况,您可以检测序列false-true-false,或者(更简单)您可以使用一个标记来跟踪对话框显示的初始时间。在此之后,该方法返回true或false,其中false将允许您检测何时选择该选项。

扩展上面mVck的答案,下面的逻辑确定是否为给定的权限请求检查了“Never ask again”:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
!bStorage && (
_bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
_bStorageRationaleBefore == false && _bStorageRationaleAfter == false
);

节选自下面(完整示例参见this 回答)

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

试试这个简单的权限库。它将在3个简单的步骤中处理与权限相关的所有操作。这节省了我的时间。您可以在15分钟内完成所有权限相关的工作

它可以处理拒绝,它可以处理永不再请求,它可以调用应用程序设置的权限,它可以给出一个Rational消息,它可以给出一个拒绝消息,它可以给出一个接受的权限列表,它可以给出一个拒绝的权限列表,等等。

https://github.com/ParkSangGwon/TedPermission

步骤1:添加你的依赖项

dependencies {
compile 'gun0912.ted:tedpermission:2.1.1'
//check the above link for latest libraries
}

步骤2:请求权限

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

步骤3:处理权限响应

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

对每一种许可情况的完整解释

/**
*    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
}

当你第一次请求一个权限时,需要设置一个共享偏好布尔值,将你想要的权限(例如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)检查用户是否已经拒绝应用程序再次询问。由于这个函数的怪癖,下面的行也是必需的。

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)这个被使用(连同在第一次权限请求时将值设置为true)来区分“Never asked”和“Never ask again”状态,因为前一行不会返回此信息。

一旦用户标记了“不要再问”,问题就不能再显示了。 但是可以向用户解释,他之前拒绝了权限,必须在设置中授予权限。并引用他的设置,使用以下代码:

@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;
}

你可以听漂亮。

侦听器

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


})

在activity或fragmnet中覆盖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)
}
在阅读了一些答案后,我发现了许多冗长而令人困惑的答案 我的结论是

if (!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_EXTERNAL_STORAGE))
Toast.makeText(this, "permanently denied", Toast.LENGTH_SHORT).show();

OnRequestPermissionResult-free和shouldshowrequestpermissionrationalfree方法:

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

如果没有权限弹出,250ms后打开设备设置(如果选择了“Never ask again”,就是这种情况)。

shouldShowRequestPermissionRationale根据之前权限请求中的用户首选项返回true或false。

如果用户只是拒绝权限(不是永远),shouldShowRequestPermissionRationale将返回true。如果永远拒绝允许,则返回false。诀窍是即使用户允许权限,shouldShowRequestPermissionRationale也会返回false

因此,我们可以结合这两个条件来得到永不再问的选择与否。

因此,如果用户不允许权限并且shouldShowRequestPermissionRationale返回false,则意味着用户选择永远不再请求权限。

https://stackoverflow.com/a/58114769/5151336 < a href = " https://stackoverflow.com/a/58114769/5151336 " > < / >

此示例演示了当用户选择"DENY &不要再问了"

enter image description here

    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
}

我有点晚了,我也遇到过类似的问题。解决此问题如下

假设您需要位置权限

请求权限启动器
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.
}
}

我发现这个问题的方法对我来说有点新鲜。我必须保留一个参考,如果用户曾经选择了一个决定。通过这种方式,如果权限没有被授予,我可以告诉用户是第一次在那里,并且应该提示看到权限弹出,或者用户暂时或永久地拒绝它。

psudocode:

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 < / p >