Skip to content

feat: add immersive mode and report image saving - #653

Open
PiscesXD wants to merge 9 commits into
mainfrom
feat/map-immersive-mode
Open

PiscesXD wants to merge 9 commits into
mainfrom
feat/map-immersive-mode

Conversation

@PiscesXD

@PiscesXD PiscesXD commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

這個 PR 做了什麼

地圖分頁與地震重播新增「沉浸式」模式:右上角一顆按鈕隱藏地圖的 UI 與底部導覽列,只留下地圖;退出鈕固定在右上角,Android 返回鍵也會先離開模式。進出都有 220ms 的淡出/滑入轉場。另外把 MapLibre 原生右下角的 attribution(i)按鈕移出畫面,授權文字仍留在「更多 → 資料來源」。

這支分支另外帶了兩個獨立的小改動:地震報告圖可以儲存到相簿(含權限流程),以及地圖圖示改依螢幕密度計算大小。兩者與沉浸模式無關,各自是一則 commit。

一、沉浸式模式

正式程式碼

  • lib/shared/map/map_immersive.dart:沉浸狀態控制器,以及兩個地圖頁共用的 220ms/easeOutCubic 轉場常數。地圖分頁用 app-wide 實例(shell 要同步收合底部導覽列);重播用 page-scoped 實例,pop 後不影響地圖分頁。
  • lib/features/map/map_providers.dart、lib/bootstrap.dart:註冊地圖分頁的 MapImmersiveController。
  • lib/shared/map/map_scaffold.dart:圖例、右上控制列、指南針、時間軸在沉浸時淡出/滑出;時間軸保持 mounted(它有 GlobalKey 量測),隱藏時關掉 hit test/semantics/focus;退出 chip 畫在最上層,圖層 detail sheet 拉滿也蓋不住;PopScope 讓返回鍵先退出模式。sheet 圖層的底部 inset 也會跟著導覽列收起,停在系統安全區上方 AppSpacing.lg。
  • lib/shared/map/map_immersive_chrome.dart:把淡出/滑動與退出 chip 抽成 MapImmersiveChrome / MapImmersiveExitChip,兩個地圖頁共用。
  • lib/app/shell/main_shell.dart:沉浸時底部導覽列用同一個時間/曲線滑出;地圖分頁與資料分支(重播)都適用,Home 的 sheet 拖曳仍直接跟隨。
  • lib/features/earthquake/presentation/pages/report_replay_page.dart:重播頁的左上、底部、右上控制都套同一套轉場;改用 app-wide MapImmersiveController,進入時重置、離開時退出。
  • lib/shared/map/base_map.dart:MapLibre attribution(i)ornament 以負 margin 推出平台視圖,並在 iOS 著透明色。
  • 12 個 ARB 語系新增 mapImmersive / mapImmersiveExit。

測試

  • test/features/map/presentation/map_scaffold_test.dart:沉浸時圖例/圖層 chip/時間軸不可點、退出 chip 出現;110ms 檢查時間軸已位移、導覽列已部分移動,220ms 後完成;退出 chip 在拉滿的 detail sheet 之上仍可點;系統返回先退出模式;attribution 參數有鎖住;sheet 圖層下移 40px。
  • test/app/shell/main_shell_test.dart:導覽列在 110ms 只移動一部分,300ms 後才完全離開;資料分支(重播)設定沉浸時也會收合。
  • test/features/earthquake/presentation/pages/report_replay_page_test.dart:進入後返回/圖例/底圖選單不可點、退出 chip 出現;返回鍵留在重播頁而不是 pop;harness 補上 app-wide controller。
  • test/shared/map/map_layer_switcher_test.dart、test/features/map/presentation/pages/map_page_test.dart:provider 與 API 調整。

二、地震報告圖儲存到相簿

地震報告圖右上角新增儲存按鈕(圖片載入成功才出現;儲存中顯示進度並忽略重複點擊)。檔名 dpip-earthquake-<report id>.png,同名不覆蓋,由平台加 (1)、 (2)。

