Haseeb

那是在午間禱告安靜進行時發生的。清真寺內一片寂靜,氣氛凝重而專注,接著——手機鈴聲那刺耳的高頻鈴音劃破了空氣。所有人都轉過頭來。我感到臉頰瞬間發燙,慌忙伸手去摸索裝置,急切地按下側邊按鈕讓它靜音。那只是一個普通的、泛用的通知,但在那個空間裡,它聽起來就像警報器。我本來打算把手機靜音已經有一個小時了,但早晨的忙碌讓我完全忘記了這件事。就是那一刻,我意識到手動管理手機的方式已經行不通了。

我們生活在一個裝置本該變得智慧的時代,但它們卻經常在最基本的上下文感知任務上失敗。我們依賴需要同步的行事曆應用,或是不斷 ping 雲端伺服器的定位工具,這些都建立了一條依賴鏈,一旦你走進地下室或失去數據覆蓋範圍,就會失效。問題不僅是我們忘記按下靜音開關;更重要的是,當前的生態系統迫使我們將裝置視為網路依賴循環中的主動參與者。如果伺服器當機或訊號微弱,自動化就會中斷。對於像禮拜場所、法庭或醫療診所這樣敏感的場所,這種延遲或故障是無法接受的。我需要一個完全在裝置上運作的系統,不依賴任何外部 ping、雲端心跳或 API 授權權杖。

當我開始開發 Muffle 時,我決定核心邏輯必須完全存在於使用者裝置的沙箱內。對於地理圍欄,這意味著要避開那些經常需要大量網路交握才能取得地圖磚或搜尋結果的第三方地圖 SDK。我使用了 Google Play Services 定位函式庫中的原生 GeofencingClient,但我必須將其包裝在自訂邏輯層中,以確保它遵循我的離線優先限制。挑戰不僅在於觸發事件,還在於管理裝置進入或離開定義半徑時的狀態轉換。我必須處理 PendingIntent 觸發,同時確保 AudioManager 服務即使在螢幕關閉且應用程式在背景時也能執行音量切換。

kotlin
val geofencingRequest = GeofencingRequest.Builder().apply {
setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
addGeofence(geofence)
}.build()

geofencingClient.addGeofences(geofenceRequest, geofencePendingIntent).run {
addOnSuccessListener { /* Geofence added locally / }
addOnFailureListener { e -> /
Handle registration failure */ }
}

透過利用 BroadcastReceiver 模式來監聽 Geofence.GEOFENCE_TRANSITION_ENTER 意圖,我能夠在不發出任何網路請求的情況下觸發我的 AudioManager 呼叫。這裡的困難在於 Android 的 Doze 模式和應用程式待命儲存貯體。如果作業系統決定終止背景處理程序以節省電池,地理圍欄的轉換可能會延遲或被忽略。我必須實作一個 ForegroundService 來保持上下文的活躍,這確保了作業系統將應用程式視為主動參與者。這是一個經過深思熟慮的架構權衡:我選擇使用少量的額外電池,以確保手機在應該靜音時確實會靜音。在我的測試中,我發現依賴標準的背景工作管理員經常導致 30 到 60 秒的延遲,當你走進會議室時,這簡直是永恆。

在這個開發週期中,最讓我驚訝的是,當你剝離網路輔助定位 (NLP) 時,LocationManager 和底層的 FusedLocationProvider 會變得多麼脆弱。我最初以為 GPS 就足夠了。我錯了。如果你在室內,GPS 經常無法鎖定,而且如果沒有啟用 Wi-Fi 掃描來進行定位,地理圍欄根本就不會觸發。我必須建立一個後備機制,檢查被動位置更新,並在主動掃描超時時使用上次已知的位置。這沒有出現在文件裡,但我意識到使用者的位置權限授予或限制程度各不相同。我的應用程式必須優雅地處理「權限被拒」的狀態,而不是當機或卡在等待迴圈中。

另一個不明顯的障礙是衝突解決。如果使用者設定了一個基於位置的例行程序和一個基於時間的例行程序,而這兩個程序發生重疊,會發生什麼事?我花了整整一個週末除錯一個迴圈,手機會切換到靜音,然後立即切換回震動,因為系統時鐘觸發了一個尚未完成其週期的例行程序。我必須為每個例行程序實作一個優先順序整數。如果一個例行程序目前是「作用中」,它會鎖定音量狀態,直到它明確完成,即使是較低優先順序的例行程序試圖啟動。這基本上是為你手機的音量設定建立一個互斥鎖 (Mutex)。我未來肯定會改變處理「緊急繞過」聯絡人的方式。目前,它是一個靜態清單,但我希望將其與 Android 的 NotificationChannel 重要性設定整合,以允許對哪些通知「穿透」靜音有更細粒度的控制。

如果你正在建立一個離線優先的工具,你最大的敵人就是假設系統 API 總是會以可預測的、線性的方式運作。它們不會。你必須編寫防禦性程式碼,假設定位訊號已斷線、電池正被作業系統節流,以及使用者就在你眼皮底下改變了他們的手機設定。目標是將複雜性從使用者身上移開,轉移到你的處理邏輯中。在安靜的房間裡讓應用程式靜默失敗並重試,遠比向使用者顯示錯誤訊息要好得多。本機儲存的優點在於它尊重使用者的隱私,並讓應用程式無論使用者的數據方案或區域連線性如何都能正常運作。這是一種更乾淨、更尊重的軟體設計方式。

當我今天看著這個應用程式時,我看到了一個解決了我在清真寺裡最初感到尷尬的方案。透過保持邏輯本機化並讓觸發器穩健,我已經建立了一個我每天親自使用的工具來管理我自己的理智。如果你有興趣了解這些例行程序在實務上是如何運作的,或者想自己嘗試實作,你可以在這裡找到這個專案:https://play.google.com/store/apps/details?id=com.muffle.app。這是一個持續進行中的工作,但它確實改變了我在公共場所與裝置互動的方式。