web-dev-qa-db-ja.com

Android Mのランタイムアクセス許可で、非要求と停止要求を区別するにはどうすればよいですか?

Google によると、M Developer Previewランタイムのアクセス許可に関しては:

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

  2. 以前に尋ねて、ユーザーが「いいえ」と言った後、ユーザーが拒否された許可を必要とすることを試みる場合、許可を再度要求する前に、許可が必要な理由を説明するようにユーザーにプロンプ​​トを出す必要があります

  3. 数回前に質問し、ユーザーが「いいえ、質問をやめる」と言った場合(ランタイムパーミッションダイアログのチェックボックスを使用)、わざわざ停止するだけです(例えば、パーミッションを必要とするUIを無効にする)

ただし、booleanを返すshouldShowRequestPermissionRationale()というメソッドは1つしかなく、3つの状態があります。両方に対してshouldShowRequestPermissionRationale()からfalseを取得するため、決して問い合わせられない状態とストップ問い合わせされる状態を区別する方法が必要です。

アプリの初回実行時に許可が要求される場合、これは大きな問題ではありません。これがおそらくアプリの最初の実行であることを判断するための多くのレシピがあります(たとえば、booleanSharedPreferences値)。したがって、アプリの最初の実行であれば、あなたは決して尋ねられていない状態です。

ただし、実行時のアクセス許可のビジョンの一部は、すべてのアクセス許可を事前に要求しない場合があることです。ユーザーがその許可を必要とするものをタップしたときにのみ、後で要求する可能性のある周辺機能に関連付けられた許可。ここで、アプリが何ヶ月も何回も実行された後、突然別の許可を要求する必要があります。

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

62
CommonsWare

現在の例に従って: 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に保存し、その設定が以前に拒否されたかどうかを示します。

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

13

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

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

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

状態2:-falseを返します-ユーザーがsを選択した場合、「二度と尋ねません。

以下は、複数の許可要求がある例です。

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

アプリが起動するとすぐに、複数の許可を一緒に要求します。両方の許可が付与された場合、通常のフローが実行されます。

enter image description here

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を返します。この場合、制限された機能を続行し、より多くの機能の設定からアクセス許可をアクティブにするようユーザーをガイドするか、アプリのアクセス許可が些細な場合はセットアップを終了できます。

CASE- 1

Case - 1

ケース-2

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

いいえ、許可を要求したかどうかを追跡する必要はありません。また、「要求なし」と「要求なし」を区別する必要はありません。

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

ただし、設計では、ある時点で許可の目的を説明することを推奨しています。状態2。shouldShowRequestPermissionRationale()は許可を要求するかどうかを決定するために使用されません。許可を求める前に。

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

  1. はい、停止要求ではなく、説明の表示を停止することでユーザーの迷惑を停止する必要があります。それが彼らがshouldShowRequestPermissionRationale()を提供した理由です。
  2. 許可のリクエストを続けるのは面倒ではありません。ユーザーが「二度と尋ねない」を選択すると、ActivityCompat.requestPermissions()はダイアログボックスをポップアップしなくなります。
  3. シングルユーザーセッション中に、権限がないことが判明するたびに、関連するUIを無効にすることをお勧めします。 shouldShowRequestPermissionRationale()がfalseを返した後にUIを無効にする代わりに。
6
ed9er

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

SharedPreferencesを使用して、要求なしと要求なしを区別し、それをどのように使用するかの例を示します。

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

許可ダイアログが初めて表示されたとき、ユーザーが二度と尋ねないをチェックしたとき、ユーザーが二度と尋ねないで許可が直接拒否されたときを追跡する方法を次に示します。 onRequestPermissionsResultになります。必要に応じて、メソッドcheckPermission()を呼び出します。

public boolean mPermissionRationaleDialogShown = false;

public void checkPermission() {
    if (ContextCompat.checkSelfPermission(this, "PermissionName")
            != PackageManager.PERMISSION_GRANTED) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")) {
            showPermissionRequiredDialog();
        } else {
            askPermission();
        }
    } else {
       // Permission Granted
    }
}

public void askPermission() {
    ActivityCompat.requestPermissions(this,
            new String[]{"PermissionName"}, permissionRequestCode);
}

