同步移动端工程、设备控制改进与发布资料
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
# Android 环境、构建与交付
|
||||
|
||||
## 安装门槛
|
||||
|
||||
Android Studio 是正常安装软件,由用户运行已核验官方安装包。安装包放用户普通下载目录,不放仓库或 Codex bin。准备好后停止等待用户安装,不自动运行安装器。安装时建议采用默认位置,首次向导安装 Android SDK;不必创建项目,也不必先下载模拟器或 NDK。
|
||||
|
||||
首次完成后记录实际安装版本、JDK、SDK、Build Tools、Kotlin、Compose、AGP 与 Gradle 的兼容组合,锁入版本目录、Wrapper 和依赖校验文件。安装位置用环境探测/local.properties,不写到可提交源码。官方参考:
|
||||
|
||||
- https://developer.android.com/studio
|
||||
- https://developer.android.com/studio/install
|
||||
- https://developer.android.com/kotlin/multiplatform/plugin
|
||||
- https://kotlinlang.org/docs/multiplatform/multiplatform-compatibility-guide.html
|
||||
|
||||
## 工程与路径
|
||||
|
||||
源码位于相邻 `安卓程序源代码/`,模块 androidApp、sharedCore、sharedUi。在 Android Studio 打开工程根,不分别打开子模块。先验证中文和空格路径;若 Android 工具拒绝该路径,通过受保护的仓库外 ASCII 构建目录同步源码并执行,结果回收到交付目录。同步只包含源码及明确资源,不复制私有登记、密钥、缓存或 `.git`;脚本记录源摘要,不覆盖用户目录。
|
||||
|
||||
已安装工具链:Android Studio Quail 4 Patch 1(用户确认中文)、JBR 25.0.3、SDK 37;工程固定 Gradle 9.5.0、AGP 9.3.1、Kotlin 2.4.20、Compose 1.12.1。Wrapper 使用官方发行地址和 SHA-256。首次 `:androidApp:assembleDebug` 与 `:sharedCore:jvmTest` 已在隔离 ASCII 路径通过;这只是骨架及协议验证,不等于功能验收。lint 与真实 BLE 已有通过记录,具体覆盖范围见测试结果归档。
|
||||
|
||||
在仓库根执行下列命令,参数替换为本机实际目录,不把替换后的路径提交:
|
||||
|
||||
```text
|
||||
python "移动端相关内容/安卓app/构建与发布/build_android.py" --work-root "<受保护临时目录的ASCII路径>" --jdk "<Android Studio的jbr目录>" --sdk "<Android SDK目录>"
|
||||
```
|
||||
|
||||
脚本每次创建独立 `qms-build-*` 子目录,只复制工程源码,忽略 build、.gradle、.kotlin、local.properties。默认使用 Wrapper;可用 `--gradle` 指定已核验的同版本 Gradle,避免重复下载。默认运行 JVM 测试和 assembleDebug,可在末尾显式传任务列表。保留构建目录用于查看测试报告和 `androidApp/build/outputs/apk/debug/androidApp-debug.apk`;验证后只清理本次明确目录。Android 调试签名沿用用户默认 Android 密钥目录,不复制到源码或隔离工程。
|
||||
|
||||
路径实测:AGP 默认拒绝非 ASCII 路径;启用 `android.overridePathCheck=true` 后编译继续,但当前 JBR/Gradle 下 JVM 测试出现类加载失败。在 ASCII 隔离目录同一测试通过,故当前使用该回退。2026-09-25 已在全新且包含空格的 ASCII 构建根目录重新执行全部 92 个任务,JVM 测试及两个 debug APK 均成功;保留中文源码位置。
|
||||
|
||||
## APK 与签名
|
||||
|
||||
- 应用名“奇妙小屏幕”,applicationId 为 `org.qimiaoscreen.controller`,minSdk 26,首版版本 0.1.0。
|
||||
- 仅 debug 签名,沿用本机调试密钥并记录证书摘要(不记录私钥)。正式签名只有用户明确命令才配置或执行。
|
||||
- 调试签名更换可能无法覆盖安装;不能自动卸载并清除用户配置来掩盖。需要时先报告原因。
|
||||
- App versionCode 独立递增,设备版本/协议版本分别登记,不能因设备部署自动提升 App 版本。
|
||||
- 每次交付放 `交付包/<App版本>-<versionCode>/`:APK、SHA256SUMS、manifest.json、README.md。manifest 包含 App/协议/兼容设备信息、源摘要、工具链版本和签名证书摘要,不包含本机路径或测试机标识。
|
||||
- APK 与发布元数据保留在私有 Git 仓库,需要时沿用 Git LFS;构建缓存、SDK、真实登记、调试及正式签名私钥都不提交。
|
||||
- 本任务不导出核桃派 IMG/OTA;设备功能时间按根规则更新,设备发布版本不因普通部署占号。
|
||||
|
||||
## 交付前检查
|
||||
|
||||
从隔离目录依文档重新构建并验证;完成相关测试、协议互操作和真实 BLE 联调;运行根仓库卫生检查。交付说明准确区分通过、未验证和不适用。第二台手机验收 APK 放到同一清晰可找到的交付目录,用户安装后再进行实机互斥测试。
|
||||
|
||||
## 可重复打包
|
||||
|
||||
```text
|
||||
python "移动端相关内容/安卓app/构建与发布/package_debug.py" --build "<本轮隔离工程目录>" --sdk "<Android SDK目录>"
|
||||
```
|
||||
|
||||
运行前将 JAVA_HOME 指向正常安装的 Android Studio jbr。脚本逐文件核对隔离工程与当前源码的 SHA-256,调用 Build Tools 36.0.0 的 apksigner 验证 Android Debug 证书,创建版本独立目录,保存 APK、摘要、工具链和源文件清单。已存在交付目录拒绝覆盖。结果默认标为开发验证包,必须结合测试报告说明验收范围,不用打包成功替代验收。versionCode 2 包含本轮连接清理和界面信息补充,可覆盖同证书的 code 1。
|
||||
|
||||
本轮 App 0.2.0 / versionCode 3 为四栏界面及设备记忆优化,仍仅 debug 签名。关于页显示本次 Gradle 构建日期(北京时间),不修改设备发布版本。
|
||||
|
||||
## 构建排障与测试复盘
|
||||
|
||||
手动调用隔离工程 Wrapper 时,每个新 shell 都须明确使用已登记的 JDK/SDK(JAVA_HOME/ANDROID_HOME);前一工具调用的进程环境不会自动传到下一次调用。缺少 SDK location 时先检查环境,不重复下载工具、不改动中文源码位置。
|
||||
|
||||
Gradle Kotlin DSL 里 `java` 可能被插件扩展名遮蔽;构建日期实现显式 import `ZonedDateTime`、`ZoneId`、`DateTimeFormatter`,避免直接以 `java.time...` 引用引发解析错误。日期仍使用北京时间,不通过硬编码本机路径解决。
|
||||
|
||||
Python 在 Windows 缺少 IANA tzdata 时,固定北京时间时间戳可使用标准库 `timezone(timedelta(hours=8))`;这一方法只适用于明确要求的固定 UTC+08:00 时间,不当作任意地区夏令时转换方案,也不因该情况自动新增依赖。
|
||||
|
||||
打包前重新核对隔离源码、实际 APK、构建日期和签名摘要;测试源码调整也要同步隔离工程,不能测试旧测试 APK。测试/构建遇到的新问题在完成后更新相应文档并在最终反馈说明,具体输入和真机排障见 [测试排障指南](../如何安卓测试/常见问题与排障.md)。
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Build from an isolated path; source stays in its original Chinese directory."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--work-root', type=Path, required=True)
|
||||
parser.add_argument('--jdk', type=Path, required=True)
|
||||
parser.add_argument('--sdk', type=Path, required=True)
|
||||
parser.add_argument('--gradle', type=Path, help='Optional verified Gradle executable; otherwise use Wrapper')
|
||||
parser.add_argument('tasks', nargs='*', default=[':sharedCore:jvmTest', ':androidApp:assembleDebug'])
|
||||
args = parser.parse_args()
|
||||
root = args.work_root.resolve()
|
||||
if not str(root).isascii():
|
||||
parser.error('work-root must use ASCII characters')
|
||||
if not (args.jdk / 'bin/java.exe').is_file() or not (args.sdk / 'platforms').is_dir():
|
||||
parser.error('JDK or SDK is missing')
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
destination = Path(tempfile.mkdtemp(prefix='qms-build-', dir=root))
|
||||
source = Path(__file__).resolve().parents[1] / '安卓程序源代码'
|
||||
shutil.copytree(source, destination, dirs_exist_ok=True,
|
||||
ignore=shutil.ignore_patterns('build', '.gradle', '.kotlin', 'local.properties'))
|
||||
environment = os.environ.copy()
|
||||
environment.update(JAVA_HOME=str(args.jdk.resolve()), ANDROID_HOME=str(args.sdk.resolve()))
|
||||
executable = args.gradle.resolve() if args.gradle else destination / 'gradlew.bat'
|
||||
result = subprocess.run([str(executable), '--console', 'plain', *args.tasks], cwd=destination, env=environment)
|
||||
# Keep this exact directory for logs, reports and APK retrieval, even on failure.
|
||||
print(f'Build directory: {destination}', flush=True)
|
||||
return result.returncode
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,65 @@
|
||||
"""Package an independently built debug APK without overwriting an old delivery."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tomllib
|
||||
|
||||
|
||||
def digest(path):
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--build', type=Path, required=True)
|
||||
parser.add_argument('--sdk', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
root = Path(__file__).resolve().parent
|
||||
source = root.parent / '安卓程序源代码'
|
||||
config = (source / 'androidApp/build.gradle.kts').read_text(encoding='utf-8')
|
||||
version = re.search(r'versionName = "([^"]+)"', config)[1]
|
||||
code = int(re.search(r'versionCode = (\d+)', config)[1])
|
||||
entries = []
|
||||
for path in sorted(source.rglob('*')):
|
||||
relative = path.relative_to(source)
|
||||
if not path.is_file() or any(p in {'build', '.gradle', '.kotlin'} for p in relative.parts) or path.name == 'local.properties':
|
||||
continue
|
||||
built = args.build / relative
|
||||
if not built.is_file() or digest(built) != digest(path):
|
||||
raise RuntimeError('Build source differs from current source: ' + relative.as_posix())
|
||||
entries.append(dict(path=relative.as_posix(), sha256=digest(path)))
|
||||
apk = args.build / 'androidApp/build/outputs/apk/debug/androidApp-debug.apk'
|
||||
signer = args.sdk / 'build-tools/36.0.0/apksigner.bat'
|
||||
result = subprocess.run([str(signer), 'verify', '--print-certs', str(apk)],
|
||||
capture_output=True, text=True, encoding='utf-8', errors='replace')
|
||||
if result.returncode or 'CN=Android Debug' not in result.stdout:
|
||||
raise RuntimeError('APK must verify with the existing Android Debug certificate')
|
||||
certificate = re.search(r'certificate SHA-256 digest: ([0-9a-fA-F]+)', result.stdout)[1].lower()
|
||||
destination = root / '交付包' / f'{version}-{code}'
|
||||
destination.mkdir(parents=True, exist_ok=False)
|
||||
name = f'奇妙小屏幕-{version}-debug.apk'
|
||||
shutil.copyfile(apk, destination / name)
|
||||
versions = tomllib.loads((source / 'gradle/libs.versions.toml').read_text(encoding='utf-8'))['versions']
|
||||
build_config = (args.build / 'androidApp/build/generated/source/buildConfig/debug/org/qimiaoscreen/controller/BuildConfig.java').read_text(encoding='utf-8')
|
||||
build_date = re.search(r'BUILD_DATE = "([^"]+)"', build_config)[1]
|
||||
manifest = dict(app_version=version, version_code=code, signing='debug', build_date=build_date,
|
||||
status='development-validation-see-test-report', apk_sha256=digest(apk),
|
||||
certificate_sha256=certificate, protocol_major=1, minimum_android_api=26,
|
||||
target_android_api=37, build_tools='36.0.0', toolchain=versions, sources=entries)
|
||||
(destination / 'manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
|
||||
(destination / 'SHA256SUMS').write_text(f'{digest(apk)} {name}\n', encoding='utf-8')
|
||||
(destination / 'README.md').write_text(
|
||||
f'# 奇妙小屏幕 {version} debug(versionCode {code})\n\n'
|
||||
'仅 debug 签名,可覆盖安装相同调试证书的旧包。Android 8.0 以上,真实验证机为 Android 13。\n\n'
|
||||
'本包为当前开发验证产物;验收范围和未验证项见 `../../../测试结果归档/实施状态.md`。'
|
||||
'不能将 APK 成功生成视为所有首版验收通过。设备需已部署移动控制功能,历史设备版本号本身不代表支持 BLE。\n\n'
|
||||
f'SHA-256:`{digest(apk)}`。源码摘要与签名证书摘要见 manifest.json。\n', encoding='utf-8')
|
||||
print(json.dumps(dict(delivery=destination.relative_to(root).as_posix(), apk_sha256=digest(apk), certificate_sha256=certificate), ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,14 @@
|
||||
# 奇妙小屏幕 0.1.0 debug 开发验证包
|
||||
|
||||
本目录提供当前连接修复阶段 APK,不是全部首版验收完成声明。仅 debug 签名,不包含私钥;可覆盖安装使用同一调试证书的此前测试版本。若提示签名不一致,不自动卸载或清除数据。
|
||||
|
||||
- 文件:`奇妙小屏幕-0.1.0-debug.apk`。
|
||||
- Android 8.0/API 26 及以上;真实验证机为 Android 13。
|
||||
- BLE 协议主版本 1;须配合本项目已部署移动控制服务的设备开发版本,仅有历史 1.1.1 发布版本号不能证明含 BLE 功能。
|
||||
- 2026-09-25 修复请求/连接建立超时残留状态,以及厂商缓存 MTU 回调触发过早服务发现的问题。
|
||||
- 已通过 JVM 6 项、Android lint(0 错误/14 警告)、真实 BLE 60 轮持续读取和 Controller 三页/后台释放/前台重连组合测试(290.908 秒)。实际 Activity 自动化另见测试报告。
|
||||
- 用户授权 root 输入后,实际 Activity 三页操作、后台 33 秒后返回自动重连及主动断开通过(49.45 秒)。
|
||||
- 仍需完成完整首版的写操作、网络切换、双手机互斥、设备恢复与性能等验收;不把只读成功当作这些项目通过。
|
||||
- 详细过程:`../../../测试结果归档/2026-09-25_重启与连接恢复.md`。
|
||||
|
||||
SHA-256:`2eedd9fccd27dcc0664e6e0f1003554677b632580d65ae17f95e7ea285a0529e`。同目录 SHA256SUMS 与 manifest.json 包含校验值、调试证书摘要、工具链与源码文件摘要;不包含本机路径或真实设备标识。
|
||||
@@ -0,0 +1 @@
|
||||
2eedd9fccd27dcc0664e6e0f1003554677b632580d65ae17f95e7ea285a0529e 奇妙小屏幕-0.1.0-debug.apk
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"app_version": "0.1.0",
|
||||
"version_code": 1,
|
||||
"status": "development-validation-not-final-release",
|
||||
"signing": "debug",
|
||||
"certificate_sha256": "b413e68c0bd2311c491ebd087ac5791c7c2ad8267793f92f6055d5521bd3ef4d",
|
||||
"apk_sha256": "2eedd9fccd27dcc0664e6e0f1003554677b632580d65ae17f95e7ea285a0529e",
|
||||
"protocol_major": 1,
|
||||
"minimum_android_api": 26,
|
||||
"target_android_api": 37,
|
||||
"build_tools": "36.0.0",
|
||||
"gradle": "9.5.0",
|
||||
"kotlin": "2.4.20",
|
||||
"compose": "1.12.1",
|
||||
"agp": "9.3.1",
|
||||
"device_release_tested": "1.1.1 with mobile development changes",
|
||||
"date": "2026-09-25",
|
||||
"sources": [
|
||||
{
|
||||
"path": "androidApp/build.gradle.kts",
|
||||
"sha256": "ed75cde57a6dd389e2a69d3d30ccecb3411e258d2cb36ec057a6f8c9fce20bb6"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/AppLifecycleTest.kt",
|
||||
"sha256": "e959936e49ad7b90acf04c8f1595d6f497d9b14d6781b13e03eb5914d59ecb1f"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/BleIntegrationTest.kt",
|
||||
"sha256": "d2236734062ce523f87a9cb7f3149a8a965616bcab3df46e954fdc8ca81f76c0"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/AndroidManifest.xml",
|
||||
"sha256": "186ed22b19731ea5bf3567e40d6487b95f1bced3dd2fce605de6557cb7c5763e"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/kotlin/org/qimiaoscreen/controller/AndroidBle.kt",
|
||||
"sha256": "453c67aab3963c12be915923c88ea1c63930c24e43842c223ca62f421574bc15"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/kotlin/org/qimiaoscreen/controller/MainActivity.kt",
|
||||
"sha256": "3a688ac321f1f06c9c61be4046cd0ebfbf53893a4742a4aaa9a77aa56bf17838"
|
||||
},
|
||||
{
|
||||
"path": "build.gradle.kts",
|
||||
"sha256": "feb3b6e64b846f0408cc40e95ae4b632ce9288a56bd6151bc3bbcc46f0549b49"
|
||||
},
|
||||
{
|
||||
"path": "gradle/libs.versions.toml",
|
||||
"sha256": "ed393bb2b4d81a162f6b3d21968eeca113e23c2f5d5d22c3be9d104bc592a633"
|
||||
},
|
||||
{
|
||||
"path": "gradle/wrapper/gradle-wrapper.jar",
|
||||
"sha256": "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7"
|
||||
},
|
||||
{
|
||||
"path": "gradle/wrapper/gradle-wrapper.properties",
|
||||
"sha256": "2cb0f9ddf992a26fc4947c91019136bff5e1ccfbf93bd905402b1abf69d15bc5"
|
||||
},
|
||||
{
|
||||
"path": "gradle.properties",
|
||||
"sha256": "e7d684b7795fd04ab2e7e96038fa2bf4f9fd633578aa3bb2f5fe367b4f5ec159"
|
||||
},
|
||||
{
|
||||
"path": "gradlew",
|
||||
"sha256": "ab5c0cad16305af2e619c159c1f58dd68d07fab9c11e36701e109c0277407f7a"
|
||||
},
|
||||
{
|
||||
"path": "gradlew.bat",
|
||||
"sha256": "475c4f08cd57cf2faa819e7f36d72aa93f0ad646ea23a8f7fa3ef54dee1cbc52"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "7232ebbac0899152e312429fecaf6b3e14b31265b0c50a95c99a6213aa04b2cd"
|
||||
},
|
||||
{
|
||||
"path": "settings.gradle.kts",
|
||||
"sha256": "3d53a77b5b92800475897fb55f910a7cfe008e2084e0b7185ebb5ce38e22cf53"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/build.gradle.kts",
|
||||
"sha256": "9a420e39e3c655f676572d4486e415dbc8b4fd45904db6ad7b64d7fcd71f084b"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/Controller.kt",
|
||||
"sha256": "792725610a2f4b802c37c708a8995b647f6482427a89f4df7d005c5b3b9f3026"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/DeviceLink.kt",
|
||||
"sha256": "8d85e3182e66f7d894f3119329a6bd3d6cdbe8f3ad725f3715dcda477c359220"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/Protocol.kt",
|
||||
"sha256": "99264aff225076ef1b423801b9bba8797e0e06a9e759339bc52e896620e4b694"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/SecureChannel.kt",
|
||||
"sha256": "bde40388bb74c9d47a4e099a14a0baeca3b6bda1539023dbd7dca831c6f24039"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonTest/kotlin/org/qimiaoscreen/core/ProtocolTest.kt",
|
||||
"sha256": "5d26d3a50c5887d72dc91b2081273fbe4a5a968f01451ef75ab78903d5d42217"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/javaCryptoMain/kotlin/org/qimiaoscreen/core/JavaSecureChannel.kt",
|
||||
"sha256": "554d663ceb6ec1be095b31e9ff9549af3ecd02f975059c22ce566c87225b05eb"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/kotlin/org/qimiaoscreen/core/CryptoInteropTest.kt",
|
||||
"sha256": "2ddb06c4476952a69bf01c0a3a7164625ae8c0bfe9194349450a2e254888d4b3"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/kotlin/org/qimiaoscreen/core/RpcTimeoutTest.kt",
|
||||
"sha256": "fca298fb2266ee7e68e6df76d837f7f191e0046aa5ba321a304c4b4369e064a6"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/resources/protocol-v1.json",
|
||||
"sha256": "6280953b95a3f5d1ae531d7feaa6a62848ffa3e438e58308d2a27f04723803dc"
|
||||
},
|
||||
{
|
||||
"path": "sharedUi/build.gradle.kts",
|
||||
"sha256": "d469b330d248647e6e93f98d69eb46730adab329ebaf71cbfd896d5426fd0ff1"
|
||||
},
|
||||
{
|
||||
"path": "sharedUi/src/commonMain/kotlin/org/qimiaoscreen/ui/ControllerApp.kt",
|
||||
"sha256": "b9a9e0f95d301d35e19424c5a6342e0157292a77044126da908341af89ea6954"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
# 奇妙小屏幕 0.1.0 debug(versionCode 2)
|
||||
|
||||
仅 debug 签名,可覆盖安装相同调试证书的旧包。Android 8.0 以上,真实验证机为 Android 13。
|
||||
|
||||
本包为当前开发验证产物;验收范围和未验证项见 `../../../测试结果归档/实施状态.md`。不能将 APK 成功生成视为所有首版验收通过。设备需已部署移动控制功能,历史设备版本号本身不代表支持 BLE。
|
||||
|
||||
SHA-256:`f7c1dc215aa5934359a37fd001e1bc3cbf959b4a386662b1a886efb1ab73ea46`。源码摘要与签名证书摘要见 manifest.json。
|
||||
|
||||
本版实际 Activity 三页、蓝牙切换、后台释放及重连测试通过(60.105 秒)。完整通过/未验证/不适用与恢复说明见 `../../../测试结果归档/2026-09-25_本轮交付核对.md`。
|
||||
@@ -0,0 +1 @@
|
||||
f7c1dc215aa5934359a37fd001e1bc3cbf959b4a386662b1a886efb1ab73ea46 奇妙小屏幕-0.1.0-debug.apk
|
||||
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"app_version": "0.1.0",
|
||||
"version_code": 2,
|
||||
"signing": "debug",
|
||||
"status": "development-validation-see-test-report",
|
||||
"apk_sha256": "f7c1dc215aa5934359a37fd001e1bc3cbf959b4a386662b1a886efb1ab73ea46",
|
||||
"certificate_sha256": "b413e68c0bd2311c491ebd087ac5791c7c2ad8267793f92f6055d5521bd3ef4d",
|
||||
"protocol_major": 1,
|
||||
"minimum_android_api": 26,
|
||||
"target_android_api": 37,
|
||||
"build_tools": "36.0.0",
|
||||
"toolchain": {
|
||||
"kotlin": "2.4.20",
|
||||
"agp": "9.3.1",
|
||||
"compose": "1.12.1",
|
||||
"coroutines": "1.10.2",
|
||||
"serialization": "1.9.0"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"path": "androidApp/build.gradle.kts",
|
||||
"sha256": "c786ed104d02a545a1824c6d3e4b4ba4d7eda0f8cdb2aacc90c7ac0203482457"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/AppLifecycleTest.kt",
|
||||
"sha256": "128df8ba46bea5c965118d6b76156389fd9d6face6129f627788ecc312383a0c"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/BleIntegrationTest.kt",
|
||||
"sha256": "db8149886b54d896148b2f07cc77dfec6906ada31a6aae54421272c0d66c276c"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/AndroidManifest.xml",
|
||||
"sha256": "186ed22b19731ea5bf3567e40d6487b95f1bced3dd2fce605de6557cb7c5763e"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/kotlin/org/qimiaoscreen/controller/AndroidBle.kt",
|
||||
"sha256": "453c67aab3963c12be915923c88ea1c63930c24e43842c223ca62f421574bc15"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/kotlin/org/qimiaoscreen/controller/MainActivity.kt",
|
||||
"sha256": "d79523c52eee42041a4e14ba664d08a9a614dc79f9578b3a6ab1f8db7268edd4"
|
||||
},
|
||||
{
|
||||
"path": "build.gradle.kts",
|
||||
"sha256": "feb3b6e64b846f0408cc40e95ae4b632ce9288a56bd6151bc3bbcc46f0549b49"
|
||||
},
|
||||
{
|
||||
"path": "gradle/libs.versions.toml",
|
||||
"sha256": "ed393bb2b4d81a162f6b3d21968eeca113e23c2f5d5d22c3be9d104bc592a633"
|
||||
},
|
||||
{
|
||||
"path": "gradle/wrapper/gradle-wrapper.jar",
|
||||
"sha256": "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7"
|
||||
},
|
||||
{
|
||||
"path": "gradle/wrapper/gradle-wrapper.properties",
|
||||
"sha256": "2cb0f9ddf992a26fc4947c91019136bff5e1ccfbf93bd905402b1abf69d15bc5"
|
||||
},
|
||||
{
|
||||
"path": "gradle.properties",
|
||||
"sha256": "e7d684b7795fd04ab2e7e96038fa2bf4f9fd633578aa3bb2f5fe367b4f5ec159"
|
||||
},
|
||||
{
|
||||
"path": "gradlew",
|
||||
"sha256": "ab5c0cad16305af2e619c159c1f58dd68d07fab9c11e36701e109c0277407f7a"
|
||||
},
|
||||
{
|
||||
"path": "gradlew.bat",
|
||||
"sha256": "475c4f08cd57cf2faa819e7f36d72aa93f0ad646ea23a8f7fa3ef54dee1cbc52"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "7232ebbac0899152e312429fecaf6b3e14b31265b0c50a95c99a6213aa04b2cd"
|
||||
},
|
||||
{
|
||||
"path": "settings.gradle.kts",
|
||||
"sha256": "3d53a77b5b92800475897fb55f910a7cfe008e2084e0b7185ebb5ce38e22cf53"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/build.gradle.kts",
|
||||
"sha256": "9a420e39e3c655f676572d4486e415dbc8b4fd45904db6ad7b64d7fcd71f084b"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/Controller.kt",
|
||||
"sha256": "1b23010730aee35f2512bb247a79040360a046a055b7029e269a17f9790686e7"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/DeviceLink.kt",
|
||||
"sha256": "8d85e3182e66f7d894f3119329a6bd3d6cdbe8f3ad725f3715dcda477c359220"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/Protocol.kt",
|
||||
"sha256": "99264aff225076ef1b423801b9bba8797e0e06a9e759339bc52e896620e4b694"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/SecureChannel.kt",
|
||||
"sha256": "bde40388bb74c9d47a4e099a14a0baeca3b6bda1539023dbd7dca831c6f24039"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonTest/kotlin/org/qimiaoscreen/core/ControllerRecoveryTest.kt",
|
||||
"sha256": "4107b56cd9a30009aa9b16ebb22e349e8a46134be1cb49078a81e1d8b5e7412b"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonTest/kotlin/org/qimiaoscreen/core/ProtocolTest.kt",
|
||||
"sha256": "5d26d3a50c5887d72dc91b2081273fbe4a5a968f01451ef75ab78903d5d42217"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/javaCryptoMain/kotlin/org/qimiaoscreen/core/JavaSecureChannel.kt",
|
||||
"sha256": "554d663ceb6ec1be095b31e9ff9549af3ecd02f975059c22ce566c87225b05eb"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/kotlin/org/qimiaoscreen/core/CryptoInteropTest.kt",
|
||||
"sha256": "2ddb06c4476952a69bf01c0a3a7164625ae8c0bfe9194349450a2e254888d4b3"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/kotlin/org/qimiaoscreen/core/RpcTimeoutTest.kt",
|
||||
"sha256": "fca298fb2266ee7e68e6df76d837f7f191e0046aa5ba321a304c4b4369e064a6"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/resources/protocol-v1.json",
|
||||
"sha256": "6280953b95a3f5d1ae531d7feaa6a62848ffa3e438e58308d2a27f04723803dc"
|
||||
},
|
||||
{
|
||||
"path": "sharedUi/build.gradle.kts",
|
||||
"sha256": "d469b330d248647e6e93f98d69eb46730adab329ebaf71cbfd896d5426fd0ff1"
|
||||
},
|
||||
{
|
||||
"path": "sharedUi/src/commonMain/kotlin/org/qimiaoscreen/ui/ControllerApp.kt",
|
||||
"sha256": "4c9f1e67aca84ed025110cd36daf2a20ee1da7fbff7f525a2eed0d4203fb2819"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
# 奇妙小屏幕 0.2.0 debug(versionCode 3)
|
||||
|
||||
仅 debug 签名,可覆盖安装相同调试证书的旧包。Android 8.0 以上,真实验证机为 Android 13。
|
||||
|
||||
本包为当前开发验证产物;验收范围和未验证项见 `../../../测试结果归档/实施状态.md`。不能将 APK 成功生成视为所有首版验收通过。设备需已部署移动控制功能,历史设备版本号本身不代表支持 BLE。
|
||||
|
||||
SHA-256:`9adc8e572384bb30a8a78115be43a8fefeefee6d25bda0136dff106e2a8bcab3`。源码摘要与签名证书摘要见 manifest.json。
|
||||
|
||||
## 本轮验证
|
||||
|
||||
四栏、菜单、设备记忆/重命名、后台重连、窄屏/横屏/大字体导航、301.514 秒完整 BLE 回归、WiFi 扫描填入及明确错误密码反馈、原网络恢复均在登记 root 手机验证通过。测试手机已覆盖安装本包,已安装 APK 摘要与交付文件一致。
|
||||
|
||||
真实双设备切换、旧 Android、跨 SSID 和实际 DHCP/静态互换未验证;不把模拟和表单测试等同于这些真机验收。详细证据与失败记录见 `../../../测试结果归档/2026-09-26_四栏界面优化.md`。
|
||||
@@ -0,0 +1 @@
|
||||
9adc8e572384bb30a8a78115be43a8fefeefee6d25bda0136dff106e2a8bcab3 奇妙小屏幕-0.2.0-debug.apk
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"app_version": "0.2.0",
|
||||
"version_code": 3,
|
||||
"signing": "debug",
|
||||
"build_date": "2026-09-26 15:13 +08:00",
|
||||
"status": "development-validation-see-test-report",
|
||||
"apk_sha256": "9adc8e572384bb30a8a78115be43a8fefeefee6d25bda0136dff106e2a8bcab3",
|
||||
"certificate_sha256": "b413e68c0bd2311c491ebd087ac5791c7c2ad8267793f92f6055d5521bd3ef4d",
|
||||
"protocol_major": 1,
|
||||
"minimum_android_api": 26,
|
||||
"target_android_api": 37,
|
||||
"build_tools": "36.0.0",
|
||||
"toolchain": {
|
||||
"kotlin": "2.4.20",
|
||||
"agp": "9.3.1",
|
||||
"compose": "1.12.1",
|
||||
"coroutines": "1.10.2",
|
||||
"serialization": "1.9.0"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"path": "androidApp/build.gradle.kts",
|
||||
"sha256": "45aeaaf7cff436d7dc93e6c6f1491925b082d989bcd3b097fb5a4052e8bbe8c7"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/AppLifecycleTest.kt",
|
||||
"sha256": "ba33ea5102809e6b0b5b594159f05adfc612b02a0430e329a0620b20c76cb21f"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/BleIntegrationTest.kt",
|
||||
"sha256": "8dc7a28279e99e46d15c914955d8974d619c4e48a2fcab887f180b15b4b183ab"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/LayoutTest.kt",
|
||||
"sha256": "930c964fab8975d681e6d698468146294653af4d66a4a7be5c162e4c14659696"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/androidTest/kotlin/org/qimiaoscreen/controller/WifiSwitchTest.kt",
|
||||
"sha256": "5ce335ff84fee01230733971964fefde3fcc6103172d3c40fee64f50838b7bcf"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/AndroidManifest.xml",
|
||||
"sha256": "186ed22b19731ea5bf3567e40d6487b95f1bced3dd2fce605de6557cb7c5763e"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/kotlin/org/qimiaoscreen/controller/AndroidBle.kt",
|
||||
"sha256": "a1af9d6593c3be1af0f2a39a3869eae2e1bbbb1dab8b775e06da5dcaa739e9ff"
|
||||
},
|
||||
{
|
||||
"path": "androidApp/src/main/kotlin/org/qimiaoscreen/controller/MainActivity.kt",
|
||||
"sha256": "7bece662b79093a19fcdf34ad344cf42a57b60291f0536823395ae06663f4f23"
|
||||
},
|
||||
{
|
||||
"path": "build.gradle.kts",
|
||||
"sha256": "feb3b6e64b846f0408cc40e95ae4b632ce9288a56bd6151bc3bbcc46f0549b49"
|
||||
},
|
||||
{
|
||||
"path": "gradle/libs.versions.toml",
|
||||
"sha256": "ed393bb2b4d81a162f6b3d21968eeca113e23c2f5d5d22c3be9d104bc592a633"
|
||||
},
|
||||
{
|
||||
"path": "gradle/wrapper/gradle-wrapper.jar",
|
||||
"sha256": "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7"
|
||||
},
|
||||
{
|
||||
"path": "gradle/wrapper/gradle-wrapper.properties",
|
||||
"sha256": "2cb0f9ddf992a26fc4947c91019136bff5e1ccfbf93bd905402b1abf69d15bc5"
|
||||
},
|
||||
{
|
||||
"path": "gradle.properties",
|
||||
"sha256": "e7d684b7795fd04ab2e7e96038fa2bf4f9fd633578aa3bb2f5fe367b4f5ec159"
|
||||
},
|
||||
{
|
||||
"path": "gradlew",
|
||||
"sha256": "ab5c0cad16305af2e619c159c1f58dd68d07fab9c11e36701e109c0277407f7a"
|
||||
},
|
||||
{
|
||||
"path": "gradlew.bat",
|
||||
"sha256": "475c4f08cd57cf2faa819e7f36d72aa93f0ad646ea23a8f7fa3ef54dee1cbc52"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "7232ebbac0899152e312429fecaf6b3e14b31265b0c50a95c99a6213aa04b2cd"
|
||||
},
|
||||
{
|
||||
"path": "settings.gradle.kts",
|
||||
"sha256": "3d53a77b5b92800475897fb55f910a7cfe008e2084e0b7185ebb5ce38e22cf53"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/build.gradle.kts",
|
||||
"sha256": "9a420e39e3c655f676572d4486e415dbc8b4fd45904db6ad7b64d7fcd71f084b"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/Controller.kt",
|
||||
"sha256": "c368f57d13ba84bcb458855465ec22843d8ed617c7242bf2d5a3b4730271dca3"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/DeviceLink.kt",
|
||||
"sha256": "870345382351bbf6d37f4332c0fab099cb47b7b703428a98567a0086c3316be6"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/Protocol.kt",
|
||||
"sha256": "99264aff225076ef1b423801b9bba8797e0e06a9e759339bc52e896620e4b694"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonMain/kotlin/org/qimiaoscreen/core/SecureChannel.kt",
|
||||
"sha256": "bde40388bb74c9d47a4e099a14a0baeca3b6bda1539023dbd7dca831c6f24039"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonTest/kotlin/org/qimiaoscreen/core/ControllerRecoveryTest.kt",
|
||||
"sha256": "a4a4c3e2ba64b0facc1e1c491d440f91e4bdcf53eb87d9991921f4f568614f58"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/commonTest/kotlin/org/qimiaoscreen/core/ProtocolTest.kt",
|
||||
"sha256": "5d26d3a50c5887d72dc91b2081273fbe4a5a968f01451ef75ab78903d5d42217"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/javaCryptoMain/kotlin/org/qimiaoscreen/core/JavaSecureChannel.kt",
|
||||
"sha256": "554d663ceb6ec1be095b31e9ff9549af3ecd02f975059c22ce566c87225b05eb"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/kotlin/org/qimiaoscreen/core/CryptoInteropTest.kt",
|
||||
"sha256": "2ddb06c4476952a69bf01c0a3a7164625ae8c0bfe9194349450a2e254888d4b3"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/kotlin/org/qimiaoscreen/core/RpcTimeoutTest.kt",
|
||||
"sha256": "fca298fb2266ee7e68e6df76d837f7f191e0046aa5ba321a304c4b4369e064a6"
|
||||
},
|
||||
{
|
||||
"path": "sharedCore/src/jvmTest/resources/protocol-v1.json",
|
||||
"sha256": "6280953b95a3f5d1ae531d7feaa6a62848ffa3e438e58308d2a27f04723803dc"
|
||||
},
|
||||
{
|
||||
"path": "sharedUi/build.gradle.kts",
|
||||
"sha256": "d469b330d248647e6e93f98d69eb46730adab329ebaf71cbfd896d5426fd0ff1"
|
||||
},
|
||||
{
|
||||
"path": "sharedUi/src/commonMain/kotlin/org/qimiaoscreen/ui/ControllerApp.kt",
|
||||
"sha256": "0f88692bb155d68baaadbad3beec5443975a7863b7a1ceb6733958aab064d132"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user