web-dev-qa-db-ja.com

Android Mパーミッション:shouldShowRequestPermissionRationale()関数の使用法について混乱しています

Android Mの新しいアクセス許可モデルに関する公式ドキュメントを調べていました。アプリが以前にこのアクセス許可を要求し、ユーザーが要求を拒否した場合にtrueを返すshouldShowRequestPermissionRationale()関数について説明します。ユーザーが過去に許可要求を拒否し、[今後尋ねない]オプションを選択した場合、このメソッドはfalseを返します。

しかし、次の2つのケースをどのように区別できますか?

事例1:アプリには許可がなく、ユーザーは以前に許可を求められていません。この場合、shouldShowRequestPermissionRationale()はfalseを返します。これは、これがユーザーに尋ねるのが初めてだからです。

事例2:ユーザーは許可を拒否し、「再度尋ねない」を選択しました。この場合もshouldShowRequestPermissionRationale()はfalseを返します。

ケース2でユーザーをアプリの設定ページに送りたいのですが、これら2つのケースを区別するにはどうすればよいですか?

129
akshayt23

Mプレビュー1の後、ダイアログが表示された場合初めてチェックボックスはありませんチェックボックスはありません。

ユーザーが許可リクエストを拒否すると、許可ダイアログに二度と尋ねないチェックボックスが表示されます2回目許可は要求された。

したがって、ロジックは次のようになります。

  1. 許可をリクエストする:

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
    } else {
        //Do the stuff that requires permission...
    }
    
  2. onRequestPermissionsResultで許可が拒否または許可されたかどうかを確認します。

    以前に許可が拒否された場合、今回は許可ダイアログに二度と尋ねないチェックボックスがあります。

    shouldShowRequestPermissionRationaleを呼び出して、ユーザーがチェックしたかどうかを確認します二度と尋ねないでくださいshouldShowRequestPermissionRationaleメソッドは、ユーザーがを選択しない場合またはデバイスポリシーがアプリにその許可を与えることを禁止している場合にのみfalseを返します。

    if (grantResults.length > 0){
        if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //Do the stuff that requires permission...
        }else if (grantResults[0] == PackageManager.PERMISSION_DENIED){
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                //Show permission explanation dialog...
            }else{
                //Never ask again selected, or device policy prohibits the app from having that permission.
                //So, disable that feature, or fall back to another situation...
            }
        }
    }
    

そのため、ユーザーがチェックした場合、再度尋ねるかどうかを追跡する必要はありません。

150
CanC

私は同じ問題を抱えていて、それを見つけました。生活をもっと簡単にするために、ランタイムのアクセス許可を処理する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();
                        }
                    });

ケース1:アプリに許可がなく、ユーザーが許可を求められたことがない。この場合、shouldShowRequestPermissionRationale()はfalseを返します。これは、これがユーザーに尋ねるのが初めてだからです。

ケース2:ユーザーが許可を拒否し、「再度尋ねない」を選択した場合、この場合もshouldShowRequestPermissionRationale()はfalseを返します。

ケース2でユーザーをアプリの設定ページに送りたいのですが、これら2つのケースを区別するにはどうすればよいですか?

ケース1ではonPermissionAsk、ケース2ではonPermissionDisabledでコールバックを取得します。

ハッピーコーディング:)

22
muthuraj

更新

以下のCanCの answer が正しいものであると信じています。確実に知る唯一の方法は、shouldShowPermissionRationaleを使用してonRequestPermissionResultコールバックでこれを確認することです。

==

私の元の答え:

私が見つけた唯一の方法は、これが初めてかどうかを自分で追跡することです(たとえば、共有設定を使用する)。初めてでない場合は、shouldShowRequestPermissionRationale()を使用して区別します。

以下も参照してください: Android M-ランタイムパーミッションの確認-ユーザーが「二度と尋ねない」をチェックしたかどうかを判断する方法?

8
Alex Florescu

ShouldShowRequestPermissionRationale()が理解しているように、shouldShowRequestPermissionRationale()は内部で多くのユースケースを実行し、要求されている権限に関する説明を表示するかどうかをアプリに通知します。

実行時許可の背後にある考え方は、ほとんどの場合、ユーザーは許可要求に対して「はい」と言うことです。そうすれば、ユーザーは1回クリックするだけで済みます。もちろん、リクエストは正しいコンテキストで使用する必要があります。つまり、「カメラ」ボタンが押されたときにカメラの許可を求めます。

