Wear OS에서 첫 카드 만들기

1. 소개

사용자가 시계 화면을 첫 번째 카드인 일기예보로 스와이프한 후 타이머 카드로 스와이프했다가 다시 돌아오는 애니메이션 시계

Wear OS 카드는 사용자가 작업 처리에 필요한 정보와 동작에 쉽게 액세스할 수 있게 해 줍니다. 사용자는 시계 화면에서 간단히 스와이프하여 최근 일기예보를 찾거나 타이머를 시작할 수 있습니다.

카드는 자체 애플리케이션 컨테이너에서 실행되지 않고 시스템 UI의 일부로 실행됩니다. Service를 사용하여 카드의 레이아웃과 콘텐츠를 설명합니다. 그러면 시스템 UI가 필요에 따라 카드를 렌더링합니다.

실행할 작업

35a459b77a2c9d52.png

최근 대화를 표시하는 메시지 앱의 카드를 빌드합니다. 이 표시 영역에서 사용자는 다음 3가지 일반적인 작업 중 하나로 바로 이동할 수 있습니다.

  • 대화 열기
  • 대화 검색
  • 새 메시지 쓰기

학습할 내용

이 Codelab에서는 다음을 포함하여 자체 Wear OS 카드를 작성하는 방법을 알아봅니다.

  • TileService를 만드는 방법
  • 기기에서 카드를 테스트하는 방법
  • Android 스튜디오에서 카드 UI를 미리 보는 방법
  • 카드 UI를 개발하는 방법
  • 이미지를 추가하는 방법
  • 상호작용 처리

기본 요건

  • Kotlin에 관한 기본적 이해

2. 설정

이 단계에서는 환경을 설정하고 시작 프로젝트를 다운로드해 보겠습니다.

필요한 항목

Wear OS 사용에 익숙하지 않은 경우 시작하기 전에 이 빠른 가이드를 읽어보는 것이 좋습니다. Wear OS 에뮬레이터 설정에 관한 안내가 포함되어 있으며 시스템 탐색 방법을 설명합니다.

코드 다운로드

git이 설치되어 있으면 아래 명령어를 실행하여 이 저장소의 코드를 클론하면 됩니다.

git clone https://github.com/android/codelab-wear-tiles.git
cd codelab-wear-tiles

git이 없는 경우 다음 버튼을 클릭하여 이 Codelab을 위한 모든 코드를 다운로드할 수 있습니다.

Android 스튜디오에서 프로젝트 열기

'Welcome to Android Studio' 창에서 c01826594f360d94.png Open an Existing Project 또는 File > Open을 선택하고 [다운로드 위치] 폴더를 선택합니다.

3. 기본 카드 만들기

카드의 진입점은 카드 서비스입니다. 이 단계에서는 카드 서비스를 등록하고 카드의 레이아웃을 정의합니다.

HelloWorldTileService

TileService를 구현하는 클래스는 다음 두 메서드를 지정해야 합니다.

  • onTileResourcesRequest(requestParams: ResourcesRequest): ListenableFuture<Resources>
  • onTileRequest(requestParams: TileRequest): ListenableFuture<Tile>

첫 번째 메서드는 문자열 ID를 카드에서 사용할 이미지 리소스에 매핑하는 Resources 객체를 반환합니다.

두 번째 메서드는 레이아웃을 포함하여 카드에 관한 설명을 반환합니다. 여기에서 카드의 레이아웃과 데이터가 이 레이아웃에 결합되는 방법을 정의합니다.

start 모듈에서 HelloWorldTileService.kt를 엽니다. 모든 변경사항은 이 모듈에 적용됩니다. 이 Codelab의 결과를 확인하려는 경우 finished 모듈도 있습니다.

HelloWorldTileServiceHorologist 카드 라이브러리의 Kotlin 코루틴 친화적인 래퍼 SuspendingTileService를 확장합니다. Horologist는 Google에서 제공하는 라이브러리 그룹으로, 개발자에게 일반적으로 필요하지만 아직 Jetpack에서 사용할 수 없는 기능을 제공하여 Wear OS 개발자를 돕는 것을 목표로 합니다.

