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

Android KTX (Google IO 2018)

Android KTX (Google IO 2018)

This talk introduces Android KTX, which is the new set of Kotlin extensions for Android. It demonstrates how Android developers writing Kotlin can use Android KTX to make their code more idiomatic, concise, and pleasant. It will also discuss how developers can make their own libraries more Kotlin-friendly.

Video: https://youtu.be/st1XVfkDWqk

54879f243e5b72eedb2d379bed6fda27?s=128

Jake Wharton
PRO

May 10, 2018
Tweet

Transcript

  1. Android KTX Jake Wharton

  2. Android KTX Jake Wharton

  3. val userLayout: ViewGroup = findViewById(R.id.users) for (index in 0 until

    userLayout.childCount) { val view = userLayout.getChildAt(index) // Do something with index and view… }
  4. fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in

    0 until childCount) { action(index, getChildAt(index)) }Z } val userLayout: ViewGroup = findViewById(R.id.users) for (index in 0 until userLayout.childCount) { val view = userLayout.getChildAt(index) // Do something with index and view… }A
  5. fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in

    0 until childCount) { action(index, getChildAt(index)) }Z } val userLayout: ViewGroup = findViewById(R.id.users) for (index in 0 until userLayout.childCount) { val view = userLayout.getChildAt(index) // Do something with index and view… }A
  6. fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in

    0 until childCount) { action(index, getChildAt(index)) }Z }D val userLayout: ViewGroup = findViewById(R.id.users) for (index in 0 until userLayout.childCount) {G val view = userLayout.getChildAt(index) // Do something with index and view… }A
  7. fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in

    0 until childCount) { action(index, getChildAt(index)) }Z }D val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed {Gindex, view -> // Do something with index and view… }A for ( in 0 until .childCount) val = userLayout.getChildAt(index)
  8. fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in

    0 until childCount) { action(index, getChildAt(index)) }Z }D val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed {Gindex, view -> // Do something with index and view… }A
  9. fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in

    0 until childCount) { action(index, getChildAt(index)) }Z }D val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed {Gindex, view -> // Do something with index and view… }A
  10. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z }D val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed {Gindex, view -> // Do something with index and view… }A
  11. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z }D val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed {Gindex, view -> // Do something with index and view… }A
  12. // In an Activity, on API 23+… val notifications =

    getSystemService(NotificationManager::class.java)
  13. // In an Activity, on API 23+… val notifications =

    getSystemService(NotificationManager::class.java) // or all API levels… val notifications = ContextCompat.getSystemService(this, NotificationManager::class.java)
  14. // In an Activity, on API 23+… val notifications =

    getSystemService(NotificationManager::class.java) // or all API levels… val notifications = ContextCompat.getSystemService(this, NotificationManager::class.java) inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java)
  15. inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) // In

    an Activity, on API 23+… val notifications = getSystemService(NotificationManager::class.java) // or all API levels… val notifications = ContextCompat.getSystemService(this, NotificationManager::class.java)
  16. inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) // In

    an Activity, on API 23+… val notifications = getSystemService(NotificationManager::class.java) // or all API levels… val notifications = ContextCompat.getSystemService(this, NotificationManager::class.java)
  17. inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) // In

    an Activity, on API 23+… val notifications = getSystemService(NotificationManager::class.java)H // or all API levels… val notifications = ContextCompat.getSystemService(this, NotificationManager::class.java) s
  18. inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) // In

    an Activity… val notifications = systemService<NotificationManager>()H S
  19. inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) // In

    an Activity… val notifications = systemService<NotificationManager>()H
  20. avatarView.setPadding( 10, avatarView.paddingTop, 10, avatarView.paddingBottom)

  21. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } avatarView.setPadding( 10, avatarView.paddingTop, 10, avatarView.paddingBottom)
  22. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } avatarView.setPadding( 10, avatarView.paddingTop, 10, avatarView.paddingBottom)
  23. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.setPadding( 10, avatarView.paddingTop, 10, avatarView.paddingBottom)R update
  24. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(10, 10)R
  25. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(10, 10)R
  26. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(left = 10, right = 10)R
  27. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(left = 10, right = 10)R
  28. val rect = avatarView.clipBounds val left = rect.left val top

    = rect.top val right = rect.right val bottom = rect.bottom // Use left, top, right, bottom…
  29. val rect = avatarView.clipBounds val left = rect.left val top

    = rect.top val right = rect.right val bottom = rect.bottom // Use left, top, right, bottom… inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom
  30. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val rect = avatarView.clipBounds val left = rect.left val top = rect.top val right = rect.right val bottom = rect.bottom // Use left, top, right, bottom…
  31. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val rect = avatarView.clipBounds val left = rect.left val top = rect.top val right = rect.right val bottom = rect.bottom // Use left, top, right, bottom…
  32. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val rect = avatarView.clipBounds val left = rect.left valTtop = rect.top valRright = rect.right valBbottom = rect.bottom // Use left, top, right, bottom…
  33. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val (left,Ttop,Rright,Bbottom) = avatarView.clipBounds // Use left, top, right, bottom…
  34. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val (left,Ttop,Rright) = avatarView.clipBounds // Use left, top, right…
  35. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val (left,T_,Rright) = avatarView.clipBounds // Use left, right…
  36. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val (left,T_,Rright) = avatarView.clipBounds // Use left, right…
  37. var onlyDigits = true for (c in phoneNumber) {A if

    (!c.isDigit()) { onlyDigits = false break }G }B
  38. r true for (c in ) if (!c. ) {

    onlyDigits = false break }G val onlyDigits = phoneNumber.all {Ait.isDigit() }B
  39. .all {Ait.isDigit() }B val onlyDigits = TextUtils.isDigitsOnly(phoneNumber)

  40. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = TextUtils.isDigitsOnly(phoneNumber)L

  41. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.isDigitsOnly()L

  42. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.

  43. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.

  44. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.is

  45. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.is

  46. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.isDigitsOnly()L

  47. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z } inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z
  48. None
  49. core-ktx Android KTX

  50. Android KTX core-ktx Android framework

  51. Android KTX Android framework support-compat core-ktx

  52. Android KTX Android framework core core-ktx

  53. Android KTX Android framework core core-ktx fragment-ktx fragment palette-ktx palette

    collection-ktx collection lifecycle-reactivestreams-ktx lifecycle-reactivestreams sqlite-ktx sqlite navigation-*-ktx navigation-* work-runtime-ktx work-runtime
  54. Android KTX Android framework core core-ktx fragment-ktx fragment palette-ktx palette

    collection-ktx collection lifecycle-reactivestreams-ktx lifecycle-reactivestreams sqlite-ktx sqlite navigation-*-ktx navigation-* work-runtime-ktx work-runtime
  55. @RequiresApi(26) operator fun Color.plus(c: Color): Color { val s =

    if (colorSpace != c.colorSpace) c.convert(colorSpace) else c val src = s.components val dst = components var sa = s.alpha() // Destination alpha pre-composited var da = alpha() * (1.0f - sa) // Index of the alpha component val ai = componentCount - 1 // Final alpha: src_alpha + dst_alpha * (1 - src_alpha) dst[ai] = sa + da // Divide by final alpha to return non pre-multiplied color if (dst[ai] > 0) { sa /= dst[ai] da /= dst[ai] } // Composite non-alpha components for (i in 0 until ai) { dst[i] = src[i] * sa + dst[i] * da } return Color.valueOf(dst, colorSpace) }
  56. @RequiresApi(26) operator fun Color.plus(c: Color): Color { val s =

    if (colorSpace != c.colorSpace) c.convert(colorSpace) else c val src = s.components val dst = components var sa = s.alpha() // Destination alpha pre-composited var da = alpha() * (1.0f - sa) // Index of the alpha component val ai = componentCount - 1 // Final alpha: src_alpha + dst_alpha * (1 - src_alpha) dst[ai] = sa + da // Divide by final alpha to return non pre-multiplied color if (dst[ai] > 0) { sa /= dst[ai] da /= dst[ai] } // Composite non-alpha components for (i in 0 until ai) { dst[i] = src[i] * sa + dst[i] * da } return Color.valueOf(dst, colorSpace) }
  57. @RequiresApi(26) operator fun Color.plus(c: Color): Color { val s =

    if (colorSpace != c.colorSpace) c.convert(colorSpace) else c val src = s.components val dst = components var sa = s.alpha() // Destination alpha pre-composited var da = alpha() * (1.0f - sa) // Index of the alpha component val ai = componentCount - 1 // Final alpha: src_alpha + dst_alpha * (1 - src_alpha) dst[ai] = sa + da // Divide by final alpha to return non pre-multiplied color if (dst[ai] > 0) { sa /= dst[ai] da /= dst[ai] } // Composite non-alpha components for (i in 0 until ai) { dst[i] = src[i] * sa + dst[i] * da } return Color.valueOf(dst, colorSpace) }
  58. public final class ColorUtils { }Z

  59. public final class ColorUtils { @ColorInt public static int compositeColors(

    @ColorInt int foreground, @ColorInt int background) { // … }X }Z
  60. public final class ColorUtils { @ColorInt public static int compositeColors(

    @ColorInt int foreground, @ColorInt int background) { // … }X @RequiresApi(26) public static Color compositeColors( Color foreground, Color background) { // … }Y }Z
  61. @RequiresApi(26) operator fun Color.plus(c: Color): Color { val s =

    if (colorSpace != c.colorSpace) c.convert(colorSpace) else c val src = s.components val dst = components var sa = s.alpha() // Destination alpha pre-composited var da = alpha() * (1.0f - sa) // Index of the alpha component val ai = componentCount - 1 // Final alpha: src_alpha + dst_alpha * (1 - src_alpha) dst[ai] = sa + da // Divide by final alpha to return non pre-multiplied color if (dst[ai] > 0) { sa /= dst[ai] da /= dst[ai] } // Composite non-alpha components for (i in 0 until ai) { dst[i] = src[i] * sa + dst[i] * da } return Color.valueOf(dst, colorSpace) }
  62. @RequiresApi(26) inline operator fun Color.plus(c: Color): Color = ColorUtils.compositeColors(c, this)

  63. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z } inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z
  64. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z } inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z
  65. KTX Principles

  66. KTX Principles Adapt existing functionality and redirect features upstream

  67. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z } inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z
  68. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z } inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z
  69. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z } inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) } inline operator fun Rect.component1() = left inline operator fun Rect.component2() = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z
  70. operator fun ViewGroup.iterator() = object : MutableIterator<View> { private var

    index = 0 override fun hasNext() = index < childCount override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException() override fun remove() = removeViewAt(--index) }
  71. operator fun ViewGroup.iterator() = object : MutableIterator<View> { private var

    index = 0 override fun hasNext() = index < childCount override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException() override fun remove() = removeViewAt(--index) } for (view in userLayout) { // Do something with view… }
  72. operator fun ViewGroup.iterator() = object : MutableIterator<View> { private var

    index = 0 override fun hasNext() = index < childCount override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException() override fun remove() = removeViewAt(--index) } for (view in userLayout) { // Do something with view… }
  73. operator fun ViewGroup.iterator() = object : MutableIterator<View> { private var

    index = 0 override fun hasNext() = index < childCount override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException() override fun remove() = removeViewAt(--index) } for (view in userLayout) { // Do something with view… }
  74. KTX Principles Adapt existing functionality and redirect features upstream

  75. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive
  76. inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index

    in 0 until childCount) { action(index, getChildAt(index)) }Z }Z val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed { index, view -> // Do something with index and view… }A
  77. inline fun <reified T> Context.systemService() = ContextCompat.getSystemService(this, T::class.java) // In

    an Activity… val notifications = systemService<NotificationManager>()H
  78. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(left = 10, right = 10)R
  79. inline operator fun Rect.component1() = left inline operator fun Rect.component2()

    = top inline operator fun Rect.component3() = right inline operator fun Rect.component4() = bottom val (left,T_,Rright) = avatarView.clipBounds // Use left, right…
  80. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)Z val onlyDigits = phoneNumber.isDigitsOnly()L

  81. operator fun ViewGroup.iterator() = object : MutableIterator<View> { private var

    index = 0 override fun hasNext() = index < childCount override fun next() = getChildAt(index++) ?: throw IndexOutOfBoundsException() override fun remove() = removeViewAt(--index) } for (view in userLayout) { // Do something with view… }
  82. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive
  83. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive Leverage features unique to Kotlin
  84. view.setOnClickListener { // React to click… }Z

  85. fun View.click(listener: (View) -> Unit) { setOnClickListener(listener) }Y view.setOnClickListener {P

    // React to click… }Z
  86. fun View.click(listener: (View) -> Unit) { setOnClickListener(listener) }Y view.setOnClickListener {P

    // React to click… }Z c
  87. fun View.click(listener: (View) -> Unit) { setOnClickListener(listener) }Y view.click {P

    // React to click… }Z C
  88. view.click {P // React to click… }Z view.setOnClickListener { //

    React to click… }
  89. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive Leverage features unique to Kotlin
  90. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive Leverage features unique to Kotlin Code golf APIs to be as short as possible
  91. if (Build.VERSION.SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Z

  92. inline fun onApi(level: Int, body: () -> Unit) { if

    (Build.VERSION.SDK_INT >= level) { body() }Z } if (Build.VERSION.SDK_INT > 19) { TransitionManager.beginDelayedTransition(viewGroup) }Z
  93. inline fun onApi(level: Int, body: () -> Unit) { if

    (Build.VERSION.SDK_INT >= level) { body() }Z }Q if (Build.VERSION.SDK_INT > 19) { TransitionManager.beginDelayedTransition(viewGroup) }Z onApi
  94. inline fun onApi(level: Int, body: () -> Unit) { if

    (Build.VERSION.SDK_INT >= level) { body() }Z }Q onApi(19) { TransitionManager.beginDelayedTransition(viewGroup) }Z
  95. onApi(19) { TransitionManager.beginDelayedTransition(viewGroup) }Z

  96. onApi(19) { TransitionManager.beginDelayedTransition(viewGroup) }Z if (Build.VERSION.SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup)

    }Z
  97. if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Z onApi(19) { TransitionManager.beginDelayedTransition(viewGroup)

    }Z
  98. if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Z onApi(19) { TransitionManager.beginDelayedTransition(viewGroup)

    }Z
  99. if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Zelse { // Something…

    } onApi(19) { TransitionManager.beginDelayedTransition(viewGroup) }Z
  100. if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Zelse { // Something…

    }L onApi(19)F{A TransitionManager.beginDelayedTransition(viewGroup) },
  101. if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Zelse { // Something…

    }L onApi(19, {A TransitionManager.beginDelayedTransition(viewGroup) }, {T // Something… })F
  102. if (SDK_INT >= 28) { // Fancy new thing… }

    else if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Zelse { // Something… }L onApi(19, {A TransitionManager.beginDelayedTransition(viewGroup) },Z{T // Something… })F
  103. if (SDK_INT >= 28) { // Fancy new thing… }

    else if (SDK_INT >= 19) { TransitionManager.beginDelayedTransition(viewGroup) }Zelse { // Something… }L onApi(19, {A TransitionManager.beginDelayedTransition(viewGroup) },Z{T // Something… })F
  104. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive Leverage features unique to Kotlin Code golf APIs to be as short as possible
  105. KTX Principles Adapt existing functionality and redirect features upstream Default

    to inline unless code size or allocation is prohibitive Leverage features unique to Kotlin Code golf APIs to be as short as possible Optimize for a single and/or specific use case
  106. Android KTX Android framework core core-ktx fragment-ktx fragment palette-ktx palette

    collection-ktx collection lifecycle-reactivestreams-ktx lifecycle-reactivestreams sqlite-ktx sqlite navigation-*-ktx navigation-* work-runtime-ktx work-runtime
  107. Android KTX Android framework core core-ktx fragment-ktx fragment palette-ktx palette

    collection-ktx collection lifecycle-reactivestreams-ktx lifecycle-reactivestreams sqlite-ktx sqlite navigation-*-ktx navigation-* work-runtime-ktx work-runtime
  108. supportFragmentManager.beginTransaction() .replace(android.R.id.content, userFragment) .commit()

  109. inline fun FragmentManager.transaction( body: FragmentTransaction.() -> Unit ) { val

    transaction = beginTransaction() transaction.body() transaction.commit() } supportFragmentManager.beginTransaction() .replace(android.R.id.content, userFragment) .commit()
  110. inline fun FragmentManager.transaction( body: FragmentTransaction.() -> Unit ) { val

    transaction = beginTransaction() transaction.body() transaction.commit() }A supportFragmentManager.beginTransaction() .replace(android.R.id.content, userFragment) t .commit()
  111. inline fun FragmentManager.transaction( body: FragmentTransaction.() -> Unit ) { val

    transaction = beginTransaction() transaction.body() transaction.commit() }A supportFragmentManager.transaction { replace(android.R.id.content, userFragment) }Z T
  112. inline fun FragmentManager.transaction( allowStateLoss: Boolean = false body: FragmentTransaction.() ->

    Unit ) { val transaction = beginTransaction() transaction.body() if (allowStateLoss) transaction.commitAllowingStateLoss() else transaction.commit() }A supportFragmentManager.transaction { replace(android.R.id.content, userFragment) }Z
  113. inline fun FragmentManager.transaction( allowStateLoss: Boolean = false body: FragmentTransaction.() ->

    Unit ) { val transaction = beginTransaction() transaction.body() if (allowStateLoss) transaction.commitAllowingStateLoss() else transaction.commit() }A supportFragmentManager.transaction {G replace(android.R.id.content, userFragment) }Z
  114. inline fun FragmentManager.transaction( allowStateLoss: Boolean = false, body: FragmentTransaction.() ->

    Unit ) { val transaction = beginTransaction() transaction.body() if (allowStateLoss) transaction.commitAllowingStateLoss() else transaction.commit() }A supportFragmentManager.transaction(allowStateLoss = true) {G replace(android.R.id.content, userFragment) }Z
  115. L7 ALOAD 3 INVOKEVIRTUAL androidx/fragment/app/FragmentManager beginTransaction() Landroidx/fragment/app/FragmentTransaction; ASTORE 7 L8

    ALOAD 7 LDC 16908290 ALOAD 2 INVOKEVIRTUAL androidx/fragment/app/FragmentTransaction replace( I Landroidx/fragment/app/Fragment; ) Landroidx/fragment/app/FragmentTransaction; POP L9 ALOAD 7 INVOKEVIRTUAL androidx/fragment/app/FragmentTransaction commitAllowingStateLoss() I POP
  116. inline fun FragmentManager.transaction( allowStateLoss: Boolean = false, body: FragmentTransaction.() ->

    Unit ) { val transaction = beginTransaction() transaction.body() if (allowStateLoss) transaction.commitAllowingStateLoss() else transaction.commit() }A supportFragmentManager.transaction(allowStateLoss = true) {G replace(android.R.id.content, userFragment) }Z
  117. inline fun FragmentManager.transaction( now: Boolean = false, allowStateLoss: Boolean =

    false, body: FragmentTransaction.() -> Unit ) { val transaction = beginTransaction() transaction.body() if (now) { if (allowStateLoss) transaction.commitNowAllowingStateLoss() else transaction.commitNow() } else { if (allowStateLoss) transaction.commitAllowingStateLoss() else transaction.commit() } }A
  118. None
  119. None
  120. None
  121. None
  122. Building Kotlin-friendly libraries

  123. Building Kotlin-friendly libraries • Port public API or entire library

    to Kotlin
  124. Building Kotlin-friendly libraries • Port public API or entire library

    to Kotlin • Ship sibling artifact with Kotlin extensions
  125. Building Kotlin-friendly libraries • Port public API or entire library

    to Kotlin • Ship sibling artifact with Kotlin extensions • ???
  126. inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) val onlyDigits = phoneNumber.isDigitsOnly()

  127. class TextUtils { static boolean isDigitsOnly(CharSequence str) { int len

    = str.length(); // … }Y }Z inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) val onlyDigits = phoneNumber.isDigitsOnly()
  128. class TextUtils { static boolean isDigitsOnly(@NonNull CharSequence str) { int

    len = str.length(); // … }Y }Z inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) val onlyDigits = phoneNumber.isDigitsOnly()
  129. class TextUtils { static boolean isDigitsOnly(@NonNull CharSequence str) { int

    len = str.length(); // … }Y }Z inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) val onlyDigits = phoneNumber.isDigitsOnly()
  130. class TextUtils { @ExtensionFunction static boolean isDigitsOnly(@NonNull CharSequence str) {

    int len = str.length(); // … }Y }Z inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) val onlyDigits = phoneNumber.isDigitsOnly()
  131. class TextUtils { @ExtensionFunction static boolean isDigitsOnly(@NonNull CharSequence str) {

    int len = str.length(); // … }Y }Z inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) val onlyDigits = phoneNumber.isDigitsOnly()
  132. class TextUtils { @ExtensionFunction static boolean isDigitsOnly(@NonNull CharSequence str) {

    int len = str.length(); // … }Y }Z val onlyDigits = phoneNumber.isDigitsOnly()
  133. class TextUtils { @ExtensionFunction static boolean isDigitsOnly(@NonNull CharSequence str) {

    int len = str.length(); // … }Y }Z val onlyDigits = phoneNumber.isDigitsOnly()
  134. class TextUtils { @ExtensionFunction static boolean isDigitsOnly(@NonNull CharSequence str) {

    int len = str.length(); // … }Y }Z val onlyDigits = phoneNumber.isDigitsOnly() // in the bytecode we get val onlyDigits = TextUtils.isDigitsOnly(phoneNumber)
  135. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(left = 10, right = 10)R
  136. inline fun View.updatePadding( left: Int = paddingLeft, top: Int =

    paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(left = 10, right = 10)R
  137. class View { void setPadding(int left, int top, int right,

    int bottom) { /* … */ }B }A inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom) }A avatarView.updatePadding(left = 10, right = 10)R
  138. class View { void setPadding( @KtName("left")Aint left, @KtName("top")Aint top, @KtName("right")Aint

    right, @KtName("bottom")Aint bottom ) { /* … */ }B }A inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom)
  139. class View { void setPadding( @KtName("left")A@DefaultValue("paddingLeft") int left, @KtName("top")A@DefaultValue("paddingTop") int

    top, @KtName("right")A@DefaultValue("paddingRight") int right, @KtName("bottom")A@DefaultValue("paddingBottom") int bottom ) { /* … */ }B }A inline fun View.updatePadding( left: Int = paddingLeft, top: Int = paddingTop, right: Int = paddingRight, bottom: Int = paddingBottom ) { setPadding(left, top, right, bottom)
  140. class View { void setPadding( @KtName("left") @DefaultValue("paddingLeft") int left, @KtName("top")

    @DefaultValue("paddingTop") int top, @KtName("right") @DefaultValue("paddingRight") int right, @KtName("bottom") @DefaultValue("paddingBottom") int bottom ) { /* … */ }B }A avatarView.updatePadding(left = 10, right = 10)R
  141. class View { void setPadding( @KtName("left") @DefaultValue("paddingLeft") int left, @KtName("top")

    @DefaultValue("paddingTop") int top, @KtName("right") @DefaultValue("paddingRight") int right, @KtName("bottom") @DefaultValue("paddingBottom") int bottom ) { /* … */ }B }A avatarView.setPadding(left = 10, right = 10)R u p d a t e
  142. class View { void setPadding( @KtName("left") @DefaultValue("paddingLeft") int left, @KtName("top")

    @DefaultValue("paddingTop") int top, @KtName("right") @DefaultValue("paddingRight") int right, @KtName("bottom") @DefaultValue("paddingBottom") int bottom ) { /* … */ }B }A avatarView.setPadding(left = 10, right = 10)R // in bytecode we get avatarView.setPadding( 10, avatarView.paddingTop, 10, avatarView.paddingBottom) u p d a t e
  143. KEEP-110

  144. KEEP-110 • @ExtensionFunction / @ExtensionProperty — Turn a static method

    with at least one argument into an extension function or an extension property.
  145. KEEP-110 • @ExtensionFunction / @ExtensionProperty — Turn a static method

    with at least one argument into an extension function or an extension property. • 
 
 • @DefaultValue — Default parameter values.
  146. KEEP-110 • @ExtensionFunction / @ExtensionProperty — Turn a static method

    with at least one argument into an extension function or an extension property. • 
 
 • @DefaultValue — Default parameter values. • 
 
 • @KtName — An alternate name for methods, fields, and parameters for use by Kotlin code.
  147. Building Kotlin-friendly libraries • Port public API or entire library

    to Kotlin • Ship sibling artifact with Kotlin extensions • ???
  148. Building Kotlin-friendly libraries • Port public API or entire library

    to Kotlin • Ship sibling artifact with Kotlin extensions • KEEP-110 annotations
  149. Android KTX Android framework core core-ktx fragment-ktx fragment palette-ktx palette

    collection-ktx collection lifecycle-reactivestreams-ktx lifecycle-reactivestreams sqlite-ktx sqlite navigation-*-ktx navigation-* work-runtime-ktx work-runtime
  150. None
  151. None
  152. None
  153. None
  154. https://github.com/Kotlin/KEEP/issues/110

  155. Jake Wharton Thank you @JakeWharton