ユーザーがリクエストを拒否し、しばらくしてから「カメラ」ボタンをもう一度押すと、shouldShowRequestPermissionRationale()がtrueを返すため、アプリは許可が要求される理由とアプリが要求されない理由を説明できます。それなしで適切に動作します。通常、そのダイアログウィンドウには、再度拒否/後で決定するためのボタンと、許可を与えるためのボタンが表示されます。理論的根拠ダイアログの許可の付与ボタンは、許可要求を再度開始する必要があります。今回は、ユーザーには「二度と表示しない」チェックボックスもあります。彼がそれを選択し、再び許可を拒否することに決めた場合、ユーザーとアプリが同じページにないことをAndroidシステムに通知します。そのアクションには2つの結果があります。shouldShowRequestPermissionRationale()は常にfalseを返し、requestPermissions()メソッドはダイアログを表示しませんが、onRequestPermissionsResultコールバックに直接拒否を返します。

しかし、onRequestPermissionsResultを使用できる別のシナリオもあります。たとえば、一部のデバイスには、カメラを無効にするデバイスポリシーがあります(CIA、DARPAなどで動作)。これらのデバイスでは、onRequestPermissionsResultは常にfalseを返し、requestPermissions()メソッドは要求をサイレントに拒否します。

それが、AndroidフレームワークのプロダクトマネージャーであるBen Poieszのポッドキャストを聞いて集めたものです。
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html

6
Shumoapp

誰かが気になるなら、別のオプションを投稿してください。 EasyPermissionsを使用して、「Android Mシステムのアクセス許可を簡素化」することができます。

その後、shouldShowRequestPermissionRationaleを直接処理する必要はありません。

6
Wei WANG

この実装を確認してください。私にとってはかなり良いです。基本的に、パーミッションのリストを渡すcheckPermissions()メソッドでパーミッションをチェックします。 onRequestPermissionsResult()で許可リクエストの結果を確認します。この実装により、ユーザーが「二度と尋ねない」または「しない」を選択した場合に両方のケースに対処できます。この実装では、seが「二度と尋ねない」を選択した場合、ダイアログには、彼をアプリ設定アクティビティに移動するオプションがあります。

このコードはすべて私のフラグメントの中にあります。これを行うために、PermissionManagerのような特別なクラスを作成する方が良いと考えていましたが、それについてはわかりません。

/**
     * responsible for checking if permissions are granted. In case permissions are not granted, the user will be requested and the method returns false. In case we have all permissions, the method return true.
     * The response of the request for the permissions is going to be handled in the onRequestPermissionsResult() method
     * @param permissions list of permissions to be checked if are granted onRequestPermissionsResult().
     * @param requestCode request code to identify this request in
     * @return true case we already have all permissions. false in case we had to Prompt the user for it.
     */
    private boolean checkPermissions(List<String> permissions, int requestCode) {
        List<String> permissionsNotGranted = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(getActivity(), permission) != PackageManager.PERMISSION_GRANTED)
                permissionsNotGranted.add(permission);
        }

        //If there is any permission we don't have (it's going to be in permissionsNotGranted List) , we need to request.
        if (!permissionsNotGranted.isEmpty()) {
            requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()]), requestCode);
            return false;
        }
        return true;
    }

    /**
     * called after permissions are requested to the user. This is called always, either
     * has granted or not the permissions.
     * @param requestCode  int code used to identify the request made. Was passed as parameter in the
     *                     requestPermissions() call.
     * @param permissions  Array containing the permissions asked to the user.
     * @param grantResults Array containing the results of the permissions requested to the user.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case YOUR_REQUEST_CODE: {
                boolean anyPermissionDenied = false;
                boolean neverAskAgainSelected = false;
                // Check if any permission asked has been denied
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        anyPermissionDenied = true;
                        //check if user select "never ask again" when denying any permission
                        if (!shouldShowRequestPermissionRationale(permissions[i])) {
                            neverAskAgainSelected = true;
                        }
                    }
                }
                if (!anyPermissionDenied) {
                    // All Permissions asked were granted! Yey!
                    // DO YOUR STUFF
                } else {
                    // the user has just denied one or all of the permissions
                    // use this message to explain why he needs to grant these permissions in order to proceed
                    String message = "";
                    DialogInterface.OnClickListener listener = null;
                    if (neverAskAgainSelected) {
                        //This message is displayed after the user has checked never ask again checkbox.
                        message = getString(R.string.permission_denied_never_ask_again_dialog_message);
                        listener = new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //this will be executed if User clicks OK button. This is gonna take the user to the App Settings
                                startAppSettingsConfigActivity();
                            }
                        };
                    } else {
                        //This message is displayed while the user hasn't checked never ask again checkbox.
                        message = getString(R.string.permission_denied_dialog_message);
                    }
                    new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme)
                            .setMessage(message)
                            .setPositiveButton(getString(R.string.label_Ok), listener)
                            .setNegativeButton(getString(R.string.label_cancel), null)
                            .create()
                            .show();
                }
            }
            break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    /**
     * start the App Settings Activity so that the user can change
     * settings related to the application such as permissions.
     */
    private void startAppSettingsConfigActivity() {
        final Intent i = new Intent();
        i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        i.addCategory(Intent.CATEGORY_DEFAULT);
        i.setData(Uri.parse("package:" + getActivity().getPackageName()));
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        getActivity().startActivity(i);
    }
