How to ask permission, the clean way - Droidcon 2021
Presentation at Droidcon 2021 by Ronaldo Pace
How to ask permission, the clean way
Tells the approach to implement Android Runtime permissions using a "clean" approach of service/repository/viewModel by the means decoupling it from the Android UI.
Slide was added after the conference I decided I'll refactor my previous library "permission-bitte" into a V2 that will be the full implementation on what's on this presentation. This will be a nights and weekends deal, so might take a lil bit to complete. So if you're interested, be sure to watch or star the repo on https://github.com/budius/permission-bitte
How to ask permission (with libraries) // delegates private val permissionDelegate = PermissionDelegate(this) fun onSomethingClick() { if (permissionDelegate.needsPermission(Manifest.permission.REQUESTED_PERMISSION)) permission.request(Manifest.permission.REQUESTED_PERMISSION) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) = permissionDelegate.result(requestCode, permissions, grantResults) // annotation @NeedsPermission(Manifest.permission.REQUESTED_PERMISSION) fun onSomethingElseClick() { // permission was approved } @OnPermissionResult(Manifest.permission.REQUESTED_PERMISSION) fun onRequestPermissionResult(result: Code) { // handle result here }
Are they really clean, if all that logic is on the presentation layer? data (services) domain / business (repository) presentations (view/viewModel) (don´t forget to mention DRY)
Dividing the problem: List Just copy from: https://github.com/budius/permission-bitte class PermissionServiceImpl(app: Application) : PermissionService { private val _permissions = MutableStateFlow(extractPermissionsFromManifest(app)) override val permissions = _permissions.map{ mapToSet(it) }.distinctUntilChanged() } private fun extractPermissionsFromManifest(context: Context): Map { val pm = context.packageManager val info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS) val names = info.requestedPermissions val groups = names.map { pm.getPermissionInfo(name, 0).group } val flags = info.requestedPermissionsFlags // parse name, group, flags into Map // but DENIED can´t be obtained here =( map[name] = GRANTED / REQUEST_PERMISSION / SHOW_RATIONALE
Dividing the problem: Updating from outside the app class PermissionServiceImpl(app: Application) : PermissionService { private val activityCallback = object : ActivityLifecycleCallbacks { override fun onActivityResumed(activity: Activity) { val newData = extractPermissionsFromManifest(activity) _permissions.value = updatePermissions(newData, _permissions.value) } } } private fun updatePermissions( newData: Map, current: Map ): Map { // any permissions that current is DENIED, // must stay DENIED // the others, pick from the newData }
Is it clean now? data (services) domain / business (repository) presentations (view/viewModel) PermissionFragment PermissonServiceImpl PermissionService PermissionRepo LocationRepo Onboarding V-VM SomeMapThing V-VM Google Play Services - Location