SuspendingTileServiceTileService의 코루틴 버전 함수인 정지 함수 두 개를 제공합니다.

  • suspend resourcesRequest(requestParams: ResourcesRequest): Resources
  • suspend tileRequest(requestParams: TileRequest): Tile

코루틴에 관한 자세한 내용은 Android의 Kotlin 코루틴 문서를 참고하세요.

HelloWorldTileService아직 완료되지 않았습니다. 매니페스트에 서비스를 등록해야 하며 tileLayout의 구현도 제공해야 합니다.

카드 서비스 등록

매니페스트에 카드 서비스가 등록되면 사용자가 추가할 수 있도록 카드 목록에 표시됩니다.

<application> 요소 내에 <service>를 추가합니다.

start/src/main/AndroidManifest.xml

<service
    android:name="com.example.wear.tiles.hello.HelloWorldTileService"
    android:icon="@drawable/ic_waving_hand_24"
    android:label="@string/hello_tile_label"
    android:description="@string/hello_tile_description"
    android:exported="true"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">

    <intent-filter>
        <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
    </intent-filter>

    <!-- The tile preview shown when configuring tiles on your phone -->
    <meta-data
        android:name="androidx.wear.tiles.PREVIEW"
        android:resource="@drawable/tile_hello" />
</service>

카드가 처음 로드될 때, 또는 카드 로드 중 오류가 발생하면, 아이콘과 라벨이 자리표시자로 사용됩니다. 끝에 있는 meta-data는 사용자가 카드를 추가할 때 캐러셀에 표시되는 미리보기 이미지를 정의합니다.

카드 레이아웃 정의

HelloWorldTileService에는 TODO()가 본문으로 있는 tileLayout이라는 함수가 있습니다. 이제 이 함수를 카드의 레이아웃을 정의하고 데이터를 결합하는 구현으로 대체해 보겠습니다.

start/src/main/java/com/example/wear/tiles/hello/HelloWorldTileService.kt

private fun tileLayout(): LayoutElement {
    val text = getString(R.string.hello_tile_body)
    return LayoutElementBuilders.Box.Builder()
        .setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
        .setWidth(DimensionBuilders.expand())
        .setHeight(DimensionBuilders.expand())
        .addContent(
            LayoutElementBuilders.Text.Builder()
                .setText(text)
                .build()
        )
        .build()
}

기본 정렬을 실행할 수 있도록 Text 요소를 만들고 Box 내부에 설정합니다.

첫 번째 Wear OS 카드를 만들었습니다. 이 카드를 설치하고 어떻게 표시되는지 살펴보겠습니다.

4. 기기에서 카드 테스트

실행 구성 드롭다운에서 시작 모듈을 선택하면 기기나 에뮬레이터에 앱(start 모듈)을 설치하고 사용자와 마찬가지로 카드를 수동으로 설치할 수 있습니다.

여기서는 Android 스튜디오 Dolphin에서 도입된 기능인 Direct Surface Launch를 사용하여 새로운 실행 구성을 만들어 Android 스튜디오에서 바로 카드를 실행해 보겠습니다. 상단 패널의 드롭다운에서 'Edit Configurations...'를 선택합니다.

Android 스튜디오의 상단 패널에 있는 실행 구성 드롭다운 강조 표시된 Edit Configurations

'Add new configuration' 버튼을 클릭하고 'Wear OS Tile'을 선택합니다. 구체적인 이름을 추가한 다음 Tiles_Code_Lab.start 모듈과 HelloWorldTileService 카드를 선택합니다.

'OK'를 눌러 종료합니다.

HelloTile이라는 Wear OS 카드가 구성되어 있는 Edit Configuration 메뉴

Direct Surface Launch를 사용하면 Wear OS 에뮬레이터나 실제 기기에서 카드를 빠르게 테스트할 수 있습니다. 'HelloTile'을 실행하여 테스트해 보세요. 아래 스크린샷과 같이 표시됩니다.

검은색 배경에 흰색 글자로 'Time to create a tile'이라고 표시된 원형 시계

