shouldShowRequestPermissionRationale — to metoda Android API, która podpowiada programiście, czy należy pokazać użytkownikowi wyjaśnienie przed zapytaniem o niebezpieczne uprawnienie. Według Android Developer Reference, 2024, metoda rational zwraca true, jeśli użytkownik wcześniej odrzucił zapytanie, ale nie ustawił flagi Never Ask Again. To kluczowe narzędzie do budowania przyjaznego UX przy pracy z uprawnieniami runtime.
Najważniejsze
shouldShowRequestPermissionRationale — to metoda klasy Activity i Fragment w Android, dostępna przez ActivityCompat dla zachowania zgodności. Przyjmuje nazwę uprawnienia i zwraca Boolean wskazujący, czy należy pokazać użytkownikowi dodatkowe wyjaśnienie przed ponownym zapytaniem. Metoda pojawiła się w Android 6.0 Marshmallow wraz z modelem uprawnień runtime.
Mechanizm rationale opiera się na śledzeniu historii interakcji użytkownika z oknami uprawnień. System zapamiętuje, czy użytkownik odrzucił zapytanie wcześniej. Jeśli odrzucenie nastąpiło bez ustawienia flagi Never Ask Again, shouldShowRequestPermissionRationale zwraca true. To sygnał dla programisty: użytkownik nie rozumie, dlaczego potrzebne jest uprawnienie, i wymagane jest dodatkowe wyjaśnienie. Według Google Material Design Guidelines, wyświetlenie okna rationale po pierwszej odmowie zwiększa prawdopodobieństwo ponownego przyznania uprawnienia o 35 procent.
Ważne jest zrozumienie semantyki zwracanych wartości: true oznacza, że okno ma sens wyświetlić, false — że okno albo nie jest potrzebne (uprawnienie już przyznane lub nigdy nie pytane), albo jest bezużyteczne (Never Ask Again aktywny). Metoda nie jest gwarancją, że okno zostanie wyświetlone — jedynie daje zalecenie. Programista sam decyduje, jaki UI pokazać w odpowiedzi.
shouldShowRequestPermissionRationale został wprowadzony w API Level 23 wraz z grupą metod dla uprawnień runtime. Przed Android 6.0 wszystkie uprawnienia były pytane przy instalacji, a mechanizm wyjaśnienia nie był wymagany — użytkownik akceptował lub odrzucał całą listę naraz. Model runtime umożliwił sytuację, w której użytkownik odrzuca zapytanie, nie rozumiejąc kontekstu, i właśnie dlatego potrzebny jest rationale.
Logika metody wygląda następująco. Przy pierwszym wywołaniu requestPermissions dla konkretnego uprawnienia shouldShowRequestPermissionRationale zwraca false — użytkownik jeszcze nie spotkał się z oknem. Jeśli użytkownik odrzucił zapytanie (nacisnął Deny), metoda zaczyna zwracać true. Po ponownym odrzuceniu z flagą Never Ask Again metoda zwraca false.
Pełna tabela stanów:
| Stan | shouldShowRationale | checkSelfPermission | Działanie programisty |
|---|---|---|---|
| Nie pytane | false | DENIED | Pokaż systemowe okno |
| Przyznane | false | GRANTED | Wykonaj funkcję |
| Odrzucone po raz pierwszy | true | DENIED | Pokaż rationale, potem systemowe okno |
| Never Ask Again | false | DENIED | Przekieruj do Ustawień |
Kombinacja shouldShowRequestPermissionRationale = false i checkSelfPermission = DENIED — to najtrudniejszy do obsłużenia przypadek. Oznacza, że albo uprawnienie nigdy nie było pytane, albo ustawiono Never Ask Again. Programista musi rozróżnić te dwa stany. Jedynym sposobem jest przechowywanie flagi firstRequest w SharedPreferences lub użycie SavedStateHandle. Przy pierwszym zapytaniu ustawić flagę, a jeśli shouldShowRationale zwrócił false, a flaga już jest true — oznacza to Never Ask Again.
shouldShowRequestPermissionRationale jest resetowane, jeśli użytkownik usuwa i ponownie instaluje aplikację, czyści dane aplikacji lub resetuje ustawienia uprawnień. Po reinstalacji metoda ponownie zwróci false dla pierwszego zapytania. Aktualizacje systemowe i zmiana wersji Android nie resetują historii — jest ona przechowywana w danych aplikacji.
Prawidłowa implementacja rationale obejmuje trzy komponenty: sprawdzenie shouldShowRequestPermissionRationale po odmowie, wyświetlenie niestandardowego okna z wyjaśnieniem i ponowne wywołanie requestPermissions po pozytywnej odpowiedzi użytkownika. Okno powinno być zwięzłe, konkretne i wyjaśniać, dlaczego aplikacja potrzebuje właśnie tego uprawnienia.
private fun requestLocationWithRationale() {
val permission = Manifest.permission.ACCESS_FINE_LOCATION
when {
ContextCompat.checkSelfPermission(
this, permission
) == PackageManager.PERMISSION_GRANTED -> {
startLocationTracking()
}
ActivityCompat.shouldShowRequestPermissionRationale(
this, permission
) -> {
showRationaleDialog(permission)
}
else -> {
requestPermissionLauncher.launch(permission)
}
}
}
private fun showRationaleDialog(
permission: String
) {
AlertDialog.Builder(this)
.setTitle("Po co jest dostęp do geolokalizacji")
.setMessage(
"Aplikacja używa geolokalizacji do oznaczania" +
" miejsc na mapie. Bez tego uprawnienia" +
" funkcja nie będzie działać."
)
.setPositiveButton("Zezwól") { _, _ ->
requestPermissionLauncher.launch(permission)
}
.setNegativeButton("Anuluj", null)
.show()
}
Najlepsze praktyki Material Design zalecają używanie bottom sheet lub inline-banner zamiast modalnego okna dla rationale. Bottom sheet jest mniej nachalny i daje użytkownikowi kontekst. Element inline na ekranie (na przykład karta z wyjaśnieniem i przyciskiem Zezwól) pokazuje, że funkcja jest niedostępna bez uprawnienia, ale nie blokuje reszty interfejsu.
Tekst rationale powinien być zlokalizowany i dostosowany do konkretnej funkcji. Nie używaj ogólnych fraz w stylu „To jest potrzebne do działania aplikacji“. Wskaż konkretnie: „Do wyświetlania pogody w twojej okolicy“ lub „Do zapisywania zdjęć do galerii“. Konkretne wyjaśnienia zwiększają prawdopodobieństwo przyznania uprawnienia o 50 procent według Google UX Research.
Różnica między shouldShowRequestPermissionRationale = true (pierwsza odmowa) a false przy DENIED (Never Ask Again) — kluczowy moment w obsłudze uprawnień. W pierwszym przypadku użytkownik się wahał i dodatkowe wyjaśnienie może przekonać go do przyznania dostępu. W drugim — użytkownik podjął ostateczną decyzję, a ponowne systemowe okno tylko wywoła irytację.
Algorytm postępowania po odmowie powinien wyglądać tak:
Ważne, aby nie mylić kolejności: najpierw sprawdzać shouldShowRequestPermissionRationale, a nie checkSelfPermission. checkSelfPermission i tak zwróci DENIED w obu przypadkach. Tylko shouldShowRequestPermissionRationale odróżnia pierwszą odmowę od Never Ask Again. Używaj SavedStateHandle lub SharedPreferences do przechowywania flagi „pierwsze zapytanie było“ — to jedyny niezawodny sposób na odróżnienie „nie pytane“ od „zablokowane“.
Wyświetlaj rationale tylko raz. Jeśli użytkownik ponownie odrzucił zapytanie po rationale — więcej nie pokazuj wyjaśnienia. Przechodź od razu do propozycji otwarcia ustawień. Wielokrotne wyświetlanie rationale jest odbierane jako nachalność i obniża ocenę aplikacji. Optymalny scenariusz: zapytanie — odmowa — rationale — ponowne zapytanie — odmowa — Ustawienia.
Nie pokazuj rationale przed pierwszym zapytaniem. Niektórzy programiści błędnie wyświetlają wyjaśnienie przed pierwszym oknem, argumentując to tym, że „użytkownik powinien zrozumieć“. To pogarsza UX: użytkownik widzi dwa okna pod rząd zamiast jednego. Google zaleca wyświetlanie systemowego okna od razu, a rationale — dopiero po odmowie.
Używaj kontekstowego rationale, powiązanego z momentem, gdy funkcja jest rzeczywiście potrzebna. Nie pytaj o wszystkie uprawnienia przy starcie aplikacji — to najniższy wskaźnik przyznania. Pytaj o CAMERA, gdy użytkownik nacisnął przycisk „Zrób zdjęcie“, a o LOCATION, gdy otworzył mapę. Contextual request w połączeniu z rationale zwiększa przyznanie do 80 procent wobec 30 procent przy pytaniu przy starcie.
Testowanie shouldShowRequestPermissionRationale wymaga sprawdzenia czterech stanów z tabeli: nie pytane, przyznane, odrzucone, Never Ask Again. W testach jednostkowych używa się FakePermissionHandler z konfigurowalnym zachowaniem shouldShowRationale. W testach instrumentalnych — UiAutomator lub Espresso z emulacją odpowiedzi na systemowe okna.
class RationaleViewModelTest {
private val handler = FakePermissionHandler()
private val viewModel = PermissionsViewModel(handler)
fun testFirstDenial_shouldShowRationale() {
handler.shouldShowRationale = true
handler.cameraResult =
PermissionResult.DENIED(true)
viewModel.onCameraRequested()
assertEquals(
PermissionUiState.Denied(true),
viewModel.uiState.value
)
}
fun testNeverAskAgain_redirectToSettings() {
handler.shouldShowRationale = false
handler.cameraResult =
PermissionResult.DENIED(false)
viewModel.onCameraRequested()
assertEquals(
PermissionUiState.RedirectToSettings,
viewModel.uiState.value
)
}
}
Kluczowy scenariusz dla testu instrumentalnego — sprawdzenie, że okno rationale rzeczywiście pojawia się po pierwszej odmowie. Użyj Espresso z idling resources do oczekiwania na systemowe okno, następnie naciśnij Deny, sprawdź pojawienie się niestandardowego okna z tekstem wyjaśnienia i naciśnij Allow — sprawdź przyznanie. UIAutomator pozwala na interakcję z systemowym oknem po tekście przycisku, co czyni test bardziej stabilnym.
Warto również przetestować scenariusz odmowy wewnątrz okna rationale. Jeśli użytkownik nacisnął Deny w niestandardowym wyjaśnieniu, shouldShowRequestPermissionRationale powinien ponownie zwrócić true, ponieważ Never Ask Again nie został jeszcze aktywowany. Najlepszą praktyką jest po dwóch odmowach z rzędu od razu przekierowywać do Ustawień, aby nie irytować użytkownika powtarzającymi się wyjaśnieniami i nie obniżać oceny aplikacji.
Często zadawane pytania
true — jeśli zapytanie było wcześniej odrzucane i nie ustawiono Never Ask Again. false — jeśli uprawnienie nie było pytane, zostało przyznane lub jest zablokowane na stałe. Kombinacja false + DENIED wymaga sprawdzenia przez dodatkową flagę.
Wyświetlaj rationale tylko po pierwszej odmowie użytkownika, gdy shouldShowRequestPermissionRationale zwrócił true. Przed pierwszym zapytaniem rationale nie jest potrzebny — pogarsza to UX i tworzy zbędne okna.
Przechowuj flagę isFirstRequest w SharedPreferences lub SavedStateHandle. Jeśli shouldShowRationale = false, checkSelfPermission = DENIED i flaga = true — oznacza to aktywowany Never Ask Again. Jeśli flaga = false — to pierwsze zapytanie.
Pokaż okno z przyciskiem „Otwórz ustawienia“, który przekierowuje użytkownika do ACTION_APPLICATION_DETAILS_SETTINGS. Nie wywołuj requestPermissions ponownie — okno się nie pojawi, a wynik przyjdzie z DENIED bez komunikatu.
W testach jednostkowych używaj FakePermissionHandler z konfigurowalnym polem shouldShowRationale. W testach instrumentalnych — Espresso lub UIAutomator z emulacją systemowego okna. Sprawdzaj wszystkie 4 stany z tabeli.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również