Commit a2ffb6c
committed
feat(android): 新增生物識別鎖、段考變更提醒、網路層執行緒安全強化、課表小工具 session 邊界與多項安全修正
--- 網路層與 Session 安全 ---
- SchoolCookieJar 改用 synchronized 同步鎖保護所有讀寫方法 (saveFromResponse、loadForRequest、replace、clear、snapshot),取代原本的 CopyOnWriteArrayList,消除並行 API 呼叫時的 Cookie 覆蓋競態導致 HTTP 401
- SchoolGradeClient 移除各 API 方法內的無條件 cookieJar.replace(),改以學號過濾與鎖保護的 prepareSession() 進行按需載入
--- 生物識別防護鎖 (Biometric Lock) ---
- 新增 BiometricHelper:實作雙層分層加密 (Key Wrapping) 模式,使用 4~6 位數 PIN 碼 + PBKDF2 加密 Session,再以 Android Keystore 硬體金鑰 (setUserAuthenticationRequired) 加密 PIN,指紋變更時捕獲 KeyPermanentlyInvalidatedException 並自動切換備用 PIN 解鎖
- 新增 BiometricLockScreen 全螢幕覆蓋層與 PinDialogs (設定/驗證 PIN)
- SessionStore 新增 saveBiometricSession / loadBiometricSession / clearBiometricSession,儲存加密後的 session 與 salt / IV
- MainActivity 整合生物識別流程:冷啟動 / 背景喚醒自動鎖定、single-flight 防止 BiometricPrompt 重複觸發、prompt 顯示期間暫停背景鎖定判斷、FLAG_SECURE 防止多工縮圖洩漏成績
- SettingsScreen 新增生物識別開關與 PIN 設定入口
--- 段考資訊變更提醒 ---
- 新增 GradeReportDiffer:比對成績、排名、平均、五標、標準差、級距與缺考/作弊狀態,五標/分布區塊的新增或移除也算可見資訊變更
- 新增 GradeReminderModels / GradeReminderText / GradeReminderRepository 管理提醒狀態、快照、最近變更集與到期邏輯 (48 小時)
- 新增 GradeReminderWorker (WorkManager 15 分鐘週期)、GradeReminderScheduler、GradeReminderNotifier (IMPORTANCE_HIGH)、BatteryOptimizationHelper (忽略電池最佳化請求)
- ScoreViewModel 新增提醒啟用/停用流程、snapshot diff 比對、App 啟動自我修復排程、連續失敗自動停止與 session 清除
- 段考提醒使用獨立 reminderSession (saveReminderSession / loadReminderSession),隨提醒狀態存在,登出/學生切換/停止/過期時清除
- 新增 debug-only GradeReminderDebugReceiver 供 ADB 測試完整 worker 鏈
- GradesScreen 新增段考提醒開關 UI 區域
- DeveloperSettingsScreen 新增段考提醒測試通知按鈕 (debug only)
--- 通知權限處理 ---
- 新增 NotificationPermissionHelper 統一通知權限檢查
- NotificationPromptDialog 改為開啟 App 通知設定頁而非直接 requestPermission,避免 FragmentActivity requestCode 超過 lower 16 bits 導致閃退
- ScoreApp 根層同步系統通知權限與 App 內通知開關狀態
--- 課表小工具 (Schedule Widget) Session 邊界 ---
- ScheduleWidget 改為只讀 GradeCacheStore 的 widget 專用課表快照,不再讀取 session / cookie / token
- GradeCacheStore 新增 widget 課表快照讀寫,登出與快取清除時連動清除
- ScheduleRepository 優先使用已解鎖的 in-memory active session,存在 biometric session 時不 fallback 到一般 SessionStore
- ScheduleWidget 自動更新改用 AlarmManager.setAndAllowWhileIdle,在午夜與每節課下課時觸發,避開 SCHEDULE_EXACT_ALARM 限制
- 新增 ArchitectureBoundaryTest 防止 widget 重新依賴登入狀態,並檢查 PIN 解鎖必須先 activate session 再解除鎖定
--- Release 瘦身與 ProGuard ---
- proguard-rules.pro 移除 broad keep (androidx.*, org.jsoup.*),改依賴 library 自帶 consumer rules,僅保留最小 WorkManager 規則
--- 測試 ---
- 新增 ArchitectureBoundaryTest (widget session 邊界 + PIN 解鎖順序)
- 新增 ActiveSessionResolverTest (biometric session 優先解析)
- 新增 GradeReportDifferTest (成績/五標/級距 diff 覆蓋)
- 新增 UpdateCheckerTest、SchoolGradeClientTest、DeveloperDiagnosticsTest、ScheduleViewModelTest
- 擴充 ScoreViewModelTest (提醒啟用/停用、snapshot diff)
--- 其他 ---
- UpdateChecker 重構錯誤處理與 edge case
- DeveloperDiagnostics 新增更多開發者診斷資訊
- GradeExporter 微調匯出格式
- Material Symbols subset 新增 icon ligature (lock、fingerprint 等)1 parent 90bd7db commit a2ffb6c
59 files changed
Lines changed: 4615 additions & 382 deletions
File tree
- android
- app
- src
- debug
- java/com/clhs/score/reminders
- main
- java/com/clhs/score
- data
- notifications
- reminders
- ui
- components
- schedule
- viewmodel
- widget
- res
- font
- values
- test/java/com/clhs/score
- data
- viewmodel
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
44 | 49 | | |
45 | 50 | | |
46 | 51 | | |
47 | 52 | | |
48 | 53 | | |
| 54 | + | |
| 55 | + | |
49 | 56 | | |
50 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
51 | 72 | | |
52 | 73 | | |
53 | 74 | | |
54 | 75 | | |
55 | 76 | | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
56 | 80 | | |
57 | 81 | | |
58 | 82 | | |
59 | 83 | | |
60 | 84 | | |
61 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
| 129 | + | |
| 130 | + | |
129 | 131 | | |
130 | 132 | | |
131 | 133 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
36 | 35 | | |
37 | 36 | | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
Lines changed: 113 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
| 7 | + | |
6 | 8 | | |
7 | 9 | | |
8 | 10 | | |
| |||
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| 21 | + | |
19 | 22 | | |
20 | 23 | | |
21 | 24 | | |
| |||
0 commit comments