Android 对话框的 M 权限问题 "Don't ask again"

Android M permission issue with Dialog "Don't ask again"

通过 Scoped Directory Access 授予权限是一种更简单的方法,但对话框会显示一个名为 "Don't ask again" 的复选框。如果用户选择“不再询问”并拒绝请求,则您应用程序对给定目录的所有未来请求都将被自动拒绝,并且不会向用户显示任何请求 UI。 如果用户后悔或错误地点击了那个复选框,应用程序如何补救?应用无法获取权限对话框。

我们该如何处理?

我认为您需要做的是使用 shouldShowRequestPermissionRationale(String) 方法,如果用户拒绝权限并检查 "Don't ask again",它会 return false。

你应该做的是向用户显示一个警告,解释为什么你需要权限或实施回退,比如禁用某些功能。

希望对您有所帮助。

我们应该使用 shouldShowRequestPermissionRationale.Please 完成这个:

private void insertDummyContactWrapper() {
        int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
        if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
                if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
                    showMessageOKCancel("You need to allow access to Contacts",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
                                            REQUEST_CODE_ASK_PERMISSIONS);
                                }
                            });
                    return;
                }
            requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
                    REQUEST_CODE_ASK_PERMISSIONS);
            return;
        }
        insertDummyContact();
    }

is any way to change that flag ?

开发人员无法更改该标志。否则,拥有复选框将毫无意义,因为开发人员会通过更改标志来忽略它。

但是,您的问题指出了范围目录访问中的一个相当大的缺陷:用户更改该标志的能力有限。设置中似乎没有专门更改此状态的地方,用户可以手动授予被拒绝的运行时权限的方式。

在 Nexus 5X 运行 7.1 预览版上,"Clear Data" 将重置此标志,尽管这具有更广泛的影响。在 Google Pixel 运行 7.1 和 Nexus 5X 运行 Android 7.0 上,nothing 将重置此标志,即使是完全卸载应用程序。

我已就此事提交 a bug report。我怀疑这种情况在短期内是否会得到很大改善 — 充其量,他们可能会修复它,以便 "Clear Data" 可靠地工作。

public class AccessDenied implements View.OnClickListener{

    public Dialog dialog;
    private LinearLayout contentLayout;
    private Activity context;
    private EditText password;
    private String passwordStr;
    private Runnable doOnAccessPermitted;
    private int wrongColor = Color.RED, defColor = Color.parseColor("#80000000");


    public AccessDenied(Activity con, String pwd) {
        passwordStr = pwd;
        context = con;
        dialog = new Dialog(context, R.style.AnimatedDialog);
        setCancelable(false);
        //init the dialog with content view/animations etc.
        dialog.setContentView(R.layout.access_denied_layout);
        contentLayout = dialog.findViewById(R.id.layoutIconDialogLinearLayout);
        password = dialog.findViewById(R.id.accessdeniedlayoutpassword);
        Button ok = dialog.findViewById(R.id.accessdeniedlayoutok);
        ok.setOnClickListener(this);
        //now the dialog is ready
    }

    public void setActionOnAccess(Runnable doOnAccess) {
        doOnAccessPermitted = doOnAccess;
    }

    public void setCancelable(boolean set) {
        dialog.setCancelable(set);
    }

    public void show() {
        dialog.show();
    }

    public void cancel() {
        dialog.cancel();
    }

    public void setPassword(String pwrd) {
        passwordStr = pwrd;
    }

    public void tryPassword(String tryp) {
        if(passwordStr.equals(tryp)){
            cancel();
            if(doOnAccessPermitted != null)
                doOnAccessPermitted.run();
        }
    }

    @Override
    public void onClick(View view) {
        if(passwordStr.equals(password.getText().toString())) {
            cancel();
            if(doOnAccessPermitted != null)
                doOnAccessPermitted.run();
        }else{
            password.getText().clear();
            Animation anim = AnimationUtils.loadAnimation(context, R.anim.edittext_shake);
            anim.setDuration(200);
            anim.setRepeatCount(5);
            decView().startAnimation(anim);
            decView().setBackgroundColor(wrongColor);
            new android.os.Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    decView().setBackgroundColor(defColor);
                }
            }, 1000);
        }
    }

    private View decView() {
        return password;
    }
}

打开应用程序的权限设置。

如果用户已勾选,则无法显示另一个权限对话框"Don't ask again."

