Android M - sprawdź uprawnienia do wykonywania - jak ustalić, czy użytkownik zaznaczył „Nigdy więcej nie pytaj”?


307

Zgodnie z tym: http://developer.android.com/preview/features/runtime-permissions.html#coding aplikacja może sprawdzać uprawnienia do wykonywania i żądać uprawnień, jeśli nie została jeszcze przyznana. Wyświetlone zostanie następujące okno dialogowe:

wprowadź opis zdjęcia tutaj

W przypadku, gdy użytkownik odrzuci ważne pozwolenie, aplikacja imo powinna wyświetlić wyjaśnienie, dlaczego pozwolenie jest potrzebne i jaki wpływ ma spadek. To okno dialogowe ma dwie opcje:

  1. spróbuj ponownie (prośba o pozwolenie ponownie)
  2. odmowa (aplikacja będzie działać bez tego pozwolenia).

Jeśli jednak użytkownik sprawdzi Never ask again, drugie okno dialogowe z wyjaśnieniem nie powinno być wyświetlane, zwłaszcza jeśli użytkownik już raz odmówił. Teraz pytanie brzmi: skąd moja aplikacja wie, czy użytkownik to sprawdził Never ask again? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)nie podaje mi tych informacji.

Drugie pytanie brzmiałoby: czy Google planuje wprowadzić niestandardowy komunikat w oknie dialogowym zezwolenia, który wyjaśnia, dlaczego aplikacja potrzebuje pozwolenia? W ten sposób nigdy nie będzie drugiego dialogu, który z pewnością poprawiłby UX.


9
„czy Google planuje dołączyć niestandardową wiadomość w oknie dialogowym zezwolenia, która wyjaśniałaby, dlaczego aplikacja potrzebuje pozwolenia?” - w prezentacji Google I | O na temat systemu uprawnień M. wydaje mi się, że ktoś pytał w pytaniach i odpowiedziach, a odpowiedź brzmiała, że ​​o tym myślą.
CommonsWare

1
Nie przetestowałem tego sam, ale dokumentacja mówi o Activity.shouldShowRequestPermissionRationale (String): Ta metoda zwraca wartość true, jeśli aplikacja wcześniej zażądała tego uprawnienia, a użytkownik odmówił żądania. Oznacza to, że prawdopodobnie powinieneś wyjaśnić użytkownikowi, dlaczego potrzebujesz pozwolenia. Jeśli użytkownik w przeszłości odrzucił żądanie uprawnień i wybrał opcję Nie pytaj ponownie w oknie dialogowym systemowym żądania uprawnień, ta metoda zwraca wartość false. Metoda zwraca również wartość false, jeśli zasady dotyczące urządzeń zabraniają aplikacji posiadania tego uprawnienia.
Fraid

1
@ Boję się: wygląda na to, że dodali to do wersji 2 Androida M: developer.android.com/preview/support.html#preview2-notes i prawdopodobnie tego właśnie szukałem. Nie mogę tego teraz przetestować, ale zrobię to w przyszłym tygodniu. Jeśli zrobi to, co mam nadzieję, zrobi to, możesz opublikować to jako odpowiedź i zdobyć reputację. Tymczasem może to pomóc innym: youtube.com/watch?v=f17qe9vZ8RM
Emanuel Moecklin

przykład Niebezpiecznych uprawnień i uprawnień specjalnych: github.com/henrychuangtw/AndroidRuntimePermission
HenryChuang

1
@Alex trudniej dla programistów, to na pewno, ale z punktu widzenia użytkownika możliwość udzielania lub odmowy określonych uprawnień ma sens. Główny problem, jaki widzę, polega na tym, że ziarnistość uprawnień jest bardzo niespójna i w rezultacie prosisz o pozwolenie, które może nie mieć prawie nic wspólnego z tym, co próbujesz zrobić w aplikacji (np. Pozwolenie na kontakty, gdy chcę się połączyć Dysk Google, ponieważ wymaga listy kont urządzeń do celów uwierzytelniania, a uprawnienia do konta należą do grupy uprawnień do kontaktów).
Emanuel Moecklin

Odpowiedzi:


341

