Android M - 检查运行时权限 - 如何确定用户是否选中"从不再问"?

Ema*_*lin 288 android android-permissions android-6.0-marshmallow

根据这个:http://developer.android.com/preview/features/runtime-permissions.html#coding应用程序可以检查运行时权限和请求权限(如果尚未授予它).然后将显示以下对话框:

在此输入图像描述

如果用户拒绝重要权限,则应用程序应显示解释为何需要权限以及拒绝具有何种影响的解释.该对话框有两个选项:

  1. 再试一次(再次请求许可)
  2. 拒绝(app将在没有该权限的情况下工作).

Never ask again但是,如果用户检查,则不应显示带有解释的第二个对话框,尤其是如果用户之前已经拒绝过一次.现在的问题是:我的应用程序如何知道用户是否已检查过Never ask again?IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)并没有给我这些信息.

第二个问题是:Google是否计划在权限对话框中添加自定义消息,以解释应用程序需要权限的原因?那样就永远不会有第二个对话框,肯定会有更好的ux.

Ema*_*lin 325

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

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

在此输入图像描述

没有"Never again again"复选框(与开发者预览1不同).如果用户拒绝该权限,并且该权限对于该应用程序至关重要,则可以提供另一个对话框来解释该应用程序要求该权限的原因,例如:

在此输入图像描述

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

在此输入图像描述

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

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...
Run Code Online (Sandbox Code Playgroud)

这就是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...*/ ) {
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下代码打开您的应用设置:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
Run Code Online (Sandbox Code Playgroud)

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

  • 根据我的理解,shouldShowRationalePermissionRationale()方法在三种情况下返回false:1.如果我们在请求权限之前第一次调用此方法.2.如果用户选择"不再询问"并拒绝许可.3.如果设备政策禁止该应用拥有该权限 (28认同)
  • 我验证了shouldShowRequestPermissionRationale()方法的返回值为false,用于检查用户是否选择了"Never again again".但是,当我第一次申请许可时,我的价值也是假的.因此,如果用户选择"永不再问"复选框,我无法区分.请建议?? (26认同)
  • 一切都很好......但我们开发人员真的需要知道用户是否说"再也不要问".我有一个很好的按钮来访问功能.用户第一次点击:应该问理由吗?不,请求许可.用户否认.用户再次点击按钮:理由?是的!显示理由,用户说好,然后否认,再也不要问(好吧他是个白痴,但用户经常这样).后来用户再次按下按钮,理由?不,请求许可,用户没有任何反应.我真的需要一种方法,告诉用户:嘿,如果你想要这个功能,现在转到应用程序设置并给予许可. (20认同)
  • 伟大的@EmanuelMoecklin现在比Google文档更好:D (4认同)
  • 除非您请求权限,否则不会调用onRequestPermissionsResult.由于第一次请求权限时没有"永不再问"复选框,因此应该返回True(请求权限但不要再请求).因此,在用户第一次拒绝许可时会始终显示基本原理,但仅在未选中复选框的情况下才会显示该基本原理. (4认同)

Abh*_*han 90

您可以检查shouldShowRequestPermissionRationale()你的onRequestPermissionsResult().

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

检查是否授予了权限onRequestPermissionsResult().如果没有,那么检查shouldShowRequestPermissionRationale().

  1. 如果此方法返回,true则显示为何需要此特定权限的说明.然后再次根据用户的选择requestPermissions().
  2. 如果它返回,false则显示一条错误消息,表明未授予权限,app无法继续进行或禁用某项特定功能.

以下是示例代码.

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,谷歌地图正是这样做的位置许可.


pat*_*ckf 37

这是检查当前权限状态的一种简单方法:

    @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;
    }
Run Code Online (Sandbox Code Playgroud)

警告:在用户通过用户提示接受/拒绝权限之前(在sdk 23+设备上),在第一个应用程序启动之前返回BLOCKED_OR_NEVER_ASKED

