Slide 1

Slide 1 text

Creating Great APIs With Kotlin David Tiago Conceição Involves Android Dev BR @davidtiagocon

Slide 2

Slide 2 text

Android Development if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return context.getResources() .getColor(colorId, context.getTheme()); } else { return context.getResources() .getColor(colorId); }

Slide 3

Slide 3 text

Android Development public final class ColorUtils { @ColorInt public static int getColor( Context context, @ColorRes int colorId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return context.getResources() .getColor(colorId, context.getTheme()); } else { return context.getResources() .getColor(colorId); } } }

Slide 4

Slide 4 text

Android Development ColorUtils.getColor(context, R.color.primary_regular)

Slide 5

Slide 5 text

Android Development context.getThemedColor(context, R.color.primary_regular)

Slide 6

Slide 6 text

Android Development

Slide 7

Slide 7 text

Extension Functions @ColorInt fun Context.getThemedColor(@ColorRes colorId: Int) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { this.resources.getColor(colorId, this.theme) } else { this.resources.getColor(colorId) }

Slide 8

Slide 8 text

Extension Functions @ColorInt fun Context.getThemedColor(@ColorRes colorId: Int) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { this.resources.getColor(colorId, this.theme) } else { this.resources.getColor(colorId) }

Slide 9

Slide 9 text

Extension Functions

Slide 10

Slide 10 text

Extension Functions public final class ContextExtensionsKt { @ColorInt public static final int getThemedColor(@NotNull Context $this$getThemedColor, @ColorRes int colorId) { return Build.VERSION.SDK_INT >= 23 ? $this$getThemedColor.getResources().getColor(colorId, $this$getThemedColor.getTheme()) : $this$getThemedColor.getResources().getColor(colorId); } }

Slide 11

Slide 11 text

Extension Functions public final class ContextExtensionsKt { @ColorInt public static final int getThemedColor(@NotNull Context $this$getThemedColor, @ColorRes int colorId) { return Build.VERSION.SDK_INT >= 23 ? $this$getThemedColor.getResources().getColor(colorId, $this$getThemedColor.getTheme()) : $this$getThemedColor.getResources().getColor(colorId); } }

Slide 12

Slide 12 text

Extension Functions ContextExtensionsKt.getThemedColor(context, -500071)

Slide 13

Slide 13 text

Extension Functions @ColorInt inline fun Context.getThemedColor(@ColorRes colorId: Int) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { this.resources.getColor(colorId, this.theme) } else { this.resources.getColor(colorId) }

Slide 14

Slide 14 text

Extension Functions var10000 = VERSION.SDK_INT >= 23 ? context.getResources().getColor(colorId$iv, context.getTheme()) : context.getResources().getColor(colorId$iv);

Slide 15

Slide 15 text

Extension Functions useCase.execute(id, date) .lastElement() .test() .assertDefaultData(pointOfSaleNameFromItinerary) .assertExpectedState(state)

Slide 16

Slide 16 text

Extension Functions useCase.execute(id, date) .lastElement() .test() .assertDefaultData(pointOfSaleNameFromItinerary) .assertExpectedState(state) private fun TestObserver.assertExpectedState( expectedState: SomeInternalState): TestObserver { assertValue { it.someInternalState == expectedState } return this }

Slide 17

Slide 17 text

Extension Properties val List.lastIndex: Int get() = size - 1

Slide 18

Slide 18 text

“Extension based development” Less Utils Less hierarchies More extensions Extensions

Slide 19

Slide 19 text

Kotlin Android Extensions Android KTX Extension Libraries

Slide 20

Slide 20 text

animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationEnd(Animation animation) { onViewShowed(); } @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} }); Arguments

Slide 21

Slide 21 text

animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationEnd(Animation animation) { onViewShowed(); } @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} }); Arguments

Slide 22

Slide 22 text

