无法拦截用户拒绝 Marshmallow 中的权限

Cannot intercept user denying permission in Marshmallow

我当前的应用程序有一个 FeedbackFragment,它使用支持库中的 Fragment 并提供一个复选框以允许用户有选择地发送系统日志及其反馈。因为将日志附加到外发电子邮件需要访问外部存储(WRITE_EXTERNAL_STORAGE 权限),所以我必须在请求 FeedbackFragment 时请求权限。如果用户拒绝写入外部存储的权限,我想 disable/hide 'Include system logs' 复选框,但我希望此屏幕的其余功能正常工作(在 Marshmallow 之前的设备上工作正常,顺便提一句)。我已经根据Android官方的文档,结合很多SO文章,写出了合适的方法,这就是我目前所拥有的。请注意,我不会在这段代码的 'deny' 分支中的任何断点处停止。

下面的所有逻辑都是在 onCreateView()

checkExternalStoragePermissions() 的调用中创建的

我错过了什么?

private void checkExternalStoragePermissions() {
    if (ContextCompat.checkSelfPermission(getContext(), WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
        canSendAttachments = true;
        systemLogsCheckbox.setEnabled(true);
    } else {
        requestExternalStoragePermission();
    }

}

private void requestExternalStoragePermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), WRITE_EXTERNAL_STORAGE)) {

        new SnackBar.Builder(getActivity())
                .withMessage(getString(R.string.write_external_storage_permission_rationale))
                .withDuration(SnackBar.PERMANENT_SNACK)
                .withActionMessage("OK")
                .withOnClickListener(new SnackBar.OnMessageClickListener() {
                    @Override
                    public void onMessageClick(Parcelable token) {
                        ActivityCompat.requestPermissions(getActivity(),
                                new String[]{WRITE_EXTERNAL_STORAGE},
                                WRITE_TO_EXTERNAL_STORAGE_PERMISSION);
                    }
                })
                .withTextColorId(R.color.almanac_red_tab_highlight)
                .show();

    } else {

        ActivityCompat.requestPermissions(getActivity(),
                new String[]{WRITE_EXTERNAL_STORAGE}, WRITE_TO_EXTERNAL_STORAGE_PERMISSION);
    }

}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
                                       @NonNull int[] grantResults) {
    if (requestCode == WRITE_TO_EXTERNAL_STORAGE_PERMISSION) {
        if ((grantResults.length == 1) && (grantResults[0] == PERMISSION_GRANTED)) {

            canSendAttachments = true;
            systemLogsCheckbox.setEnabled(true);
        } else {
            canSendAttachments = false;
            systemLogsCheckbox.setEnabled(false);
            systemLogsCheckbox.setChecked(false);
        }
    } else
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

如果您从 Fragment 调用,您应该调用 FragmentCompat.requestPermissions 以便回调路由您的片段而不是您的 activity。

您可能需要将此添加到您的 gradle 文件中,因为 FragmentCompat 在 support-v13 库中。

compile 'com.android.support:support-v13:23.1.1'

首先你需要在你的 Activity 中实现 OnRequestPermissionsResultCallback Listener of ActivityCompat class 并覆盖 void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 方法来检查用户是否允许或拒绝权限

你可以这样做:

int REQUEST_STORAGE = 1;

private void checkExternalStoragePermissions() {
    if (hasStoragePermissionGranted()) {
        canSendAttachments = true;
        systemLogsCheckbox.setEnabled(true);
    } else {
        requestExternalStoragePermission();
    }
}

public boolean hasStoragePermissionGranted(){
        return  ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}

public void requestExternalStoragePermission() {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
        ActivityCompat.requestPermissions(MainActivity.this, {Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_STORAGE);
    }
}

这里是 onRequestPermissionsResult() 当用户允许或拒绝运行时权限对话框中的权限时将调用的方法。

您还可以处理用户选中从不显示运行时权限对话框的情况。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {

        if (requestCode == REQUEST_STORAGE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               //Storage permission is enabled
               canSendAttachments = true;
               systemLogsCheckbox.setEnabled(true);
            } else if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivtity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                //User has deny from permission dialog
                Snackbar.make(mainLayout, getResources().getString("Please enable storage permission"),
                        Snackbar.LENGTH_INDEFINITE)
                        .setAction("OK", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                ActivityCompat.requestPermissions(MainActivity.this, {Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_STORAGE);
                            }
                        })
                        .show();
            } else {
                // User has deny permission and checked never show permission dialog so you can redirect to Application settings page
                Snackbar.make(mainLayout, getResources().getString("Please enable permission from settings"),
                        Snackbar.LENGTH_INDEFINITE)
                        .setAction("OK", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                Intent intent = new Intent();
                                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null);
                                intent.setData(uri);
                                startActivity(intent);
                            }
                        })
                        .show();
            }
        }
    }

希望对你有帮助。

虽然 @iagreen 的答案很接近,但我最初遇到的问题是因为我使用的是适当的 ActivityCompat 版本我的代码中的方法。一旦我从方法调用中删除了该前缀并使用了 Fragment class 中的相应方法,现在一切都按我预期的那样工作并且原始问题已经解决。

话虽如此,我也在研究如何使用 ContentProvider 来完全否定对 WRITE_EXTERNAL_STORAGE 权限的需求,正如 @Chris Stratton 所建议的那样.