SlideShare a Scribd company logo
1 of 53
Download to read offline
RuntimePermissionsExtended
Nebojša Vukšić
Nebojša Vukšić
Android developer @ codecentric
Founder of Kotlin User Group Serbia
TheTechW0lf
nebojsa92vuksic@gmail.com
Kotlin User Group Serbia
https://www.meetup.com/Serbia-Kotlin-User-Group
https://www.facebook.com/kotlinserbia/
https://twitter.com/kotlin_serbia
Android permissions definition
● part of Android security system
● used to control access to system features, data, etc.
● apps are in sandbox
● needed permission needs to be annotated in
AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
Pre-Marshmallow
● targetSdkVersion < 23
● all permissions are granted in
install time
● less control for user
● no explanation for permission
usage
● when app is updated we need to
check additional permissions
After Marshmallow
● targetSdkVersion >= 23
● permissions are requested in
runtime
● all edge cases need to be
covered
● User can revoke permission from
settings screen
● Process is revoked
After Marshmallow
● targetSdkVersion >= 23
● permissions are requested in
runtime
● all edge cases need to be
covered
● User can revoke permission from
settings screen
● Process is revoked
Permission protection level
● Normal permissions
● Dangerous permissions
● Signature permissions
● Signature and system permissions
Permission protection level
● Normal permissions
● Dangerous permissions
● Signature permissions
● Signature and system permissions
Normal permissions
● granted when app is installed
● they are not risk for users data privacy and can’t affect other apps
Permission list:
● ACCESS_NETWORK_STATE
● BLUETOOTH
● INTERNET
● NFC
● VIBRATE
● WAKE_LOCK
● etc...
Permission protection level
● Normal permissions
● Dangerous permissions
● Signature permissions
● Signature and system permissions
Permission protection level
● Normal permissions
● Dangerous permissions
● Signature permissions
● Signature and system permissions
Dangerous permissions
● requested in runtime
● when permission from one group
is granted, all permissions in
group are granted
● they are risk for data privacy and
can affect other apps
Runtime permission user flow
Twitter user flow example
Let’s see some code...
Pre-Marshmallow - targetSdk < 23
import kotlinx.android.synthetic.main.activity_camera.*
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
startCameraPreviewScreen()
}
}
After Marshmallow - targetSdk >= 23
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCameraPreviewScreen()
}
}
}
After Marshmallow - targetSdk >= 23
class CameraActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCameraPreviewScreen()
} else {
ActivityCompat.requestPermissions(this, arrayOf<String>(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
}
}
After Marshmallow - targetSdk >= 23
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> {
if (grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty()) {
startCameraPreviewScreen()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
showSnackBarWithExplanation()
} else {
startGoToSettingsScreenIntent()
}
}
}
}
}
After Marshmallow - targetSdk >= 23
class CameraActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCameraPreviewScreen()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
showSnackBarWithExplanation()
} else {
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
}
}
}
Lots of boilerplate code
It’s time to become extended!
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCameraPreviewScreen()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
showSnackBarWithExplanation()
} else {
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
}
}
}
OnPermissionGranted Block
OnExplanationNeeded Block
OnPermissionDenied Block
RuntimePermissionExtended
inline fun handlePermission(onGranted: () -> Unit, onExplanationNeeded: () -> Unit, onDenied: () -> Unit) {
if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
onExplanationNeeded()
} else {
onDenied()
}
}
}
inline modifier - inlines onGranted, onDenied an onExplanationNeeded functions on call site
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
handlePermission(
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
//Show explanation
},
onDenied = {
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
)
}
}
RuntimePermissionExtended
inline fun handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: () -> Unit, onDenied: () -> Unit) {
if (PermissionChecker.checkSelfPermission(this, permission.permissionName)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) {
onExplanationNeeded()
} else {
onDenied()
}
}
}
sealed class AppPermission(val permissionName: String, val requestCode: Int, val deniedMessageId: Int, val explanationMessageId: Int) {
companion object { val permissions: List<AppPermission> by lazy { listOf(CAMERA) } }
object CAMERA: AppPermission(permissionName = Manifest.permission.CAMERA,
requestCode = 1,
deniedMessageId = R.string.permission_camera_denied,
explanationMessageId = R.string.permission_camera_explanation)
}
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
handlePermission(AppPermission.CAMERA,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
//Show explanation
},
onDenied = {
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
)
}
}
No information about requested permission
RuntimePermissionExtended
inline fun handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
if (PermissionChecker.checkSelfPermission(this, permission.permissionName)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) {
onExplanationNeeded(permission)
} else {
onDenied(permission)
}
}
}
sealed class AppPermission(val permissionName: String, val requestCode: Int, val deniedMessageId: Int, val explanationMessageId: Int) {
companion object { val permissions: List<AppPermission> by lazy { listOf(CAMERA) } }
object CAMERA: AppPermission(permissionName = Manifest.permission.CAMERA,
requestCode = 1,
deniedMessageId = R.string.permission_camera_denied,
explanationMessageId = R.string.permission_camera_explanation)
}
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
handlePermission(AppPermission.CAMERA,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = { permission ->
showSnackBarWithExplanation(permission.explanationMessageId)
},
onDenied = { permission ->
ActivityCompat.requestPermissions(this, arrayOf(permission.permissionName), permission.requestCode)
}
)
}
}
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
handlePermission(AppPermission.CAMERA,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
showSnackBarWithExplanation(it.explanationMessageId)
},
onDenied = {
ActivityCompat.requestPermissions(this, arrayOf(it.permissionName), it.requestCode)
}
)
}
}
RuntimePermissionExtended
inline fun handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
if (PermissionChecker.checkSelfPermission(this, permission.permissionName)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) {
onExplanationNeeded(permission)
} else {
onDenied(permission)
}
}
}
RuntimePermissionExtended
inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
if (PermissionChecker.checkSelfPermission(this, permission.permissionName)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) {
onExplanationNeeded(permission)
} else {
onDenied(permission)
}
}
}
fun Activity.isPermissionGranted(permission: AppPermission)
= (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
fun Activity.isExplanationNeeded(permission: AppPermission)
= ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
RuntimePermissionExtended
inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
if (isPermissionGranted(this, permission.permissionName)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (isExplanationNeeded(this, permission.permissionName)) {
onExplanationNeeded(permission)
} else {
onDenied(permission)
}
}
}
fun Activity.isPermissionGranted(permission: AppPermission)
= (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
fun Activity.isExplanationNeeded(permission: AppPermission)
= ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
RuntimePermissionExtended
inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
if (isPermissionGranted(this, permission.permissionName)
== PackageManager.PERMISSION_GRANTED) {
onGranted()
} else {
if (isExplanationNeeded(this, permission.permissionName)) {
onExplanationNeeded(permission)
} else {
onDenied(permission)
}
}
}
fun Activity.isPermissionGranted(permission: AppPermission)
= (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
fun Activity.isExplanationNeeded(permission: AppPermission)
= ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
RuntimePermissionExtended
inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
when {
isPermissionGranted(permission) -> onGranted()
isExplanationNeeded(permission) -> onExplanationNeeded(permission)
else -> onDenied(permission)
}
}
fun Activity.isPermissionGranted(permission: AppPermission)
= (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
fun Activity.isExplanationNeeded(permission: AppPermission)
= ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
handlePermission(AppPermission.CAMERA,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
showSnackBarWithExplanation(it.explanationMessageId)
},
onDenied = {
ActivityCompat.requestPermissions(this, arrayOf(it.permissionName), it.requestCode)
}
)
}
}
fun Activity.requestPermission(permission: AppPermission)
= ActivityCompat.requestPermissions(this, arrayOf(permission.permissionName), permission.requestCode)
RuntimePermissionExtended
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
handlePermission(AppPermission.CAMERA,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
showSnackBarWithExplanation(it.explanationMessageId)
},
onDenied = {
requestPermission(it)
}
)
}
}
fun Activity.requestPermission(permission: AppPermission)
= ActivityCompat.requestPermissions(this, arrayOf(permission.permissionName), permission.requestCode)
Don’t forget about onRequestPermissionResult
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> {
if (grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty()) {
startCameraPreviewScreen()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
showSnackBarWithExplanation()
} else {
startGoToSettingsIntent()
}
}
}
}
}
RuntimePermissionExtended
fun Activity.onPermissionRequestResultReceived(requestCode: Int, grantResults: IntArray,
onGranted: () -> Unit,
onExplanationNeeded: (AppPermission) -> Unit,
onDenied: (AppPermission) -> Unit) {
AppPermission.permissions.find { requestCode == it.requestCode }?.let {
when {
arePermissionsGranted(grantResults) -> onGranted()
isExplanationNeeded(it) -> onExplanationNeeded(it)
else -> onDenied(it)
}
}
}
private fun arePermissionsGranted(grantResults: IntArray) = grantResults.filterNot {
it == PackageManager.PERMISSION_GRANTED
}.isEmpty()
RuntimePermissionExtended
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
onPermissionRequestResultReceived(requestCode, grantResults,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
showSnackBarWithExplanation(it.explanationMessageId)
},
onDenied = {
startGoToSettingsScreenIntent()
}
)
}
RuntimePermissionExtended
Overview and summary
Overview - before and now
class CameraActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
//Start camera preview
if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCameraPreviewScreen()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) {
showSnackBarWithExplanation()
} else {
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA),
REQUEST_CAMERA_PERMISSION)
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults:
IntArray) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> {
if (grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty()) {
showCameraPreviewScreen()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)){
showSnackBarWithExplanation()
} else {
startGoToSettingsScreenIntent()
}
}
}
}
}
}
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
setSupportActionBar(toolbar)
handlePermission(AppPermission.CAMERA,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
showSnackbarExplanation
},
onDenied = {
requestPermission(it)
}
)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults:
IntArray) {
onPermissionRequestResultReceived(requestCode, grantResults,
onGranted = {
startCameraPreviewScreen()
},
onExplanationNeeded = {
showSnackbarExplanation()
},
onDenied = {
startGoToSettingsScreenIntent()
}
)
}
}
Before Now
Summary
● use PermissionChecker
● extension functions
● higher order functions
● use combination of two concepts to reduce amount of
boilerplate code
● use inline modifier
Resources
● RuntimePermissionsExtended
● Official Kotlin documentation
● Official Kotlin Github
● Kotlin koans
● Awesome Kotlin – collection of materials
● Slack kanal
● #droidconpl2015 - Shintaro Katafuchi 'Managing Runtime Permissions'
● Sonja Kesić from Endava: May I? - droidcon Zagreb 2016
● Runtime Permissions - official Android documentation
● PermissionsDispatcher library
Android Serbia
Slack team of local Android community
Join us:
https://android-serbia.slack.com/
Questions?.Answers!!
Thank you!
Nebojša Vukšić
Android developer @ codecentric
Founder of Kotlin User Group Serbia
nebojsa92vuksic@gmail.com
Kotlin User Group Serbia
https://www.meetup.com/Serbia-Kotlin-User-Group
https://www.facebook.com/kotlinserbia/
https://twitter.com/kotlin_serbia
TheTechW0lf