5. 메시지 카드 빌드

2x3 피라미드형으로 배열된 원형 버튼 5개가 있는 원형 시계 첫 번째 버튼과 세 번째 버튼은 이니셜을 보라색 텍스트로 표시하고 두 번째 버튼과 네 번째 버튼은 프로필 사진을 보여주며 마지막 버튼은 검색 아이콘입니다. 버튼 아래에는 검은색 텍스트로 'New'라고 적힌 보라색 소형 칩이 있습니다.

빌드할 메시지 카드는 실제 카드와 더 비슷합니다. HelloWorld 예와 달리 이 예는 로컬 저장소에서 데이터를 로드하고, 표시할 이미지를 네트워크에서 가져오며, 카드에서 직접 앱을 여는 상호작용을 처리합니다.

MessagingTileService

MessagingTileService는 앞에서 본 SuspendingTileService 클래스를 확장합니다.

이 예와 이전 예의 주요 차이점은 이제 저장소에서 데이터를 관찰하고 네트워크에서 이미지 데이터도 가져온다는 것입니다.

MessagingTileRenderer

MessagingTileRendererSingleTileLayoutRenderer 클래스(Horologist 카드의 또 다른 추상화)를 확장합니다. MessagingTileRenderer는 완전히 동기식입니다. 상태는 렌더기 함수로 전달되므로 테스트와 Android 스튜디오 미리보기에서 더 쉽게 사용할 수 있습니다.

다음 단계에서는 카드의 Android 스튜디오 미리보기를 추가하는 방법을 살펴봅니다.

6. 미리보기 함수 추가

Jetpack Tiles 라이브러리 버전 1.4(현재 알파 버전)에서 출시된 카드 미리보기 함수를 사용하여 Android 스튜디오에서 카드 UI를 미리 볼 수 있습니다. 이렇게 하면 UI를 개발할 때 피드백 루프가 단축되어 개발 속도가 빨라집니다.

파일 끝에 MessagingTileRenderer의 카드 미리보기를 추가합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

@Preview(device = WearDevices.SMALL_ROUND)
@Preview(device = WearDevices.LARGE_ROUND)
fun messagingTileLayoutPreview(context: Context): TilePreviewData {
    return TilePreviewData { request ->
        MessagingTileRenderer(context).renderTimeline(
            MessagingTileState(knownContacts),
            request
        )
    }
}

@Composable 주석은 제공되지 않습니다. 카드는 컴포저블 함수와 동일한 미리보기 UI를 사용하지만 Compose를 사용하지 않으며 컴포저블이 아닙니다.

'Split' 편집기 모드를 사용하여 카드의 미리보기를 확인합니다.

왼쪽에는 미리보기 코드가, 오른쪽에는 카드 이미지가 있는 Android 스튜디오의 화면 분할 뷰

다음 단계에서는 Tiles Material을 사용하여 레이아웃을 업데이트합니다.

7. Tiles Material 추가

Tiles Material은 사전 빌드된 Material 구성요소레이아웃을 제공하므로 Wear OS용 최신 Material Design을 수용하는 카드를 만들 수 있습니다.

Tiles Material 종속 항목을 build.gradle 파일에 추가합니다.

start/build.gradle

implementation "androidx.wear.protolayout:protolayout-material:$protoLayoutVersion"

렌더기 파일 하단에 버튼의 코드를 추가하고 미리보기도 추가합니다.

start/src/main/java/MessagingTileRenderer.kt

private fun searchLayout(
    context: Context,
    clickable: ModifiersBuilders.Clickable,
) = Button.Builder(context, clickable)
    .setContentDescription(context.getString(R.string.tile_messaging_search))
    .setIconContent(MessagingTileRenderer.ID_IC_SEARCH)
    .setButtonColors(ButtonColors.secondaryButtonColors(MessagingTileTheme.colors))
    .build()

연락처 레이아웃을 빌드하는 것과 비슷한 작업도 할 수 있습니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