3
Thiago Saraiva

誰かに役立つかもしれません:-

気づいたのは、onShowPermissionsResult()コールバックメソッドでshouldShowRequestPermissionRationale()フラグをチェックすると、2つの状態しか表示されないことです

状態1:-trueを返します:-ユーザーが[アクセス許可の拒否]をクリックしたとき(最初を含む)。

状態2:-falseを返します-ユーザーが「二度と尋ねない」を選択した場合。

詳細な作業例のリンク

2
Nicks

Kotlinソリューションに興味がある人は、@ muthurajの回答をKotlinにリファクタリングしました。また、リスナーの代わりに完了ブロックを持つように少し近代化しました。

PermissionUtil

object PermissionUtil {
    private val PREFS_FILE_NAME = "preference"

    fun firstTimeAskingPermission(context: Context, permission: String, isFirstTime: Boolean) {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        sharedPreference.preferences.edit().putBoolean(permission,
                isFirstTime).apply()
    }

    fun isFirstTimeAskingPermission(context: Context, permission: String): Boolean {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        return sharedPreference.preferences.getBoolean(permission,
                true)
    }
}

PermissionHandler

enum class CheckPermissionResult {
    PermissionAsk,
    PermissionPreviouslyDenied,
    PermissionDisabled,
    PermissionGranted
}

typealias PermissionCheckCompletion = (CheckPermissionResult) -> Unit


object PermissionHandler {

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        return ContextCompat.checkSelfPermission(context,
                permission) != PackageManager.PERMISSION_GRANTED
    }

    fun checkPermission(context: Context, permission: String, completion: PermissionCheckCompletion) {
        // If permission is not granted
        if (shouldAskPermission(context, permission)) {
            //If permission denied previously
            if ((context as Activity).shouldShowRequestPermissionRationale(permission)) {
                completion(CheckPermissionResult.PermissionPreviouslyDenied)
            } else {
                // Permission denied or first time requested
                if (PermissionUtil.isFirstTimeAskingPermission(context,
                                permission)) {
                    PermissionUtil.firstTimeAskingPermission(context,
                            permission,
                            false)
                    completion(CheckPermissionResult.PermissionAsk)
                } else {
                    // Handle the feature without permission or ask user to manually allow permission
                    completion(CheckPermissionResult.PermissionDisabled)
                }
            }
        } else {
            completion(CheckPermissionResult.PermissionGranted)
        }
    }
}

実装

PermissionHandler.checkPermission(activity,
                    Manifest.permission.CAMERA) { result ->
                when (result) {
                    CheckPermissionResult.PermissionGranted -> {
                        // openCamera()
                    }
                    CheckPermissionResult.PermissionDisabled -> {
                        // displayAlert(noPermissionAlert)
                    }
                    CheckPermissionResult.PermissionAsk -> {
                        // requestCameraPermissions()
                    }
                    CheckPermissionResult.PermissionPreviouslyDenied -> {
                        // displayAlert(permissionRequestAlert)
                    }
                }
            }
1
bmjohns

この方法でできますか?

@Retention(RetentionPolicy.SOURCE)
@IntDef({GRANTED, DENIED, NEVER})
public @interface PermissionStatus {
}