更新:

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;
Run Code Online (Sandbox Code Playgroud)

  • 是的,这就是它被称为"BLOCKED_OR_NEVER_ASKED"的原因,也看到了最后一句话 (6认同)
  • 如果尚未请求权限,则始终返回"BLOCKED_OR_NEVER_ASKED". (4认同)
  • `android.content.pm`已经定义了'PERMISSION_GRANTED = 0`和'PERMISSION_DENIED = -1`.也许设置`BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1`或者什么? (3认同)

Nab*_*ari 25

可以通过检查是否要在回调方法中显示权限基本原理来确定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).
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


רות*_*כטר 24

一旦用户标记为"不再询问",则无法再次显示该问题.但是可以向用户解释他之前拒绝了许可,并且必须在设置中授予权限.并使用以下代码引用他的设置:

@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;
}
Run Code Online (Sandbox Code Playgroud)

  • 在迁移到 androidX 时,您可以将 android.support.design.R 替换为 com.google.android.material.R (2认同)

Nic*_*cks 22

可能对某人有用: -

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

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

状态2: - 返回错误: - 如果用户选择"永不再问".

详细工作实例的链接

  • 这是检测用户是否选择了从不再询问选项的正确方法. (2认同)

mVc*_*Vck 13

如果您想要检测所有"状态"(第一次被拒绝,被拒绝,被拒绝"永不再问"或被永久拒绝),您可以执行以下操作:

创建2个布尔值

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;
Run Code Online (Sandbox Code Playgroud)

在请求许可之前设置第一个:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);
Run Code Online (Sandbox Code Playgroud)

在onRequestPermissionsResult方法中设置第二个:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);
Run Code Online (Sandbox Code Playgroud)

使用以下"表"在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
Run Code Online (Sandbox Code Playgroud)

  • @EmanuelMoecklin,据我所知,它是检查它是否已经被拒绝的唯一方法(通过检查它之前和之后,如我的真值表中所解释的)或者它是否是第一次拒绝(在我的情况下我将用户重定向到应用程序设置是否被永久拒绝) (2认同)

mut*_*raj 9

我有同样的问题,我想出来了.为了简化生活,我编写了一个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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

现在,您只需要使用方法*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();
                        }
                    });
Run Code Online (Sandbox Code Playgroud)

我的应用程序如何知道用户是否已选中"永不再问"?

如果用户选中" 永不再问",您将获得onPermissionDisabled的回调.

快乐编码:)

  • 这应该很容易理解 - 如果用户关闭系统中的权限,它不会更新您所做的解决方法首选项。然后,当用户尝试再次使用该功能时,他们不会获得预期的系统对话框,而是跳过预期的第一步并立即触发您所谓的 onPermissionDisabled - 现在也在 android 11 上,甚至操作系统也可以从外部撤销权限 (2认同)

Ale*_*sio 6

shouldShowRequestPermissionRationale()方法可用于检查用户是否选择了“不再询问”选项并拒绝了权限。有很多代码示例,所以我宁愿解释如何将它用于这样的目的,因为我认为它的名称和实现使得它比实际情况更复杂。

如运行时请求权限中所述,如果“不再询问”选项可见,则该方法返回 true,否则返回 false;因此,第一次显示对话框时它返回 false,然后从第二次开始它返回 true,并且只有当用户拒绝选择该选项的权限时,它才会再次返回 false。

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


vis*_*nny 5

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

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

因此,我们可以结合这两个条件来决定是否选择“不再询问”。

因此,如果用户未获得许可并shouldShowRequestPermissionRationale返回false,则意味着用户选择不再请求许可。

/sf/answers/4068033861/

  • 这并不完全正确。如果用户在系统对话框上执行外部单击或后按,则“shouldShowRequestPermissionRationale”将返回 false。即使用户没有永久拒绝该权限。 (2认同)

归档时间:

查看次数:

152138 次

最近记录:

6 年,7 月 前