$30 off During Our Annual Pro Sale. View Details »

[DroidconSF]Developing Apps optimized for Wear OS with Jetpack Compose

[DroidconSF]Developing Apps optimized for Wear OS with Jetpack Compose

This is the slide, "Developing Apps optimized for Wear OS with Jetpack Compose"
#wearos #android #droidconSF #dcsf23 #Droidcon

Kenichi Kambara

June 09, 2023
Tweet

More Decks by Kenichi Kambara

Other Decks in Technology

Transcript

  1. Developing Apps optimized for Wear OS
    with Jetpack Compose
    Kenichi Kambara (@korodroid)


    June 9, 2023
    2023

    View Slide

  2. 2
    About me
    Kenichi Kambara (@korodroid)
    •Mobile App Development


    •Speeches (e.g. 14 Int’l Confs.)


    •Writings (e.g. 8 Dev Books)


    •Principal Evangelist at NTT TechnoCross


    •[Private] iplatform.org

    View Slide

  3. Agenda
    •Wear OS Overview


    •Wear OS Topics from I/O 2023


    •Compose for Wear OS


    •How to optimize Apps for Wear OS?
    Source: Google I/O 2023 Keynote / Developer Keynote / Wear OS Session


    Android Developers Blog

    View Slide

  4. Wear OS Overview

    Noti
    fi
    cation Complication
    Based on Android & optimized for the wrist
    Tile

    App

    View Slide

  5. Wear OS Topics from I/O 2023

    Watch Face Format Watch Face Studio Tile (Animation)

    View Slide

  6. Wear OS ΞϓϦ։ൃೖ໳ with Jetpack Compose
    Compose for Wear OS

    View Slide

  7. Wear OS Features

    App Noti
    fi
    cation Complication
    Based on Android & optimized for the wrist
    Tile

    View Slide

  8. Implementation Example (Button)

    @Composable
    fun ButtonExample() {
    Button(
    modifier =
    Modifier.size(ButtonDefaults.LargeButtonSize),
    onClick = { /* do anything */ },
    enabled = true
    ){
    Icon(Icons.Default.Mic, contentDescription
    = "mic")
    }
    }

    View Slide

  9. Very similar to mobile.


    What’s the difference?
    9

    View Slide

  10. 1st difference: UI & UX
    Input components
    Dialogs Page Indicator
    Progress Indicator

    Navigation

    View Slide


  11. 2nd difference: Screen Sizes & Shapes

    View Slide


  12. Wear OS

    (androidx.wear.*)
    Mobile

    (androidx.*)
    androidx.wear.compose:compose-material
    androidx.compose.foundation:foundation
    androidx.wear.compose:compose-foundation
    androidx.compose.material:material
    androidx.navigation:navigation-compose
    androidx.compose.foundation:foundation
    ref: https://developer.android.com/training/wearables/compose
    Material
    androidx.wear.compose:compose-navigation
    Navigation
    Foundation
    3rd difference: Development

    View Slide

  13. e.g. Button

    Wear OS

    (androidx.wear.*)
    Mobile

    (androidx.*)
    androidx.wear.compose:compose-material androidx.compose.material:material

    View Slide

  14. 14
    Asked 1 Question to Studio Bot

    View Slide

  15. 15
    Wear OS Development Environment

    Can start easily with Android Studio

    View Slide

  16. 16
    Wear OS Development Environment

    View Slide

  17. 17
    Create a Wear OS app project

    Simple Template

    View Slide

  18. Live Demo💪

    View Slide

  19. 19
    How to optimize Apps for Wear OS?

    View Slide

  20. Today's example: Contact App

    @Composable
    fun ContactListScreenV0() {
    LazyColumn(
    horizontalAlignment = Alignment.CenterHorizontally
    ) {
    item {
    ListHeader {
    Text(text = "Contacts")
    }
    }
    items(model.data.size) {
    Chip(
    modifier = Modifier
    .fillMaxSize(),
    icon = {
    Icon(
    Icons.Rounded.Face,
    contentDescription = "faceIcon",
    )
    },
    label = { Text(model.data[it]) },
    colors = ChipDefaults.primaryChipColors(),
    onClick = { },
    )
    }
    }
    }
    not BAD, but not GOOD

    View Slide

  21. How to make better UI for Wear OS?
    21

    View Slide

  22. Step1. Replacing to ScalingLazyColumn

    @Composable
    fun ContactListScreenV1() {
    ScalingLazyColumn(
    horizontalAlignment = Alignment.CenterHorizontally
    ) {
    item {
    ListHeader {
    Text(text = "Contacts")
    }
    }
    items(model.data.size) {
    Chip(
    modifier = Modifier
    .fillMaxSize(),
    icon = {
    Icon(
    Icons.Rounded.Face,
    contentDescription = "faceIcon",
    )
    },
    label = { Text(model.data[it]) },
    colors = ChipDefaults.primaryChipColors(),
    onClick = { },
    )
    }
    }
    }
    /MainActivity.kt

    View Slide

  23. How should we provide better UI/UX?

    TimeText Vignette PositionIndicator

    View Slide

  24. Step2. Using Scaffold

    @Composable
    fun ContactListScreenV2() {
    val listState = rememberScalingLazyListState()
    Scaffold(
    timeText = {
    if (!listState.isScrollInProgress) {
    TimeText()
    }
    },
    vignette = {
    Vignette(
    vignettePosition = VignettePosition.TopAndBottom
    )
    },
    positionIndicator = {
    PositionIndicator(
    scalingLazyListState = listState,
    )
    }
    ) {
    ScalingLazyColumn(state = listState) {
    // …
    }

    View Slide

  25. How to support more screens?
    25

    +

    View Slide

  26. Step3. Adding a new Screen

    @Composable
    fun ContactDetailScreen(id: Int) {
    Chip(
    modifier = Modifier
    .fillMaxSize(),
    icon = {
    Icon(
    Icons.Rounded.Face,
    contentDescription = "faceIcon",
    )
    },
    label = {
    Column(
    ) {
    Text(text = model.data[id].name,

    fontWeight = FontWeight.Bold)
    Text(text = model.data[id].phone)
    Text(text = model.data[id].nation)
    }
    },
    colors = ChipDefaults.primaryChipColors(),
    onClick = { /* do anything */ },
    )
    }

    View Slide

  27. How to support navigation?
    27

    View Slide

  28. Implementing with Wear Navigation

    Wear OS

    (androidx.wear.*)
    Mobile

    (androidx.*)
    androidx.navigation:navigation-compose
    ref: https://developer.android.com/training/wearables/compose/navigation
    androidx.wear.compose:compose-navigation
    Navigation
    /build.gradle
    dependencies {
    def wear_compose_version = “1.1.2"
    implementation "androidx.wear.compose:compose-navigation:$wear_compose_version"
    }

    View Slide


  29. @Composable
    fun ScreenNavigation_GoodImpl(
    navController: NavHostController =
    rememberSwipeDismissableNavController()
    ) {
    SwipeDismissableNavHost(
    navController = navController,
    startDestination = "contact_list"
    ) {
    composable("contact_list") {
    ContactListScreenV3(navController = navController)
    }
    composable("contact_detail/{id}") {
    val id = it.arguments?.getString("id")!!
    ContactDetailScreen(id = id.toInt())
    }
    }
    }
    Step4. Implementing Wear Navigation

    View Slide

  30. Step4. Implementing Wear Navigation

    @Composable
    fun ContactListScreenV3(navController: NavHostController) {
    Scaffold(
    // just same
    ) {
    ScalingLazyColumn(state = listState) {
    item {
    // just same
    }
    items(model.data.size) {
    Chip(
    modifier = Modifier.fillMaxSize(),
    icon = {
    // just same },
    label = { Text(model.data[it].name) },
    colors = ChipDefaults.primaryChipColors(),
    onClick =
    { navController.navigate("contact_detail/$it") },
    )
    }
    }
    }
    }

    View Slide

  31. Looks good, but to make it better…
    31

    View Slide

  32. Adding a Tile feature

    Noti
    fi
    cation Complication
    Provide easier access to this app
    Tile

    App +

    View Slide

  33. Step5. Implementing a Tile feature

    class MainTileService : CoroutinesTileService() {
    // Abbreviates some procedures, such as reading data from the repository

    override suspend fun resourcesRequest(
    requestParams: RequestBuilders.ResourcesRequest
    ): ResourceBuilders.Resources {
    return ResourceBuilders.Resources.Builder().setVersion(RESOURCES_VERSION).build()
    }
    override suspend fun tileRequest(
    requestParams: RequestBuilders.TileRequest
    ): TileBuilders.Tile {
    val singleTileTimeline = TimelineBuilders.Timeline.Builder().addTimelineEntry(
    TimelineBuilders.TimelineEntry.Builder().setLayout(
    LayoutElementBuilders.Layout.Builder().setRoot(tileLayout(this)).build()
    ).build()
    ).build()
    return TileBuilders.Tile.Builder().setResourcesVersion(RESOURCES_VERSION)
    .setTimeline(singleTileTimeline).build()
    }
    }
    a Kotlin coroutine-friendly wrapper

    from the Horologist Tiles library
    /MainTileService.kt

    View Slide

  34. Step5. Adding a Tile de
    fi
    nition

    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@android:style/Theme.DeviceDefault">

    …

    android:name=".tile.MainTileService"
    android:exported="true"
    android:label="@string/tile_label"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">



    android:name="androidx.wear.tiles.PREVIEW"
    android:resource="@drawable/tile_preview" />


    /AndroidManifest.xml

    View Slide

  35. That’s all! Can show the last contact easily.
    35

    View Slide


  36. Tips💡 Useful features for Wear OS

    View Slide


  37. Tips💡 Next Step (How to optimize more)
    ref: Principles of Wear OS development

    https://developer.android.com/training/wearables/principles

    View Slide

  38. Reference

    Code (My Codelab)

    bit.ly/3UWME7M

    Slide

    @korodroid

    View Slide

  39. Go future with Wear OS & Jetpack Compose🚀




    http://www.linkedin.com/in/korodroid
    Thank you so much
    http://fb.com/kanbara.kenichi
    @korodroid

    View Slide