先確認權限才寫檔,而且依平台只問該問的:

  • Android 10+:MediaStore 寫入 Pictures/DPIP,IS_PENDING 插入、位元組寫完才發布,不需要任何權限。
  • Android 8–9:公開 Pictures 目錄 + WRITE_EXTERNAL_STORAGE(manifest 宣告 maxSdkVersion="28")。由原生 status 判斷是否真的需要,需要才向使用者要;已永久拒絕時不再彈窗。
  • iOS:PHPhotoLibrary add-only 授權,status / request 由 plugin 回答(需要 NSPhotoLibraryAddUsageDescription)。

失敗分兩種說法:權限被拒的 SnackBar 直接給「開啟設定」,寫入失敗則是一般的錯誤訊息。

正式程式碼

  • lib/core/platform/image_saver.dart:跨平台邊界,ensurePermission() 在寫檔前先取得授權,saveToGallery() 才交給平台;錯誤分類為 ImageSaveFailure(lib/core/error/failure.dart)。
  • android/app/src/main/kotlin/com/exptech/dpip/ImageSaverChannel.kt:MediaStore 與舊版公開目錄兩條路徑、status,以及 MainActivity 的註冊、manifest 權限。
  • ios/Runner/ImageSaverPlugin.swift:PHPhotoLibrary add-only 的 status / request / save,已登記進 project.pbxproj,Info.plist 補上用途說明。
  • lib/features/earthquake/presentation/pages/report_detail_page.dart:儲存按鈕、檔名、SnackBar 與「開啟設定」。
  • 4 個新字串 × 12 個語系:reportDetailImageSave / Saved / SaveFailed / SavePermissionDenied。

測試

  • test/core/platform/image_saver_test.dart:iOS 的 status → request → save 順序;拒絕時不會寫檔;Android 10+ 完全不碰權限;Android 8–9 只問一次;永久拒絕不彈窗;寫入失敗與權限失敗分開分類;沒有 plugin 時回 unavailable。
  • test/features/earthquake/presentation/pages/report_detail_page_test.dart:按鈕存在、檔名正確、成功與權限被拒兩種 SnackBar,「開啟設定」只在後者出現。

三、地圖圖示改依螢幕密度計算大小

問題:addImage 沒有 pixelRatio 參數,圖示大小由「平台用什麼密度解這張 PNG」決定,而兩邊不同——iOS 用 UIScreen.main.scale,Android 的 plugin 用 inDensity = 0,MapLibre 再以 density/160 得到 pixelRatio 1.0。同一張 64px badge 因此在 Android 上大約是 iPhone 3x 的三倍大,1x 裝置(回報來源是一台 540×960 模擬器)最誇張。

修法:lib/shared/map/map_image_density.dart 提供 mapIconDensityFactor() 與 intensityIconSizeCurve(),以 3x iPhone 的大小為基準,把原本的像素曲線換算成 dp/pt;套用到測站 badge、EEW 震央叉(96px 圖),以及報告圖上的震度標記。圓點本來就是 dp,不受影響。

  • test/shared/map/map_image_density_test.dart:各平台/密度的 factor、曲線縮放,以及「三種平台組合渲染出同一個點數」的性質測試。

相關 issue

怎麼驗

  • tool/check.sh:all gates passed,3,701 項測試通過(新增 14 項:7 個 ImageSaver、6 個圖示密度、1 個報告頁 widget 測試),format + analyze、l10n、layering、storage、codegen 等 gates 全過。
  • tool/check/commits.sh origin/main..HEAD:9 則 commit 全部通過格式 gate,0 behind origin/main。
  • tool/dev/analyze.sh:無問題。
  • 原生建置:tool/dev/build.sh android-debug 與 tool/dev/build.sh ios --debug 都成功——新增的 Kotlin/Swift channel 兩端都編得過(iOS 的 ImageSaverPlugin.swift 已加進 project.pbxproj,否則 AppDelegate 會編不過)。
  • 實機:尚未 在 iOS/Android 實機驗過。需要特別看兩件事——(1) 存圖在 Android 10+ 與 8–9、iOS 上的權限流程與相簿結果;(2) 圖示大小在回報的 540×960(1x)模擬器與一台 3x 手機上是否一致。attribution 的負 margin 也仍待實機確認(需 hot restart/重新 build)。
  • 未動 OS 狀態列/系統導覽列:沉浸只收 app 自己的 chrome 與 shell 的底部導覽列。
  • 已知取捨:attribution 按鈕是 workaround(目前 pin 的 maplibre_gl fork 沒有 attributionButtonEnabled);圖示密度的修正也是因為 fork 的 addImage 沒有 pixelRatio 參數,若上游補上,map_image_density.dart 可以整支拿掉。
  • UI 截圖:待補(淺色/深色各一組)。

