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

Make time count with Wear OS 🤖⌚️

Make time count with Wear OS 🤖⌚️

This talk will teach you about building Wear OS applications with Compose and Material Design. I will discuss my process of creating a countdown application with Jetpack Compose, Tiles and the Horologist APIs. You will also learn how to test your application on a Wear emulator.

You will leave this talk ready to (re)build your Wear OS applications with Jetpack Compose and Material Design.

Moyinoluwa Adeyemi

November 28, 2022
Tweet

More Decks by Moyinoluwa Adeyemi

Other Decks in Technology

Transcript

  1. Lagos
    Moyin Adeyemi
    Android GDE
    Make time count with
    Wear OS

    View Slide

  2. Lagos
    Moyin Adeyemi
    Android GDE
    Make time count with
    Wear OS

    View Slide

  3. “This talk will teach you about building Wear OS
    applications with Compose and Material Design. I will
    discuss my process of creating a countdown application
    with Jetpack Compose, Tiles and the Horologist APIs. You
    will also learn how to test your application on a Wear
    emulator.
    You will leave this talk ready to (re)build your Wear OS
    applications with Jetpack Compose and Material Design.”

    View Slide

  4. Case Study: Confetti
    https://github.com/joreilly/Confetti

    View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. What is important for Confetti Wear?

    View Slide

  16. “Focus on one or two needs of your target users
    rather than a full app experience. Don't migrate an
    entire mobile codebase and put a Wear OS user
    interface on top.
    Instead, find critical tasks that work well on the wrist
    and streamline the experience on Wear OS.”
    https://developer.android.com/training/wearables/design/getting-started

    View Slide

  17. P0
    What talk is happening now?
    What talk is happening next?
    Confetti Wear

    View Slide

  18. P1
    Tell me more about this talk
    Confetti Wear

    View Slide

  19. P1
    Tell me more about this talk
    Confetti Wear
    P2
    Tell me more about the speakers

    View Slide

  20. P3
    Navigate to the conference venue
    Confetti Wear

    View Slide

  21. P0
    What talk is happening
    now/next?

    View Slide

  22. https://developer.android.com/training/wearables/principles#appropriate-surface
    Now called “Apps”

    View Slide

  23. An entrypoint into your application. Like app widgets, but for Wear OS

    View Slide

  24. Confetti Wear
    A tale of two tiles
    Next
    Current
    Session

    View Slide

  25. View Slide

  26. Horologist
    Horologist is a group of libraries that aim to supplement Wear
    OS developers with features that are commonly required by
    developers but not yet available.
    https://github.com/google/horologist#-tiles

    View Slide

  27. Horologist

    View Slide

  28. Android Studio 2021.3.1 or newer

    View Slide

  29. Jetpack Compose?

    View Slide

  30. Jetpack Compose?
    ❌ (not for tiles 🥲)

    View Slide

  31. SuspendingTileService
    class ConfettiWearTileService : SuspendingTileService() {
    override suspend fun resourcesRequest(
    requestParams: RequestBuilders.ResourcesRequest
    ): ResourceBuilders.Resources {}
    override suspend fun tileRequest(
    requestParams: RequestBuilders.TileRequest
    ): TileBuilders.Tile {}
    }

    View Slide

  32. SuspendingTileService
    class ConfettiWearTileService : SuspendingTileService() {
    override suspend fun resourcesRequest(
    requestParams: RequestBuilders.ResourcesRequest
    ): ResourceBuilders.Resources {}
    override suspend fun tileRequest(
    requestParams: RequestBuilders.TileRequest
    ): TileBuilders.Tile {}
    }

    View Slide

  33. SuspendingTileService
    class ConfettiWearTileService : SuspendingTileService() {
    override suspend fun resourcesRequest(
    requestParams: RequestBuilders.ResourcesRequest
    ): ResourceBuilders.Resources {}
    override suspend fun tileRequest(
    requestParams: RequestBuilders.TileRequest
    ): TileBuilders.Tile {}
    }

    View Slide

  34. tileRequest()
    override suspend fun tileRequest(
    requestParams: RequestBuilders.TileRequest
    ): TileBuilders.Tile {
    ...
    return TileBuilders.Tile.Builder()
    .setResourcesVersion(RESOURCES_VERSION)
    .setTimeline(singleTileTimeline)
    .build()
    }

    View Slide

  35. singleTileTimeline
    val singleTileTimeline = TimelineBuilders.Timeline.Builder()
    .addTimelineEntry(
    TimelineBuilders.TimelineEntry.Builder()
    .setLayout(
    LayoutElementBuilders.Layout.Builder()
    .setRoot(
    tileLayout(...)
    .build()
    ...

    View Slide

  36. tileLayout()
    tileLayout(
    context = this,
    deviceParameters = requestParams.deviceParameters,
    currentSessionClickable = launchActivityClickable(
    clickableId = "current_session",
    androidActivity = openCurrentSession()
    ),
    nextSessionClickable = launchActivityClickable(
    clickableId = "next_session",
    androidActivity = openNextSession()
    ),
    )

    View Slide

  37. tileLayout()
    private fun tileLayout(
    context: Context,
    deviceParameters: DeviceParametersBuilders.DeviceParameters,
    currentSessionClickable: ModifiersBuilders.Clickable,
    nextSessionClickable: ModifiersBuilders.Clickable
    ): LayoutElementBuilders.LayoutElement {
    return PrimaryLayout.Builder(deviceParameters)
    ...

    View Slide

  38. tileLayout()
    return PrimaryLayout.Builder(deviceParameters)
    .setContent(
    CompactChip.Builder(
    context,
    "Happening Now!",
    currentSessionClickable,
    deviceParameters
    )
    .setChipColors(ChipColors.primaryChipColors(ConfettiTileTheme.colors))
    .build()
    ).setPrimaryChipContent(
    CompactChip.Builder(context, "Next talk", nextSessionClickable, deviceParameters)
    .setChipColors(ChipColors.secondaryChipColors(ConfettiTileTheme.colors))
    .build()
    )
    .build()

    View Slide

  39. return PrimaryLayout.Builder(deviceParameters)
    .setContent(
    CompactChip.Builder(
    context,
    "Happening Now!",
    currentSessionClickable,
    deviceParameters
    )
    .setChipColors(ChipColors.primaryChipColors(ConfettiTileTheme.colors))
    .build()
    ).setPrimaryChipContent(
    CompactChip.Builder(context, "Next talk", nextSessionClickable, deviceParameters)
    .setChipColors(ChipColors.secondaryChipColors(ConfettiTileTheme.colors))
    .build()
    )
    .build()
    tileLayout()

    View Slide

  40. Register the Service in the AndroidManifest.xml file
    android:name=".presentation.ui.tile.ConfettiWearTileService"
    android:description="@string/description"
    android:exported="true"
    android:icon="@drawable/icon"
    android:label="@string/label"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">




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

    View Slide

  41. Session Info Screen

    View Slide

  42. View Slide

  43. Jetpack Compose ✅

    View Slide

  44. Session Info Card
    AppCard(
    modifier = Modifier
    .fillMaxWidth()
    .padding(bottom = 8.dp),
    appName = { Text("Main Room") },
    time = { Text("40m") },
    title = { Text("Make time count with Wear OS") },
    onClick = TODO()
    ) {
    Text("Moyin")
    }

    View Slide

  45. Curved “Happening Now!” Text
    if (LocalConfiguration.current.isScreenRound) {
    val happeningNow = stringResource(R.string.happening_now)
    val primaryColor = MaterialTheme.colors.primary
    CurvedLayout(
    anchor = 90F,
    anchorType = AnchorType.Center,
    modifier = Modifier.fillMaxSize()
    ) {
    curvedRow {
    ...
    }

    View Slide

  46. curvedRow()
    curvedRow {
    curvedText(
    text = happeningNow,
    angularDirection = CurvedDirection.Angular.CounterClockwise,
    style = CurvedTextStyle(
    fontSize = 18.sp,
    color = primaryColor
    ),
    modifier = CurvedModifier
    .radialGradientBackground(
    0f to Color.Transparent,
    0.2f to Color.DarkGray.copy(alpha = 0.2f),
    0.6f to Color.DarkGray.copy(alpha = 0.2f),
    0.7f to Color.DarkGray.copy(alpha = 0.05f),
    1f to Color.Transparent
    )
    )

    View Slide

  47. Navigating from the
    tile to the app

    View Slide

  48. View Slide

  49. Remember the TileLayout?
    tileLayout(
    context = this,
    deviceParameters = requestParams.deviceParameters,
    currentSessionClickable = launchActivityClickable(
    clickableId = "current_session",
    androidActivity = openCurrentSession()
    ),
    nextSessionClickable = launchActivityClickable(
    clickableId = "next_session",
    androidActivity = openNextSession()
    ),
    )

    View Slide

  50. tileLayout()
    tileLayout(
    context = this,
    deviceParameters = requestParams.deviceParameters,
    currentSessionClickable = launchActivityClickable(
    clickableId = "current_session",
    androidActivity = openCurrentSession()
    ),
    nextSessionClickable = launchActivityClickable(
    clickableId = "next_session",
    androidActivity = openNextSession()
    ),
    )

    View Slide

  51. launchActivityClickable()
    internal fun launchActivityClickable(
    clickableId: String,
    androidActivity: ActionBuilders.AndroidActivity
    ) = ModifiersBuilders.Clickable.Builder()
    .setId(clickableId)
    .setOnClick(
    ActionBuilders.LaunchAction.Builder()
    .setAndroidActivity(androidActivity)
    .build()
    )
    .build()

    View Slide

  52. openCurrentSession()
    internal fun openCurrentSession() =
    ActionBuilders.AndroidActivity.Builder()
    .setConfettiWearActivity()
    .addKeyToExtraMapping(
    MainActivity.EXTRA_SESSION,
    ActionBuilders.stringExtra(MainActivity.EXTRA_CURRENT_SESSION)
    )
    .build()

    View Slide

  53. openCurrentSession()
    internal fun openCurrentSession() =
    ActionBuilders.AndroidActivity.Builder()
    .setConfettiWearActivity()
    .addKeyToExtraMapping(
    MainActivity.EXTRA_SESSION,
    ActionBuilders.stringExtra(MainActivity.EXTRA_CURRENT_SESSION)
    )
    .build()

    View Slide

  54. setConfettiWearActivity()
    internal fun
    ActionBuilders.AndroidActivity.Builder.setConfettiWearActivity()
    : ActionBuilders.AndroidActivity.Builder {
    return setPackageName("com.devfestlagos.wearapp")
    .setClassName("com.devfestlagos.wearapp.presentation.MainActivity")
    }

    View Slide

  55. Debugging

    View Slide

  56. Debugging
    1. Debug over Bluetooth
    https://developer.android.com/training/wearables/get-started/debugging

    View Slide

  57. Debugging
    1. Debug over Bluetooth
    2. Debug over Wi-Fi
    https://developer.android.com/training/wearables/get-started/debugging

    View Slide

  58. Debugging
    1. Debug over Bluetooth
    2. Debug over Wi-Fi
    3. Wireless Debugging

    View Slide

  59. Wireless Debugging
    1. Enable Developer Options on the watch

    View Slide

  60. 1. Enable Developer Options on the watch
    Settings
    Wireless Debugging

    View Slide

  61. 1. Enable Developer Options on the watch
    System > About
    Settings
    Wireless Debugging

    View Slide

  62. 1. Enable Developer Options on the watch
    System > About
    Settings
    Build number x7
    Wireless Debugging

    View Slide

  63. 1. Enable Developer Options on the watch
    System > About
    Settings
    “You are now a developer!”
    Build number x7
    Wireless Debugging

    View Slide

  64. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    Wireless Debugging

    View Slide

  65. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    Settings
    Wireless Debugging

    View Slide

  66. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    Settings
    Wireless Debugging
    Connectivity > Wi-Fi

    View Slide

  67. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    Settings
    Wireless Debugging
    Connectivity > Wi-Fi
    Connect to a network
    (it must be the same network the
    development machine is connected to)

    View Slide

  68. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    Wireless Debugging

    View Slide

  69. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    Wireless Debugging
    Settings

    View Slide

  70. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    Wireless Debugging
    Settings
    Developer Options >
    Wireless Debugging

    View Slide

  71. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    Wireless Debugging
    Settings
    Developer Options >
    Wireless Debugging
    Wireless Debugging >
    Pair New Device

    View Slide

  72. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    4. Pair using the pairing code in Android Studio
    Wireless Debugging

    View Slide

  73. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    4. Pair using the pairing code in Android Studio
    Wireless Debugging

    View Slide

  74. 1. Enable Developer Options on the watch
    2. Connect the watch to a Wi-Fi network
    3. Retrieve Wi-Fi pairing code from the watch
    4. Pair using the pairing code in Android Studio
    Wireless Debugging

    View Slide

  75. Resources
    ● Ataul - androiddev.social/@ataulm
    ● d.android.com/wear
    ● github.com/android/wear-os-samples/tree/main/ComposeAd
    vanced

    View Slide

  76. Questions?

    View Slide

  77. Feedback
    androiddev.social/@moyheen
    twitter.com/moyheen

    View Slide

  78. Lagos
    Moyin Adeyemi
    Android GDE
    Thank you!

    View Slide