public static final int GRANTED = 0;
public static final int DENIED = 1;
public static final int NEVER = 2;

@PermissionStatus
public static int getPermissionStatus(Activity activity, String permission) {
    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
        return DENIED;
    } else {
        if (ActivityCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED) {
            return GRANTED;
        } else {
            return NEVER;
        }
    }
}
1
Dr. aNdRO

このコードは、実行中に許可を求めるようユーザーに要求します。ユーザーが許可する場合は結果メソッドを実行し、ユーザーが拒否する場合はユーザー拒否で説明を付けて再度要求します(指示で再度要求します)。二度と聞かないで処理し、指示とともにオープン設定オプションを表示します。

public String storagePermissions = Manifest.permission.READ_EXTERNAL_STORAGE;   
private static final int REQUEST_ACCESS =101;  

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      if(checkSelfPermission(storagePermissions)== PackageManager.PERMISSION_GRANTED){
          result();    // result  is your block of code 
      }else {
          requestPermissions(new String[]{storagePermissions},REQUEST_ACCESS);
      }

    }
    else{
        result();    //so if user is lower than api verison M, no permission is requested
    } 

}

 private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(MainActivity.this)
            .setMessage(message)
            .setTitle("Hi User..")
            .setPositiveButton("Ok", okListener)
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {        //idea calling showMessage funtion again
                    Snackbar mySnackbar = Snackbar.make( findViewById(R.id.coordinatorlayout),"You Press Cancel.. ", Snackbar.LENGTH_INDEFINITE);
                    mySnackbar.setAction("Exit", new cancelButton());
                    mySnackbar.show();

                }
            })
            .create()
            .show();
}


private void result(){
          //your code
}

    @RequiresApi(api = Build.VERSION_CODES.M)
public class NeverAskAgain implements View.OnClickListener{
    @Override
    public void onClick(View view)
    {
        goToSettings();
    }
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void goToSettings() {
    Intent myAppSettings = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
    finish();
    myAppSettings.addCategory(Intent.CATEGORY_DEFAULT);
    myAppSettings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivityForResult(myAppSettings, REQUEST_APP_SETTINGS);
}
public class cancelButton implements View.OnClickListener{
    @Override
    public void onClick(View view){
        Toast.makeText(MainActivity.this,"To use this app , you must grant storage permission",Toast.LENGTH_SHORT);
        finish();
    }
    }


 @Override
@RequiresApi(api = Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode,permissions,grantResults);

    switch(requestCode) {
        case REQUEST_ACCESS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission is granted
                    result();
                    break;
                }
                else if (!shouldShowRequestPermissionRationale(permissions[0])){
                    showMessageOKCancel("You choose Never Ask Again,option",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Snackbar mySnackbar = Snackbar.make(findViewById(R.id.coordinatorlayout), "Permission=>Storage=>On", Snackbar.LENGTH_INDEFINITE);
                        mySnackbar.setAction("Settings", new NeverAskAgain());
                        mySnackbar.show();
                    }
                     });
                    break;
                }
                else {
                    showMessageOKCancel("You Denid permission Request..",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            requestPermissions(new String[]{storagePermissions}, REQUEST_ACCESS);
                        }
                    });
                    break;
                }
        }
}
0
Abhiode

shouldShowRequestPermissionRationaleSPECIALパーミッションの場合は常にTRUEのみを返しますユーザーが拒否した後チェックボックスなし

興味があるFALSE value

false valueで失われたケースがあります:

1。以前はそのようなアクションはありませんでしたが、ユーザーは同意または拒否することにしました。

単に存在するプリファレンスASKED_PERMISSION_*を定義します。これは、同意または拒否の場合に開始されるonRequestPermissionsResulttrueになります。

したがって、この設定は存在しませんが、shouldShowRequestPermissionRationaleをチェックする理由なしがあります

2。ユーザーがクリックして同意します。

単に行う:

checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED

trueを返し、shouldShowRequestPermissionRationaleを確認するために理由なしが返されます

ユーザーがチェックボックス付きの拒否をクリックした(2回目以降の質問)

THE TIMEであるshouldShowRequestPermissionRationaleを使用するとFALSEが返されます

(設定が存在し、許可がありません)

0
V. Kalyuzhnyu