Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

Wear OS Tiles로 실시간 심박수와 운동 시간 표시하기

Billcorea 2025. 8. 13. 15:52
반응형

 

Wear OS Tiles로 실시간 심박수와 운동 시간 표시하기

앱 화면 예시

 

Wear OS의 Tiles API를 활용하면 사용자가 시계를 켜지 않고도 실시간 정보를 확인할 수 있습니다. 이 글에서는 최신 SuspendingTileService 기반으로, 실시간 심박수운동 시간, 그리고 사용자 버튼을 표시하는 타일을 구현하는 방법을 알아봅니다.

이 예제는 다음 라이브러리 버전을 기준으로 작성되었습니다. (2025년 8월 기준)
  • androidx.wear.tiles:tiles-material: 1.5.0-rc01
  • androidx.wear.protolayout:protolayout-material: 1.3.0-rc01
  • com.google.android.horologist:horologist-health-data: 0.7.15

1. 환경 설정

build.gradle.kts (모듈 수준)에 다음 의존성을 추가합니다.

dependencies {
    implementation("androidx.wear.tiles:tiles-material:1.5.0-rc01")
    implementation("androidx.wear.protolayout:protolayout-material:1.3.0-rc01")
    implementation("com.google.android.horologist:horologist-health-data:0.7.15")
}

2. 타일 서비스 구현

아래 코드는 SuspendingTileService를 사용하여 운동 시간과 심박수를 표시하는 예제입니다. 버튼 클릭 시 앱을 실행하도록 설정했습니다.

class HealthTileService : SuspendingTileService() {

    override suspend fun tileRequest(requestParams: RequestBuilders.TileRequest): Tile {
        val currentTimeMillis = System.currentTimeMillis()
        val exerciseStartTime = getExerciseStartTime()
        val elapsedTimeSeconds = ((currentTimeMillis - exerciseStartTime) / 1000).toInt()
        val bpm = getCurrentHeartRate()

        return Tile.Builder()
            .setResourcesVersion("1")
            .setFreshnessIntervalMillis(30_000) // 30초마다 갱신
            .setTimeline(
                Timeline.Builder()
                    .addTimelineEntry(
                        TimelineEntry.Builder()
                            .setLayout(
                                Layout.Builder()
                                    .setRoot(
                                        PrimaryLayout.Builder()
                                            .setContent(
                                                Column.Builder()
                                                    .addContent(
                                                        Text.Builder()
                                                            .setText("❤️ 심박수: $bpm bpm")
                                                            .build()
                                                    )
                                                    .addContent(
                                                        Text.Builder()
                                                            .setText("⏱ 운동 시간: ${formatSeconds(elapsedTimeSeconds)}")
                                                            .build()
                                                    )
                                                    .addContent(
                                                        Button.Builder()
                                                            .setText("좋아")
                                                            .setOnClick(
                                                                LaunchAction.Builder()
                                                                    .setAndroidActivity(
                                                                        AndroidActivity.Builder()
                                                                            .setPackageName("com.example.app")
                                                                            .setClassName("com.example.app.MainActivity")
                                                                            .build()
                                                                    )
                                                                    .build()
                                                            )
                                                            .build()
                                                    )
                                                    .build()
                                            )
                                            .build()
                                    )
                                    .build()
                            )
                            .build()
                    )
                    .build()
            )
            .build()
    }

    override suspend fun resourcesRequest(requestParams: RequestBuilders.ResourcesRequest): Resources {
        return Resources.Builder()
            .setVersion("1")
            .build()
    }

    // 현재 심박수를 가져오는 함수 (실제 구현 시 Health Services API 연동)
    private suspend fun getCurrentHeartRate(): Int {
        return 82 // 예시 값
    }

    // 운동 시작 시간 불러오기 (SharedPreferences 등 사용 가능)
    private suspend fun getExerciseStartTime(): Long {
        return System.currentTimeMillis() - 720_000 // 예시: 12분 전
    }

    private fun formatSeconds(totalSeconds: Int): String {
        val minutes = totalSeconds / 60
        val seconds = totalSeconds % 60
        return "%02d:%02d".format(minutes, seconds)
    }
}

3. AndroidManifest.xml 설정

타일 서비스를 시스템에 등록하려면 AndroidManifest.xml에 아래 내용을 추가합니다.

<service
    android:name=".HealthTileService"
    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>
</service>

4. 코드 해설

  • SuspendingTileService: 코루틴을 사용하여 데이터 로딩이 비동기적으로 가능.
  • setFreshnessIntervalMillis(): 타일 자동 갱신 주기 설정.
  • PrimaryLayout + Column: UI 요소들을 세로로 배치.
  • Button LaunchAction: 버튼 클릭 시 앱의 특정 액티비티 실행.

5. 확장 아이디어

  • 실제 Health Services API 연동하여 정확한 심박수 표시
  • 운동 시작/종료 버튼 추가
  • 칼로리 소모량, 걸음 수 등 다른 건강 데이터 표시

마무리

이번 글에서는 최신 Wear OS Tiles API를 활용해 간단한 헬스 모니터링 타일을 구현해 보았습니다. Health Services API를 추가하면 훨씬 정확하고 실시간성 높은 데이터를 제공할 수 있습니다.

 

*** ps : Chat Gpt 와 나누었던 이야기를 기반으로 작성된 글 입니다.  이 이야기에는 약간의 허구(?)가 있습니다. 

이전에 게시했던 글을 참고해 보세요.

반응형