public void showPermissionRequiredDialog() {
    mPermissionRationaleDialogShown = true;
    // Dialog to show why permission is required
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == PERMISSION_REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission Granted
        } else {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
                    && !mPermissionRationaleDialogShown) {
                // Permission dialog was shown for first time
            } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")
                    && mPermissionRationaleDialogShown){
                // User deny permission without Never ask again checked
            } else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, PERMISSION_READ_EXTERNAL)
                    && mPermissionRationaleDialogShown) {
                // User has checked Never ask again during this permission request
            } else {
                // No permission dialog shown to user has user has previously checked Never ask again. Here we can show dialog to open setting screen to change permission
            }
        }
    } else {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}
1
Yogesh Pareek

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

  1. 初めて許可を求められた場合。この場合、shouldShowRequestPermissionRationalefalseを返し、isLocationPermissionDialogShownfalseを返します
  2. 2回目はshouldShowRequestPermissionRationaletrueを返し、ダイアログの表示中にisLocationPermissionDialogShowntrueに設定します。条件をチェックすると、両方がtrueになります
  3. 二度と尋ねられなくなるまで毎回shouldShowRequestPermissionRationaletrueを返し、isLocationPermissionDialogShowntrueを返します
  4. 二度と尋ねない場合は、shouldShowRequestPermissionRationalefalseを返し、isLocationPermissionDialogShowntrueを返します。これが必要なものです。

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

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

}

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

1
Natwar Singh

ついに私の時間は、Commswareからの質問に答えるようになりました


ビジネスフロー:-

1。 ユーザーが「許可を拒否する」を初めてクリックすると、許可の必要性を説明するための理論的ダイアログが表示されます。次に、ユーザーが根拠ダイアログの「キャンセル」ボタンをクリックすると、「場所を取得する許可を与えてください」というメッセージを示すトーストが表示されます。

2。 その後、ユーザーがアクセス許可ダイアログでアクセス許可の拒否(再度尋ねない)をクリックすると、「アプリの設定から場所のアクセス許可を与えてください」というメッセージが表示されます。ユーザーが「再度尋ねない」のボックスをチェックしたため、「アプリの設定から」という言葉を追加したことに注意してください。

3。 したがって、これ以降、許可ダイアログは表示されません。 根拠のあるダイアログは表示されません。

したがって、ここで重要なのは、許可ダイアログと理論的ダイアログの両方が表示されない場合、ユーザーが「再度尋ねない」チェックボックスをチェックしたことを意味します。

コード:-

        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)){
                AlertDialogHelper.showDialogWithYesNoCallback(mContext, getString(R.string.confirm), getString(R.string.please_give_permission_to_get_location), new onItemClickReturnBoolean() {
                    @Override
                    public void onItemClick(Boolean status) {
                        if(status){
                            ActivityCompat.requestPermissions(SplashScreenActivity.this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
                        }
                        else{
                            ShowToast.showShortToast(SplashScreenActivity.this,getString(R.string.please_give_permission_to_get_location));
                            finish();
                        }
                    }
                });
            }
            else{
                ActivityCompat.requestPermissions(this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE);
            }
        }
        else{
            gettingLocationAfterPermissionGranted();
        }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE){
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                gettingLocationAfterPermissionGranted();
            }
            else{
                if(ActivityCompat.shouldShowRequestPermissionRationale(SplashScreenActivity.this,Manifest.permission.ACCESS_FINE_LOCATION)){
                    ShowToast.showShortToast(this,getString(R.string.please_give_permission_to_get_location));
                }
                else{
                    ShowToast.showShortToast(this,getString(R.string.please_give_location_permission_from_app_settings));
                }
                finish();
            }
        }
    }

このリポジトリを確認してください: https://github.com/debChowdhury/PermissionHelperEasy


Easy peasy


0

here を見ることができます-非常に良いプロセスを説明するフローチャートがあります。また、shouldShowRequestPermissionRationale()をいつ呼び出すべきか、いつtrueを返すかについても説明します。

基本的にAndroidのドキュメントによれば、持っていない場合は常に許可を求める必要があり(ユーザーが二度と尋ねない場合はAndroidはコールバックで自動的に拒否を返します)、ユーザーが既に辞退している場合は短いメッセージを表示する必要があります過去に一度だけ表示したが、二度と尋ねないオプションをマークしていない。

0
Ronnie