Wersja zapoznawcza dla deweloperów 2 wprowadza pewne zmiany w sposobie żądania uprawnień przez aplikację (patrz także http://developer.android.com/preview/support.html#preview2-notes ).

Pierwsze okno dialogowe wygląda teraz tak:

wprowadź opis zdjęcia tutaj

Nie ma pola wyboru „Nigdy nie pokazuj ponownie” (w przeciwieństwie do wersji 1 dla programistów). Jeśli użytkownik odmówi pozwolenia i jeśli zezwolenie jest niezbędne dla aplikacji, może wyświetlić inne okno dialogowe wyjaśniające powód, dla którego aplikacja prosi o takie pozwolenie, np. W następujący sposób:

wprowadź opis zdjęcia tutaj

Jeśli użytkownik ponownie odmówi, aplikacja powinna się zamknąć, jeśli absolutnie potrzebuje tego pozwolenia, lub kontynuować działanie z ograniczoną funkcjonalnością. Jeśli użytkownik ponownie rozważy (i wybierze ponowną próbę), prośba o pozwolenie zostanie ponownie zażądana. Tym razem monit wygląda następująco:

wprowadź opis zdjęcia tutaj

Po raz drugi wyświetlane jest pole wyboru „Nigdy więcej nie pytaj”. Jeśli użytkownik ponownie zaprzecza, a pole wyboru jest zaznaczone, nic więcej nie powinno się wydarzyć. To, czy pole wyboru jest zaznaczone, można określić za pomocą Activity.shouldShowRequestPermissionRationale (String), np. W następujący sposób:

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

Tak mówi dokumentacja Androida ( https://developer.android.com/training/permissions/requesting.html ):

Aby znaleźć sytuacje, w których należy podać dodatkowe wyjaśnienie, system udostępnia metodę Activity.shouldShowRequestPermissionRationale (String). Ta metoda zwraca wartość true, jeśli aplikacja wcześniej zażądała tego uprawnienia, a użytkownik odrzucił żądanie. Oznacza to, że prawdopodobnie powinieneś wyjaśnić użytkownikowi, dlaczego potrzebujesz pozwolenia.

Jeśli użytkownik w przeszłości odrzucił żądanie uprawnień i wybrał opcję Nie pytaj ponownie w oknie dialogowym systemowym żądania uprawnień, ta metoda zwraca wartość false. Metoda zwraca również wartość false, jeśli zasady dotyczące urządzeń zabraniają aplikacji posiadania tego uprawnienia.

Aby wiedzieć, czy użytkownik odmówił za pomocą opcji „nigdy więcej nie pytaj”, możesz ponownie sprawdzić metodę shouldShowRequestPermissionRationale w swoim polu onRequestPermissionsResult, gdy użytkownik nie udzielił pozwolenia.

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

Możesz otworzyć ustawienia aplikacji za pomocą tego kodu:

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

Nie ma możliwości wysłania użytkownika bezpośrednio na stronę autoryzacji.


30
Zweryfikowałem wartość zwracaną metody shouldShowRequestPermissionRationale () na false, aby sprawdzić, czy użytkownik wybrał opcję „Nigdy więcej nie pytaj”. Ale po raz pierwszy otrzymuję też wartość fałszywą, gdy pytam o pozwolenie. Nie mogę więc rozróżnić, czy użytkownik zaznaczy pole wyboru „Nigdy więcej nie pytaj”, czy nie. Proszę zasugeruj??
Sagar Trehan

32
Zgodnie z moim rozumieniem metoda shouldShowRationalePermissionRationale () zwraca false w trzech przypadkach: 1. Jeśli wywołamy tę metodę po raz pierwszy, zanim poprosimy o pozwolenie. 2. Jeśli użytkownik wybierze opcję „Nie pytaj ponownie” i odmów zgody. 3. Jeśli zasady dotyczące urządzeń zabraniają aplikacji posiadania tego pozwolenia
Sagar Trehan

24
Wszystko dobrze ... ale my, programiści, naprawdę musimy wiedzieć, czy użytkownik powiedział „nigdy więcej nie pytaj”, czy nie. Mam ładny przycisk, aby uzyskać dostęp do funkcji. Przy pierwszym kliknięciu przez użytkownika: należy zapytać o uzasadnienie? nie, zapytaj o pozwolenie. Użytkownik odmawia. Użytkownik ponownie kliknij przycisk: uzasadnienie? tak! Pokaż uzasadnienie, użytkownik mówi Ok, a następnie zaprzeczaj i nigdy więcej nie pytaj (ok, jest idiotą, ale użytkownicy często nim są). Później użytkownik ponownie naciśnij przycisk, uzasadnienie? nie, zapytaj o pozwolenie, nic się nie dzieje dla użytkownika. Naprawdę potrzebuję sposobu, aby powiedzieć użytkownikowi: hej, jeśli chcesz tę funkcję, teraz przejdź do ustawienia aplikacji i daj pozwolenie.
Daniele Segato

4
Świetnie @EmanuelMoecklin, to jest lepsze niż Dokumentacja Google teraz: D
Daniele Segato,

4
onRequestPermissionsResult nie zostanie wywołany, chyba że poprosisz o pozwolenie. Ponieważ pole wyboru „Nigdy więcej nie pytaj” nie jest zaznaczone przy pierwszym zapytaniu o pozwolenie, funkcjaShowRequestPermissionRationale zwróci wartość True (wymagane zezwolenie, ale nigdy więcej nie pytaj). W związku z tym uzasadnienie jest zawsze wyświetlane przy pierwszym odrzuceniu przez użytkownika pozwolenia, ale dopiero wtedy, gdy pole wyboru nie zostało zaznaczone.
Emanuel Moecklin,

95

Możesz sprawdzić shouldShowRequestPermissionRationale()w swoim onRequestPermissionsResult().

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

Sprawdź, czy zezwolenie zostało udzielone czy nie onRequestPermissionsResult(). Jeśli nie, sprawdź shouldShowRequestPermissionRationale().

  1. Jeśli ta metoda powróci, truepokaż wyjaśnienie, dlaczego potrzebne jest to uprawnienie. Następnie w zależności od wyboru użytkownika ponownie requestPermissions().
  2. Jeśli zwróci, falseto pokaż komunikat o błędzie, że zezwolenie nie zostało udzielone i aplikacja nie może kontynuować pracy lub określona funkcja jest wyłączona.

Poniżej znajduje się przykładowy kod.

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

Najwyraźniej mapy Google robią to właśnie w celu uzyskania zezwolenia na lokalizację.


Dziękujemy za zdjęcie i link do YouTube. Odpowiada mniej więcej mojej własnej odpowiedzi. Należy zauważyć, że pytanie zostało zadane, gdy dostępna była tylko wersja 1 dla programistów, która nie miała metody shouldShowRequestPermissionRationale.
Emanuel Moecklin

Jestem nowy w Androidzie i chcę przejechać tą metodą onRequestPermissionsResult (). ale pojawia się błąd, że musi implementować metodę supertypu. czy możesz powiedzieć, jak tego używać
Andrain

39

Oto przyjemna i łatwa metoda sprawdzenia aktualnego stanu uprawnień:

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

Ostrzeżenie: zwraca BLOCKED_OR_NEVER_ASKED przy pierwszym uruchomieniu aplikacji, zanim użytkownik zaakceptuje / odmówi uprawnienia w wierszu polecenia (na urządzeniach SDK 23+)

Aktualizacja:

Biblioteka obsługi Androida wydaje się teraz mieć bardzo podobną klasę, android.support.v4.content.PermissionCheckerktóra zawiera checkSelfPermission()zwracającą:

public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;

1
Przy pierwszym uruchomieniu przechowuję wartość logiczną we wspólnych preferencjach.
Saeid Farivar

5
To zawsze zwraca, BLOCKED_OR_NEVER_ASKEDjeśli nie zażądano jeszcze pozwolenia.
Saket

6
Tak, to jest powód jego nazwie „BLOCKED_OR_NEVER_ASKED”, również ostatnie zdanie
Patrick Favre

3
android.content.pmjuż definiuje PERMISSION_GRANTED = 0i PERMISSION_DENIED = -1. Może zestaw BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1czy coś?
samis,

Zobacz odpowiedź mVck poniżej na temat postępowania z zastrzeżeniem.
samis,

28

Gdy użytkownik zaznaczy „Nie pytaj ponownie”, pytanie nie może być ponownie wyświetlone. Można jednak wyjaśnić użytkownikowi, że wcześniej odmówił pozwolenia i musi udzielić pozwolenia w ustawieniach. Odwołaj go do ustawień za pomocą następującego kodu:

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

podczas migracji do systemu AndroidX możesz zastąpić android.support.design.R przez com.google.android.material.R
Ridha Rezzag

26

Może być przydatny dla kogoś: -

Zauważyłem, że jeśli sprawdzimy flagę shouldShowRequestPermissionRationale () w metodzie wywołania zwrotnego onRequestPermissionsResult (), pokażą tylko dwa stany .

Stan 1: -Powrót prawda: - Za każdym razem, gdy użytkownik kliknie Odmów uprawnień (w tym za pierwszym razem).

Stan 2: -Wraca zwraca fałsz: - jeśli użytkownik wybierze „nigdy więcej nie pyta”.

Link do szczegółowego przykładu roboczego


2
Jest to właściwy sposób na wykrycie, czy użytkownik wybrał opcję Nigdy nie pytaj ponownie.
Muhammad Babar

Ach, kluczem tutaj jest to, że poradzisz sobie z tym onRequestPermissionsResult, a nie wtedy, gdy faktycznie poprosisz o pozwolenie.
Joshua Pinter

26

Możesz to ustalić , sprawdzając, czy uzasadnienie uprawnień ma być wyświetlane w onRequestPermissionsResult()metodzie wywołania zwrotnego. A jeśli znajdziesz jakieś zezwolenie, aby nigdy więcej nie pytać , możesz poprosić użytkowników o przyznanie uprawnień z ustawień.

Moja pełna implementacja byłaby jak poniżej. Działa zarówno z jednym, jak i wieloma żądaniami uprawnień. Skorzystaj z poniższych lub bezpośrednio z mojej biblioteki.

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

hii @nabin moim wymaganiem jest to, że kiedy klikam przycisk pobierania (który plik pdf), muszę sprawdzić, czy zezwolenie na zapis jest dozwolone lub zabronione, więc jak korzystać z tego kodu! możesz mi poprowadzić plz
Rucha Bhatt Joshi

cześć @RuchaBhatt Spójrz na moją bibliotekę. github.com/nabinbhandari/Android-Permissions
Nabin Bhandari

15

Jeśli chcesz wykryć wszystkie „stany” (po raz pierwszy odmowa, po prostu odmowa, po prostu odmowa z „Nigdy nie pytaj ponownie” lub trwałe odrzucenie), możesz wykonać następujące czynności:

Utwórz 2 booleany

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

Ustaw pierwszy przed pytaniem o pozwolenie:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Ustaw drugi w swojej metodzie onRequestPermissionsResult:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Użyj poniższej „tabeli”, aby zrobić wszystko, czego potrzebujesz w onRequestPermissionsResult () (po sprawdzeniu, że nadal nie masz uprawnień):

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

Przed wywołaniem metody requestPermissions nie ma sensu sprawdzanie opcji shouldShowRequestPermissionRationale, chyba że chcesz pokazać uzasadnienie przed zażądaniem pozwolenia. Przedstawienie uzasadnienia dopiero po odmowie przez użytkownika zezwolenia wydaje się być sposobem, w jaki większość aplikacji obsługuje go obecnie.
Emanuel Moecklin

2
@EmanuelMoecklin, o ile wiem, jest to jedyny sposób, aby sprawdzić, czy został już odrzucony (sprawdzając go przed i po, jak wyjaśniono w mojej tabeli prawdy) lub jeśli jest to pierwszy raz odmowy (w moim przypadku przekierowuję użytkownika do ustawienia aplikacji, jeśli zostanie ona trwale odrzucona)
mVck 30.01.2017

1
// TRUE FALSEwystępuje również, gdy użytkownik zezwala na pozwolenie po uprzednim odmowie.
samis,

11

Miałem ten sam problem i zrozumiałem. Aby uprościć życie, napisałem klasę util do obsługi uprawnień środowiska wykonawczego.

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

A metody PreferenceUtil są następujące.

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

Teraz wystarczy użyć metody * checkPermission * z odpowiednimi argumentami.

Oto przykład,

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

skąd moja aplikacja wie, czy użytkownik zaznaczył „Nigdy więcej nie pytaj”?

Jeśli użytkownik zaznaczy Nigdy nie pytaj ponownie , otrzymasz oddzwonienie na onPermissionDisabled .

Miłego kodowania :)


shouldShowRequestPermissionRationale Mam błąd tutaj, czy możesz mi pomóc?
Rucha Bhatt Joshi

nie mogę znaleźć tej metody powinienShowRequestPermissionRationale może nie udało mi się uzyskać kontekstu .. ale jest w porządku, znalazłem inne alternatywne rozwiązanie .. Dziękuję za pomoc :)
Rucha Bhatt Joshi

