65

Googleによると、M Developer Preview のランタイム権限に関しては、次のようになります。

  1. 以前に特定の許可を求めたことがない場合は、それを求めてください

  2. 以前に尋ねたときに、ユーザーが「いいえ」と答え、そのユーザーが拒否されたアクセス許可を必要とする何かを実行しようとした場合は、再度アクセス許可を要求する前に、アクセス許可が必要な理由をユーザーに説明するように求める必要があります。

  3. 以前に数回尋ねたときに、ユーザーが (実行時のアクセス許可ダイアログのチェックボックスを介して) 「いいえ、尋ねるのをやめてください」と言った場合は、気にするのをやめるべきです (たとえば、許可を必要とする UI を無効にします)。

ただし、 をshouldShowRequestPermissionRationale()返すメソッドは 1 つだけで、boolean状態は 3 つあります。false両方から得られるように、決して要求されない状態と停止要求状態を区別する方法が必要shouldShowRequestPermissionRationale()です。

アプリの最初の実行時にアクセス許可が要求される場合、これは大きな問題ではありません。これがおそらくアプリの最初の実行であると判断するためのレシピはたくさんあります (例: のbooleanSharedPreferences)。

ただし、実行時のパーミッションのビジョンの一部は、すべてのパーミッションを事前に要求しない可能性があるということです。ユーザーがその許可を必要とするものをタップしたときにのみ、後で要求する可能性のあるフリンジ機能に関連付けられた許可。ここで、アプリは何ヶ月にもわたって何度も実行された後、突然別の許可を要求する必要が生じた可能性があります。

そのような場合、自分で許可を求めたかどうかを追跡する必要がありますか? それとも、以前に尋ねたかどうかを教えてくれる、私が見逃している Android M API の何かがありますか?

4

10 に答える 10

68

私は非常に遅れて投稿していることを知っていますが、詳細な例は誰かにとって役立つかもしれません.

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

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

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

複数の許可リクエストの例を次に示します。

アプリは起動時に 2 つの権限を必要とします。SEND_SMS および ACCESS_FINE_LOCATION (どちらも manifest.xml に記載されています)。

アプリが起動するとすぐに、複数の権限をまとめて要求します。両方の権限が付与されている場合は、通常のフローになります。

ここに画像の説明を入力

public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(checkAndRequestPermissions()) {
        // carry on the normal flow, as the case of  permissions  granted.
    }
}

private  boolean checkAndRequestPermissions() {
    int permissionSendMessage = ContextCompat.checkSelfPermission(this,
            Manifest.permission.SEND_SMS);
    int locationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
    List<String> listPermissionsNeeded = new ArrayList<>();
    if (locationPermission != PackageManager.PERMISSION_GRANTED) {
        listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
    }
    if (permissionSendMessage != PackageManager.PERMISSION_GRANTED) {
        listPermissionsNeeded.add(Manifest.permission.SEND_SMS);
    }
    if (!listPermissionsNeeded.isEmpty()) {
        ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),REQUEST_ID_MULTIPLE_PERMISSIONS);
        return false;
    }
    return true;
}

1 つ以上のアクセス許可が付与されていない場合、 activityCompat.requestPermissions() はアクセス許可を要求し、コントロールは onRequestPermissionsResult() コールバック メソッドに移動します。

onRequestPermissionsResult() コールバック メソッドの shouldShowRequestPermissionRationale() フラグの値を確認する必要があります。

2 つのケースのみがあります:--

ケース 1: - ユーザーが [アクセス許可を拒否] をクリックするたびに (初回を含む)、true が返されます。そのため、ユーザーが拒否した場合は、より多くの説明を表示して、再度質問を続けることができます。

ケース 2: -ユーザーが「二度と質問しない」を選択した場合のみ、false が返されます。この場合、制限された機能を続行し、ユーザーをガイドしてより多くの機能の設定からアクセス許可を有効にするか、アプリのアクセス許可が些細な場合はセットアップを終了できます。

ケース-1

ケース - 1

CASE-2

ケース - 2

@Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        Log.d(TAG, "Permission callback called-------");
        switch (requestCode) {
            case REQUEST_ID_MULTIPLE_PERMISSIONS: {

                Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.SEND_SMS, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                // Fill with actual results from user
                if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);
                    // Check for both permissions
                    if (perms.get(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                        Log.d(TAG, "sms & location services permission granted");
                        // process the normal flow
                        //else any one or both the permissions are not granted
                    } else {
                            Log.d(TAG, "Some permissions are not granted ask again ");
                            //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
//                        // shouldShowRequestPermissionRationale will return true
                            //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
                            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
                                showDialogOK("SMS and Location Services Permission 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.
                                                        break;
                                                }
                                            }
                                        });
                            }
                            //permission is denied (and never ask again is  checked)
                            //shouldShowRequestPermissionRationale will return false
                            else {
                                Toast.makeText(this, "Go to settings and enable permissions", Toast.LENGTH_LONG)
                                        .show();
    //                            //proceed with logic by disabling the related features or quit the app.
                            }
                    }
                }
            }
        }

    }

    private void showDialogOK(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", okListener)
                .create()
                .show();
    }
于 2016-02-19T00:49:44.777 に答える
15

現在の例によると: https://github.com/googlesamples/android-RuntimePermissions/blob/master/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java#L195

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
        int[] grantResults) {
    if (requestCode == REQUEST_CAMERA) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            doThing();
            //STORE FALSE IN SHAREDPREFERENCES
        } else {
            //STORE TRUE IN SHAREDPREFERENCES
        }
    }

その設定が以前に拒否されたかどうかを示すために、ブール値を SharedPreferences に格納し、キーをパーミッション コードとして、値を上記のように指定します。

残念ながら、アプリの実行中に受け入れられ、後で拒否された設定を確認することはおそらくできません。最終的な仕様は入手できませんが、アプリが再起動されるか、次の起動までモック値が取得される可能性があります。

于 2015-08-10T21:02:36.677 に答える
6

いいえ、許可を求めたかどうかを追跡する必要はありません。また、Never-Asked と Stop-Asking を区別する必要もありません。

状態 1 と 3 は、アプリ開発者にとって同じです。許可が必要であり、許可が必要な機能をユーザーがタップするたびに、何度要求しても、ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTEDを介して許可を求めるだけです。ActivityCompat.requestPermissions()ユーザーは最終的にそれを「許可」するか、「二度と確認しない」をオンにして「拒否」します。この設計では、許可要求ダイアログボックスを複数回ポップアップすることを思いとどまらせることはありません。

ただし、この設計では、ある時点で許可の目的を説明することをお勧めします。2.状態shouldShowRequestPermissionRationale()は、許可を要求する必要があるかどうかを判断するために使用されるのではなく、許可を要求する前に説明を表示する必要があるかどうかを判断するために使用されます。

状態 3 に関するいくつかの説明:

  1. はい、リクエストを停止するのではなく、説明の表示を停止することで、ユーザーを煩わせることをやめるべきです。それが彼らが提供した理由shouldShowRequestPermissionRationale()です。
  2. 許可のリクエストを保持することは面倒ではありません。ユーザーが「二度と質問しない」を選択すると、ActivityCompat.requestPermissions()ダイアログボックスがポップアップしなくなります。
  3. 単一ユーザー セッション中にアクセス許可がないことが判明するたびに、関連する UI を無効にすることをお勧めします。shouldShowRequestPermissionRationale()false を返した後に UI を無効にする代わりに。
于 2015-09-16T10:03:24.673 に答える
2

私はあなたの問題を解決するためのアプローチを持っています。それは私にとってはかなりうまくいくようです。

SharedPreferences を使用して、Never-Asked と Stop-Asking を区別します。それをどのように使用するかの例を示します。

private void requestAccountPermission() {

        SharedPreferences mPreferences = getSharedPreferences("configuration", MODE_PRIVATE);
        boolean firstTimeAccount = mPreferences.getBoolean("firstTimeAccount", true);

        if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.GET_ACCOUNTS)) {
            // 2. Asked before, and the user said "no"
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_CODE_ACCOUNTS);
        }else {
            if(firstTimeAccount) { 
                // 1. first time, never asked 
                SharedPreferences.Editor editor = mPreferences.edit();
                editor.putBoolean("firstTimeAccount", false);
                editor.commit();

                // Account permission has not been granted, request it directly.
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS},REQUEST_CODE_ACCOUNTS);
            }else{
                // 3. If you asked a couple of times before, and the user has said "no, and stop asking"

                // Your code
            }
        }
    }
