Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effortless Notifications and Permissions with K...

Effortless Notifications and Permissions with Kotlin-friendly APIs

On almost every new Android release, there's an API that becomes more capable, but also more complex: Notifications.

What might NotificationCompat be if it was made all in Kotlin instead of in Java with a builder API?
How can we simplify the permission request and notification channels?

We'll look into that, seeing how your notifications can look like with Android's latest features in that area, with the simplest Kotlin code I could find, instead of verbose Java-like code.

Louis CAD

July 05, 2023
Tweet

More Decks by Louis CAD

Other Decks in Technology

Transcript

  1. @Louis_CAD D. La réponse D* C. Camera B. Business A.

    AI Which Android API has changed nearly every year since API 1? *Answer D (translated from French) MOBILE DEVELOPER
  2. @Louis_CAD D. was designed in 2010 C. is @Deprecated B.

    is in alpha A. just migrated to Kotlin NotificationsCompat… MOBILE DEVELOPER
  3. @Louis_CAD Noti f icationCompat.java through time Was introduced in 2012,

    was 388 lines long 🔗 Copied Noti f ication.java from 2010 🔗 25X larger as of 2023 (almost 10 000 lines) 🔗
  4. @Louis_CAD 526 public symbols (!) Noti f icationCompat.java is almost

    10 000 lines Noti f icationCompat.Builder 111 SCREAMING_CASE_CONSTANTS 86 public methods 6 styles : 70 symbols 11+ other nested classes 3 extenders : 82 symbols Action.Builder : 15 symbols BubbleMetadata more…
  5. @Louis_CAD 526 public symbols (!) Noti f icationCompat.java is almost

    10 000 lines Noti f icationCompat.Builder 111 SCREAMING_CASE_CONSTANTS 86 public methods 6 styles : 70 symbols 11+ other nested classes 3 extenders : 82 symbols Action.Builder : 15 symbols BubbleMetadata more… Good luck reading the docs! B-)
  6. @Louis_CAD Noti f icationCompat.java in a Kotlin world Large API

    surface, time consuming to discover Little semantic grouping Builder APIs are not leveraging default parameter values setA, setB, setC, setD, setE, setF… no property syntax Weak typing: integer constants aren't checked at compile time
  7. @Louis_CAD Noti f icationCompat.java in a "Kotlin f irst" world

    Large API surface, time consuming to discover Little semantic grouping Builder APIs are not leveraging default parameter values setA, setB, setC, setD, setE, setF… no property syntax Weak typing: integer constants aren't checked at compile time De f initely... not Kotlin-friendly
  8. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build()
  9. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1
  10. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2
  11. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3
  12. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3 4 4 Could be default values 4
  13. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3 4 4 Could be default values 4 5 Could be an extension function 5
  14. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3 4 4 Could be default values 4 5 Could be an extension function 5 Builder… build… 👷 🔨 …ceremony 6 6 6
  15. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Current API Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3 4 4 Could be default values 4 5 Could be an extension function 5 Builder… build… 👷 🔨 …ceremony 6 set, set, set, set, set, set, set… 7 6 6
  16. @Louis_CAD D. channelId, & smallIcon C. channelId, & contentText B.

    bigIcon, & channelId A. A permission, & channelId What is required to post a notification on API level 30*? MOBILE DEVELOPER *Android 11 25X Engineer
  17. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3 4 4 Could be default values 4 5 Could be an extension function 5 Builder… build… 👷 🔨 …ceremony 6 set, set, set, set, set, set, set… 7 6 6
  18. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() Only needs the applicationContext : could be omitted 1 1 1 1 2 Limited type safety : can lead to mistakes 2 2 3 3 Duplication 3 4 4 Could be default values 4 5 Could be an extension function 5 Builder… build… 👷 🔨 …ceremony 6 set, set, set, set, set, set, set… 7 6 6 8 Doesn't look like it, but small icon is actually required! 8
  19. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API
  20. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API
  21. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API 1 Required parameters are required 1
  22. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API 1 2 Required parameters are required 1 Autocomplete-friendly, for better discoverability 2
  23. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API 1 2 3 Required parameters are required 1 Autocomplete-friendly, for better discoverability 2 Sets contentTitle & contentText too for collapsed state 3
  24. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API 1 2 3 4 Required parameters are required 1 Autocomplete-friendly, for better discoverability 2 Sets contentTitle & contentText too for collapsed state 3 Extension function with default parameter values 4
  25. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API 1 2 3 4 5 5 Required parameters are required 1 Autocomplete-friendly, for better discoverability 2 Sets contentTitle & contentText too for collapsed state 3 Extension function with default parameter values 4 Concise & intuitive naming 5
  26. @Louis_CAD What if there Noti f ications were Kotlin- f

    irst? Current API @CheckResult fun bigText( context: Context, channelId: String, title: String, multiLineText: String, openAction: Intent ): Notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_my_fancy_monochrome_icon) .setContentTitle(title) .setContentText(multiLineText) .setStyle( NotificationCompat.BigTextStyle() .setBigContentTitle(title) .bigText(multiLineText) ) .setContentIntent( PendingIntent.getActivity( context, 0, // requestCode openAction, PendingIntent.FLAG_IMMUTABLE ) ) .build() @CheckResult fun bigText( channelId: NotificationChannelId, title: String, multiLineText: String, openAction: Intent ): Notification = notification( channelId = channelId, smallIcon = R.drawable.ic_my_fancy_monochrome_icon ) { style.bigText( title = title, text = multiLineText, onClick = openAction.toPendingActivity() ) } Next API 1 2 3 4 5 5 6 Required parameters are required 1 Autocomplete-friendly, for better discoverability 2 Sets contentTitle & contentText too for collapsed state 3 Extension function with default parameter values 4 Concise & intuitive naming 5 Strong typing to avoid mistakes 6
  27. @Louis_CAD D. Permission C. Lots of code B. Luck A.

    Experience What is required to post a notification since API level 33*? MOBILE DEVELOPER *Android 13
  28. @Louis_CAD Runtime permissions: breaking it down Input: We want permission

    X Output: 
 Granted or Denied Looks like a function!
  29. @Louis_CAD Runtime permissions: breaking it down Input: We want permission

    X Output: 
 Granted or Denied Looks like a function! But
  30. @Louis_CAD Runtime permissions: breaking it down Input: We want permission

    X Output: 
 Granted or Denied Looks like a function! But No connection between steps 6 & 7…
  31. @Louis_CAD Runtime permissions: breaking it down Input: We want permission

    X Output: 
 Granted or Denied Looks like a function! But No connection between steps 6 & 7… is that a callback?? 😰
  32. @Louis_CAD Runtime permissions: breaking it down Input: We want permission

    X Output: 
 Granted or Denied Looks like a function! But No connection between steps 6 & 7… is that a callback?? 😰 No problem! In , callbacks can become functions. 😎
  33. @Louis_CAD Runtime permissions: breaking it down Input: We want permission

    X Output: 
 Granted or Denied Looks like a function! But No connection between steps 6 & 7… is that a callback?? 😰
  34. @Louis_CAD Runtime permissions: just one function? Input: We want permission

    X Output: 
 Granted or Denied Looks like a suspending function!
  35. @Louis_CAD Runtime permissions: just one function? Naming ideas 1. getPermissionFromUserBestEffort

    💪 2. forceUserToGrantPermission 👿 3. askPermissionIfPossible 🙈 4. ensurePermission 🤔 5. attemptGettingPermission 👀 Using this one since early 2019. Using that too since 2023. 
 You'll learn why.
  36. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing )
  37. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) In case the permission requirement isn't that obvious
  38. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Let user back out right at the rationale
  39. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Let user back out right at the rationale UI agnostic
  40. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Take user to settings if needed, or let them back out
  41. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) still UI agnostic Take user to settings if needed, or let them back out
  42. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Don't let the function continue if user backed out
  43. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Simpler control fl ow Don't let the function continue if user backed out
  44. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Simpler control fl ow If ensurePermission returns/resumes, app got the permission, for sure!
  45. @Louis_CAD The actual function signature suspend inline fun ComponentActivity.ensurePermission( permission:

    String, showRationaleAndContinueOrReturn: () -> Boolean, showRationaleBeforeFirstAsk: Boolean = true, askOpenSettingsOrReturn: () -> Boolean, returnOrThrowBlock: () -> Nothing ) Simpler control fl ow If ensurePermission returns/resumes, app got the permission, for sure! Works because the function is inline
  46. @Louis_CAD What about ActivityResultContracts.RequestPermission?? Still has callbacks that break the

    control flow. Doesn't handle taking the user to the settings Doesn't handle empty grantResults edge case properly Doesn't handle lying shouldShowRequestPermissionRationale* *Android 11+ "feature" It's the latest and greatest, right? 👀 Used internally by ensurePermission
  47. @Louis_CAD Where can I f ind these APIs? On my

    machine™ In Splittie s​ /fun-pack on later this year
  48. @Louis_CAD What is Splitties? Now LouisCAD/Splitties is a set of

    small libraries, most Android- only, some multiplatform. 
 It contains some of my best extensions and API simplifiers. Soon An OSS organization hosting multiple Kotlin projects. 
 
 Will contain more thoroughly crafted API simplifiers and extensions. Code of ensurePermission is there* Noti fi cations API will be there *latest implementation isn't published on Maven Central yet.
  49. @Louis_CAD Goal: more clarity for you folks Multiplatform stu f

    Splitties/fun-pack Splitties/fun-pack-views Splitties/fun-pack-dialogs Splitties/fun-pack-views-dsl Splitties/fun-pack-graphics Splitties/fun-pack-compose Splitties/funX-concurrent Splitties/funX-compose Splitties/funX funX for platform X
  50. @Louis_CAD Goal: more clarity for you folks Android-only stu f

    Splitties/fun-pack Splitties/fun-pack-views Splitties/fun-pack-dialogs Splitties/fun-pack-views-dsl Splitties/fun-pack-graphics Splitties/fun-pack-compose Splitties/funX-concurrent Splitties/funX-compose Splitties/funX fun-pack for beyond Jetpack
  51. @Louis_CAD More API simpli f iers, more organized Android-only stu

    f Splitties/fun-pack Splitties/fun-pack-views Splitties/fun-pack-dialogs Splitties/fun-pack-views-dsl Splitties/fun-pack-graphics Splitties/fun-pack-compose Splitties/funX-concurrent Splitties/funX-compose Splitties/funX fun-pack for beyond Jetpack funX for platform X Multiplatform stu f
  52. @Louis_CAD Before your brain leaves… Don't forget to watch releases

    of LouisCAD/Splitties You can subscribe to blog.louiscad.com (I rarely post) You can ask me any question in person, or online. That's it!