feat(rtos): 首次发布手机真机调试桥与S2探针
这个提交包含在:
当前提交
2f0f4b9702
59 个文件已更改
+4303
没有与搜索条件相匹配的文件
@@ -0,0 +1 @@
|
||||
.glyphix-work/
|
||||
@@ -0,0 +1,131 @@
|
||||
# PolyForm Noncommercial License 1.0.0
|
||||
|
||||
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
||||
|
||||
## Acceptance
|
||||
|
||||
In order to get any license under these terms, you must agree
|
||||
to them as both strict obligations and conditions to all
|
||||
your licenses.
|
||||
|
||||
## Copyright License
|
||||
|
||||
The licensor grants you a copyright license for the
|
||||
software to do everything you might do with the software
|
||||
that would otherwise infringe the licensor's copyright
|
||||
in it for any permitted purpose. However, you may
|
||||
only distribute the software according to [Distribution
|
||||
License](#distribution-license) and make changes or new works
|
||||
based on the software according to [Changes and New Works
|
||||
License](#changes-and-new-works-license).
|
||||
|
||||
## Distribution License
|
||||
|
||||
The licensor grants you an additional copyright license
|
||||
to distribute copies of the software. Your license
|
||||
to distribute covers distributing the software with
|
||||
changes and new works permitted by [Changes and New Works
|
||||
License](#changes-and-new-works-license).
|
||||
|
||||
## Notices
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of
|
||||
the software from you also gets a copy of these terms or the
|
||||
URL for them above, as well as copies of any plain-text lines
|
||||
beginning with `Required Notice:` that the licensor provided
|
||||
with the software. For example:
|
||||
|
||||
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
||||
|
||||
## Changes and New Works License
|
||||
|
||||
The licensor grants you an additional copyright license to
|
||||
make changes and new works based on the software for any
|
||||
permitted purpose.
|
||||
|
||||
## Patent License
|
||||
|
||||
The licensor grants you a patent license for the software that
|
||||
covers patent claims the licensor can license, or becomes able
|
||||
to license, that you would infringe by using the software.
|
||||
|
||||
## Noncommercial Purposes
|
||||
|
||||
Any noncommercial purpose is a permitted purpose.
|
||||
|
||||
## Personal Uses
|
||||
|
||||
Personal use for research, experiment, and testing for
|
||||
the benefit of public knowledge, personal study, private
|
||||
entertainment, hobby projects, amateur pursuits, or religious
|
||||
observance, without any anticipated commercial application,
|
||||
is use for a permitted purpose.
|
||||
|
||||
## Noncommercial Organizations
|
||||
|
||||
Use by any charitable organization, educational institution,
|
||||
public research organization, public safety or health
|
||||
organization, environmental protection organization,
|
||||
or government institution is use for a permitted purpose
|
||||
regardless of the source of funding or obligations resulting
|
||||
from the funding.
|
||||
|
||||
## Fair Use
|
||||
|
||||
You may have "fair use" rights for the software under the
|
||||
law. These terms do not limit them.
|
||||
|
||||
## No Other Rights
|
||||
|
||||
These terms do not allow you to sublicense or transfer any of
|
||||
your licenses to anyone else, or prevent the licensor from
|
||||
granting licenses to anyone else. These terms do not imply
|
||||
any other licenses.
|
||||
|
||||
## Patent Defense
|
||||
|
||||
If you make any written claim that the software infringes or
|
||||
contributes to infringement of any patent, your patent license
|
||||
for the software granted under these terms ends immediately. If
|
||||
your company makes such a claim, your patent license ends
|
||||
immediately for work on behalf of your company.
|
||||
|
||||
## Violations
|
||||
|
||||
The first time you are notified in writing that you have
|
||||
violated any of these terms, or done anything with the software
|
||||
not covered by your licenses, your licenses can nonetheless
|
||||
continue if you come into full compliance with these terms,
|
||||
and take practical steps to correct past violations, within
|
||||
32 days of receiving notice. Otherwise, all your licenses
|
||||
end immediately.
|
||||
|
||||
## No Liability
|
||||
|
||||
***As far as the law allows, the software comes as is, without
|
||||
any warranty or condition, and the licensor will not be liable
|
||||
to you for any damages arising out of these terms or the use
|
||||
or nature of the software, under any kind of legal claim.***
|
||||
|
||||
## Definitions
|
||||
|
||||
The **licensor** is the individual or entity offering these
|
||||
terms, and the **software** is the software the licensor makes
|
||||
available under these terms.
|
||||
|
||||
**You** refers to the individual or entity agreeing to these
|
||||
terms.
|
||||
|
||||
**Your company** is any legal entity, sole proprietorship,
|
||||
or other kind of organization that you work for, plus all
|
||||
organizations that have control over, are under the control of,
|
||||
or are under common control with that organization. **Control**
|
||||
means ownership of substantially all the assets of an entity,
|
||||
or the power to direct its management and policies by vote,
|
||||
contract, or otherwise. Control can be direct or indirect.
|
||||
|
||||
**Your licenses** are all the licenses granted to you for the
|
||||
software under these terms.
|
||||
|
||||
**Use** means anything you do with the software requiring one
|
||||
of your licenses.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 真机链路验证
|
||||
|
||||
自研部分采用 [PolyForm Noncommercial 1.0.0](LICENSE),仅限许可允许的非商业用途;版权、Required Notice 和第三方声明见[手机桥 NOTICE](../phone-debug-bridge/NOTICE.md)。
|
||||
|
||||
独立 Glyphix 应用 `com.sydxky.rtos.deviceprobe`,版本 1.0.4 / 10004。供 [手机调试桥](../phone-debug-bridge/README.md) 测试安装、启动、手动点击和应用内调试常亮,保留 `icon` 清单字段。
|
||||
|
||||
```powershell
|
||||
.\build.ps1 -QuickAppOnly
|
||||
node --test tests/screen-lifecycle.cjs
|
||||
```
|
||||
|
||||
沿用 RTOS 固定厂商 `gx.exe`,生成圆 / 方两种 QuickApp 包。页面初始化输出 `RTOS_DEVICE_PROBE_READY_V1`,点击按钮增加次数并输出 `RTOS_DEVICE_PROBE_TAP_V1=<次数>`。无网络、账号和后台订阅;前台常亮只维护一个 15 秒定时器。`READY` 日志只证明执行到页面就绪阶段,屏幕和点击须另行观察。
|
||||
|
||||
1.0.3 仅调用 `@system.brightness.setKeepScreenOn(true)`,在 S2 上静置至少 79 秒后回到表盘,用户确认已熄屏,常亮实测失败。1.0.4 增加官方 `wakeScreenOn({ screenOn: true })`,省略 timeout(文档定义为不限时间),每 15 秒续期。隐藏/销毁均清理定时器、使旧回调失效并释放 setKeepScreenOn(false);亮屏调用缺失、抛错或拒绝时停止续期。不会修改亮度或自动亮度,不在后台续期。
|
||||
|
||||
2026-11-14,1.0.4 已在 S2 / OWW265 实装。用户取下充电座并打开后,起始采集结束 13:01:35,复查请求始于 13:04:14,结束 13:04:25(Asia/Shanghai),保守静置至少 159 秒;截图仍为探针,用户确认实体屏幕仍亮着。随后用户手动点击,截图确认次数由 0 变成 1。该结果只覆盖本固件上的短时前台调试常亮及手动触摸;不是永久系统常亮或电脑远程输入。页面保留“常亮续期中 · 待实测”,因为每次新会话仍需观察实际结果。
|
||||
|
||||
1.0.4 的圆/方 QuickApp 构建、两种原生模拟器 UI 和 5 项生命周期回归通过。真机 READY/TAP 日志与 Inspector 隧道尚未取得。来源:[官方亮度接口](https://docs.xfaith-tech.com/glyphix/docs/api/system-brightness.html);完整证据见[补测记录](../docs/VALIDATION.md)。退出探针可结束调试常亮。
|
||||
|
||||
图标暂沿用本仓库贪吃蛇应用的本地资源,仅供开发验证。实际设备状态见手机桥的验收记录。
|
||||
@@ -0,0 +1,2 @@
|
||||
param([switch]$QuickAppOnly, [switch]$Clean)
|
||||
& (Join-Path $PSScriptRoot '..\build-rtos-game.ps1') -ProjectRoot $PSScriptRoot -QuickAppOnly:$QuickAppOnly -Clean:$Clean
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = { minify: false };
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
onCreate() { console.log('RTOS_DEVICE_PROBE_APP_CREATE_V1'); },
|
||||
onShow() { console.log('RTOS_DEVICE_PROBE_APP_SHOW_V1'); },
|
||||
onHide() { console.log('RTOS_DEVICE_PROBE_APP_HIDE_V1'); },
|
||||
onDestroy() { console.log('RTOS_DEVICE_PROBE_APP_DESTROY_V1'); }
|
||||
};
|
||||
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 2.1 KiB |
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<p class="title">真机链路验证</p>
|
||||
<p class="status">手机桥接 · S2 · 1.0.4</p>
|
||||
<p class="body">{{ screenStatus }}</p>
|
||||
<p class="body">点击次数:{{ count }}</p>
|
||||
<button on:click="tap">点击验证</button>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.page { width: 100%; height: 100%; background-color: #102338; align-items: center; justify-content: center; flex-direction: column; padding: 48px 42px; }
|
||||
.title { color: #ffffff; font-size: 1.15rem; margin-bottom: 12px; }
|
||||
.status { color: #83ddd4; font-size: 0.8rem; margin-bottom: 20px; }
|
||||
.body { color: #e1e9f2; font-size: 0.8rem; margin-bottom: 12px; }
|
||||
button { width: 220px; height: 60px; margin-top: 12px; font-size: 0.8rem; }
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
data: { count: 0, screenStatus: '页面已就绪' },
|
||||
screenEpoch: 0,
|
||||
screenActive: false,
|
||||
wakeTimer: null,
|
||||
onReady() { console.log('RTOS_DEVICE_PROBE_READY_V1'); },
|
||||
onShow() { this.keepScreen(true); },
|
||||
onHide() { this.keepScreen(false); },
|
||||
onDestroy() { this.keepScreen(false); },
|
||||
keepScreen(on) {
|
||||
if (on && this.screenActive) return;
|
||||
this.screenActive = on;
|
||||
const epoch = ++this.screenEpoch;
|
||||
if (this.wakeTimer !== null) { clearInterval(this.wakeTimer); this.wakeTimer = null; }
|
||||
try {
|
||||
const brightness = require('@system.brightness').default;
|
||||
const result = brightness.setKeepScreenOn(on);
|
||||
this.screenStatus = on ? '常亮待实测' : '已释放常亮';
|
||||
Promise.resolve(result).catch(() => {
|
||||
if (epoch === this.screenEpoch) this.screenStatus = '当前固件未接受常亮';
|
||||
});
|
||||
if (on) {
|
||||
if (typeof brightness.wakeScreenOn !== 'function') {
|
||||
this.screenStatus = '固件缺少亮屏接口';
|
||||
return;
|
||||
}
|
||||
if (!this.wakeScreen(brightness, epoch)) return;
|
||||
this.wakeTimer = setInterval(() => this.wakeScreen(brightness, epoch), 15000);
|
||||
}
|
||||
} catch (error) { this.screenStatus = '当前固件不支持常亮'; }
|
||||
},
|
||||
wakeScreen(brightness, epoch) {
|
||||
if (!this.screenActive || epoch !== this.screenEpoch) return false;
|
||||
try {
|
||||
// The official API documents an omitted timeout as unlimited.
|
||||
const result = brightness.wakeScreenOn({ screenOn: true });
|
||||
this.screenStatus = '常亮续期中 · 待实测';
|
||||
Promise.resolve(result).catch(() => {
|
||||
if (this.screenActive && epoch === this.screenEpoch) {
|
||||
if (this.wakeTimer !== null) { clearInterval(this.wakeTimer); this.wakeTimer = null; }
|
||||
this.screenStatus = '固件未接受亮屏';
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (this.wakeTimer !== null) { clearInterval(this.wakeTimer); this.wakeTimer = null; }
|
||||
this.screenStatus = '亮屏调用失败';
|
||||
return false;
|
||||
}
|
||||
},
|
||||
tap() { this.count += 1; console.log('RTOS_DEVICE_PROBE_TAP_V1=' + this.count); }
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"package": "com.sydxky.rtos.deviceprobe",
|
||||
"name": "真机链路验证",
|
||||
"icon": "/assets/icon.png",
|
||||
"versionName": "1.0.4",
|
||||
"versionCode": 10004,
|
||||
"features": [],
|
||||
"router": { "entry": "main", "pages": { "main": { "component": "index" } } },
|
||||
"config": { "designWidth": 410 }
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const path = require('node:path');
|
||||
|
||||
function load(brightness) {
|
||||
const source = fs.readFileSync(path.join(__dirname, '../src/main/index.ux'), 'utf8')
|
||||
.match(/<script>([\s\S]*?)<\/script>/)[1].replace('export default', 'module.exports =');
|
||||
const active = new Map();
|
||||
const callbacks = [];
|
||||
let next = 0;
|
||||
const context = { module: { exports: {} }, Promise, console,
|
||||
require: () => ({ default: brightness }),
|
||||
setInterval(fn) { callbacks.push(fn); active.set(++next, fn); return next; },
|
||||
clearInterval(id) { active.delete(id); } };
|
||||
vm.runInNewContext(source, context);
|
||||
const page = context.module.exports;
|
||||
Object.assign(page, page.data);
|
||||
return { page, active, callbacks };
|
||||
}
|
||||
|
||||
test('repeated shows own one renewal; hide and destroy prevent queued wakeups', () => {
|
||||
const calls = [];
|
||||
const { page, active, callbacks } = load({
|
||||
setKeepScreenOn: value => calls.push(['keep', value]),
|
||||
wakeScreenOn: value => calls.push(['wake', value.screenOn]) });
|
||||
page.onShow(); page.onShow();
|
||||
assert.equal(active.size, 1);
|
||||
assert.deepEqual(calls, [['keep', true], ['wake', true]]);
|
||||
callbacks[0]();
|
||||
page.onHide();
|
||||
assert.equal(active.size, 0);
|
||||
const afterHide = calls.length;
|
||||
callbacks[0]();
|
||||
assert.equal(calls.length, afterHide);
|
||||
page.onShow();
|
||||
assert.equal(active.size, 1);
|
||||
page.onDestroy();
|
||||
assert.equal(active.size, 0);
|
||||
const afterDestroy = calls.length;
|
||||
for (const callback of callbacks) callback();
|
||||
assert.equal(calls.length, afterDestroy);
|
||||
assert.deepEqual(calls.at(-1), ['keep', false]);
|
||||
});
|
||||
|
||||
test('destroy releases a visible page even without hide', () => {
|
||||
const calls = [];
|
||||
const { page, active } = load({ setKeepScreenOn: value => calls.push(value), wakeScreenOn() {} });
|
||||
page.onShow(); page.onDestroy();
|
||||
assert.deepEqual(calls, [true, false]);
|
||||
assert.equal(active.size, 0);
|
||||
});
|
||||
|
||||
test('late rejection cannot overwrite the new visible generation', async () => {
|
||||
let rejectOld;
|
||||
let first = true;
|
||||
const { page } = load({ setKeepScreenOn() {}, wakeScreenOn() {
|
||||
if (first) { first = false; return new Promise((_, reject) => { rejectOld = reject; }); }
|
||||
} });
|
||||
page.onShow(); page.onHide(); page.onShow();
|
||||
const status = page.screenStatus;
|
||||
rejectOld(new Error('old request'));
|
||||
await Promise.resolve(); await Promise.resolve();
|
||||
assert.equal(page.screenStatus, status);
|
||||
page.onDestroy();
|
||||
});
|
||||
|
||||
test('a missing wake API is reported and does not start renewal', () => {
|
||||
const { page, active } = load({ setKeepScreenOn() {} });
|
||||
page.onShow();
|
||||
assert.equal(active.size, 0);
|
||||
assert.equal(page.screenStatus, '固件缺少亮屏接口');
|
||||
page.onDestroy();
|
||||
});
|
||||
|
||||
test('a rejected wake stops renewal and remains visible as a failure', async () => {
|
||||
const { page, active } = load({ setKeepScreenOn() {},
|
||||
wakeScreenOn: () => Promise.reject(new Error('unsupported')) });
|
||||
page.onShow();
|
||||
await Promise.resolve(); await Promise.resolve();
|
||||
assert.equal(active.size, 0);
|
||||
assert.equal(page.screenStatus, '固件未接受亮屏');
|
||||
page.onDestroy();
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户