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

时间:2015-06-08 21:03:35

标签: android android-permissions android-6.0-marshmallow

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

enter image description here

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

  1. 再次尝试(再次请求许可)
  2. 拒绝(app将在没有该权限的情况下工作)。
  3. 但是,如果用户检查Never ask again,则不应显示带有解释的第二个对话框,尤其是如果用户之前已经拒绝过一次。 现在的问题是:我的应用程序如何知道用户是否已检查Never ask again? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)没有给我这些信息。

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

27 个答案:

答案 0 :(得分:310)

开发者预览版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

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

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

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

  

帮助您找到需要额外提供的情况   解释,系统提供   Activity.shouldShowRequestPermissionRationale(String)方法。这个   如果应用已请求此权限,则method返回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...*/ ) {
                }
            }
        }
    }
}

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

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

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

答案 1 :(得分:86)

您可以在shouldShowRequestPermissionRationale()

中查看onRequestPermissionsResult()

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

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

  1. 如果此方法返回true,则显示为何需要此特定权限的说明。然后再次根据用户的选择requestPermissions()
  2. 如果它返回false,则会显示一条错误消息,指出未授予权限且应用无法继续进行或已禁用某项功能。
  3. 以下是示例代码。

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

    显然,谷歌地图确实是为了获得位置许可。

答案 2 :(得分:33)

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

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

警告:在用户通过用户提示(在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;

答案 3 :(得分:21)

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

我的全面实施如下。它适用于单个多个权限请求。使用以下或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).
        }
    }
}

答案 4 :(得分:19)

可能对某人有用: -

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

状态1: - 返回true: - 任何时候用户点击拒绝权限(包括第一次)。

状态2:-Returns false: - 如果用户选择“never again again”。

Link of detailed working example

答案 5 :(得分:15)

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

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

答案 6 :(得分:9)

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

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

答案 7 :(得分:8)

我有同样的问题,我想出来了。为了简化生活,我编写了一个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 上获得回调。

快乐编码:)

答案 8 :(得分:3)

每个许可案例的完整说明

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

    }
}

答案 9 :(得分:2)

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

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

答案 10 :(得分:2)

一个有用的功能,用于确定是否已阻止任意权限进行请求(在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)用于区分“从未询问”和“不再询问”状态,因为上一行不会返回此信息

答案 11 :(得分:2)

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

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

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

答案 12 :(得分:2)

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

它可以处理Deny,它可以处理从不再问,它可以调用app设置以获得权限,它可以给出一条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();
    }
};

答案 13 :(得分:1)

请不要向我扔石头以获得此解决方案。

这有效,但有点“hacky”。​​

致电$('#addrow').click(function(e) { $('#tab_logic tr:last').after('<tr><td><input type="text" list="inppara" id="inpparameter" ><datalist id="inppara"><option value="TypeRating">TypeRating</option><option value="Operation Check">Operation Check</option><option value="Ext.Apperance">Ext.Apperance</option><option value="Verify TC">Verify TC</option><option value="Material">Material</option><option value="Dimension Check">Dimension Check</option><option value="Threads Check">Threads Check</option><option value="Visual Check">Visual Check</option><option value="Specification">Specification</option><option value="Batch no">Batch no</option><option value="Mfg.Date">Mfg.Date</option><option value="Exp.Date">Exp.Date</option></datalist><td><input type="text" list="inpsepci" id="inpspec" ><datalist id="inpsepci"><option value="AsperPO">AsperPO</option><option value="Damage">Damage</option><option value="OK">OK</option><option value="Stainless Steel">Stainless Steel</option><option value="Gaues">Gaues</option><option value="Mild Steel">Mild Steel</option><option value="As per Drawing">As per Drawing</option><option value="Cast Iron">Cast Iron</option><option value="Copper">Copper</option><option value="Aluminium">Aluminium</option><option value="Brass">Brass</option><option value="Spring Steel">Spring Steel</option><option value="Tool Steel">Tool Steel</option><option value="Nylon">Nylon</option><option value="CRGO">CRGO</option><option value="EN1">EN1</option><option value="EN2">EN2</option></datalist></td><td><input type="text" list="act1" id="inpact10" class="tb3"><datalist id="act1"><option value="YES">YES</option><option value="NO">NO</option><option value="OK">OK</option><option value="NOT OK">NOT OK</option></datalist></td><td><input type="text" list="act2" id="inpact20" class="tb3"><datalist id="act2"><option value="YES">YES</option><option value="NO">NO</option><option value="OK">OK</option><option value="NOT OK">NOT OK</option></datalist></td><td><input type="text" list="act3" id="inpact30" class="tb3"><datalist id="act3"><option value="YES">YES</option><option value="NO">NO</option><option value="OK">OK</option><option value="NOT OK">NOT OK</option></datalist></td><td><input type="text" list="act4" id="inpact40" class="tb3"><datalist id="act4"><option value="YES">YES</option><option value="NO">NO</option><option value="OK">OK</option><option value="NOT OK">NOT OK</option></datalist></td><td><input type="text" list="act5" id="inpact50" class="tb3"><datalist id="act5"><option value="YES">YES</option><option value="NO">NO</option><option value="OK">OK</option><option value="NOT OK">NOT OK</option></datalist></td><td><img src="delete.gif" height="42" width="42" alt="idata" class="del"></td></tr>'); }); 时,请注册当前时间。

requestPermissions

然后在 mAskedPermissionTime = System.currentTimeMillis();

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

onRequestPermissionsResult

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

使用风险自负。

答案 14 :(得分:1)

你可以听漂亮的声音。

监听器

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

                })

覆盖活动或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()
        }
        }
    }

答案 15 :(得分:1)

您可以使用

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

}

答案 16 :(得分:1)

在阅读了很少的答案后,我发现了许多冗长而令人困惑的答案 我的结论是

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

答案 17 :(得分:0)

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

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

答案 18 :(得分:0)

要精确回答该问题,当用户按下“不再询问”时会发生什么?

被覆盖的方法/函数

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

答案 19 :(得分:0)

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

答案 20 :(得分:0)

OnRequestPermissionResult-free和shouldShowRequestPermissionRationale-free方法

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后打开设备设置(如果选择了“不再询问”。

答案 21 :(得分:0)

shouldShowRequestPermissionRationale 根据之前权限请求中的用户偏好返回 true 或 false。

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

所以我们可以结合这两个条件来选择或不选择从不再次询问。

因此,如果用户不允许权限并且 shouldShowRequestPermissionRationale 返回 false,则表示用户选择不再请求权限。

<块引用>

https://stackoverflow.com/a/58114769/5151336

答案 22 :(得分:0)

扩展上面 mVck 的答案,以下逻辑确定是否已针对给定的权限请求检查“从不再询问”:

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

答案 23 :(得分:0)

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

答案 24 :(得分:0)

您可以使用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();
    }

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

答案 25 :(得分:0)

相反,当您在onRequestPermissionsResult()

的错误状态下再次请求权限时,您将在shouldShowRequestPermissionRationale()上收到回复作为PERMISSION_DENIED

来自Android doc:

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

答案 26 :(得分:0)

我还希望获得用户是否已选择&#34;再也不再询问&#34;的信息。我已经实现了一个几乎解决方案&#39;带着难看的旗帜,但在我告诉你如何之前,我会告诉你我的动机:

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

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