private fun contactLayout(
    context: Context,
    contact: Contact,
    clickable: ModifiersBuilders.Clickable,
) = Button.Builder(context, clickable)
    .setContentDescription(contact.name)
    .apply {
        if (contact.avatarUrl != null) {
            setImageContent(contact.imageResourceId())
        } else {
            setTextContent(contact.initials)
            setButtonColors(ButtonColors.secondaryButtonColors(MessagingTileTheme.colors))
        }
    }
    .build()

구성요소만 Tiles Material에 포함되어 있는 것은 아닙니다. 일련의 중첩된 열과 행을 사용하는 대신 Tiles Material의 레이아웃을 사용하여 원하는 모양을 빠르게 만들 수 있습니다.

여기서는 PrimaryLayoutMultiButtonLayout을 사용하여 연락처 4개와 검색 버튼을 정렬할 수 있습니다. 다음 레이아웃으로 MessagingTileRenderermessagingTileLayout() 함수를 업데이트합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

private fun messagingTileLayout(
    context: Context,
    deviceParameters: DeviceParametersBuilders.DeviceParameters,
    state: MessagingTileState
) = PrimaryLayout.Builder(deviceParameters)
    .setResponsiveContentInsetEnabled(true)
    .setContent(
        MultiButtonLayout.Builder()
            .apply {
                // In a PrimaryLayout with a compact chip at the bottom, we can fit 5 buttons.
                // We're only taking the first 4 contacts so that we can fit a Search button too.
                state.contacts.take(4).forEach { contact ->
                    addButtonContent(
                        contactLayout(
                            context = context,
                            contact = contact,
                            clickable = emptyClickable
                        )
                    )
                }
            }
            .addButtonContent(searchLayout(context, emptyClickable))
            .build()
    )
    .build()

96fee80361af2c0f.png

MultiButtonLayout은 버튼을 최대 7개 지원하고 적절한 간격으로 배치합니다.

messagingTileLayout() 함수에 'New' CompactChip을 PrimaryLayout의 'primary' 칩으로 추가하겠습니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

.setPrimaryChipContent(
        CompactChip.Builder(
            /* context = */ context,
            /* text = */ context.getString(R.string.tile_messaging_create_new),
            /* clickable = */ emptyClickable,
            /* deviceParameters = */ deviceParameters
        )
            .setChipColors(ChipColors.primaryChipColors(MessagingTileTheme.colors))
            .build()
    )

2041bdca8a46458b.png

다음 단계에서는 누락된 이미지를 수정합니다.

8. 이미지를 추가하는 방법

카드는 크게 두 가지 요소로 구성됩니다. 바로 (문자열 ID로 리소스를 참조하는) 레이아웃 요소와 리소스 자체(이미지일 수 있음)입니다.

로컬 이미지를 사용할 수 있도록 하는 것은 간단한 작업입니다. Android 드로어블 리소스를 직접 사용할 수는 없지만 Horologist에서 제공하는 편의 함수를 사용하여 필요한 형식으로 간단하게 변환할 수 있습니다. 그런 다음 addIdToImageMapping 함수를 사용하여 이미지를 리소스 식별자에 연결합니다. 예:

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

addIdToImageMapping(
    ID_IC_SEARCH,
    drawableResToImageResource(R.drawable.ic_search_24)
)

원격 이미지의 경우 Kotlin 코루틴 기반 이미지 로더인 Coil을 사용하여 네트워크를 통해 로드합니다.

다음은 이미 작성된 코드입니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileService.kt

override suspend fun resourcesRequest(requestParams: ResourcesRequest): Resources {
    val avatars = imageLoader.fetchAvatarsFromNetwork(
        context = this@MessagingTileService,
        requestParams = requestParams,
        tileState = latestTileState()
    )
    return renderer.produceRequestedResources(avatars, requestParams)
}

카드 렌더기는 완전히 동기식이므로 카드 서비스가 네트워크에서 비트맵을 가져옵니다. 이전과 마찬가지로 이미지 크기에 따라 WorkManager를 사용하여 미리 이미지를 가져오는 것이 더 적절할 수 있지만 이 Codelab에서는 이미지를 직접 가져옵니다.