1
Mój błąd. shouldShowRequestPermissionRationale jest dostępny poprzez działanie, a nie kontekst. Zaktualizowałem odpowiedź, przesyłając kontekst do działania przed wywołaniem tej metody. Sprawdź to :)
muthuraj

1
Jest to jedyny sposób na obejście pierwszej fałszywej wartości zwróconej przez shouldShowRequestPermissionRationale, zapisując preferencję żądania wysłanego do użytkownika. Miałem ten sam pomysł i znalazłem twoją odpowiedź. Niezły robotnik
MatPag,

4

Pełne wyjaśnienie każdego przypadku zezwolenia

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

    }
}

4

Przydatna funkcja pozwalająca ustalić, czy nie można żądać (w Kotlinie) arbitralnego pozwolenia:

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
}

Użycie tego wymaga ustawienia wspólnego logicznego ustawienia preferencji z nazwą żądanego uprawnienia (np. android.Manifest.permission.READ_PHONE_STATE) trueNa pierwsze żądanie zezwolenia.


Wyjaśnienie:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ponieważ część kodu można uruchomić tylko na poziomie API 23+.

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED aby sprawdzić, nie mamy jeszcze pozwolenia.

!activity.shouldShowRequestPermissionRationale(permission)aby sprawdzić, czy użytkownik odrzucił aplikację, pytając ponownie. Ze względu na dziwactwa tej funkcji wymagany jest również następujący wiersz.

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) służy to (wraz z ustawieniem wartości true na pierwsze żądanie zezwolenia) w celu rozróżnienia stanów „Nigdy nie pytano” i „Nigdy więcej nie pytaj”, ponieważ poprzedni wiersz nie zwraca tej informacji.