檢查清單

  • tool/check/commits.sh origin/main..HEAD 通過
  • 一個 commit 一件事(沉浸模式、attribution、轉場、重播、存圖、圖示密度各自獨立)
  • tool/dev/analyze.sh 與 tool/check.sh 通過
  • 新的使用者可見字串都走 AppLocalizations,沒有寫死
  • 新原生程式碼兩端都能建置(Android debug APK、iOS unsigned)
  • CI 全部通過——等 gh pr checks
  • 有 UI 變更的話:用的是 AppSpacing / AppRadius / AppMotion,深色模式看過,文字對比度可接受——深色模式與存圖按鈕外觀尚未在實機確認

@codecov

codecov Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.29897% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.42%. Comparing base (07c0c3d) to head (2161f97).

Files with missing lines Patch % Lines
...thquake/presentation/pages/report_replay_page.dart 90.74% 10 Missing ⚠️
lib/core/platform/image_saver.dart 88.33% 7 Missing ⚠️
lib/shared/map/map_image_density.dart 71.42% 4 Missing ⚠️
lib/shared/map/map_scaffold.dart 98.09% 2 Missing ⚠️
lib/bootstrap.dart 0.00% 1 Missing ⚠️
lib/core/error/failure.dart 0.00% 1 Missing ⚠️
...thquake/presentation/pages/report_detail_page.dart 97.95% 1 Missing ⚠️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lowrt lowrt added this to the V26.5 milestone Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

✅ OpenCodeReview: Review complete: 0 finding(s) across 24 selected item(s).

@lowrt
lowrt requested a review from a team November 4, 2026 01:31
lowrt
lowrt previously approved these changes Nov 4, 2026
PiscesXD and others added 7 commits November 7, 2026 08:21
Platform: all

New(zh-Hant): 地圖右上角新增沉浸式按鈕,隱藏控制項與導覽列讓地圖佔滿螢幕
New(en-US): an immersive button on the map hides the controls and the navigation bar for a full-screen map
Platform: all

Fix(zh-Hant): 地圖右下角的 MapLibre 資訊按鈕已移除
Fix(en-US): the MapLibre info button is gone from the map's bottom-right corner
Platform: all

Optimization(zh-Hant): 進出沉浸式地圖時有轉場動畫,不再瞬間切換
Optimization(en-US): entering and leaving immersive map mode animates instead of snapping
Platform: all

New(zh-Hant): 地震重播也能進入沉浸式模式,隱藏介面並以同樣的轉場顯示地圖
New(en-US): the earthquake replay can enter immersive mode, hiding its chrome with the same transition
Platform: all

Fix(zh-Hant): 沉浸式地圖時,強震監視器面板會跟著底部導覽列往下收
Fix(en-US): the monitor panel follows the hidden navigation bar down in immersive mode
Platform: all

Fix(zh-Hant): 地震重播進入沉浸式時,底部導覽列也會收起
Fix(en-US): the bottom navigation bar now retracts for the earthquake replay's immersive mode
Platform: all

Fix(zh-Hant): 修正地圖設定按鈕顯示正確的標籤名稱
Fix(en-US): Fixed the map settings button displaying the correct label name
lowrt
lowrt previously approved these changes Nov 9, 2026
Platform: all

New(zh-Hant): 地震報告圖可以儲存到相簿
New(en-US): the earthquake report image can be saved to your photo library
Platform: all

Fix(zh-Hant): 地圖上的震度圖示大小不再隨裝置解析度改變
Fix(en-US): intensity icons on the map no longer grow with the screen density
@PiscesXD PiscesXD changed the title feat: add immersive mode to the map and earthquake replay feat: add immersive mode and report image saving Nov 9, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Idea]: 新增一個 沉浸式地圖的功能選項

2 participants