318 lines
23 KiB
Markdown
318 lines
23 KiB
Markdown
# MAG160C 统一安卓APP — 完整开发交接文档(换模型用)
|
||
|
||
> 生成时间:2026-09-07。给接手模型读取本文件即可恢复全部上下文。
|
||
> 结合 `docs/android_app/session_state.md`(心跳)与 `docs/android_app/reverse_apk_features.md`(逆向总表)。
|
||
|
||
---
|
||
|
||
## 0. 给新模型的读取提示词
|
||
|
||
> 你是本项目的续任开发者。请先完整阅读 `C:\Project\MAG160C\docs\android_app\HANDOFF_DEVELOPMENT.md`
|
||
> 和 `C:\Project\MAG160C\docs\android_app\session_state.md`、`C:\Project\MAG160C\docs\android_app\reverse_apk_features.md`、
|
||
> `C:\Project\MAG160C\analysis\protocol_spec.md`,再继续开发。项目根目录 `C:\Project\MAG160C` 是 git 仓库,
|
||
> 源码在 `android\` 子目录(Gradle 工程,package `com.mag160c.thermal`)。当前会话状态与待办见 session_state.md。
|
||
|
||
---
|
||
|
||
## 1. 项目目标与背景
|
||
|
||
- 把 MAG160C 热像仪(160×120, 15fps, USB VID 0x833C)的官方 4 个安卓APP
|
||
(普通版 MAG-Cx / 专业版 MAG-Mx / ThermoScope 数据分析 / CoreSdkSample)逆向整合
|
||
为一款现代化安卓APP。
|
||
- 适配 **Android 16(API 36)**;用户反馈旧专业版在 Android 16 上花屏,重写根治。
|
||
- 逆向证据:`docs/android_app/reverse_apk_features.md`(4 APP 功能总表)、
|
||
`analysis/protocol_spec.md`(USB 协议/温度算法全逆向)。
|
||
- 用户要求:现代 UI、**构图钉死竖屏框架**(竖屏锁定,见 §4.3)、**不建立流氓文件夹**(媒体→MediaStore DCIM/MAG160C)、
|
||
无 32 位库(纯 Kotlin,无 NDK)。
|
||
|
||
## 2. 技术栈与工具链(已部署)
|
||
|
||
- **纯 Kotlin + Jetpack Compose (Material 3)**,无 NDK、无 .so、无 native。
|
||
- **工具链(2026-09-09 本机重装;交接文档旧机器路径 C:\Tools\gradle-8.14.3、
|
||
Zulu、C:\Users\ZXC\AppData\Local\Android\Sdk 均已失效)**:
|
||
- JDK Temurin 21:`C:\Tools\jdk-21`
|
||
- Android SDK:`C:\Tools\android-sdk`(cmdline-tools\latest、platforms/android-36、
|
||
build-tools 36.0.0、platform-tools/adb;许可已接受;无模拟器/AVD/system-images)
|
||
- Gradle:用工程 wrapper `android\gradlew.bat`(8.14.3 自动下载)
|
||
- `android\local.properties` 已写 `sdk.dir`(已 gitignore)
|
||
- 构建需 `JAVA_HOME=C:\Tools\jdk-21`;国内下载慢可给 JVM 挂本机代理
|
||
(Clash 7890:`GRADLE_OPTS="-Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=7890 ..."`)
|
||
- 工程版本目录 `gradle\libs.versions.toml`
|
||
(AGP 8.11.1 / Kotlin 2.2.0 / Compose BOM 2025.06.01 / lifecycle 2.9.1 / junit 4.13.2)。
|
||
- **磁盘轻量约定**:不装 NDK/CMake/zig(曾占用 2.4GB+,已卸载);全 Kotlin。
|
||
- 模拟器实测命令(无窗口):
|
||
```powershell
|
||
Start-Process "$env:LOCALAPPDATA\Android\Sdk\emulator\emulator.exe" -ArgumentList @('-avd','Medium_Phone_API_36.1','-no-window','-no-audio','-gpu','swiftshader_indirect') -WindowStyle Hidden
|
||
# 安装/启动/截图:
|
||
adb -s emulator-5554 install -r build-artifacts\mag160c-app-debug.apk
|
||
adb -s emulator-5554 shell am force-stop com.mag160c.thermal
|
||
adb -s emulator-5554 shell am start -n com.mag160c.thermal/.MainActivity
|
||
adb -s emulator-5554 exec-out screencap -p > analysis\preview\xxx.png # 注意:exec-out 才二进制安全
|
||
adb -s emulator-5554 emu rotate # 旋转模拟器(每次90°)
|
||
```
|
||
|
||
## 3. 源码结构与核心模块
|
||
|
||
```
|
||
android/app/src/main/kotlin/com/mag160c/thermal/
|
||
├─ MainActivity.kt 启动,enableEdgeToEdge + 隐藏状态栏(沉浸)
|
||
├─ ui/AppRoot.kt App壳:底部导航(实时/相册/分析/设置)恒贴竖屏底边(Activity 竖屏锁定)
|
||
├─ ui/DeviceOrientation.kt 加速度计→手机物理朝向(条栏/OSD 文字补偿旋转用)
|
||
├─ ui/UiInsets.kt 单例:底部导航高度 px(渲染器用它留白)
|
||
├─ ui/theme/Theme.kt M3 动态取色(Android12+)
|
||
├─ ui/live/ ★实时画面(最核心)
|
||
│ ├─ LiveViewModel.kt 状态机/多点测温/追踪开关/拍照/录像/inset上报/PIP状态/
|
||
│ │ 远程服务端开关+rawHook→RemoteHost
|
||
│ ├─ LiveScreen.kt 稳定单分支相机布局:顶栏5控制项(FFC/变倍/追踪/调色板/画中画)
|
||
│ │ +底部快门区(相册/拍照/录像)+SurfaceView+PIP浮层
|
||
│ ├─ PipCameraView.kt 可见光PIP相机引擎(Camera2最小实现,异常只记日志)
|
||
│ ├─ ImageTransform.kt 方向/翻转/letterbox/缩放几何 + 传感器↔屏幕互逆映射(纯 Kotlin,单测覆盖)
|
||
│ └─ LiveRenderer.kt SurfaceView 软件渲染:图像按握持角补偿旋转(rot=90-φ)、
|
||
│ letterbox+变倍、色标条/圆环标记/中心温OSD(标签按旋转包围盒定位)
|
||
├─ ui/gallery/ 相册页(MediaStore DCIM/MAG160C 扫描 + 内嵌JPEG缩略图 + 运行时媒体权限)
|
||
├─ ui/analyze/ MDT 离线分析(缩放/调色板重渲染/温度条+点击测温/备注回写/PDF报告)
|
||
├─ ui/settings/ 设置页(默认调色板/发射率/报警温度/语言/云同步/远程预览两行/关于)
|
||
├─ ui/remote/ 远程预览客户端(Phase F)
|
||
│ ├─ RemoteClientListScreen.kt 主机列表(UDP发现/手动IP/重新扫描)
|
||
│ ├─ RemoteViewerViewModel.kt 本地管线渲染远端原始帧(调色板/变倍本地化)
|
||
│ ├─ RemoteViewerScreen.kt 查看页(与实时页同构图,快门区换成红色"断开")
|
||
│ └─ RemoteRendererHost.kt 与 LiveRenderer 同构图的渲染器(LiveRenderer 已冻结)
|
||
├─ net/ 网络互连(Phase F,纯 java.net,可 JVM 单测)
|
||
│ ├─ RemoteContract.kt 端口/魔数/JSON data/帧封包与流式重组 FramePacketReader
|
||
│ ├─ RemoteHost.kt 服务端:UDP 47510 广播 + TCP 47511 单客户端流
|
||
│ └─ RemoteClient.kt 客户端:发现(去重3s) + 连接/行模式→帧模式/10s判死
|
||
├─ cloud/CloudApi.kt 云接口占位(Retrofit,opt-in 默认关闭,api() 未开启即抛异常)
|
||
├─ media/
|
||
│ ├─ Mdt.kt MDT 容器格式(JPG + DDT段[info块+原始帧] + 152B Tail)+ parse
|
||
│ ├─ PhotoSaver.kt MediaStore 保存(DCIM/MAG160C,无权限也可写自有文件)
|
||
│ ├─ Mp4Recorder.kt MP4录像(Surface 输入 H.264,MediaCodec+MediaMuxer)
|
||
│ ├─ DebugLog.kt 现场调试日志(落盘 DCIM→Download→私有目录 + logcat 镜像)
|
||
│ └─ PdfReport.kt PDF 巡检报告(PdfDocument)
|
||
├─ usb/
|
||
│ ├─ UsbTransport.kt UsbManager 枚举(VID 0x833C)/权限/claim/EP
|
||
│ ├─ MagProtocol.kt 命令码 0x6BB6B6xx(小端)/响应 0x5BB5B5xx
|
||
│ └─ IrSession.kt 连接/启动流/读线程→FrameStream→RenderPipeline→回调;
|
||
│ FFC回调;rawHook(远程预览)/recorderHook(录像);
|
||
│ deviceLifetimeMs(675);cali缓存MD5对照
|
||
├─ core/
|
||
│ ├─ OfficialTables.kt 自动生成:PALETTE256_ARGB(官方铁虹)/T2E(646)/T2E274/E2T
|
||
│ ├─ VendorPalettes.kt 官方运行时时序生成器的移植结果(11/12 精确表,生成物)
|
||
│ ├─ Palettes.kt 12调色板(0..10=官方精确表,11 红热=近似)
|
||
│ ├─ RenderPipeline.kt ★官方渲染管线 Kotlin 移植(与C参考逐字节一致)
|
||
│ ├─ TempMath.kt 温度换算(counts→毫度,T2E)+ tempMapFromPixels(MDT温度图)
|
||
│ └─ FrameStream.kt 流帧重组(0x1BB1B11B 搜索)
|
||
└─ task/TaskParser.kt 任务巡检 sqlite/xml 解析
|
||
assets/mag160c.ddt 官方 DDT 标定文件(1.8MB,渲染必需)
|
||
res/drawable/*.xml 自绘矢量图标(双弧圆等可靠几何图形)
|
||
```
|
||
|
||
## 4. 核心知识(必须知道)
|
||
|
||
### 4.1 渲染管线(已逐字节验证)
|
||
- `core/RenderPipeline.kt` 移植自 `csdk/src/mag160c_render.c`(官方管线,像素级验证)。
|
||
- 输入:USB 帧(0x38 头 + 38400B u16 小端 + 尾部);输出 320×240 ARGB。
|
||
- FFC 状态机:暖机20帧内暂停;第75帧强制FFC;FFC(0)→5隐藏→FFC(1)→4参考帧→4隐藏→正常。
|
||
- **移植陷阱(已解决,改代码时务必保持)**:
|
||
1. C 的 32 位无符号回绕:所有 `u32()` 掩码必须保留(cdf*denom、0xffc0000-iv7*0x40000、(v-lo)*S 等)。
|
||
2. lutRebuild 里 `u12` 的基址是常量 `iv7*0x10+0x10`,**不是**链式 u21v。
|
||
3. ByteArray 存 255=-1:判 0xFF 必须 `(b.toInt() and 0xFF)`。
|
||
4. 暖机窗口检查在 ffc_step **之前**(顺序不能改)。
|
||
- 验证方式:`android/app/src/test/.../RenderPipelineTest.kt`(JVM 差分测试,参照 `analysis/render_offline.c` 的 C 输出,60帧序列与官方DDT)。
|
||
|
||
### 4.2 USB 协议(权威版:analysis/magcx_official_flow.md)
|
||
- **2026-09-10 已反编译官方普通版全量源码**(jadx:`analysis/sdk_re/android_app/jadx_magcx/`,
|
||
UsbCommunication.java 是 USB 层真身;Ghidra:libcxsdk 全量伪代码
|
||
`analysis/sdk_re/android_app/libcxsdk_decomp.txt`,1290 函数)。
|
||
- 协议要点:命令 4 字节**小端**;超时全线 800ms;序列
|
||
GetParameter1(66b)→GetParameter2(66c)→GetCaliInfo(66f)→[缓存缺失]
|
||
GetCaliFile(670)+EP 0x84 拉 16KB 块→StartTransferImg(673);FFC=672(帧驱动)。
|
||
- 官方响应格式:0x5BB5B55B BasePara1(60B,含 serial/devType/宽/高/fps)。
|
||
- 旧 analysis/protocol_spec.md 的 66f/670 描述("prepare/version query")不准,
|
||
以 magcx_official_flow.md 为准。
|
||
|
||
### 4.3 实时画面渲染(2026-09-11 修订:图像内容随握持朝向补偿)
|
||
|
||
- **Activity 锁定竖屏**(manifest `screenOrientation="portrait"`):屏幕相对手机框架
|
||
永不旋转。顶栏、热像区域、底栏的**绝对位置**永远贴着手机竖屏的物理顶边(挖孔侧)、
|
||
中间、物理底边;手机怎么物理旋转,构图都不动。**不要改回 fullSensor。**
|
||
- **图像内容补偿旋转(本轮修订,勿回退)**:构图不动,但**画面内容**必须随握持朝向
|
||
反向补偿,否则转身后场景跟着转、与镜头实际指向脱节。规则:
|
||
`rot = 90 - φ`(φ = DeviceOrientation 握持角 0/90/180/270)。
|
||
依据官方实现:`MainActivity.windowOrientationListener` 把 Display.rotation
|
||
(0/1/2/3) 映射成图像 90/0/270/180,再经 `ImageViewer.drawImage` 的
|
||
`matrix.postRotate` 应用;官方**窗口会随传感器旋转**,故可见结果恒为 90。
|
||
我们锁了窗口,图像就要承担全部差值 → `90 - φ`。
|
||
显示旋转索引→角度约定取自官方自家相机代码
|
||
(`VisibleCameraHelper.setPreviewOrientation`:case 0→0/1→90/2→180/3→270,
|
||
即索引 N = 顺时针 N×90)。
|
||
- **手工修正项**(官方"旋转USB画面/水平翻转/竖直翻转"同款):设置页三项,
|
||
叠加在自动补偿之上,持久化于 `AppSettings.imageRotateDeg/imageFlipH/imageFlipV`,
|
||
用于传感器安装方向特殊的机器。翻转在像素拷贝阶段完成,保证几何映射可测。
|
||
- **图标/文字按握持角补偿**:`graphicsLayer rotationZ=-φ`(Compose)/
|
||
`canvas.rotate(-φ)`(Canvas)。**标签一律按旋转后包围盒定位**(
|
||
`ImageTransform.rotatedBoxHalfExtents` + `drawGripText`),不要再拿基线锚点
|
||
摆位——色标条两端数字曾因此与色条错位。
|
||
- **几何单一来源**:`ui/live/ImageTransform.kt`(纯 Kotlin,可单测)同时供
|
||
渲染器与点击/探针映射使用(`sensorToScreen` / `screenToSensor` 互为逆映射,
|
||
已对 0/90/180/270 × 翻转组合做往返测试)。渲染与取温映射**必须**用同一套参数,
|
||
否则标记会落到错误像素上。
|
||
- **加速度计符号约定(第七轮教训,勿改回)**:真机 TYPE_ACCELEROMETER 静止读数
|
||
指向世界上方(竖屏正持 y=+9.81);模拟器虚拟传感器是反的重力约定(y=-9.81)。
|
||
DeviceOrientation 映射按**真机约定**写,模拟器测试须用反号值驱动
|
||
(φ=0→`adb emu sensor set acceleration 0:9.81:0`)。
|
||
- **追踪开关控制最高+最低两个标记**(此前只关最高,最低永远画着)。
|
||
- 顶栏=5 个控制项(FFC / 变倍×N / 追踪·开 / 调色板+名称 / 画中画),恒在顶部并带
|
||
小字标签,`safeDrawing` 顶部 inset 适配挖孔屏。
|
||
- 底部(仅实时页)导航栏上方为**相机快门区**:相册快捷入口 / 大快门拍照 /
|
||
录像-停止;快门区高度并入 `vm.uiBottomPx`(= 导航高+快门区高)。
|
||
- 顶栏高度经 `onSizeChanged`→`vm.uiTopPx`(**必须挂在 safeDrawing inset 之前**,
|
||
即包含挖孔安全区高度,否则真机热像顶端会插进顶栏背后);底导航高度经
|
||
`UiInsets.navPx`、快门区高度经其 `onSizeChanged`→`vm.uiBottomPx`(= 导航+快门区),
|
||
渲染器据此留白。
|
||
- **演示模式已撤下**(2026-09-09 用户要求,接真机实测显示,勿加回来):无设备时
|
||
`connect()` 置 `status="no_device"`,LiveScreen 显示占位文案"未检测到热像仪,
|
||
请插入MAG160C",渲染器无帧黑底;startDemo/合成帧代码已删除。
|
||
|
||
### 4.3.1 温度显示(2026-09-11 修复三个真机缺陷)
|
||
|
||
- **中心温度**:`probeTemp()` 返回的**已经是毫度**,界面只许除以 1000 一次。
|
||
旧代码又调了一次 `countsToTempMc()`,真机显示 108.7℃(实际约 24℃)。
|
||
- **FFC 期间的温度跳变**:FFC 参考帧过去被解码进 `nuc`(OSD 采样的同一缓冲区),
|
||
未补偿的原始 counts 直接参与显示 → 最高/最低短暂跳到 ~150℃。
|
||
现在参考帧走独立 `refScratch` 缓冲。
|
||
- **上电/未就绪**:首帧渲染前 `nuc` 全 0,而 `countsToTempMc(0) = -161.0℃`
|
||
(看着像合法读数)。`RenderPipeline.tempsReady()` / `IrSession.tempsReady()`
|
||
门控 UI 取温;远程页同样门控。
|
||
- 回归测试:`core/PipelineTemperatureStateTest.kt`。
|
||
|
||
### 4.4 温度
|
||
- 温度 = 毫度 int(÷1000 = ℃)。`counts_to_temp_mc`(NUC域→T2E逆映射)用于探针/OSD,
|
||
在真机上需按 DDT 标定核对绝对值(待办)。
|
||
- 离线 MDT 温度解码(ConvertResponse2Temperature + 标定参数)未实现(待真机文件对照)。
|
||
|
||
## 5. 构建 / 测试 / 提交
|
||
|
||
```powershell
|
||
# 构建 + 单元测试(无 --daemon 单次,约1分钟;首次在本机需下载依赖较久)
|
||
cd C:\Project\MAG160C\android
|
||
$env:JAVA_HOME="C:\Tools\jdk-21"
|
||
.\gradlew.bat :app:assembleDebug test --no-daemon
|
||
# 产物
|
||
Copy-Item app\build\outputs\apk\debug\app-debug.apk C:\Project\MAG160C\build-artifacts\mag160c-app-debug.apk
|
||
# 提交(每里程碑一个 commit,不 push)
|
||
git -C C:\Project\MAG160C add -A
|
||
git -C C:\Project\MAG160C commit -m "android: ..."
|
||
```
|
||
|
||
## 6. 严重教训(反复踩坑,务必遵守)
|
||
|
||
1. **编码**:所有源码必须 UTF-8。本机默认编码是 GBK!
|
||
- **python 读写文件必须显式 `encoding='utf-8'`**(`open(p,'w')` 裸写会变 GBK→编译出的中文全乱码)。
|
||
- 检测/修复脚本:`C:\Users\ZXC\AppData\Local\Temp\opencode\fix_encoding.py`、
|
||
`check_mojibake.py`。改完必须跑一遍确认全 OK,再构建。
|
||
- gradle.properties 已有 `-Dfile.encoding=UTF-8`(gradle + kotlin daemon)。
|
||
2. **图标**:手写 pathData 的弧线易坏,一律用双弧圆 `M x,y a r,r 0 1,0 2r,0 a r,r 0 1,0 -2r,0z` + 直线/矩形。
|
||
3. **SurfaceView 与旋转**:不要在横竖屏间切换不同布局分支(SurfaceView 会被销毁且 surface 不重建)。
|
||
用"稳定单分支 + 覆盖层"布局(当前实现即如此)。
|
||
4. **写代码要谨慎**:本会话多次因急于成稿写出语法错误/残留死代码。每写一个文件后立即编译。
|
||
5. **ByteBuffer.putInt 默认大端**:USB 命令必须小端(官方 intToByteArray)。
|
||
16 轮黑屏调试的根因就是它(66b 回文数掩盖了字节反转),MagProtocolTest 已锁死。
|
||
6. **官方 App 源码已入库,先读它再写协议代码**:analysis/sdk_re/android_app/jadx_magcx/(Java)、
|
||
analysis/sdk_re/android_app/libcxsdk_decomp.txt(native)、
|
||
analysis/magcx_official_flow.md(权威结论)。不要凭旧文档猜协议。
|
||
|
||
## 7. 当前状态与待办
|
||
|
||
**已完成**:核心管线移植(字节级验证)、USB 层、实时画面(相机式布局:
|
||
顶栏5控制项 FFC/变倍/追踪/调色板/画中画 + 底部快门区 相册/拍照/录像 + 底导航)、
|
||
图标文字按物理持机朝向补偿旋转(加速度计)、拍照 MDT 容器→MediaStore、
|
||
MP4 录像、媒体库、离线分析(含温度条+点击测温)、PDF 报告、任务解析、设置页、
|
||
Android16 沉浸(状态栏隐藏+竖屏锁定+挖孔安全区)、乱码多次修复。
|
||
(演示模式 2026-09-09 按用户要求撤下,见 §4.3。)
|
||
|
||
**执行计划 2026-09-10 七阶段全部落地**(docs/android_app/execution_plan.md):
|
||
- Phase A:GetLifeTime(675) 查询→`deviceLifetimeMs` + 心跳 stats 带 lifetime;
|
||
cali 缓存与内置 DDT 的 MD5 一致性日志;真机自检清单
|
||
`docs/android_app/real_device_checklist.md`(24 步,含预期日志行)。
|
||
- Phase B:`Mdt.parse` 返回 `MdtFile`(jpg 按 FFD9 裁尾、text 去 NUL 填充、
|
||
framePixels 齐全);`TempMath.tempMapFromPixels` 毫度图;分析页温度条
|
||
(中心/最低/最高)+ 点击测温探针。**注意:分析页图像按原生横向绘制,
|
||
探针映射是直接映射,不要照抄实时页的 90° 逆映射。**
|
||
- Phase C:厂商调色板——结论是 **libcxsdk.so 无静态表**,全部由
|
||
`CFunctions::SetColorPalette` 运行时算术生成;移植该函数后 11/12 个表
|
||
已达官方精确(0..10,铁虹与官方表 256/256 一致),索引 11 红热保留近似。
|
||
详见 `analysis/sdk_re/android_app/palette_extraction_findings.md`。
|
||
- Phase D:云模块占位(Retrofit 2.11.0,`AppSettings.cloudEnabled` 默认 false,
|
||
`CloudClient.api()` 未开启即 check 抛异常——**不允许静默联网**)。
|
||
- Phase E:可见光 PIP(Camera2 最小实现,三档尺寸、可拖动、双击关闭;
|
||
相机任何异常只记 `[pip]` 日志并收窗,绝不影响热像主画面)。
|
||
- Phase F:局域网远程预览(UDP 47510 发现 + TCP 47511 控制与原始帧流,
|
||
**客户端本地管线渲染**,调色板/变倍不过网;单测含真实 TCP loopback 端到端)。
|
||
|
||
**布局约定(最终,勿回退)**:
|
||
- Activity **锁定竖屏**(`portrait`),构图钉死手机竖屏框架:顶栏/热像区域/快门区/底导航
|
||
绝对位置永不随手机物理旋转变化(屏显方向永远对应镜头方向)。
|
||
- 图标/文字持机朝向补偿:`ui/DeviceOrientation`(加速度计,真机约定 y=+9.81=0°;
|
||
模拟器是反的重力约定)→ `rotationZ=-φ`(Compose)/`canvas.rotate(-φ)`(Canvas)。
|
||
- 顶栏 `uiTopPx` 必须挂在 `safeDrawing` inset **之前**(包含挖孔安全区,否则真机热像
|
||
顶端被顶栏盖住);`uiBottomPx` = 底导航高 + 快门区高。
|
||
|
||
**禁改清单**(详见 execution_plan.md §0,改前先读):Manifest 的
|
||
`screenOrientation="portrait"`、`MagProtocol` 命令字节序(MagProtocolTest 锁定)、
|
||
`IrSession` 握手序列、`LiveRenderer` 构图逻辑、`analysis/` 目录。
|
||
|
||
**待办**:
|
||
1. 真机 USB 实测:温度绝对值标定、FFC/录像/MDT 保存端到端——照
|
||
`docs/android_app/real_device_checklist.md` 24 步走。
|
||
2. 红热调色板(索引 11)精确表:官方 APK 的预览图是占位副本,需真机抓帧反推。
|
||
3. 双机远程预览实测(清单第 16-24 步)。
|
||
4. 云服务真实接口对接(当前仅占位,需账号与接口文档)。
|
||
|
||
## 8. 文件路径速查
|
||
|
||
- 心跳/状态:`docs/android_app/session_state.md`
|
||
- 逆向功能总表:`docs/android_app/reverse_apk_features.md`
|
||
- USB 协议/温度算法:`analysis/protocol_spec.md`
|
||
- C 参考实现(Kotlin 管线的蓝本):`csdk/src/mag160c_render.c`、`mag160c_temp.c`、`mag160c_ir.c`
|
||
- PC 参考工具:`analysis/render_offline.c`、`analysis/gen_kotlin_tables.py`
|
||
- 预览截图:`analysis/preview/preview_*.png`
|
||
- APK 产物:`build-artifacts/mag160c-app-debug.apk`
|
||
|
||
## 9. 下一轮提示词(交付新模型直接用)
|
||
|
||
> 你是本项目的续任开发者。请先完整阅读 `C:\Project\MAG160C\docs\android_app\HANDOFF_DEVELOPMENT.md`
|
||
> 和 `C:\Project\MAG160C\docs\android_app\session_state.md`、`docs\android_app\reverse_apk_features.md`、
|
||
> `C:\Project\MAG160C\analysis\protocol_spec.md`,再继续开发。
|
||
> 项目根 `C:\Project\MAG160C` 是 git 仓库,源码在 `android\`(Gradle 工程,package
|
||
> `com.mag160c.thermal`)。Android 16 已适配,目标是一个 MAG160C 热像仪(160×120, 15fps,
|
||
> USB VID 0x833C PID 1)统一 APP。
|
||
>
|
||
> 当前已完成到第 10 轮反馈修复:核心渲染管线 Kotlin 移植(逐字节验证)、USB 层、
|
||
> 相机式实时布局(顶栏4控制项+底部快门区+底导航)、图标文字持机朝向补偿旋转、
|
||
> 拍照MDT存MediaStore、MP4录像、媒体库、离线分析、PDF报告、任务解析、设置页、
|
||
> Android16沉浸(竖屏锁定+挖孔适配)。
|
||
> 第 10 轮(2026-09-09):按用户要求撤下演示模式合成热像图(无设备只显示
|
||
> "未检测到热像仪"占位),接真机+热像仪实测显示。
|
||
>
|
||
> 硬性约定(勿回退):① Activity 竖屏锁定 `portrait`,构图钉死手机竖屏框架,
|
||
> 屏显方向必须始终对应镜头方向;图标/文字用 `ui/DeviceOrientation`(加速度计,
|
||
> 真机约定 y=+9.81=0°)补偿旋转。② 源码全 UTF-8;python 读写必须显式
|
||
> `encoding='utf-8'`(本机默认 GBK,裸写会乱码)。③ 图标用双弧圆+直线/矩形。
|
||
>
|
||
> 下一步建议按 session_state.md「待办」推进:
|
||
> 1. 真机 USB 实测——温度绝对值标定(对 DDT 核对 counts→毫度);
|
||
> 2. 网络互连远程预览——UDP 广播自动发现 + 手动内网 IP,主机手机插热像仪、
|
||
> 局域网另一台手机远程实时预览(自定义轻量协议:控制通道 JSON + 图像流,
|
||
> 不走厂商 33596/33597);
|
||
> 3. 离线 MDT 温度解码(ConvertResponse2Temperature + 标定参数);
|
||
> 4. 厂商 12 调色板精确提取;5. 可见光 PIP 融合、云模块(Retrofit opt-in 预留)。
|
||
>
|
||
> 构建/提交:
|
||
> ```powershell
|
||
> cd C:\Project\MAG160C\android; $env:JAVA_HOME="C:\Tools\jdk-21"
|
||
> .\gradlew.bat :app:assembleDebug test --no-daemon
|
||
> git -C C:\Project\MAG160C add -A; git -C C:\Project\MAG160C commit -m "android: ..."
|
||
> ```
|
||
> 环境(2026-09-09 本机重装):JDK Temurin 21 `C:\Tools\jdk-21`、SDK `C:\Tools\android-sdk`
|
||
> (无模拟器/AVD)、Gradle 用工程 wrapper。注意:git 工作树里有大量与本任务无关的脏文件
|
||
> (`.tools/`、`IR_Camera_SDK/`、`csdk/`、`analysis/` 等),**不要提交它们**,只提交你的改动。 |