3

Proszę, nie rzucaj we mnie kamieniami za to rozwiązanie.

To działa, ale jest nieco „hacky”.

Kiedy dzwonisz requestPermissions, zarejestruj bieżącą godzinę.

        mAskedPermissionTime = System.currentTimeMillis();

Następnie w onRequestPermissionsResult

jeśli wynik nie zostanie przyznany, sprawdź ponownie czas.

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

Ponieważ użytkownik nie mógł tak szybko kliknąć przycisku odmowy, wiemy, że wybrał opcję „nigdy więcej nie pytaj”, ponieważ oddzwanianie jest natychmiastowe.

Używaj na własne ryzyko.


co jeśli zobaczymy to okno dialogowe przez 5 minut, a następnie odmówisz?
saksham

Więc jaki jest pożytek z tego, jeśli nie może spełnić podstawowego wymogu. Kod może być włamaniem, jeśli zostanie zaakceptowany, jeśli w oczywisty sposób spełnia wszystkie wymagania.
saksham

Tak, to źle.
Tacy

3

Metoda powinnaShShRRequestPermissionRationale () można wykorzystać do sprawdzenia, czy użytkownik wybrał opcję „nigdy więcej nie pytał” i odmówił pozwolenia. Istnieje wiele przykładów kodu, więc wolałbym wyjaśnić, jak go używać do takiego celu, ponieważ myślę, że jego nazwa i implementacja sprawiają, że jest to bardziej skomplikowane niż w rzeczywistości.

