Haseeb

那发生在一个安静的正午祈祷时刻。清真寺内一片寂静,空气中弥漫着专注的气氛,突然——手机铃声那刺耳的高音划破了宁静。所有人转过头来。我感到脸颊泛起红晕,慌乱地伸手去按侧键静音。那是一个普通的通知,但在那种场合,它听起来像警报。我本该一小时前就把手机调成静音,但忙碌的早晨完全让我忘记了这件事。那一刻我意识到,手动管理手机的方式已经失效了。

我们生活在一个设备本该智能的时代,但它们在最基本的上下文感知任务上却一再失败。我们依赖需要同步的日历应用,或不断 ping 云服务器的位置工具,构建了一条依赖链,一旦你进入地下室或失去数据覆盖,这条链就会断裂。问题不只是我们忘记按静音开关;而是当前的生态系统迫使我们将设备视为网络依赖循环中的主动参与者。如果服务器宕机或信号微弱,自动化就会失效。对于像礼拜场所、法庭或医疗诊所这样敏感的场景,这种延迟或失败是不可接受的。我需要一个完全在设备上运行的系统,独立于任何外部 ping、云心跳或 API 授权令牌。

当我开始构建 Muffle 时,我决定核心逻辑必须完全驻留在用户设备的沙箱内。对于地理围栏,这意味着要避免使用通常需要大量网络握手来获取图块或搜索结果的第三方地图 SDK。我使用了 Google Play 服务位置库中的原生 GeofencingClient,但我必须用一个自定义逻辑层来封装它,以确保它遵守我的离线优先约束。挑战不仅在于触发事件,还在于管理设备进入或退出定义半径时的状态转换。我必须处理 PendingIntent 触发器,同时确保即使屏幕关闭且应用在后台运行,AudioManager 服务也能执行音量切换。

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

geofencingClient.addGeofences(geofenceRequest, geofencePendingIntent).run {
addOnSuccessListener { /* 地理围栏已在本地添加 / }
addOnFailureListener { e -> /
处理注册失败 */ }
}

通过利用 BroadcastReceiver 模式来监听 Geofence.GEOFENCE_TRANSITION_ENTER 意图,我能够在不发送任何网络请求的情况下触发 AudioManager 调用。这里的难点在于 Android 的 Doze 模式和应用待机桶。如果操作系统决定终止后台进程以节省电池,地理围栏转换可能会被延迟或忽略。我必须实现一个 ForegroundService 来保持上下文活跃,这确保了操作系统将应用视为活跃参与者。这是一个经过深思熟虑的架构权衡:我选择使用少量额外电池电量来确保手机在应该静音时确实会静音。在我的测试中,我发现依赖标准后台工作管理器通常会导致 30 到 60 秒的延迟,当你走进会议室时,这简直是永恒。

在这个开发周期中,最让我惊讶的是,当你剥离网络辅助定位 (NLP) 后,LocationManager 和底层的 FusedLocationProvider 是多么脆弱。我最初以为 GPS 就足够了。我错了。如果你在室内,GPS 往往无法锁定,而且如果没有启用 Wi-Fi 扫描进行定位,地理围栏根本不会触发。我不得不构建一个回退机制,检查被动位置更新,并在主动扫描超时时使用最后已知位置。这不在文档中,但我意识到用户通常拥有不同级别的已授予或受限的位置权限。我的应用程序必须优雅地处理“权限被拒绝”状态,而不是崩溃或挂起在等待循环中。

另一个不明显的障碍是冲突解决。如果用户设置了一个基于位置的例程和一个基于时间的例程,它们重叠了怎么办?我花了整个周末调试一个循环,手机会切换到静音,然后立即切换回振动,因为系统时钟触发了一个尚未完成其周期的例程。我必须为每个例程实现一个优先级整数。如果一个例程当前是“活动的”,它会锁定音量状态,直到它明确完成,即使较低优先级的例程试图触发。它本质上是手机音量设置的互斥锁。我将来肯定会改变处理“紧急绕过”联系人的方式。目前,它是一个静态列表,但我希望将它与 Android NotificationChannel 重要性设置集成,以允许对哪些通知“穿透”静音进行更细粒度的控制。

如果你正在构建一个离线优先的工具,你最大的敌人是假设系统 API 总是以可预测的、线性的方式运行。它们不会。你必须编写防御性代码,假设位置信号已断开,电池正被操作系统节流,以及用户刚刚在你脚下更改了他们的手机设置。目标是将复杂性从用户转移到你的处理逻辑中。最好是静默失败并重试,而不是在安静的房间里向用户提示错误消息。本地存储的美妙之处在于它尊重用户的隐私,并确保无论用户的数据计划或区域连接性如何,应用都能正常运行。这是一种更简洁、更尊重的方式来设计软件。

当我今天查看这个应用时,我看到的是一个能解决我在清真寺里最初尴尬的解决方案。通过保持逻辑本地化和触发器健壮性,我创建了一个我每天亲自使用来管理自己理智的工具。如果你对这些例程在实践中的样子感兴趣,或者想自己尝试实现,你可以在这里找到这个项目:https://play.google.com/store/apps/details?id=com.muffle.app。它还在开发中,但它确实改变了我与设备在公共空间互动的方式。