android+docs: debug 日志默认关闭;操作手册升级为插图版

debug 日志:DebugLog 增加总开关 enabled(默认 false),log() 直接返回、
init() 不装崩溃钩子、startFile() 不建文件。要在设备上排查时改成 true 重新构建
即可,其它代码不动。关闭后崩溃仍按安卓常规方式可见(logcat AndroidRuntime +
系统弹窗),只是不再有落在相册目录旁的 debug_*.log。

真机烟雾测试(小米 22041211AC,相机插着):出流正常、拍照成功(相册 19→20,
新增 MAG160C_20260912_153522.jpg)、崩溃 0、App 自身日志行数 0、无新日志文件。

插图版手册 docs/android_app/app_manual.md + manual_images/:
- 02_markers:标记结构解剖(准星/读数/引线/描边字),真机照片放大 3 倍并标注
- 03/04:横屏 960x720 与竖屏 720x960 的真实照片
- 05/06/07:设置、分析(含底部测量面板)、相册 真机截图
- 01_live_schematic:实时界面改为**自绘示意图**,因为实时 OSD 文字按握持角度预旋转
  (用户要求的可读性设计),手册拍摄时手机横放,截图里文字全是侧躺的无法阅读;
  其余界面文字本就水平,直接用截图。

图由 manual_images/MakeFigures.java 生成(裁剪缩放/放大标注/示意图绘制),
界面改动后重跑即可;原始截图存为 JPEG(PNG→JPEG 省约 8MB)作为可再生输入。
.class 已加入 .gitignore。

README 阅读顺序加入操作手册、结构表更新、里程碑补记根因级修复。105 项测试全绿。
This commit is contained in:
ZXCLI
2026-09-12 15:36:34 +08:00
parent 6ba90f2bb6
commit ee6859877a
20 changed files with 459 additions and 70 deletions
@@ -12,8 +12,10 @@ import com.mag160c.thermal.ui.theme.Mag160cTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// field-debug crash hook + log file (opened at startup so even
// early crashes are captured; logging never throws)
// Field-debug logging is OFF by default (see DebugLog.enabled): a shipping
// build writes no log file and no logcat lines. Flip that flag and rebuild to
// capture a detailed trace on a device — these two calls then behave exactly
// as they used to.
com.mag160c.thermal.media.DebugLog.init(applicationContext)
com.mag160c.thermal.media.DebugLog.startFile(applicationContext)
// enableEdgeToEdge exists since API 21 but reaches for the modern inset
@@ -16,8 +16,20 @@ import java.util.Locale
/**
* Field-debug logger: mirrors every line to logcat AND appends it to a
* timestamped text file next to the photos (DCIM/MAG160C), so the user can
* hand over the file without adb (real-device bring-up, round 11).
* timestamped text file next to the photos (DCIM/MAG160C), so a problem on the
* user's phone can be diagnosed without adb.
*
* ## It is OFF by default (user decision, 2026-09-12)
*
* A shipping build has no business writing a log file beside the user's photos or
* filling logcat at 15 frames per second, so [enabled] defaults to false and every
* entry point returns immediately. To investigate something on a device, flip
* [enabled] to true and rebuild — that is the whole procedure; the file then
* appears exactly as it used to.
*
* With logging off, crashes are still reported the normal Android way (logcat
* `AndroidRuntime` plus the system's "app keeps stopping" dialog); only the
* convenient on-device copy is gone.
*
* Logging must NEVER crash the app: every sink operation is guarded, and
* writes are capped. Sink fallback chain (scoped storage only allows media
@@ -28,6 +40,13 @@ import java.util.Locale
* The header line records where the file actually ended up.
*/
object DebugLog {
/**
* Master switch. OFF in shipping builds: writes nothing, to no sink.
* Set to true (and rebuild) to capture a detailed trace on a device.
*/
@Volatile
var enabled: Boolean = false
private val fmt = SimpleDateFormat("HH:mm:ss.SSS", Locale.ENGLISH)
private val fileFmt = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH)
private val lock = Any()
@@ -38,6 +57,7 @@ object DebugLog {
/** Install once from MainActivity.onCreate: crash hook + app context. */
fun init(context: Context) {
if (!enabled) return
synchronized(lock) {
if (previousHandler != null) return
previousHandler = Thread.getDefaultUncaughtExceptionHandler()
@@ -51,6 +71,7 @@ object DebugLog {
/** Log one line (also to logcat as MAG160C/<tag>). Never throws. */
fun log(tag: String, msg: String) {
if (!enabled) return
try {
Log.d("MAG160C/$tag", msg)
} catch (_: Exception) {
@@ -80,9 +101,10 @@ object DebugLog {
/**
* Open a fresh debug_*.log. Called once at app start (so even startup
* crashes are captured); never throws. Returns the display path for
* logging. Closes any previous file.
* logging. Closes any previous file. Does nothing when [enabled] is false.
*/
fun startFile(context: Context): String {
if (!enabled) return "(debug log disabled)"
val name = "debug_${fileFmt.format(Date())}.log"
closeFile()
synchronized(lock) {