Jak wyjaśniono w części Żądanie uprawnień w czasie wykonywania , ta metoda zwraca wartość true, jeśli opcja „nigdy więcej nie pytaj” jest widoczna, w przeciwnym razie wartość false; więc zwraca wartość false przy pierwszym wyświetleniu okna dialogowego, a następnie po raz drugi zwraca wartość true, i tylko wtedy, gdy użytkownik odmówi uprawnienia, wybierając opcję, w tym momencie ponownie zwraca wartość false.

Aby wykryć taki przypadek, możesz wykryć sekwencję fałsz-prawda-fałsz lub (prościej) możesz mieć flagę, która śledzi początkowy czas wyświetlania okna dialogowego. Następnie metoda ta zwraca true lub false, gdzie false pozwoli ci wykryć, kiedy wybrana jest opcja.


2

Napisałem skrót dla prośby o pozwolenie w Androidzie M. Ten kod obsługuje również wsteczną kompatybilność ze starszymi wersjami Androida.

Cały brzydki kod jest wyodrębniany do fragmentu, który dołącza się i odłącza do działania z żądaniem uprawnień. Możesz użyć PermissionRequestManagerw następujący sposób:

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

Zobacz: https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa


2
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    switch (requestCode) {
        case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
            if (grantResults.length > 0) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    // Denied
                } else {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                        // To what you want
                    } else {
                       // Bob never checked click
                    }
                }
            }
        }
    }
}