fun Animation.listener( onAnimationStart: ((Animation) -> Unit)? = null, onAnimationEnd: ((Animation) -> Unit)? = null, onAnimationRepeat: ((Animation) -> Unit)? = null) { ... Default Arguments

Slide 23

Slide 23 text

fun Animation.listener( onAnimationStart: ((Animation) -> Unit)? = null, onAnimationEnd: ((Animation) -> Unit)? = null, onAnimationRepeat: ((Animation) -> Unit)? = null) { this.setAnimationListener(object : Animation.AnimationListener { override fun onAnimationRepeat(animation: Animation?) { onAnimationStart?.invoke(animation!!) ... Default Arguments

Slide 24

Slide 24 text

animation.listener(onAnimationEnd = { onViewShowed()}) Named Arguments

Slide 25

Slide 25 text

data class ClassWithManyIntegers( val integerProperty: Int = 0, val anotherIntegerProperty: Int = 0, val someExtraIntegerProperty: Int = 0) Builders

Slide 26

Slide 26 text

data class ClassWithManyIntegers( val integerProperty: Int = 0, val anotherIntegerProperty: Int = 0, val someExtraIntegerProperty: Int = 0) val complexInstance = ClassWithManyIntegers() Builders

Slide 27

Slide 27 text

data class ClassWithManyIntegers( val integerProperty: Int = 0, val anotherIntegerProperty: Int = 0, val someExtraIntegerProperty: Int = 0) val complexInstance = ClassWithManyIntegers( someExtraIntegerProperty = 10) Builders

Slide 28

Slide 28 text

val firstPoint = Point(10, 10) val secondPoint = Point(20, 20) Operations

Slide 29

Slide 29 text

val firstPoint = Point(10, 10) val secondPoint = Point(20, 20) val firstX = firstPoint.x val firstY = firstPoint.y val secondX = secondPoint.x val secondY = secondPoint.y val plusPoint = Point(firstX + secondX, secondY + secondY) Operations

Slide 30

Slide 30 text

val firstPoint = Point(10, 10) val secondPoint = Point(20, 20) val plusPoint = firstPoint + secondPoint Operations

Slide 31

Slide 31 text

val firstPoint = Point(10, 10) val secondPoint = Point(20, 20) val plusPoint = firstPoint + secondPoint operator fun Point.plus(another: Point) = Point(x + another.x, y + another.y) Operators

Slide 32

Slide 32 text

val (x,y) = firstPoint Operators

Slide 33

Slide 33 text

val (x,y) = firstPoint operator fun Point.component1() = x operator fun Point.component2() = y Operators

Slide 34

Slide 34 text

val viewGroup: ViewGroup = getViewGroup() viewGroup.addView(view) Operators

Slide 35

Slide 35 text

val viewGroup: ViewGroup = getViewGroup() viewGroup += view Operators

Slide 36

Slide 36 text

val viewGroup: ViewGroup = getViewGroup() viewGroup += view operator fun ViewGroup.plusAssign(view: View) = addView(view) Operators

Slide 37

Slide 37 text

val view = viewGroup[0] Operators

Slide 38

Slide 38 text

val view = viewGroup[0] operator fun ViewGroup.get(index: Int): View? = getChildAt(index) Operators

Slide 39

Slide 39 text

for (index in 0 until viewGroup.childCount) { doSomething(viewGroup.getChildAt(index)) } Functions

Slide 40

Slide 40 text

for (index in 0 until viewGroup.childCount) { doSomething(viewGroup.getChildAt(index)) } viewGroup.forEach { view -> doSomething(view) } Functions

Slide 41

Slide 41 text

viewGroup.forEach { view -> doSomething(view) } fun ViewGroup.forEach(onEach: (View) -> Unit) { for (index in 0 until childCount) { onEach(getChildAt(index)) } } Functions

Slide 42

Slide 42 text

viewGroup.forEach { view -> doSomething(view) } fun ViewGroup.forEach(onEach: (View) -> Unit) { for (index in 0 until childCount) { onEach(getChildAt(index)) } } Functions

Slide 43

Slide 43 text

runInDatabaseTransaction { // Some database stuff } Functions

Slide 44

Slide 44 text

fun runInDatabaseTransaction(databaseFunction: (SQLiteDatabase) -> Unit) { val dataBase = getDatabase() try { dataBase.beginTransaction() databaseFunction(dataBase) dataBase.setTransactionSuccessful() } finally { dataBase.endTransaction() } Functions

Slide 45

Slide 45 text

for (index in 0 until viewGroup.childCount) { doSomething(viewGroup.getChildAt(index)) } Functions

Slide 46

Slide 46 text

/** Returns a range from this value up to but excluding the specified [to] value. * If the [to] value is less than or equal to `this` value, then the returned range is empty.*/ public infix fun Int.until(to: Int): IntRange { if (to <= Int.MIN_VALUE) return IntRange.EMPTY return this .. (to - 1).toInt() } Functions

Slide 47

Slide 47 text

val untilRange = 0 until 10 val untilCharRange = 'a' until 'z' val downProgression = 10 downTo 0 Functions

Slide 48

Slide 48 text

//Mockk every { saveEvent(any(), any()) } returns Unit every { mock invoke "openDoor" withArguments listOf("left", "rear") } returns "OK" verify { sendMessageHandler wasNot Called } DSLs

Slide 49

Slide 49 text

//Mockk infix fun returns(returnValue: T) = answers(ConstantAnswer(returnValue)) DSLs

Slide 50

Slide 50 text

class Loading class Error class Success class ViewModel { var currentState: Any = Loading() } State

Slide 51

Slide 51 text

open class PossibleState class Loading : PossibleState() class Error : PossibleState() class Success : PossibleState() class ViewModel { var currentState: PossibleState = Loading() } State

Slide 52

Slide 52 text

sealed class PossibleState { object Loading : PossibleState() object Error : PossibleState() object Success : PossibleState()} class ViewModel { var currentState: PossibleState = PossibleState.Loading } Sealed Classes

Slide 53

Slide 53 text

class Scheduler { fun schedule(delay: Long, action: () -> Unit) { //... } } Argument Types

Slide 54

Slide 54 text

class Scheduler { fun schedule( delay: Long, timeUnit: TimeUnit, action: () -> Unit) { //... } } Argument Types

Slide 55

Slide 55 text

class Minutes(val value: Long) class Scheduler { fun schedule( delay: Minutes, action: () -> Unit) { //... } } Argument Types

Slide 56

Slide 56 text

inline class Minutes(val value: Long) class Scheduler { fun schedule( delay: Minutes, action: () -> Unit) { //... } } Inline classes

Slide 57

Slide 57 text

Extensions Named and Default arguments Operators Higher order functions Infix DSLs Sealed classes Inline classes Wrapping up

Slide 58

Slide 58 text

How to know more? ● Extensions: kotlinlang.org/docs/reference/extensions.html ● Kotlin Android Extensions: kotlinlang.org/docs/tutorials/android-plugin.html ● Android KTX: developer.android.com/kotlin/ktx ● Keywords and Operators: kotlinlang.org/docs/reference/keyword-reference.html ● Functions: kotlinlang.org/docs/reference/functions.html ● MockK: mockk.io ● Sealed Classes: kotlinlang.org/docs/reference/sealed-classes.html ● KotlinConf 2018 - Representing State: the Kotlin Edition by Christina Lee: www.youtube.com/watch?v=-lVVfxsRjcY ● Inline classes: kotlinlang.org/docs/reference/inline-classes.html

Slide 59

Slide 59 text

slack.AndroidDevBR.org Android Dev BR

Slide 60

Slide 60 text

Thank you! David Tiago Conceição [email protected] Android Dev BR @davidtiagoconceicao twitter.com/@davidtiagocon speakerdeck.com/davidtcdeveloper