AI Usage Dashboard Docs

Android 위젯 구현 노트

이 문서는 AI Usage Dashboard의 Android 앱/홈 화면 위젯이 어떻게 동작하는지 설명합니다. 일반 사용자가 설치만 하려면 모바일 위젯 싱크 설치/세팅 가이드를 먼저 보세요.

왜 별도 Android 앱인가?

현재 데스크탑 앱은 로컬 PC의 provider credential, CLI session file, keychain/env 등을 이용해 사용량을 조회합니다. Android에서 같은 credential을 그대로 읽을 수 없기 때문에 Android 앱은 provider를 직접 조회하지 않고, 데스크탑 앱이 만든 sanitized snapshot을 받아 표시합니다.

즉 현재 Android 앱은 다음 역할입니다.

데스크탑 provider 조회 결과를 모바일에서 보는 snapshot viewer + 홈 화면 위젯

현재 MVP 구성

데이터 흐름

Desktop app
  provider usage refresh
  sanitized widget snapshot 생성
  PUT /v1/snapshots/:pairId Authorization: Bearer <syncToken>

Cloudflare Relay
  최신 snapshot 저장
  snapshot etag가 바뀌면 FCM wake signal 전송

Android app/widget
  FCM wake signal 수신
  WidgetSyncWorker one-shot enqueue
  GET /v1/snapshots/:pairId?token=<syncToken>
  SharedPreferences에 snapshot 저장
  홈 화면 위젯 갱신

Snapshot 형식

위젯은 provider credential이나 raw token을 저장하지 않습니다. 표시용 필드만 저장합니다.

{
  "schemaVersion": 1,
  "fetchedAt": "2026-04-24T00:00:00Z",
  "providers": [
    {
      "id": "codex",
      "name": "Codex",
      "percentUsed": 42,
      "usageLabel": "128K tokens today",
      "summary": "128K tokens today",
      "accentColor": "#111827",
      "state": "normal"
    }
  ]
}

Android 쪽 자동 sync trigger

Android는 다음 상황에서 sync를 시도합니다.

FCM이 지연되거나 실패해도 WorkManager polling이 fallback으로 동작합니다.

Cloudflare Relay + Push

Snapshot API

데스크탑 앱은 최신 snapshot을 relay에 업로드합니다.

PUT /v1/snapshots/:pairId
Authorization: Bearer <syncToken>
Content-Type: application/json

Android 앱은 저장된 URL로 snapshot을 가져옵니다.

GET /v1/snapshots/:pairId?token=<syncToken>

Push 등록 API

Android 앱은 sync URL이 저장된 뒤 FCM token을 relay에 등록합니다.

POST /v1/push/:pairId/register
Authorization: Bearer <syncToken>
Content-Type: application/json

{
  "platform": "android",
  "provider": "fcm",
  "pushToken": "<fcm device token>",
  "appVersion": "0.1.0",
  "deviceId": "locally-generated-random-id"
}

등록 해제 API도 있습니다.

POST /v1/push/:pairId/unregister
Authorization: Bearer <syncToken>
Content-Type: application/json

{
  "platform": "android",
  "provider": "fcm",
  "pushToken": "<fcm device token>"
}

Relay는 device token을 pair별 KV key로 저장합니다. 중복 등록은 idempotent하게 처리됩니다.

FCM payload 원칙

FCM payload는 wake signal만 담습니다.

포함 가능:

포함 금지:

Android는 push payload의 데이터로 화면을 직접 갱신하지 않고, push를 받으면 WidgetSyncWorker를 깨워 relay에서 snapshot을 다시 가져옵니다.

Firebase 설정

Android에서 실제 FCM token을 받으려면 Firebase client config가 필요합니다.

apps/android/app/google-services.json

이 파일이 있으면 Gradle build가 com.google.gms.google-services plugin을 조건부로 적용합니다. 파일이 없어도 debug build는 가능하며, 이 경우 push 등록은 동작하지 않고 polling fallback만 사용합니다.

Worker secret

Cloudflare Worker에서 FCM을 보내려면 service-account 기반 secret이 필요합니다.

npx wrangler secret put FCM_PROJECT_ID --config apps/relay-cloudflare/wrangler.toml
npx wrangler secret put FCM_CLIENT_EMAIL --config apps/relay-cloudflare/wrangler.toml
npx wrangler secret put FCM_PRIVATE_KEY --config apps/relay-cloudflare/wrangler.toml

FCM_PRIVATE_KEY는 escaped newline(\\n)을 포함할 수 있습니다. Worker 코드는 이를 실제 newline으로 복원해 JWT를 만듭니다.

iOS/APNs 확장 가능성

Relay data model은 나중에 iOS/APNs 등록을 받을 수 있도록 ios / apns 형태를 받아들일 수 있습니다.

{
  "platform": "ios",
  "provider": "apns",
  "pushToken": "<apns device token>",
  "appVersion": "0.1.0",
  "deviceId": "locally-generated-random-id"
}

다만 APNs 전송 자체는 아직 구현되어 있지 않습니다. iPhone 위젯까지 지원하려면 별도 iOS 앱, WidgetKit, APNs provider token 구현이 필요합니다.

Node Relay

Cloudflare가 아닌 환경에서 테스트하고 싶다면 Node relay를 사용할 수 있습니다.

npm run dev:relay

실제 모바일 사용에는 Cloudflare relay가 더 적합합니다. 로컬 LAN URL은 개발 fallback 성격입니다.

보안 체크포인트

다음 개선 후보