avatars 맵(Contact에서 Bitmap으로)을 리소스의 '상태'로 렌더기에 전달합니다. 이제 렌더기는 이러한 비트맵을 카드의 이미지 리소스로 변환할 수 있습니다.

이 코드도 이미 작성되어 있습니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

override fun ResourceBuilders.Resources.Builder.produceRequestedResources(
    resourceState: Map<Contact, Bitmap>,
    deviceParameters: DeviceParametersBuilders.DeviceParameters,
    resourceIds: List<String>
) {
    addIdToImageMapping(
        ID_IC_SEARCH,
        drawableResToImageResource(R.drawable.ic_search_24)
    )

    resourceState.forEach { (contact, bitmap) ->
        addIdToImageMapping(
            /* id = */ contact.imageResourceId(),
            /* image = */ bitmap.toImageResource()
        )
    }
}

서비스가 비트맵을 가져오고 렌더기가 이러한 비트맵을 이미지 리소스로 변환한다면 카드에 이미지가 표시되지 않는 이유는 무엇일까요?

이유는 다음과 같습니다. 기기(인터넷에 액세스할 수 있음)에서 카드를 실행하면 이미지가 실제로 로드됩니다. TilePreviewData()에 리소스를 전달하지 않았으므로 문제는 미리보기에만 있습니다.

실제 카드의 경우 네트워크에서 비트맵을 가져오고 이를 다른 연락처에 매핑하지만, 미리보기와 테스트의 경우에는 네트워크에 연결할 필요가 전혀 없습니다.

두 가지를 변경해야 합니다. 첫째, Resources 객체를 반환하는 previewResources() 함수를 만듭니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

private fun previewResources() = Resources.Builder()
    .addIdToImageMapping(ID_IC_SEARCH, drawableResToImageResource(R.drawable.ic_search_24))
    .addIdToImageMapping(knownContacts[1].imageResourceId(), drawableResToImageResource(R.drawable.ali))
    .addIdToImageMapping(knownContacts[2].imageResourceId(), drawableResToImageResource(R.drawable.taylor))
    .build()

둘째, messagingTileLayoutPreview()를 업데이트하여 리소스를 전달합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

@Preview(device = WearDevices.SMALL_ROUND)
@Preview(device = WearDevices.LARGE_ROUND)
fun messagingTileLayoutPreview(context: Context): TilePreviewData {
    return TilePreviewData({ previewResources() }) { request ->
        MessagingTileRenderer(context).renderTimeline(
            MessagingTileState(knownContacts),
            request
        )
    }
}

이제 미리보기를 새로고침하면 이미지가 표시됩니다.

3142b42717407059.png

다음 단계에서는 각 요소에 대한 클릭을 처리합니다.

9. 상호작용 처리

카드로 할 수 있는 가장 유용한 작업 중 하나는 중요한 사용자 여정에 대한 바로가기를 제공하는 것입니다. 이는 단순히 앱을 여는 앱 런처와는 다릅니다. 여기서는 앱의 특정 화면에 컨텍스트 바로가기를 제공할 공간이 있습니다.

지금까지는 칩과 각 버튼에 emptyClickable을 사용했습니다. 이는 대화형이 아닌 미리보기에는 적합하지만 이제는 요소의 작업을 추가하는 방법을 살펴보겠습니다.

'ActionBuilders' 클래스의 두 빌더는 클릭 가능한 작업(LoadActionLaunchAction)을 정의합니다.

LoadAction

LoadAction은 사용자가 요소를 클릭할 때 카드 서비스에서 로직을 실행(예: 카운터 증가)하려는 경우에 사용할 수 있습니다.

.setClickable(
    Clickable.Builder()
        .setId(ID_CLICK_INCREMENT_COUNTER)
        .setOnClick(ActionBuilders.LoadAction.Builder().build())
        .build()
    )
)

이를 클릭하면 onTileRequest가 서비스(SuspendingTileServicetileRequest)에서 호출되므로 카드 UI를 새로고침할 수 있습니다.