2

Wypróbuj tę prostą bibliotekę uprawnień. Obsługuje wszystkie operacje związane z uprawnieniami w 3 łatwych krokach. Oszczędzało mi to czas. Możesz zakończyć wszystkie prace związane z pozwoleniami w 15 minut .

Może obsłużyć Odmów, Może obsłużyć Nigdy więcej nie pytaj, Może wywołać ustawienia aplikacji w celu uzyskania zgody, Może dać wiadomość wymierną, Może dać wiadomość odmowy, Może podać listę zaakceptowanych uprawnień, Może podać listę odmowy uprawnienia itp.

https://github.com/ParkSangGwon/TedPermission

Krok 1: dodaj swoją zależność

dependencies {
     compile 'gun0912.ted:tedpermission:2.1.1'
     //check the above link for latest libraries
}

Krok 2: Zapytaj o uprawnienia

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

Krok 3: obsłużyć odpowiedź uprawnień

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

Świetny. Oszczędzało mi to czas
Vigneswaran A

Ładny, łatwy w użyciu
Uray Febri

2

możesz ładnie słuchać.

Słuchacz

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

MainClass o pozwolenie

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

    }
}

Używany w ten sposób

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

                })

przesłonić onRequestPermissionsResult w aktywności lub fragmnet

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
 if (requestCode == 118) {
        if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getLastLocationInMap()
        }
        }
    }

1

Zamiast tego otrzymasz oddzwonienie onRequestPermissionsResult()jako PERMISSION_DENIED, gdy ponownie poprosisz o pozwolenie, gdy popełnisz błądshouldShowRequestPermissionRationale()

Z dokumentu Androida:

Gdy system poprosi użytkownika o udzielenie pozwolenia, użytkownik może powiedzieć mu, aby nie pytał o to pozwolenie ponownie. W takim przypadku za każdym razem, gdy aplikacja requestPermissions()ponownie zażąda tego pozwolenia, system natychmiast odrzuca żądanie. System wywołuje onRequestPermissionsResult()metodę wywołania zwrotnego i przechodzi PERMISSION_DENIEDw ten sam sposób, jak gdyby użytkownik ponownie wyraźnie odrzucił żądanie. Oznacza to, że kiedy zadzwoniszrequestPermissions() , nie możesz zakładać, że miała miejsce jakakolwiek bezpośrednia interakcja z użytkownikiem.


1

Możesz użyć if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) metody wykrywania, czy opcja Nigdy nie pytaj jest zaznaczona, czy nie.

Więcej informacji: sprawdź to

Aby sprawdzić, czy masz wiele uprawnień, użyj:

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

Metoda replace ()

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

Powyższy kod pokaże także okno dialogowe, które przekieruje użytkownika do ekranu ustawień aplikacji, z którego może on wyrazić zgodę, jeśli zaznaczył przycisk Nigdy więcej nie pytaj.


1

Możesz użyć

shouldShowRequestPermissionRationale()

wewnątrz

onRequestPermissionsResult()

Zobacz przykład poniżej:

Sprawdź, czy ma uprawnienia, gdy użytkownik kliknie przycisk:

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

