首页 文章

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

提问于
浏览
264

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

enter image description here

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

  • 再次重试(再次请求许可)

  • 拒绝(app将在没有该权限的情况下工作) .

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

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

23 回答

  • 19

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

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

    enter image description here

    没有“Never again again”复选框(与开发者预览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 . 这表明您应该向用户解释为什么需要该权限 . 如果用户过去拒绝了权限请求并在权限请求系统对话框中选择了“不再询问”选项,则此方法返回false . 如果设备策略禁止应用程序具有该权限,则该方法也返回false .

    要知道用户是否使用"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);
    

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

  • 0

    您可以在 onRequestPermissionsResult() 中查看 shouldShowRequestPermissionRationale() .

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

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

    • 如果此方法返回 true ,则显示为何需要此特定权限的说明 . 然后再次根据用户的选择 requestPermissions() .

    • 如果它返回 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;
        }
    }
    

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

  • 1

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

    @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设备上)

    Update:

    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;
    
  • 0

    can determine 检查是否要在 onRequestPermissionsResult() 回调方法中显示权限 rationale . 如果您发现任何权限设置为 never ask again ,则可以请求用户从设置中授予权限 .

    我的完整实现如下 . 它适用于 singlemultiple 权限要求 . 使用以下或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).
            }
        }
    }
    
  • 0

    可能对某人有用: -

    What I have noticed is, if we check the shouldShowRequestPermissionRationale() flag in to onRequestPermissionsResult() callback method, it shows only two states .

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

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

    Link of detailed working example

  • 8

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

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

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

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

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

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

    如果用户选中 Never ask again ,您将获得onPermissionDisabled的回调 .

    快乐编码:)

  • 2

    Complete explanation for every case of permission

    /**
     *    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.
            }
    
        }
    }
    
  • 2

    我在Android M中写了一个权限请求的简写 . 此代码还处理旧版Android版本的向后兼容性 .

    所有丑陋的代码都被提取到一个片段中,片段将自身附加到请求权限的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

  • 1

    方法shouldShowRequestPermissionRationale()可以是用户检查用户是否选择了'never asked again'选项并拒绝了该权限 . 有很多代码示例,所以我宁愿解释如何将它用于这样的目的,因为我认为它的名称及其实现使它实际上变得更加复杂 .

    Requesting Permissions at Run Time中所述,如果选项'never ask again'可见,则该方法返回true,否则返回false;因此它在第一次显示对话框时返回false,然后从第二次开始返回true,并且只有当用户拒绝选择该选项的权限时,此时它才会再次返回false .

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

  • 20

    试试这个简单的权限库 . 它将通过3个简单步骤处理与权限相关的所有操作 . 它节省了我的时间 . You can finish all permission related work in 15 mins .

    它可以处理Deny,它可以处理Never never再次询问,它可以调用app设置以获得权限,它可以给出一条Rational消息,它可以给出一个Denial消息,它可以给出一个已接受权限的列表,它可以给出一个被拒绝的列表权限等

    https://github.com/ParkSangGwon/TedPermission

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

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

    一个有用的函数,用于确定是否已阻止任意权限请求(在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) 检查用户是否已拒绝该应用再次询问 . 由于quirks of this function,还需要以下行 .

    PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) 使用此方法(以及在第一个权限请求时将值设置为true)以区分"Never asked"和"Never ask again"状态,因为前一行不返回此信息 .

  • 2

    对于这个解决方案,请不要向我扔石头 .

    这有效,但有点“hacky” .

    当您拨打 requestPermissions 时,请注册当前时间 .

    mAskedPermissionTime = System.currentTimeMillis();
    

    然后在 onRequestPermissionsResult

    如果未授予结果,请再次检查时间 .

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

    由于用户确实无法点击这么快拒绝按钮,我们知道他选择“永不再问”,因为回调是即时的 .

    使用风险自负 .

  • 0

    您可以使用

    shouldShowRequestPermissionRationale()
    

    onRequestPermissionsResult()
    

    请参阅以下示例:

    Check if it has permission when the user clicks the button:

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

    When the user answer the permission dialog box we will go to 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();
                }
            }
        }
    
    }
    
  • 0

    你可以听得很漂亮 .

    Listener

    interface PermissionListener {
        fun onNeedPermission()
        fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
        fun onPermissionDisabledPermanently(numberDenyPermission: Int)
        fun onPermissionGranted()
    }
    

    MainClass for permission

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

    Used by this way

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

    override onRequestPermissionsResult in activity or fragmnet

    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()
            }
            }
        }
    
  • 3

    我还想获得用户是否选择“永不再问”的信息 . 我已经实现了一个带有丑陋外观的“几乎解决方案”,但在我告诉你如何之前,我会告诉你我的动机:

    我想最初提供权限引用功能 . 如果用户使用它并且没有权限,他/她将获得上面的第1个对话框或第2个和第3个对话框 . 当用户选择“永不再问”时,我想禁用该功能并以不同方式显示它 . - 我的动作是由一个微调文本条目触发的,我还想在显示的标签文本中添加'(Permission revoked)' . 这向用户显示:“由于我的权限设置,有功能但我无法使用它 . ”但是,这似乎不可能,因为我无法检查是否已经选择了“永不再问” .

    我通过主动权限检查启用了我的功能,从而找到了我可以忍受的解决方案 . 我在onRequestPermissionsResult()中显示Toast消息,如果是否定响应,但仅当我没有显示我的自定义原理弹出窗口时 . 因此,如果用户选择了“永不再问”,他只会收到一个祝酒词 . 如果用户不愿意选择“永不再问”,他只能获得操作系统弹出的自定义原理和权限请求,而不是吐司,因为连续三个通知会太麻烦 .

  • 33

    相反,当您在 shouldShowRequestPermissionRationale() 处于错误状态时再次请求权限时,您将收到 onRequestPermissionsResult() 上的回调作为PERMISSION_DENIED

    来自Android doc:

    当系统要求用户授予权限时,用户可以选择告知系统不要再次请求该权限 . 在这种情况下,只要应用程序使用 requestPermissions() 再次请求该权限,系统就会立即拒绝该请求 . 系统调用 onRequestPermissionsResult() 回调方法并传递 PERMISSION_DENIED ,就像用户再次明确拒绝您的请求一样 . 这意味着当您调用 requestPermissions() 时,您不能假设已经发生了与用户的任何直接交互 .

  • 1

    我必须实现相机的动态权限 . 如果发生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;
        }
    }
    
  • 13

    您可以使用 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) 方法来检测是否从未询问过 .

    如需更多参考: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.
                            }
    

    explain()方法

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

    上面的代码也会显示对话框,它会将用户重定向到应用程序设置屏幕,如果已经选中,则可以给予许可,从不再询问按钮 .

  • 0

    扩展 mVck 上面的答案,以下逻辑确定是否已针对给定的权限请求检查了"Never ask again":

    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);
    }
    
  • 2
    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
                        }
                    }
                }
            }
        }
    }
    
  • 84

    你可以阅读android官方文档Request App Permissions

    或者你可以在Github上找到许多流行的Android权限库

相关问题