override suspend fun tileRequest(requestParams: TileRequest): Tile {
    if (requestParams.state.lastClickableId == ID_CLICK_INCREMENT_COUNTER) {
        // increment counter
    }
    // return an updated tile
}

LaunchAction

LaunchAction은 활동을 시작하는 데 사용할 수 있습니다. MessagingTileRenderer에서 검색 버튼의 클릭 가능 항목을 업데이트해 보겠습니다.

검색 버튼은 MessagingTileRenderersearchLayout() 함수로 정의됩니다. 이미 Clickable을 매개변수로 사용하지만 지금까지는 버튼이 클릭되면 아무 작업도 하지 않는 노옵(no-op) 구현인 emptyClickable을 전달했습니다.

실제 클릭 작업을 전달하도록 messagingTileLayout()을 업데이트해 보겠습니다.

  1. 새 매개변수 searchButtonClickable(ModifiersBuilders.Clickable 유형)을 추가합니다.
  2. 이 매개변수를 기존 searchLayout() 함수에 전달합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

private fun messagingTileLayout(
    context: Context,
    deviceParameters: DeviceParametersBuilders.DeviceParameters,
    state: MessagingTileState,
    searchButtonClickable: ModifiersBuilders.Clickable
...
    .addButtonContent(searchLayout(context, searchButtonClickable))

방금 새 매개변수(searchButtonClickable)를 추가했으므로 messagingTileLayout을 호출하는 renderTile도 업데이트해야 합니다. launchActivityClickable() 함수를 사용하여 새 클릭 가능 항목을 만들어 openSearch() ActionBuilder를 작업으로 전달합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/MessagingTileRenderer.kt

override fun renderTile(
    state: MessagingTileState,
    deviceParameters: DeviceParametersBuilders.DeviceParameters
): LayoutElementBuilders.LayoutElement {
    return messagingTileLayout(
        context = context,
        deviceParameters = deviceParameters,
        state = state,
        searchButtonClickable = launchActivityClickable("search_button", openSearch())
    )
}

launchActivityClickable을 열고 이러한 함수(이미 정의됨)가 어떻게 작동하는지 확인합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/ClickableActions.kt

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

LoadAction과 매우 유사합니다. 주요 차이점은 setAndroidActivity가 호출된다는 점입니다. 같은 파일에 다양한 ActionBuilder.AndroidActivity 예가 있습니다.

이 클릭 가능 항목에 사용되는 openSearch의 경우 setMessagingActivity를 호출하고 문자열 추가 항목을 전달하여 어떤 버튼 클릭이 발생했는지 식별합니다.

start/src/main/java/com/example/wear/tiles/messaging/tile/ClickableActions.kt

internal fun openSearch() = ActionBuilders.AndroidActivity.Builder()
    .setMessagingActivity()
    .addKeyToExtraMapping(
        MainActivity.EXTRA_JOURNEY,
        ActionBuilders.stringExtra(MainActivity.EXTRA_JOURNEY_SEARCH)
    )
    .build()

...

internal fun ActionBuilders.AndroidActivity.Builder.setMessagingActivity(): ActionBuilders.AndroidActivity.Builder {
    return setPackageName("com.example.wear.tiles")
        .setClassName("com.example.wear.tiles.messaging.MainActivity")
}

카드를 실행하고('hello' 카드가 아닌 'messaging' 카드를 실행해야 함) 검색 버튼을 클릭합니다. MainActivity가 열리고 텍스트가 표시되어 검색 버튼이 클릭되었음을 확인할 수 있습니다.

다른 버튼의 작업을 추가하는 것도 비슷합니다. ClickableActions에는 필요한 함수가 포함되어 있습니다. 힌트가 필요하다면 finished 모듈에서 MessagingTileRenderer를 확인하세요.

10. 축하합니다

축하합니다. 지금까지 Wear OS용 카드를 빌드하는 방법을 알아보았습니다.

다음 단계는 무엇일까요?

자세한 내용은 GitHub의 Golden Tiles 구현, Wear OS 카드 가이드, 디자인 가이드라인을 참고하세요.