More Related Content

Similar to Android RuntimePermissionsExtended

What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018Somkiat Khitwongwattana
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blinkInnovationM
 
What's new in android jakarta gdg (2015-08-26)
What's new in android   jakarta gdg (2015-08-26)What's new in android   jakarta gdg (2015-08-26)
What's new in android jakarta gdg (2015-08-26)Google
 
[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojek[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojekraditya gumay
 
#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps runFrederik De Bruyne
 
Magento 2 Seminar - Anton Kril - Magento 2 Summary
Magento 2 Seminar - Anton Kril - Magento 2 SummaryMagento 2 Seminar - Anton Kril - Magento 2 Summary
Magento 2 Seminar - Anton Kril - Magento 2 SummaryYireo
 
Android app development basics
Android app development basicsAndroid app development basics
Android app development basicsAnton Narusberg
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applicationsGabor Varadi
 
Inside Android's UI at AnDevCon V
Inside Android's UI at AnDevCon VInside Android's UI at AnDevCon V
Inside Android's UI at AnDevCon VOpersys inc.
 
Building an app with Google's new suites
Building an app with Google's new suitesBuilding an app with Google's new suites
Building an app with Google's new suitesToru Wonyoung Choi
 
Inside Android's UI at AnDevCon VI
Inside Android's UI at AnDevCon VIInside Android's UI at AnDevCon VI
Inside Android's UI at AnDevCon VIOpersys inc.
 
Rambler.iOS #6: App delegate - разделяй и властвуй
Rambler.iOS #6: App delegate - разделяй и властвуйRambler.iOS #6: App delegate - разделяй и властвуй
Rambler.iOS #6: App delegate - разделяй и властвуйRAMBLER&Co
 
Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30Somkiat Khitwongwattana
 
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...QAFest
 

Similar to Android RuntimePermissionsExtended (20)

What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blink
 
Camera
CameraCamera
Camera
 
What's new in android jakarta gdg (2015-08-26)
What's new in android   jakarta gdg (2015-08-26)What's new in android   jakarta gdg (2015-08-26)
What's new in android jakarta gdg (2015-08-26)
 
[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojek[Slide Deck] - Mobile Observability Internals at Gojek
[Slide Deck] - Mobile Observability Internals at Gojek
 
#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run
 
Magento 2 Seminar - Anton Kril - Magento 2 Summary
Magento 2 Seminar - Anton Kril - Magento 2 SummaryMagento 2 Seminar - Anton Kril - Magento 2 Summary
Magento 2 Seminar - Anton Kril - Magento 2 Summary
 
QXCameraKit
QXCameraKitQXCameraKit
QXCameraKit
 
Android app development basics
Android app development basicsAndroid app development basics
Android app development basics
 
My name is Trinidad
My name is TrinidadMy name is Trinidad
My name is Trinidad
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applications
 
Inside Android's UI at AnDevCon V
Inside Android's UI at AnDevCon VInside Android's UI at AnDevCon V
Inside Android's UI at AnDevCon V
 
Building an app with Google's new suites
Building an app with Google's new suitesBuilding an app with Google's new suites
Building an app with Google's new suites
 
Android swedroid
Android swedroidAndroid swedroid
Android swedroid
 
Inside Android's UI at AnDevCon VI
Inside Android's UI at AnDevCon VIInside Android's UI at AnDevCon VI
Inside Android's UI at AnDevCon VI
 
Rambler.iOS #6: App delegate - разделяй и властвуй
Rambler.iOS #6: App delegate - разделяй и властвуйRambler.iOS #6: App delegate - разделяй и властвуй
Rambler.iOS #6: App delegate - разделяй и властвуй
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Android Oreo
Android OreoAndroid Oreo
Android Oreo
 
Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30Get Ready for Target SDK Version 29 and 30
Get Ready for Target SDK Version 29 and 30
 
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
QA Fest 2019. Алексей Альтер-Песоцкий. Snapshot testing with native mobile fr...
 

Recently uploaded

Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
cybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningcybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningVitsRangannavar
 
buds n tech IT solutions
buds n  tech IT                solutionsbuds n  tech IT                solutions
buds n tech IT solutionsmonugehlot87
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 

Recently uploaded (20)

Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
cybersecurity notes for mca students for learning
cybersecurity notes for mca students for learningcybersecurity notes for mca students for learning
cybersecurity notes for mca students for learning
 
buds n tech IT solutions
buds n  tech IT                solutionsbuds n  tech IT                solutions
buds n tech IT solutions
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 

Android RuntimePermissionsExtended

  • 2. Nebojša Vukšić Android developer @ codecentric Founder of Kotlin User Group Serbia TheTechW0lf nebojsa92vuksic@gmail.com
  • 3. Kotlin User Group Serbia https://www.meetup.com/Serbia-Kotlin-User-Group https://www.facebook.com/kotlinserbia/ https://twitter.com/kotlin_serbia
  • 4. Android permissions definition ● part of Android security system ● used to control access to system features, data, etc. ● apps are in sandbox ● needed permission needs to be annotated in AndroidManifest.xml <uses-permission android:name="android.permission.CAMERA" />
  • 5. Pre-Marshmallow ● targetSdkVersion < 23 ● all permissions are granted in install time ● less control for user ● no explanation for permission usage ● when app is updated we need to check additional permissions
  • 6. After Marshmallow ● targetSdkVersion >= 23 ● permissions are requested in runtime ● all edge cases need to be covered ● User can revoke permission from settings screen ● Process is revoked
  • 7. After Marshmallow ● targetSdkVersion >= 23 ● permissions are requested in runtime ● all edge cases need to be covered ● User can revoke permission from settings screen ● Process is revoked
  • 8. Permission protection level ● Normal permissions ● Dangerous permissions ● Signature permissions ● Signature and system permissions
  • 9. Permission protection level ● Normal permissions ● Dangerous permissions ● Signature permissions ● Signature and system permissions
  • 10. Normal permissions ● granted when app is installed ● they are not risk for users data privacy and can’t affect other apps Permission list: ● ACCESS_NETWORK_STATE ● BLUETOOTH ● INTERNET ● NFC ● VIBRATE ● WAKE_LOCK ● etc...
  • 11. Permission protection level ● Normal permissions ● Dangerous permissions ● Signature permissions ● Signature and system permissions
  • 12. Permission protection level ● Normal permissions ● Dangerous permissions ● Signature permissions ● Signature and system permissions
  • 13. Dangerous permissions ● requested in runtime ● when permission from one group is granted, all permissions in group are granted ● they are risk for data privacy and can affect other apps
  • 15.
  • 16. Twitter user flow example
  • 17.
  • 18.
  • 19. Let’s see some code...
  • 20. Pre-Marshmallow - targetSdk < 23 import kotlinx.android.synthetic.main.activity_camera.* class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview startCameraPreviewScreen() } }
  • 21. After Marshmallow - targetSdk >= 23 class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { startCameraPreviewScreen() } } }
  • 22. After Marshmallow - targetSdk >= 23 class CameraActivity : AppCompatActivity() { private val REQUEST_CAMERA_PERMISSION: Int = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { startCameraPreviewScreen() } else { ActivityCompat.requestPermissions(this, arrayOf<String>(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } } }
  • 23. After Marshmallow - targetSdk >= 23 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { REQUEST_CAMERA_PERMISSION -> { if (grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty()) { startCameraPreviewScreen() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) { showSnackBarWithExplanation() } else { startGoToSettingsScreenIntent() } } } } }
  • 24. After Marshmallow - targetSdk >= 23 class CameraActivity : AppCompatActivity() { private val REQUEST_CAMERA_PERMISSION: Int = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { startCameraPreviewScreen() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) { showSnackBarWithExplanation() } else { ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } } } }
  • 26. It’s time to become extended!
  • 27. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { private val REQUEST_CAMERA_PERMISSION: Int = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { startCameraPreviewScreen() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) { showSnackBarWithExplanation() } else { ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } } } } OnPermissionGranted Block OnExplanationNeeded Block OnPermissionDenied Block
  • 28. RuntimePermissionExtended inline fun handlePermission(onGranted: () -> Unit, onExplanationNeeded: () -> Unit, onDenied: () -> Unit) { if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) { onExplanationNeeded() } else { onDenied() } } } inline modifier - inlines onGranted, onDenied an onExplanationNeeded functions on call site
  • 29. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { private val REQUEST_CAMERA_PERMISSION: Int = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview handlePermission( onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { //Show explanation }, onDenied = { ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } ) } }
  • 30. RuntimePermissionExtended inline fun handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: () -> Unit, onDenied: () -> Unit) { if (PermissionChecker.checkSelfPermission(this, permission.permissionName) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) { onExplanationNeeded() } else { onDenied() } } } sealed class AppPermission(val permissionName: String, val requestCode: Int, val deniedMessageId: Int, val explanationMessageId: Int) { companion object { val permissions: List<AppPermission> by lazy { listOf(CAMERA) } } object CAMERA: AppPermission(permissionName = Manifest.permission.CAMERA, requestCode = 1, deniedMessageId = R.string.permission_camera_denied, explanationMessageId = R.string.permission_camera_explanation) }
  • 31. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { private val REQUEST_CAMERA_PERMISSION: Int = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview handlePermission(AppPermission.CAMERA, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { //Show explanation }, onDenied = { ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } ) } } No information about requested permission
  • 32. RuntimePermissionExtended inline fun handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { if (PermissionChecker.checkSelfPermission(this, permission.permissionName) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) { onExplanationNeeded(permission) } else { onDenied(permission) } } } sealed class AppPermission(val permissionName: String, val requestCode: Int, val deniedMessageId: Int, val explanationMessageId: Int) { companion object { val permissions: List<AppPermission> by lazy { listOf(CAMERA) } } object CAMERA: AppPermission(permissionName = Manifest.permission.CAMERA, requestCode = 1, deniedMessageId = R.string.permission_camera_denied, explanationMessageId = R.string.permission_camera_explanation) }
  • 33. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview handlePermission(AppPermission.CAMERA, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { permission -> showSnackBarWithExplanation(permission.explanationMessageId) }, onDenied = { permission -> ActivityCompat.requestPermissions(this, arrayOf(permission.permissionName), permission.requestCode) } ) } }
  • 34. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview handlePermission(AppPermission.CAMERA, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { showSnackBarWithExplanation(it.explanationMessageId) }, onDenied = { ActivityCompat.requestPermissions(this, arrayOf(it.permissionName), it.requestCode) } ) } }
  • 35. RuntimePermissionExtended inline fun handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { if (PermissionChecker.checkSelfPermission(this, permission.permissionName) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) { onExplanationNeeded(permission) } else { onDenied(permission) } } }
  • 36. RuntimePermissionExtended inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { if (PermissionChecker.checkSelfPermission(this, permission.permissionName) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)) { onExplanationNeeded(permission) } else { onDenied(permission) } } } fun Activity.isPermissionGranted(permission: AppPermission) = (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) fun Activity.isExplanationNeeded(permission: AppPermission) = ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
  • 37. RuntimePermissionExtended inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { if (isPermissionGranted(this, permission.permissionName) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (isExplanationNeeded(this, permission.permissionName)) { onExplanationNeeded(permission) } else { onDenied(permission) } } } fun Activity.isPermissionGranted(permission: AppPermission) = (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) fun Activity.isExplanationNeeded(permission: AppPermission) = ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
  • 38. RuntimePermissionExtended inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { if (isPermissionGranted(this, permission.permissionName) == PackageManager.PERMISSION_GRANTED) { onGranted() } else { if (isExplanationNeeded(this, permission.permissionName)) { onExplanationNeeded(permission) } else { onDenied(permission) } } } fun Activity.isPermissionGranted(permission: AppPermission) = (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) fun Activity.isExplanationNeeded(permission: AppPermission) = ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
  • 39. RuntimePermissionExtended inline fun Activity.handlePermission(permission: AppPermission, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { when { isPermissionGranted(permission) -> onGranted() isExplanationNeeded(permission) -> onExplanationNeeded(permission) else -> onDenied(permission) } } fun Activity.isPermissionGranted(permission: AppPermission) = (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) fun Activity.isExplanationNeeded(permission: AppPermission) = ActivityCompat.shouldShowRequestPermissionRationale(this, permission.permissionName)
  • 40. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview handlePermission(AppPermission.CAMERA, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { showSnackBarWithExplanation(it.explanationMessageId) }, onDenied = { ActivityCompat.requestPermissions(this, arrayOf(it.permissionName), it.requestCode) } ) } } fun Activity.requestPermission(permission: AppPermission) = ActivityCompat.requestPermissions(this, arrayOf(permission.permissionName), permission.requestCode)
  • 41. RuntimePermissionExtended class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview handlePermission(AppPermission.CAMERA, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { showSnackBarWithExplanation(it.explanationMessageId) }, onDenied = { requestPermission(it) } ) } } fun Activity.requestPermission(permission: AppPermission) = ActivityCompat.requestPermissions(this, arrayOf(permission.permissionName), permission.requestCode)
  • 42. Don’t forget about onRequestPermissionResult
  • 43. override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { REQUEST_CAMERA_PERMISSION -> { if (grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty()) { startCameraPreviewScreen() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) { showSnackBarWithExplanation() } else { startGoToSettingsIntent() } } } } } RuntimePermissionExtended
  • 44. fun Activity.onPermissionRequestResultReceived(requestCode: Int, grantResults: IntArray, onGranted: () -> Unit, onExplanationNeeded: (AppPermission) -> Unit, onDenied: (AppPermission) -> Unit) { AppPermission.permissions.find { requestCode == it.requestCode }?.let { when { arePermissionsGranted(grantResults) -> onGranted() isExplanationNeeded(it) -> onExplanationNeeded(it) else -> onDenied(it) } } } private fun arePermissionsGranted(grantResults: IntArray) = grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty() RuntimePermissionExtended
  • 45. override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { onPermissionRequestResultReceived(requestCode, grantResults, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { showSnackBarWithExplanation(it.explanationMessageId) }, onDenied = { startGoToSettingsScreenIntent() } ) } RuntimePermissionExtended
  • 47. Overview - before and now class CameraActivity : AppCompatActivity() { private val REQUEST_CAMERA_PERMISSION: Int = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) //Start camera preview if (PermissionChecker.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { startCameraPreviewScreen() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)) { showSnackBarWithExplanation() } else { ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) } } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { when (requestCode) { REQUEST_CAMERA_PERMISSION -> { if (grantResults.filterNot { it == PackageManager.PERMISSION_GRANTED }.isEmpty()) { showCameraPreviewScreen() } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA)){ showSnackBarWithExplanation() } else { startGoToSettingsScreenIntent() } } } } } } class CameraActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) setSupportActionBar(toolbar) handlePermission(AppPermission.CAMERA, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { showSnackbarExplanation }, onDenied = { requestPermission(it) } ) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { onPermissionRequestResultReceived(requestCode, grantResults, onGranted = { startCameraPreviewScreen() }, onExplanationNeeded = { showSnackbarExplanation() }, onDenied = { startGoToSettingsScreenIntent() } ) } } Before Now
  • 48. Summary ● use PermissionChecker ● extension functions ● higher order functions ● use combination of two concepts to reduce amount of boilerplate code ● use inline modifier
  • 49. Resources ● RuntimePermissionsExtended ● Official Kotlin documentation ● Official Kotlin Github ● Kotlin koans ● Awesome Kotlin – collection of materials ● Slack kanal ● #droidconpl2015 - Shintaro Katafuchi 'Managing Runtime Permissions' ● Sonja Kesić from Endava: May I? - droidcon Zagreb 2016 ● Runtime Permissions - official Android documentation ● PermissionsDispatcher library
  • 50. Android Serbia Slack team of local Android community Join us: https://android-serbia.slack.com/
  • 53. Nebojša Vukšić Android developer @ codecentric Founder of Kotlin User Group Serbia nebojsa92vuksic@gmail.com Kotlin User Group Serbia https://www.meetup.com/Serbia-Kotlin-User-Group https://www.facebook.com/kotlinserbia/ https://twitter.com/kotlin_serbia TheTechW0lf