アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

【WearOS】既存のWatch Faceが使用できなくなるのでWatch Face Formatで作り直してみた

今まで自作のプログラムコードでレンダリングするWatch Faceを使用していました。
https://github.com/matsudamper/WearOS-WatchFace

それをプログラムコードを使用しないWatch Face Formatで作り直しました。
https://github.com/matsudamper/WearOS-WatchFaceFormat

背景

Wear OS 5からコードを使用して文字盤をレンダリングする事は不可能になりました。
https://support.google.com/wearos/thread/284572445

全てXMLで記述する必要があります。
https://android-developers.googleblog.com/2023/05/introducing-watch-face-format-for-wear-os.html

SamsungWatch Face Studioを使用してWatch Face Format準拠のWatch Faceを作成する事もできます。
現時点では既存のテンプレートを選択するとコードが含まれるApp Bundleが出力されるので注意。
https://developer.samsung.com/watch-face-studio/user-guide/index.html

思うこと

プログラムコードでレンダリングするのはパフォーマンスが悪いのはわかる。XML直で書くのは辛い。Complication Slotや目盛り、中心から角度でぐるっとforをや変数を使って定義したいのでWatch Face Studioは微妙。

対応

DSLを作成して変数、テンプレート化をできるようにした。 Gradle Pluginでビルド時にXMLを生成されるようにした。(使ってない要素は定義してないけど)

この様にComposeっぽく大文字関数にしてみた。

@Suppress("FunctionName")
internal fun SceneScope.Slots(
    width: Int,
    height: Int,
) {
    val slotCount = 6
    val slotSize = (width * height) / 1500
    val slotMargin = slotSize * 1.05

    val angleStep = 360 / slotCount
    for (i in 0 until slotCount) {
        val angle = i * angleStep + (angleStep / 2)
        ComplicationSlot(
            x = ((width - slotSize) / 2) + (cos(Math.toRadians(angle.toDouble())) * slotMargin).toInt(),
            y = ((height - slotSize) / 2) + (sin(Math.toRadians(angle.toDouble())) * slotMargin).toInt(),
            width = slotSize,
            height = slotSize,
            slotId = i,
            supportedTypes = listOf(
                ComplicationSlotSupportedType.RANGED_VALUE,
                ComplicationSlotSupportedType.SMALL_IMAGE,
                ComplicationSlotSupportedType.LONG_TEXT,
                ComplicationSlotSupportedType.SHORT_TEXT,
                ComplicationSlotSupportedType.MONOCHROMATIC_IMAGE,
                ComplicationSlotSupportedType.PHOTO_IMAGE,
                ComplicationSlotSupportedType.EMPTY,
            ),
            isCustomizable = true,
        ) {
            DefaultProviderPolicy(
                primaryProvider = "com.fitbit.FitbitMobile/com.fitbit.complications.calories.CaloriesComplicationDataSourceService",
                defaultSystemProviderType = ComplicationSlotSupportedType.RANGED_VALUE,
                defaultSystemProvider = DefaultProvider.STEP_COUNT,
            )
            BoundingOval(
                x = 0,
                y = 0,
                width = slotSize,
                height = slotSize,
                outlinePadding = 2,
            )
            Complication(type = ComplicationSlotSupportedType.RANGED_VALUE) {
                RangeValueLayout(
                    slotSize = slotSize,
                )
            }
        }
    }
}

出力されたXMLは1.4万行程。こんなの直で書いてられない。
https://github.com/matsudamper/WearOS-WatchFaceFormat/blob/a6f5341ef1e7c0e715f9c6426a986bfdc5e7a7ae/app/src/main/res/raw/watchface.xml

条件分岐もXML

おわりに

型が違うというドキュメントの不備を見つけた。あんまり自前でXML組む人居ないのかな…
https://issuetracker.google.com/issues/358594663

1時間位溶かした。Watch Face Studioで出力した結果をapk analyzerで確認してドキュメントの方の不具合が発覚した。