shouldShowRequestPermissionRationale — је Android API метода која програмеру сугерише да ли треба да покаже кориснику објашњење пре захтева за опасно дозволу. Према Android Developer Reference, 2024, rational метода враћа true ако је корисник претходно одбио захтев, али није поставио заставицу Never Ask Again. Ово је кључни алат за изградњу љубазног UX-а при раду са runtime дозволама.
Главно
shouldShowRequestPermissionRationale — је метода класе Activity и Fragment у Android-у, доступна кроз ActivityCompat ради компатибилности. Прима име дозволе и враћа Boolean који показује да ли треба приказати кориснику додатно објашњење пре поновног захтева. Метода се појавила у Android 6.0 Marshmallow заједно са runtime моделом дозвола.
Механизам rationale-а је изграђен на праћењу историје интеракције корисника са дијалозима за дозволе. Систем памти да ли је корисник претходно одбио захтев. Ако је одбијање наступило без постављања заставице Never Ask Again, shouldShowRequestPermissionRationale враћа true. Ово је сигнал програмеру: корисник не разуме зашто је потребна дозвола и потребно је додатно објашњење. Према Google Material Design Guidelines, приказивање rationale дијалога након првог одбијања повећава вероватноћу поновног одобравања дозволе за 35 процената.
Важно је разумети семантику повраћених вредности: true значи да има смисла приказати дијалог, false — дијалог или није потребан (дозвола је већ одобрена или никада није затражена), или је безкористан (Never Ask Again је активан). Метода није гаранција да ће дијалог бити приказан — она само даје препоруку. Програмер сам одлучује који UI да прикаже као одговор.
shouldShowRequestPermissionRationale је представљен у API Level 23 заједно са групом метода за runtime дозволе. Пре Android 6.0, све дозволе су затраживане при инсталацији и механизам објашњења није био потребан — корисник је прихватао или одбијао целу листу одједном. Runtime модел је омогућио ситуацију у којој корисник одбија захтев не разумевши контекст и управо зато је потребан rationale.
Логика методе је следећа. При првом позиву requestPermissions за одређену дозволу, shouldShowRequestPermissionRationale враћа false — корисник се још није срео са дијалогом. Ако је корисник одбио захтев (притиснуо Deny), метода почиње да враћа true. Након поновног одбијања са заставицом Never Ask Again, метода враћа false.
Пуна табела стања:
| Стање | shouldShowRationale | checkSelfPermission | Акција програмера |
|---|---|---|---|
| Није затраживано | false | DENIED | Прикажи системски дијалог |
| Одобрено | false | GRANTED | Изврши функцију |
| Одбијено први пут | true | DENIED | Прикажи rationale, затим системски дијалог |
| Never Ask Again | false | DENIED | Преусмери на Поставке |
Комбинација shouldShowRequestPermissionRationale = false и checkSelfPermission = DENIED — је најтежи случај за обраду. Значи да или дозвола никада није затражена, или је Never Ask Again постављен. Програмер мора да разликује ова два стања. Једини начин — чување заставице firstRequest у SharedPreferences или коришћење SavedStateHandle. При првом захтеву поставите заставицу, и ако shouldShowRationale врати false, а заставица је већ true — значи Never Ask Again.
shouldShowRequestPermissionRationale се ресетује ако корисник обрише и поново инсталира апликацију, обрише податке апликације или ресетује поставке дозвола. Након поновне инсталације, метода ће поново вратити false за први захтев. Системска ажурирања и промена верзије Android-а не ресетују историју — она се чува у подацима апликације.
Исправна имплементација rationale-а укључује три компоненте: проверу shouldShowRequestPermissionRationale након одбијања, приказ прилагођеног дијалога са објашњењем и поновно позивање requestPermissions након позитивног одговора корисника. Дијалог треба да буде кратак, конкретан и објашњава зашто је апликацији потребна управо та дозвола.
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("Зашто је потребан приступ геолокацији")
.setMessage(
"Апликација користи геолокацију за означавање" +
" места на мапи. Без ове дозволе" +
" функција неће радити."
)
.setPositiveButton("Дозволи") { _, _ ->
requestPermissionLauncher.launch(permission)
}
.setNegativeButton("Откажи", null)
.show()
}
Најбоље праксе Material Design-а препоручују коришћење bottom sheet-а или inline банера уместо модалног дијалога за rationale. Bottom sheet је мање наметљив и пружа кориснику контекст. Inline елеменат на екрану (на пример, картица са објашњењем и дугметом Дозволи) показује да функција није доступна без дозволе, али не блокира остали интерфејс.
Текст rationale-а треба да буде локализован и прилагођен конкретној функцији. Не користите опште фразе попут „Ово је потребно за рад апликације”. Наведите конкретно: „За приказ времена у вашој близини” или „За чување фотографија у галерију”. Према Google UX Research, конкретна објашњења повећавају вероватноћу одобравања дозволе за 50 процената.
Разлика између shouldShowRequestPermissionRationale = true (први одбијање) и false при DENIED (Never Ask Again) — је кључни тренутак у обради дозвола. У првом случају корисник је оклевао и додатно објашњење га може убедити да одобри приступ. У другом — корисник је донео коначну одлуку и поновни системски дијалог ће само изазвати иритацију.
Алгоритам обраде након одбијања треба да изгледа овако:
Важно је не помешати редослед: прво проверите shouldShowRequestPermissionRationale, а не checkSelfPermission. checkSelfPermission ће свеједно вратити DENIED у оба случаја. Само shouldShowRequestPermissionRationale разликује први одбијање од Never Ask Again. Користите SavedStateHandle или SharedPreferences за чување заставице „први захтев је био” — ово је једини поуздан начин да се разликује „није затраживано” од „блокирано”.
Прикажите rationale само једном. Ако је корисник поново одбио захтев након rationale-а — више не приказујте објашњење. Пређите директно на предлог да се отворе поставке. Вишеструки приказивање rationale-а доживљава се као наметљивост и смањује рејтинг апликације. Оптимални сценарио: захтев — одбијање — rationale — поновни захтев — одбијање — Поставке.
Не приказујте rationale пре првог захтева. Неки програмери грешком приказују објашњење пре првог дијалога, аргументујући да „корисник мора да разуме”. Ово погоршава UX: корисник види два дијалога узастопно уместо једног. Google препоручује да се системски дијалог прикаже одмах, а rationale — само након одбијања.
Користите контекстуални rationale, везан за тренутак када је функција стварно потребна. Не затраживајте све дозволе при покретању апликације — то је најнижи проценат одобравања. Затраживајте CAMERA када корисник притисне дугме „Усликај”, а LOCATION када отвори мапу. Контекстуални захтев у комбинацији са rationale-ом повећава одобравање на 80 процената наспрам 30 процената код почетног захтева.
Тестирање shouldShowRequestPermissionRationale захтева проверу четири стања из табеле: није затраживано, одобрено, одбијено, Never Ask Again. У јединичним тестовима користи се FakePermissionHandler са подешивим shouldShowRationale понашањем. У инструменталним тестовима — UiAutomator или Espresso са емулацијом одговора на системске дијалоге.
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
)
}
}
Кључни сценарио за инструментални тест — провера да се rationale дијалог заиста приказује након првог одбијања. Користите Espresso са idling resources за чекање системског дијалога, затим притисните Deny, проверите појаву прилагођеног дијалога са текстом објашњења и притисните Allow — проверите одобравање. UIAutomator омогућава интеракцију са системским дијалогом путем текста дугмета, што чини тест стабилнијим.
Такође вреди тестирање сценарија одбијања унутар rationale дијалога. Ако је корисник притиснуо Deny у прилагођеном објашњењу, shouldShowRequestPermissionRationale би требало поново да врати true, јер Never Ask Again још није активиран. Најбоља пракса — након два узастопна одбијања директно преусмерити на Поставке како би се избегло наметање корисника поновним објашњењима и смањење рејтинга апликације.
Често постављана питања
true — ако је захтев претходно одбијен и Never Ask Again није постављен. false — ако дозвола није затраживана, је одобрена или заувек блокирана. Комбинација false + DENIED захтева проверу кроз додатну заставицу.
Прикажите rationale само након првог одбијања корисника, када shouldShowRequestPermissionRationale врати true. Пре првог захтева rationale није потребан — то погоршава UX и ствара непотребне дијалоге.
Чувајте заставицу isFirstRequest у SharedPreferences или SavedStateHandle. Ако shouldShowRationale = false, checkSelfPermission = DENIED и заставица = true — значи Never Ask Again је активиран. Ако заставица = false — ово је први захтев.
Прикажите дијалог са дугметом „Отвори поставке” који преусмерава корисника на ACTION_APPLICATION_DETAILS_SETTINGS. Не позивајте requestPermissions поновно — дијалог се неће појавити, а резултат ће доћи са DENIED без поруке.
У јединичним тестовима користите FakePermissionHandler са подешивим пољем shouldShowRationale. У инструменталним тестовима — Espresso или UIAutomator са емулацијом системског дијалога. Проверите све 4 стања из табеле.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође