Slide 1

Slide 1 text

Effortless Noti f ications and Permissions with Kotlin-friendly APIs Louis CAD - droidcon Berlin 2023

Slide 2

Slide 2 text

@Louis_CAD MOBILE DEVELOPER

Slide 3

Slide 3 text

@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

Slide 4

Slide 4 text

@Louis_CAD MOBILE DEVELOPER

Slide 5

Slide 5 text

@Louis_CAD D. was designed in 2010 C. is @Deprecated B. is in alpha A. just migrated to Kotlin NotificationsCompat… MOBILE DEVELOPER

Slide 6

Slide 6 text

@Louis_CAD MOBILE DEVELOPER

Slide 7

Slide 7 text

@Louis_CAD Noti f icationCompat.java

Slide 8

Slide 8 text

@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) 🔗

Slide 9

Slide 9 text

@Louis_CAD Noti f icationCompat.java 25X larger as of 2023 (almost 10 000 lines) 🔗

Slide 10

Slide 10 text

@Louis_CAD Noti f icationCompat.java is almost 10 000 lines

Slide 11

Slide 11 text

@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…

Slide 12

Slide 12 text

@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-)

Slide 13

Slide 13 text

@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

Slide 14

Slide 14 text

@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

Slide 15

Slide 15 text

@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()

Slide 16

Slide 16 text

@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

Slide 17

Slide 17 text

@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

Slide 18

Slide 18 text

@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

Slide 19

Slide 19 text

@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

Slide 20

Slide 20 text

@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

Slide 21

Slide 21 text

@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

Slide 22

Slide 22 text

@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

Slide 23

Slide 23 text

@Louis_CAD MOBILE DEVELOPER

Slide 24

Slide 24 text

@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

Slide 25

Slide 25 text

@Louis_CAD MOBILE DEVELOPER 25X Engineer

Slide 26

Slide 26 text

@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

Slide 27

Slide 27 text

@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

Slide 28

Slide 28 text

@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

Slide 29

Slide 29 text

@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

Slide 30

Slide 30 text

@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

Slide 31

Slide 31 text

@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

Slide 32

Slide 32 text

@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

Slide 33

Slide 33 text

@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

Slide 34

Slide 34 text

@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

Slide 35

Slide 35 text

@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

Slide 36

Slide 36 text

@Louis_CAD Live demo Kotlin-friendly Noti f ications API in one of my projects

Slide 37

Slide 37 text

@Louis_CAD MOBILE DEVELOPER

Slide 38

Slide 38 text

@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

Slide 39

Slide 39 text

@Louis_CAD MOBILE DEVELOPER

Slide 40

Slide 40 text

@Louis_CAD Since Android 13, apps need a runtime permission to post noti f ications.

Slide 41

Slide 41 text

@Louis_CAD Runtime permissions 😍

Slide 42

Slide 42 text

@Louis_CAD Runtime permissions APIs 🧐

Slide 43

Slide 43 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 44

Slide 44 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 45

Slide 45 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 46

Slide 46 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 47

Slide 47 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 48

Slide 48 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 49

Slide 49 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 50

Slide 50 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 51

Slide 51 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 52

Slide 52 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 53

Slide 53 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 54

Slide 54 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 55

Slide 55 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 56

Slide 56 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 57

Slide 57 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 58

Slide 58 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 59

Slide 59 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 60

Slide 60 text

@Louis_CAD Runtime permissions: in a "nutshell" Taken from d.android.com/training/permissions/requesting

Slide 61

Slide 61 text

@Louis_CAD Runtime permissions: breaking it down Green schema from d.android.com/training/permissions/requesting

Slide 62

Slide 62 text

@Louis_CAD Runtime permissions: breaking it down Green schema from d.android.com/training/permissions/requesting Obvious & natural - no other way

Slide 63

Slide 63 text

@Louis_CAD Runtime permissions: breaking it down Green schema from d.android.com/training/permissions/requesting Obvious & natural - no other way

Slide 64

Slide 64 text

@Louis_CAD Runtime permissions: breaking it down Green schema from d.android.com/training/permissions/requesting Obvious & natural - no other way Obvious & natural - no other way

Slide 65

Slide 65 text

@Louis_CAD Runtime permissions: breaking it down Green schema from d.android.com/training/permissions/requesting Obvious & natural - no other way Obvious & natural - no other way

Slide 66

Slide 66 text

@Louis_CAD Runtime permissions: breaking it down Green schema from d.android.com/training/permissions/requesting Obvious & natural - no other way Obvious & natural - no other way

Slide 67

Slide 67 text

@Louis_CAD Runtime permissions: breaking it down

Slide 68

Slide 68 text

@Louis_CAD Runtime permissions: breaking it down

Slide 69

Slide 69 text

@Louis_CAD Runtime permissions: breaking it down Input: We want permission X

Slide 70

Slide 70 text

@Louis_CAD Runtime permissions: breaking it down Input: We want permission X Output: 
 Granted or Denied

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

@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…

Slide 74

Slide 74 text

@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?? 😰

Slide 75

Slide 75 text

@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. 😎

Slide 76

Slide 76 text

@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?? 😰

Slide 77

Slide 77 text

@Louis_CAD Runtime permissions: just one function? Input: We want permission X Output: 
 Granted or Denied Looks like a suspending function!

Slide 78

Slide 78 text

@Louis_CAD Runtime permissions: just one function? Naming ideas?

Slide 79

Slide 79 text

@Louis_CAD NAMER

Slide 80

Slide 80 text

@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.

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

@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

Slide 83

Slide 83 text

@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

Slide 84

Slide 84 text

@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

Slide 85

Slide 85 text

@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

Slide 86

Slide 86 text

@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

Slide 87

Slide 87 text

@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

Slide 88

Slide 88 text

@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

Slide 89

Slide 89 text

@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!

Slide 90

Slide 90 text

@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

Slide 91

Slide 91 text

@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

Slide 92

Slide 92 text

@Louis_CAD Live demo 2 Kotlin-friendly Permissions API in one of my projects

Slide 93

Slide 93 text

@Louis_CAD Where can I f ind these APIs? On my machine™ In Splittie s​ /fun-pack on later this year

Slide 94

Slide 94 text

@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.

Slide 95

Slide 95 text

@Louis_CAD Splitting Splitties

Slide 96

Slide 96 text

@Louis_CAD The repository split 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 LouisCAD/Splitties

Slide 97

Slide 97 text

@Louis_CAD The repository split 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

Slide 98

Slide 98 text

@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

Slide 99

Slide 99 text

@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

Slide 100

Slide 100 text

@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

Slide 101

Slide 101 text

@Louis_CAD Before your brain leaves… Don't forget to watch releases of LouisCAD/Splitties Don't touch that one

Slide 102

Slide 102 text

@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!