但是,您可以帮助用户并打开应用程序的设置,用户可以在其中手动启用所需的权限。

为此,您需要启动一个 Intent,类似于:

public void openAppSettings() {

    Uri packageUri = Uri.fromParts( "package", getApplicationContext().getPackageName(), null );

    Intent applicationDetailsSettingsIntent = new Intent();

    applicationDetailsSettingsIntent.setAction( Settings.ACTION_APPLICATION_DETAILS_SETTINGS );
    applicationDetailsSettingsIntent.setData( packageUri );
    applicationDetailsSettingsIntent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );

    getApplicationContext().startActivity( applicationDetailsSettingsIntent );

}

现在,要知道用户何时勾选了 "Don't ask again" 复选框是另一回事,可以使用 来完成。

我创建了一种方法,通过使用串联的 if...else if...else 来捕获所有用户操作,它对我来说效果很好。
首先,为了确定是否 两个权限都被拒绝并且不再询问是否也是 'ticked',我结合了权限状态检查和 shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS ).
然后判断是否只有权限被拒绝without 'ticking' 不要再问了,我用权限状态检查。以下片段:

@RequiresApi(api = Build.VERSION_CODES.M) //this is added for API lower than 23

public void myPermissionRationale(){
        //This checks both permission status and the Don't ask again check box
        if (ContextCompat.checkSelfPermission(this,Manifest.permission.SEND_SMS )
                == PackageManager.PERMISSION_DENIED && !shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS)) {
            //open app permission settings here for instance 

        }
           //only checks permission status then shows permission request pop up again
           else if (ContextCompat.checkSelfPermission(this,Manifest.permission.SEND_SMS )
                    == PackageManager.PERMISSION_DENIED){
                            // Request the permission
                            ActivityCompat.requestPermissions(this,
                                    new String[]{Manifest.permission.SEND_SMS},
                                    10);

                        }
        }

我为此找到了一个干净有效的解决方案(但这里没有代码示例,- 阅读并理解它的原因)确保在采取通常要求许可的操作时没有任何场景,不做任何事情.

标志 shouldShowPermission() - 仅此一项,应用程序第一次请求给定权限与“点击不再显示”后没有区别,在这两种情况下它都会 return false。每次都将保持 returning 为真,直到设置了不再询问权限。意思是第二次开始我们请求无限权限或直到用户点击不再询问。

因此,为了区分首次请求权限和请求权限的时间,在用户已经设置“不再询问”后,您可以使用自定义标志。

这是一个干净而简单的解决方法,它将确定是否设置了不再询问选项。 (经过测试并在 2 个制作应用程序中工作)

解决方案:

添加一个名为“rationaleDisplayed”的标志(或任何您想要的,这将指示向用户显示 permissionRationeDialog)- 默认值为 false,将其存储在 prefs 中。至少向用户展示一次基本原理对话框后,将此标志设置为真。

现在你有两个参数,当 shouldShowRationaleDialog = false 和 rationalePermissionDisplayed = true 时,它​​们的组合有效地设置了“不再显示”的指示。

为什么这有效? 它之所以有效,是因为 shouldShowRationale, will return false 当第一次请求许可时并且 rationaleDisplayedFlag 都是 false,所以弹出窗口会正确显示。 (2 false) - 这是第一次请求权限的情况。

然后,如果您第一次拒绝,sshouldShowRationale 将为 true,rationaleDisplayed 将为 true - (2 trues),直到使用 dont set again 选项之前都是如此。 - 这是在第一次请求被拒绝后第二次请求许可的情况。

最后,如果您设置不再询问 - 或 Android api 30 并拒绝许可 2 次,下次调用 shouldShowRationale 标志时将 return 为 false。

这里有一个 shouldShowRationale = false 的情况,并且您自己的标志 rationaleDisplayed = true,它告诉您已设置不再询问。 (否则,shouldShowRationale 仍然是错误的)。 - 当用户拒绝权限两次 (api 30) 或在拒绝时设置“不再显示”选项时就是这种情况。

有了这个逻辑案例,您现在可以添加一个自定义权限对话框,其中包含有关如何手动启用权限的说明,并使用确定按钮打开应用程序设置。 (意图在对话积极听众中打开设置)。

基本原理 rationaleDisplayed 标志基本上是为了确保应用第一次请求权限时它会正确显示,但它的值允许确定用户设置为不再询问时的状态。

很有魅力。