import Toybox.Lang; import Toybox.Application; // ============================================================================ // 设置读取 // ---------------------------------------------------------------------------- // 所有存储的偏好设置集中在这里,默认值就是设计稿的那套配置。 // 键名与 resources/settings/properties.xml 一一对应。 // // 为什么要包一层而不是各处直接调 Properties.getValue: // Connect IQ 的 Properties.getValue 遇到「手表还没存过的键」时**不是返回 null, // 而是抛 Out of Bounds 异常**。每次版本更新新增设置项,在手表把新的 // properties.xml 合并进去之前都会撞上这个坑,轻则某个字段读不到,重则整个表盘 // 崩掉。所以全部读取都走本模块,异常一律降级成默认值。 // ============================================================================ module Settings { // ---- 四个数据位的默认字段 ---- // 字段编号沿用 https://watchface.io/docs/datafields,与 Fields.mc 中一致。 // 这四个默认值合起来就是设计稿上的样子。 const DEF_LEFT_TOP = 712; // 左上:日出 const DEF_RIGHT_TOP = 639; // 右上:预报最高/最低气温 const DEF_BOTTOM_LEFT = 3; // 左下:距离 const DEF_BOTTOM_RIGHT = 1; // 右下:步数 // ------------------------------------------------------------------------ // 底层读取:吞掉「键不存在」异常,返回 null 交给上层兜底。 // 注意 catch 在这里是有效的(Properties 抛的是普通异常);而 Background // 模块的 Out of Bounds 属于 VM 级错误,catch 不住,那边只能用别的办法绕开, // 详见 Fenix8V3App.scheduleWeather()。 // ------------------------------------------------------------------------ function raw(key as String) { try { return Application.Properties.getValue(key); } catch (e) { return null; } } // 读数字型设置,取不到就用 def。 function number(key as String, def as Number) as Number { var v = raw(key); if (v == null) { return def; } return v as Number; } // 读布尔型设置,取不到就用 def。 function bool(key as String, def as Boolean) as Boolean { var v = raw(key); if (v == null) { return def; } return v as Boolean; } // 读字符串型设置,取不到返回空串(不是 null,省得每个调用点再判一次)。 function text(key as String) as String { var v = raw(key); if (v == null) { return ""; } return v.toString(); } // ------------------------------------------------------------------------ // 主题序号(0 基)。做范围钳制是为了防止这种情况:用户存的是「主题 7」, // 之后某个版本删掉了一套主题,存量值就会越界 —— 直接崩数组下标。 // ------------------------------------------------------------------------ function themeIndex(count as Number) as Number { var i = number("Theme", 1) - 1; if (i < 0 || i >= count) { return 0; } return i; } // ---- 四个数据位选中的字段编号 ---- function leftTop() as Number { return number("LeftTop", DEF_LEFT_TOP); } function rightTop() as Number { return number("RightTop", DEF_RIGHT_TOP); } function bottomLeft() as Number { return number("BottomLeft", DEF_BOTTOM_LEFT); } function bottomRight() as Number { return number("BottomRight", DEF_BOTTOM_RIGHT); } // ---- 进度环四段各自绑定的指标 ---- // 注意这里的编号是进度环自己的一张短表(0 步数 / 1 卡路里 / 2 距离 / // 3 楼层 / 4 动动条 / 5 活动分钟 / 6 身体电量),与上面数据位的字段编号 // 是两套体系,不要混用。取值逻辑在 Fenix8V3View.ringProgress()。 function ringTR() as Number { return number("RingTR", 0); } // 右上:步数 function ringTL() as Number { return number("RingTL", 1); } // 左上:卡路里 function ringBR() as Number { return number("RingBR", 4); } // 右下:动动条 function ringBL() as Number { return number("RingBL", 3); } // 左下:楼层 // 温度单位:0 跟随手表 / 1 强制摄氏 / 2 强制华氏。 function tempUnit() as Number { return number("TempUnit", 0); } // ---- 三个显隐开关 ---- function showTop() as Boolean { return bool("ShowTop", true); } function showBand() as Boolean { return bool("ShowBand", true); } function showBottom() as Boolean { return bool("ShowBottom", true); } }