Gdy użytkownik odpowie na okno dialogowe uprawnień, przejdziemy do onRequestPermissionResult:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {
        // Case 1. Permission is granted.  
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                // Before navigating, I still check one more time the permission for good practice.
                navigateTo(MainActivity.class);
            }
        } else { // Case 2. Permission was refused
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                // Case 2.1. shouldShowRequest... returns true because the
                // permission was denied before. If it is the first time the app is running we will 
                // end up in this part of the code. Because he need to deny at least once to get 
                // to onRequestPermissionsResult. 
                Snackbar snackbar = Snackbar.make(findViewById(R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);
                snackbar.setAction("VERIFY", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        ActivityCompat.requestPermissions(SettingsActivity.this
                                , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
                                , WRITE_EXTERNAL_PERMISSION_REQUEST_CODE);
                    }
                });
                snackbar.show();
            } else {
                // Case 2.2. Permission was already denied and the user checked "Never ask again". 
                // Navigate user to settings if he choose to allow this time.
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(R.string.instructions_to_turn_on_storage_permission)
                        .setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                settingsIntent.setData(uri);
                                startActivityForResult(settingsIntent, 7);
                            }
                        })
                        .setNegativeButton(getString(R.string.not_now), null);
                Dialog dialog = builder.create();
                dialog.show();
            }
        }
    }

}

0

Chciałbym również uzyskać informację, czy użytkownik wybrał opcję „nigdy więcej nie pytaj”. Osiągnąłem „prawie rozwiązanie” z brzydko wyglądającą flagą, ale zanim powiem ci, jak to zrobić, powiem ci o mojej motywacji:

Na początku chciałbym zaoferować uprawnienia dotyczące funkcjonalności. Jeśli użytkownik korzysta z niego i nie ma żadnych uprawnień, otrzymuje albo 1 okno dialogowe z góry, albo 2 i 3. Gdy użytkownik wybierze opcję „Nigdy więcej nie pytaj”, chciałbym wyłączyć tę funkcję i wyświetlić ją inaczej. - Moje działanie jest wywoływane przez wpisywanie tekstu tarczy, chciałbym również dodać „(Odwołanie uprawnień)” do wyświetlanego tekstu etykiety. To pokazuje użytkownikowi: „Istnieje funkcjonalność, ale nie mogę jej używać ze względu na moje ustawienia uprawnień”. Nie wydaje się to jednak możliwe, ponieważ nie mogę sprawdzić, czy wybrano opcję „Nigdy więcej nie pytaj”.

Doszedłem do rozwiązania, w którym mogę żyć, mając zawsze włączoną funkcjonalność z aktywnym sprawdzaniem uprawnień. Wyświetlam komunikat Toast w onRequestPermissionsResult () w przypadku negatywnej odpowiedzi, ale tylko wtedy, gdy nie pokazałem mojego niestandardowego wyskakującego uzasadnienia. Jeśli więc użytkownik wybrał opcję „Nigdy więcej nie pytaj”, otrzyma tylko wiadomość toast. Jeśli użytkownik niechętnie wybierze opcję „nigdy więcej nie pytaj”, otrzymuje tylko niestandardowe uzasadnienie i wyskakujące okienko z prośbą o pozwolenie przez system operacyjny, ale nie wznosi toastu, ponieważ trzy powiadomienia z rzędu byłyby zbyt uciążliwe.


0

Muszę wdrożyć dynamiczne uprawnienia do kamery. W przypadku wystąpienia 3 możliwych przypadków: 1. Zezwól, 2. Odmowa, 3. Nie pytaj ponownie.

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

0

Rozwijając powyższą odpowiedź mVck , następująca logika określa, czy „Nigdy więcej nie pytaj” zostało sprawdzone dla danego Żądania Pozwolenia:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
    !bStorage && (
        _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
        _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
    );

który jest fragmentem poniżej (pełny przykład zobacz tę odpowiedź )

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


0

Aby dokładnie odpowiedzieć na pytanie: Co się stanie, gdy użytkownik naciśnie „Nigdy więcej nie pytaj”?

Przesłonięta metoda / funkcja

onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)

Tablica grantResult okazuje się być pusta, więc możesz coś tam zrobić? Ale nie najlepsza praktyka.

Jak obsługiwać „Nigdy więcej nie pytaj”?

Pracuję z Fragmentem, który wymagał pozwolenia 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()

            }
        }
    }

Pozostałe funkcje są banalne.

// 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)
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.