于 2015-11-10T16:22:02.830 に答える
1

ここですべての答えとインターネット上の他の投稿を試した後。isLocationPermissionDialogShownsharedPreference (デフォルトは false) を使用する必要があり、すべてが期待どおりに機能することがわかりました。

  1. 初めて許可を求めた場合。この場合、shouldShowRequestPermissionRationale戻り値falseisLocationPermissionDialogShownfalse
  2. 2 回目shouldShowRequestPermissionRationaleに戻りtrue、ダイアログを表示しながら に設定isLocationPermissionDialogShowntrueます。状態を確認すると、両方がtrue
  3. shouldShowRequestPermissionRationaleNever Ask Again にtrueチェックを入れるまで毎回 isLocationPermissionDialogShowntrue
  4. Never Ask Again にチェックが入っている場合 は、 return とshouldShowRequestPermissionRationalereturnfalseisLocationPermissionDialogShown返しますtrue。これが私たちが必要としているものです。

作業例を確認してください。

public class MainActivity extends AppCompatActivity {
    SharedPreferences sharedPreferences;
    String locationPermission;
    String prefLocationPermissionKey = "isLocationPermissionDialogShown";
    private final int PERMISSION_REQUEST_CODE_LOCATION = 1001;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        locationPermission = Manifest.permission.ACCESS_FINE_LOCATION;
        sharedPreferences = getSharedPreferences("configuration", MODE_PRIVATE);

        //check for android version
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //Check for permission
            if (checkSelfPermission(locationPermission) != PackageManager.PERMISSION_GRANTED) {
                //check if clarification dialog should be shown.
                if (shouldShowRequestPermissionRationale(locationPermission)) {
                    showClarificationDialog(locationPermission, PERMISSION_REQUEST_CODE_LOCATION);
                } else  {
                    requestPermissions(new String[] { locationPermission}, PERMISSION_REQUEST_CODE_LOCATION);
                }
            } else {
                Log.d("nets-debug", "permission already grranted");
            }
        }

    }

    @Override
    @TargetApi(Build.VERSION_CODES.M)
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            //for location permission
            if (requestCode == PERMISSION_REQUEST_CODE_LOCATION) {
                boolean isLocationPermissionDialogShown = sharedPreferences.getBoolean(prefLocationPermissionKey, false);

                if (!shouldShowRequestPermissionRationale(locationPermission) && isLocationPermissionDialogShown) {
                    // user selected Never Ask Again. do something
                    Log.d("nets-debug", "never ask again");
                } else {
                    // all other conditions like first time asked, previously denied etc are captured here and can be extended if required.
                    Log.d("nets-debug", "all other cases");
                }
            }

        }

    }

    @TargetApi(Build.VERSION_CODES.M)
    public void showClarificationDialog(final String permission, final int requestCode) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Permission Required");
        builder.setMessage("Please grant Location permission to use all features of this app");
        builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putBoolean(prefLocationPermissionKey, true);
                editor.apply();
                requestPermissions(new String[] {permission}, requestCode);
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), "This permission required", Toast.LENGTH_LONG).show();
            }
        });
        builder.create().show();
    }

}

これが役立つことを願っています。

于 2017-05-21T16:31:29.623 に答える
-3

パーミッション状態の並列永続状態を作成する必要はありません。いつでも現在のパーミッション状態を返すこのメソッドを使用できます。

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

    public static final int GRANTED = 0;
    public static final int DENIED = 1;
    public static final int BLOCKED = 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;
            }
            return DENIED;
        }
        return GRANTED;
    }

警告: ユーザーがユーザー プロンプトを介して許可を承認/拒否する前に、最初のアプリの起動をブロックします (SDK 23 以降のデバイス)。

こちらの回答も使用しました。

于 2016-01-27T14:58:06.917 に答える