Add the on-device data field catalogue, always-on mode, and real icons
Fields 75 selectable fields across date/time, activity, body, system, environment, weather and custom text, numbered to match watchface.io/docs/datafields so the two catalogues line up. All four slots (both top, both bottom) pick from the same table and the bottom captions follow the selection. tools/gen_fields.py owns the table and emits FieldTable.mc, settings.xml and both languages' strings, so those cannot drift apart. Fields.Ctx fetches each API at most once per draw, and only if some slot asks -- the forecast, user profile and sensor history lookups are lazy. The rest of that catalogue is deliberately absent: OpenWeatherMap and StormGlass need network calls and user API keys, the per-sport weekly and 28-day aggregates come from a phone-side Garmin Connect integration rather than any on-device API, and the app complications need those apps installed. README section 7 lists what was left out and why. Permissions UserProfile and SensorHistory are now declared; without them resting heart rate, VO2 max, pulse ox and body battery throw at runtime. Sensor turns out to be rejected outright for type="watchface", so altitude and pressure read from Activity.Info instead. Always-on display AMOLED devices need the lit-pixel count cut while asleep. drawAmbient drops the band fill and the unlit ticks, dims what remains, hides the complications, and walks the layout within +/-4 px per minute so nothing burns in. Gated on requiresBurnInProtection, so MIP devices are untouched. Icons The comps' sunrise and weather icons have two- and three-pixel features. Rebuilding them from circles and lines looked right at 454 px and turned to mush at 280. They are now rasterised from the design paths once per screen width and tinted with drawBitmap2(:tintColor) -- which needs packingFormat="png", since a palettised source is refused at runtime. Launcher icons are generated at the five sizes the devices ask for. All 17 devices build; checked on fenix847mm and enduro3 in the simulator. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
121
README.md
121
README.md
@@ -23,17 +23,30 @@ fenix8v3-watchface/
|
||||
│ └── rebuild.py # 原作者从位图逆向出 SVG 的脚本(仅存档,不参与构建)
|
||||
├── tools/
|
||||
│ ├── gen_themes.py # design/*.svg -> source/Themes.mc
|
||||
│ ├── gen_fields.py # 字段表 -> FieldTable.mc + settings.xml + 两种语言文案
|
||||
│ ├── gen_icons.py # design/*.svg -> 各尺寸图标位图 + 启动图标
|
||||
│ └── gen_preview_svg.py # source/Themes.mc -> preview-themes.svg + preview.html
|
||||
├── source/
|
||||
│ ├── Fenix8V3App.mc # 应用入口
|
||||
│ ├── Fenix8V3View.mc # 全部渲染逻辑
|
||||
│ └── Themes.mc # ⚠️ 自动生成,勿手改
|
||||
├── resources/ # 英文文案 + 设置项定义
|
||||
│ ├── Fenix8V3View.mc # 渲染逻辑(含常亮模式)
|
||||
│ ├── Fields.mc # 75 项数据字段的取值与格式化
|
||||
│ ├── FieldTable.mc # ⚠️ 自动生成:字段 id 表
|
||||
│ └── Themes.mc # ⚠️ 自动生成:七套主题配色
|
||||
├── resources/ # 英文文案 + 设置项定义(部分自动生成)
|
||||
├── resources-chn/ # 简体中文文案
|
||||
├── resources-icons-<宽度>/ # ⚠️ 自动生成:按屏幕尺寸光栅化的图标
|
||||
├── resources-launcher-<尺寸>/ # ⚠️ 自动生成:各尺寸启动图标
|
||||
└── bin/ # 编译产物(已 gitignore)
|
||||
```
|
||||
|
||||
三份实现(表盘 / SVG 预览 / HTML 预览)只有 **一份颜色数据**:`Themes.mc`。预览脚本直接解析它,不会与固件漂移。
|
||||
重新生成全部派生文件:
|
||||
|
||||
```bash
|
||||
python3 tools/gen_themes.py && python3 tools/gen_fields.py \
|
||||
&& python3 tools/gen_icons.py && python3 tools/gen_preview_svg.py
|
||||
```
|
||||
|
||||
三份渲染实现(表盘 / SVG 预览 / HTML 预览)只有 **一份颜色数据**:`Themes.mc`。预览脚本直接解析它,不会与固件漂移。字段表、设置项、两种语言的文案同样只有 `tools/gen_fields.py` 里那一张表。
|
||||
|
||||
---
|
||||
|
||||
@@ -127,42 +140,94 @@ python3 tools/gen_themes.py && python3 tools/gen_preview_svg.py
|
||||
|
||||
---
|
||||
|
||||
## 7. 可配置设置
|
||||
## 7. 可配置设置与数据字段
|
||||
|
||||
共 13 项,通过 Garmin Connect 手机 App 的「表盘设置」修改(定义在 `resources/settings/settings.xml`,默认值在 `properties.xml`)。
|
||||
19 项设置,在 Garmin Connect 手机 App 的「表盘设置」里改(定义在 `resources/settings/settings.xml`,默认值在 `properties.xml`,两者由 `tools/gen_fields.py` 生成)。
|
||||
|
||||
| 设置 | 键 | 可选值 | 默认 |
|
||||
|------|----|--------|------|
|
||||
| 主题 | `Theme` | 1–7 | 1 |
|
||||
| 顶右 / 顶左 / 底右 / 底左弧指标 | `RingTR/TL/BR/BL` | 0 步数 / 1 卡路里 / 2 距离 / 3 楼层 / 4 动动条 | 0 / 1 / 4 / 3 |
|
||||
| 左上数据 | `LeftTop` | 见下 | 6(日出) |
|
||||
| 右上数据 | `RightTop` | 见下 | 8(最高/最低气温) |
|
||||
| 左下数据 | `BottomLeft` | 见下 | 3(距离) |
|
||||
| 右下数据 | `BottomRight` | 见下 | 2(步数) |
|
||||
| 温度单位 | `TempUnit` | 0 跟随手表 / 1 °C / 2 °F | 0 |
|
||||
| 显示顶行 / 日期带 / 底行 | `ShowTop` / `ShowBand` / `ShowBottom` | 布尔 | true |
|
||||
| 设置 | 键 | 默认 |
|
||||
|------|----|------|
|
||||
| 主题 | `Theme` | 1 (Ember) |
|
||||
| 四段进度环指标 | `RingTR/TL/BR/BL` | 步数 / 卡路里 / 动动条 / 楼层 |
|
||||
| 四个数据位 | `LeftTop` `RightTop` `BottomLeft` `BottomRight` | 日出 / 高低温 / 距离 / 步数 |
|
||||
| 温度单位 | `TempUnit` | 跟随手表 |
|
||||
| 第二时区 1 / 2 偏移(分钟) | `AltOffset1` `AltOffset2` | 0 |
|
||||
| 自定义文字 1 / 2 / 3 | `Custom1/2/3` | 空 |
|
||||
| 显示顶行 / 日期带 / 底行 | `ShowTop` `ShowBand` `ShowBottom` | true |
|
||||
|
||||
**数据项**(四个数据位共用同一张表):`0` 当前气温 · `1` 卡路里 · `2` 步数 · `3` 距离 · `4` 楼层 · `5` 电量 · `6` 日出 · `7` 日落 · `8` 最高/最低气温 · `9` 心率。
|
||||
进度环指标:步数、卡路里、距离、楼层、动动条、活动分钟(对周目标)、身体电量。
|
||||
|
||||
默认值即设计稿的配置:左上=日出时间、右上=`63°/52°`、左下=距离、右下=步数。
|
||||
### 数据字段
|
||||
|
||||
底部两格的小标签跟随所选数据自动变化(`距离` / `步数` / `心率` …),不需要单独配置。
|
||||
四个数据位共用一张 **75 项** 的字段表,编号沿用 [watchface.io/docs/datafields](https://watchface.io/docs/datafields),两边可以直接对照。底部两格的小标签跟随所选字段自动变化,不需要单独配置。
|
||||
|
||||
| 分类 | 字段 |
|
||||
|------|------|
|
||||
| 日期时间 | 时间、UTC 时间、第二时区 1/2、星期月日、星期日、月日、日、星期(简写/全称)、月份(名称/数字)、上午下午、周数(ISO / 日历) |
|
||||
| 活动 | 步数、步数取整、剩余步数、卡路里、活动卡路里、活动分钟(总/中强度/高强度/本周)、距离、上楼层数、下楼层数、爬升高度、推行距离、推行次数 |
|
||||
| 身体 | 心率、静息心率、压力指数、血氧、呼吸频率、身体电量、活动条(正/反向)、体重、BMI、恢复时间、最大摄氧量(跑/骑) |
|
||||
| 系统 | 电量、剩余天数、电量+天数、闹钟、通知、勿扰、蓝牙、太阳能强度 |
|
||||
| 环境 | 海拔、海平面气压、环境气压、日出、日落、下一太阳事件、距太阳事件、月龄、月球照明 |
|
||||
| 天气 | 气温、最高/最低、体感、最高、最低、湿度、降水概率、风速、风向角、风向、更新时间 |
|
||||
| 自定义 | 自定义文字 1 / 2 / 3 |
|
||||
|
||||
加字段:在 `tools/gen_fields.py` 的 `FIELDS` 表里加一行,跑一遍生成器,再到 `source/Fields.mc` 的 `value()` 里补一个分支。设置项、两种语言的文案、表盘标签会自动跟上。
|
||||
|
||||
### ⚠️ 没有实现的字段,以及原因
|
||||
|
||||
watchface.io 的完整目录约 300 项,本表只收了 **手表本机能算出来的** 那些。其余几类不是没做,是一个自包含的 Connect IQ 表盘做不了:
|
||||
|
||||
| 没做的 | 原因 |
|
||||
|--------|------|
|
||||
| OpenWeatherMap(约 30 项)、StormGlass.io(5 项) | 需要 `Communications.makeWebRequest` + 用户自备 API key + 后台服务定时拉取与缓存。是另一个子系统,且要求用户自己去注册 key |
|
||||
| 各运动项目的 WTD / 近 7 天 / 本月 / 近 28 天 汇总(约 80 项) | Connect IQ 不暴露按运动分类的历史累计。这些数据在 GreenBlack 那边是通过手机侧与 Garmin Connect 的集成同步来的,本机 API 只有 `ActivityMonitor.getHistory()` 的最近几天步数/卡路里/距离 |
|
||||
| 第三方 App 复杂功能(CGM、Hydration、QuoteGlance 等 12 项) | 需要用户装了对应的 App,并通过 Complications API 订阅 |
|
||||
| 日历事件、计时器、倒计时 | 日历不对 CIQ 开放;表盘拿不到触摸输入,没法做启停 |
|
||||
| 秒数 | 需要 `onPartialUpdate` 每秒重绘。技术上可行,但会明显吃电,暂未做 |
|
||||
| 晨昏蒙影、黄金/蓝调时刻(约 14 项) | 可以自己算太阳高度角推出来,只是还没写;月龄和月球照明已经是这样算的 |
|
||||
| 睡眠得分、训练状态 | `ActivityMonitor.Info` 上没有这两个字段(SDK 9.1.0 实测) |
|
||||
| 标记类字段(Marker,约 35 项) | 那是给刻度型表盘做环形标记用的,本表盘的进度环用的是自己的指标体系 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据来源
|
||||
## 8. 数据来源与权限
|
||||
|
||||
| 元素 | API |
|
||||
|------|-----|
|
||||
| 步数 / 卡路里 / 距离 / 楼层 / 动动条 | `ActivityMonitor.getInfo()` |
|
||||
| 日出 / 日落 | `Weather.getSunrise/getSunset(location, Time.now())`,位置取 `CurrentConditions.observationLocationPosition`,回退 `Activity.getActivityInfo().currentLocation` |
|
||||
| 最高 / 最低气温 | `Weather.getDailyForecast()[0].highTemperature / lowTemperature` |
|
||||
| 当前气温 | `Weather.getCurrentConditions().temperature` |
|
||||
| 电量 | `System.getSystemStats().battery`(0–100 的百分比) |
|
||||
| 时间 / 日期 | `Time.Gregorian`,`FORMAT_SHORT`(只有短格式的 `day_of_week` / `month` 是数字) |
|
||||
| 蓝牙 | `DeviceSettings.phoneConnected`,未连接时图标压暗 |
|
||||
| 步数 / 卡路里 / 距离 / 楼层 / 动动条 / 活动分钟 / 呼吸 / 压力 / 恢复时间 | `ActivityMonitor.getInfo()` |
|
||||
| 心率 | `Activity.getActivityInfo().currentHeartRate`,回退 `ActivityMonitor.getHeartRateHistory()` |
|
||||
| 血氧 / 身体电量 | `SensorHistory`(需 `SensorHistory` 权限) |
|
||||
| 静息心率 / 体重 / 身高 / 最大摄氧量 | `UserProfile.getProfile()`(需 `UserProfile` 权限) |
|
||||
| 海拔 / 环境气压 / 海平面气压 | `Activity.getActivityInfo()` |
|
||||
| 日出 / 日落 | `Weather.getSunrise/getSunset(location, time)`,位置取 `CurrentConditions.observationLocationPosition`,回退 `Activity.currentLocation` |
|
||||
| 气温 / 体感 / 湿度 / 降水 / 风 | `Weather.getCurrentConditions()` |
|
||||
| 最高 / 最低气温 | `Weather.getDailyForecast()[0]` |
|
||||
| 电量 / 剩余天数 / 太阳能 | `System.getSystemStats()`(`battery` 是 0–100 的百分比,不是 0–1) |
|
||||
| 闹钟 / 通知 / 勿扰 / 蓝牙 / 单位制 | `System.getDeviceSettings()` |
|
||||
| 月龄 / 月球照明 | 自算(儒略日 + 朔望月 29.530588853 天) |
|
||||
| 周数 | 自算(ISO 8601 与日历周两种) |
|
||||
|
||||
单位与制式跟随手表设置:`is24Hour`(24 小时制下不显示 AM/PM)、`distanceUnits`(km / mi)、`temperatureUnits`(°C / °F)。天气与日出需手表已配对并同步过数据,否则显示 `--`。
|
||||
### 权限
|
||||
|
||||
`manifest.xml` 声明两项:`UserProfile`(静息心率、体重、VO2max)与 `SensorHistory`(血氧、身体电量)。用户安装时会看到授权提示。
|
||||
|
||||
> `Sensor` 权限**不能**用于 `type="watchface"`(除非同时申请 `Background`),编译器会直接拒绝。所以海拔与气压走 `Activity.Info` 而不是 `Sensor.getInfo()`。
|
||||
|
||||
单位与制式跟随手表:`is24Hour`、`distanceUnits`、`temperatureUnits`(温度可用 `TempUnit` 覆盖)。天气与日出需手表已配对并同步过数据,否则显示 `--`。
|
||||
|
||||
---
|
||||
|
||||
## 8.2 常亮显示(AOD)
|
||||
|
||||
AMOLED 机型在常亮模式下必须压低点亮像素,否则既费电又有烧屏风险,Garmin 上架审核也会卡。`onEnterSleep` / `onExitSleep` 切换到 `drawAmbient()`:
|
||||
|
||||
- 不画日期带底色(整条亮橙色横幅是最费电的元素)
|
||||
- 进度环只画**点亮的**刻度,且整体压暗到 45%
|
||||
- 时间与日期压暗到 55%,其余数据位全部隐藏
|
||||
- 整个画面按分钟在 ±4px 内游走,避免固定像素长期点亮
|
||||
|
||||
只在 `DeviceSettings.requiresBurnInProtection` 为真时启用;MIP 机型(Fenix 7 / Enduro 3 等)不受影响,照常全量绘制。
|
||||
|
||||
模拟器可用 `Settings → Display Mode` 切换验证。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user