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

Homo Kotlin, a brief History of Kotlin in Agoda

Homo Kotlin, a brief History of Kotlin in Agoda

Android BKK Conference 2018
Android Night CBA - Vietnam

Presentation developed additionally by :
Verachad Wongsawangtham
Tipatai Puthanukunkit

Iñaki Villar

March 31, 2018
Tweet

More Decks by Iñaki Villar

Other Decks in Technology

Transcript

  1. fun String.isThaiCharacter(): Boolean public final class Extension { public static

    final boolean isThaiCharacter(@NotNull String $var) }
  2. data class Player( val id: Int, val name: String, var

    level: Int) public String toString() { return "Player(id=" + this.id + ", name=" + this.name + ", level=" + this.level + ")"; }
  3. public static final int USER = 1; interface Player<in T,

    out R> { fun name(other: T): String fun upgradePlayer(): R }
  4. public static final int USER = 1; sealed class Error

    { object UserNotFound : Error() object ExpiredSession : Error() } interface Player<in T, out R> { fun name(other: T): String fun upgradePlayer(): R }
  5. public static final int USER = 1; sealed class Error

    { object UserNotFound : Error() object ExpiredSession : Error() } interface Player<in T, out R> { fun name(other: T): String fun upgradePlayer(): R } private inline fun perform
  6. open class VActions { fun click() = ViewActions.click() … }

    open class VAssertions { fun isNotEnabled() = ViewAssertions.matches(not(isEnabled())) fun doesNotExist() = ViewAssertions.doesNotExist() … }
  7. • Lots of interesting features • Still not reach our

    goals!! What we found so far: LET’S EXPLORE MORE!!!
  8. class HTML() { fun body(Body.() -> Unit) { ... }

    } fun html(HTML.() -> Unit) { ... }
  9. class HTML() { fun body(Body.() -> Unit) { ... }

    } fun html(HTML.() -> Unit) { ... } html { body { ... } }
  10. fun expectToBeVisible() { screen { layout { isDisplayed() } }

    } fun expectToBeVisible() { interaction(vm.layout, va.isDisplayed()) }
  11. class OccupancyPageKt { val screen = OccupancyScreen() fun shouldBeVisible() {

    screen { layout { isDisplayed() } } } fun shouldSeeGuestsAndRoomLabel() { screen { guestAndRoomLabel { isDisplayed() } } }
  12. A bit of summary • Experiment on non-production codebases •

    Explore on the field we know best • Contribute back to community
  13. Why we start with companion app? • Isolate from production

    code • Make us familiar android environment • Easier to evaluate new idea Image from github.com/artem-zinnatullin/qualitymatters
  14. What we found in build process? • Build time ->

    Not Significant different • APK Size
  15. What we found in build process? • Build time ->

    Not Significant different • APK Size -> ~400KB Added
  16. What we found in build process? • Build time ->

    Not Significant different • APK Size -> ~400KB Added • Methods Count
  17. What we found in build process? • Build time ->

    Not Significant different • APK Size -> ~400KB Added • Methods Count -> ~2k methods
  18. After half a year… • Everyone is writing Kotlin now.

    • We still sharing knowledge & Best practices among developers
  19. After half a year… • Everyone is writing Kotlin now.

    • We still sharing knowledge & Best practices among developers • Problems (e.g. code generation coverage)
  20. Code Generation Problems public final class InstayFeedbackCategoryEntity { … public

    int hashCode() { return this.id * 31 + (this.title != null?this.title.hashCode():0); } public boolean equals(Object var1) { if(this != var1) { if(var1 instanceof InstayFeedbackCategoryEntity) { InstayFeedbackCategoryEntity var2 = (InstayFeedbackCategoryEntity)var1; if(this.id == var2.id && Intrinsics.areEqual(this.title, var2.title)) { return true; } } return false; } else { return true; } } }
  21. @AutoValue public abstract class HotelDetailEntity { public abstract int hotelId();

    public abstract String basicInformation(); public abstract List<String> images(); public abstract List<AttractionEntity> attractions(); public abstract List<Integer> facilityIds(); public abstract List<HotelUsefulInformationEntity> usefulInformation(); public abstract List<ReviewEntity> reviews(); public static Builder builder() { return new AutoValue_HotelDetailEntity.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract HotelDetailEntity.Builder hotelId(int hotelId); public abstract HotelDetailEntity.Builder basicInformation(String basicInformation); public abstract HotelDetailEntity.Builder images(List<String> images); public abstract HotelDetailEntity.Builder attractions(List<AttractionEntity> attractionEntities); public abstract HotelDetailEntity.Builder facilityIds(List<Integer> facilityIds); public abstract HotelDetailEntity.Builder usefulInformation(List<HotelUsefulInformationEntity> usefulInformation); public abstract HotelDetailEntity.Builder reviews(List<ReviewEntity> reviews); public abstract HotelUsefulInformationEntity build(); } }
  22. @AutoValue public abstract class HotelDetailEntity { public abstract int hotelId();

    public abstract String basicInformation(); public abstract List<String> images(); public abstract List<AttractionEntity> attractions(); public abstract List<Integer> facilityIds(); public abstract List<HotelUsefulInformationEntity> usefulInformation(); public abstract List<ReviewEntity> reviews(); public static Builder builder() { return new AutoValue_HotelDetailEntity.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract HotelDetailEntity.Builder hotelId(int hotelId); public abstract HotelDetailEntity.Builder basicInformation(String basicInformation); public abstract HotelDetailEntity.Builder images(List<String> images); public abstract HotelDetailEntity.Builder attractions(List<AttractionEntity> attractionEntities); public abstract HotelDetailEntity.Builder facilityIds(List<Integer> facilityIds); public abstract HotelDetailEntity.Builder usefulInformation(List<HotelUsefulInformationEntity> usefulInformation); public abstract HotelDetailEntity.Builder reviews(List<ReviewEntity> reviews); public abstract HotelUsefulInformationEntity build(); } }
  23. data class HotelDetailEntity( val hotelId: Int, val basicInformation: BasicInformationEntity, val

    images: ImagesEntity, val attractions: List<AttractionEntity> val facilityIds: List<Int>, val usefulInformation: List<HotelUsefulInformationEntity>, val reviews: List<ReviewEntity> )
  24. public class CheckInCheckOutDayTimeView extends LinearLayout { private TextView checkInLabel; private

    TextView checkOutLabel; public CheckInCheckOutDayTimeView(final Context context) { super(context); initView(); } public CheckInCheckOutDayTimeView(final Context context, @Nullable final AttributeSet attrs) { super(context, attrs); initView(); } public CheckInCheckOutDayTimeView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CheckInCheckOutDayTimeView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(); } private void initView() { View.inflate(getContext(), R.layout.layout_check_in_check_out_view, this); checkInLabel = findViewById(R.id.check_in_label); checkOutLabel = findViewById(R.id.check_out_label); } }
  25. public class CheckInCheckOutDayTimeView extends LinearLayout { private TextView checkInLabel; private

    TextView checkOutLabel; public CheckInCheckOutDayTimeView(final Context context) { super(context); initView(); } public CheckInCheckOutDayTimeView(final Context context, @Nullable final AttributeSet attrs) { super(context, attrs); initView(); } public CheckInCheckOutDayTimeView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CheckInCheckOutDayTimeView(final Context context, @Nullable final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(); } private void initView() { View.inflate(getContext(), R.layout.layout_check_in_check_out_view, this); checkInLabel = findViewById(R.id.check_in_label); checkOutLabel = findViewById(R.id.check_out_label); } }
  26. class CheckInCheckOutDayTimeView : LinearLayout { private val checkInLabel by lazy

    { findViewById<TextView>(R.id.check_in_label) } private val checkOutLabel by lazy { findViewById<TextView>(R.id.check_out_label) } @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) @TargetApi(Build.VERSION_CODES.LOLLIPOP) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) init { View.inflate(context, R.layout.layout_check_in_check_out_view, this) } }
  27. public void setItems(final List<String> items) { gridLayout.removeAllViews(); for (final String

    item : items) { if (!item.isEmpty()) { final TextView itemView = createItemView(item); gridLayout.addView(itemView); } } }
  28. public void setItems(final List<String> items) { gridLayout.removeAllViews(); for (final String

    item : items) { if (!item.isEmpty()) { final TextView itemView = createItemView(item); gridLayout.addView(itemView); } } }
  29. public class SimpleFragment extends Fragment { interface Listener { void

    showErrorDialog(final Throwable throwable); } public void onError(final Throwable throwable) { final Activity activity = getActivity(); if (activity instanceof Listener) { ((Listener) activity).showErrorDialog(throwable); } } }
  30. public class SimpleFragment extends Fragment { interface Listener { void

    showErrorDialog(final Throwable throwable); } public void onError(final Throwable throwable) { final Activity activity = getActivity(); if (activity instanceof Listener) { ((Listener) activity).showErrorDialog(throwable); } } }
  31. class SimpleFragment : Fragment() { interface Listener { fun showErrorDialog(throwable:

    Throwable) } fun onError(throwable: Throwable) { (activity as? Listener)?.showErrorDialog(throwable) } }
  32. @Module class DataModule { @Provides fun provideSneakerRepository(sneakerApi: SneakerApi) = SneakerRepositoryImpl(sneakerApi)

    } @Module class DataModule { @Provides fun provideSneakerRepository(sneakerApi: SneakerApi): SneakerRepository = SneakerRepositoryImpl(sneakerApi) }
  33. @AutoValue public abstract class HotelDetailEntity { public abstract int hotelId();

    public abstract String basicInformation(); public abstract List<String> images(); public abstract List<ReviewEntity> reviews(); }
  34. @AutoValue public abstract class HotelDetailEntity { public abstract int hotelId();

    public abstract String basicInformation(); public abstract List<String> images(); public abstract List<ReviewEntity> reviews(); }
  35. @AutoValue public abstract class HotelDetailEntity { public abstract int hotelId();

    @NotNull public abstract String basicInformation(); @NotNull public abstract List<String> images(); @Nullable public abstract List<ReviewEntity> reviews(); }
  36. Result • Development is easier, faster and more happy. •

    Goodbye Java :P • Kotlin forever !!
  37. HALL OF FAME https://github.com/agoda-com/fork https://github.com/VerachadW/kraph IVAN BALAKSHA VERACHAD W. BIRTH

    https://github.com/Judrummer/JxAdapter TIPATAI P. JU https://github.com/andreyfomenkov/kotlin ANDREI FOMENKOV