跳转至

Android 通知

Android 应用接收 Firebase Cloud Messaging (FCM) 数据消息。relay 会通过 FCM HTTP v1 直接发送这些消息,无需 Expo Push 账户。

Android 兼容性与自动化检查

应用的最低版本为 Android 7.0 (API 24),在 app.config.ts 中声明,并由 relay 的设备注册模式强制执行。编译/目标 SDK 版本遵循锁定的 Expo/React Native 工具链(当前为 API 36)。通知渠道从 API 26 开始;通知权限提示从 API 33 开始。Live Update 提升需要 API 36,并且仍受系统设置和设备支持的影响。低于 API 36 时,提醒和普通活动卡片可正常工作。

在 Android 16+ 上,打开 T3 Code 设置 → Live Update 设置以允许状态栏小芯片(chip)。Android 将其与通知权限分开控制。该 chip 在工作期间显示 Working,审批时显示 Approve,输入请求时显示 Answer;完成的工作将恢复为普通通知。请使用 Android 16 QPR2 或更高版本的模拟器镜像来验证已发布的提升行为。

API 24–25 使用单个不精确的系统闹钟在进程退出后让卡片过期,且无需精确闹钟权限。在省电模式下,Android 可能会延迟该闹钟。API 26+ 使用通知超时机制。禁用活动、解除卡片、账户变更和退出登录都会取消旧版闹钟。陈旧的到期广播无法移除较新一次运行的卡片。

原生通知测试使用 Robolectric 覆盖 API 24、26、33 和 36(外加用于旧版过期的 API 25)。要编译该模块、运行这些测试并运行 Android lint,请在生成的 apps/mobile/android 项目中执行以下命令,并确保 JDK 21 可用。无需 Firebase、签名或 relay 密钥:

./gradlew :t3-agent-notifications:testDebugUnitTest :t3-agent-notifications:lintRelease -Pandroid.lint.useK2Uast=false

Robolectric 的 API 36 运行时需要 JDK 21;模块编译仍使用 Expo 的 Java 17 工具链。现有的 Mobile Native Static Analysis 任务会单独运行 ktlint 和 detekt。原生指纹检查将此变更标记为需要新二进制文件;生产工作流无法通过 OTA 将其交付给旧二进制文件。如果已安装的原生模块缺少必需的方法,设置会禁用 Android 通知。

lint 命令使用 K1 前端,因为 AGP 的 K2 前端在分析 Worklets 0.10 的 Gradle Kotlin 脚本时会崩溃。这不会禁用 lint 检查。Live Update 资格必须在设备上验证:Robolectric 的 API 36 镜像实现了要求着色(colorization)的旧版提升规则,而已发布的 Live Updates 要求未着色的通知。

Firebase 与应用构建

  1. 创建一个 Firebase 项目,并为你计划构建的每个 Android 应用标识符注册:com.t3tools.t3code.dev、com.t3tools.t3code.preview 或 com.t3tools.t3code。
  2. 下载 google-services.json。在运行 Expo prebuild 和构建应用时,将 T3CODE_ANDROID_GOOGLE_SERVICES_FILE 设置为其路径。该 JSON 必须包含所选变体的包标识符。
  3. 创建一个具有为该 Firebase 项目发送 FCM 消息权限的服务账户密钥。将此私有 JSON 保存在仓库和应用包之外。
  4. 如果 Google 项目中尚未启用 Firebase Cloud Messaging API,请启用它。对于托管交付,将 relay 的 FCM_SERVICE_ACCOUNT 密钥设置为该服务账户 JSON。
  5. 构建一个新的 Android 二进制文件。仅 JavaScript 的更新无法安装原生通知处理器或 Firebase 配置。托管交付还需要 relay 数据库迁移和更新的 relay 部署;本地验证可以使用下面的 watcher。

对于本地开发构建,在 apps/mobile 目录下执行:

APP_VARIANT=development \
T3CODE_ANDROID_GOOGLE_SERVICES_FILE=/absolute/path/google-services.json \
vp run android:dev

对于 EAS 构建,请通过每个选定的构建环境提供相同的配置,并使用名为 T3CODE_ANDROID_GOOGLE_SERVICES_FILE 的 EAS 文件变量来存放 Google services 文件。使该文件同时可用于指纹生成和原生构建。FCM 服务账户凭据属于 relay,而不属于 EAS 的应用环境。如果部署独立托管 relay,请按照 T3 Connect 中的说明,为该 relay 和 Clerk 应用配置构建的 T3 Connect 公共设置。

在 prebuild 和打包私有二进制文件之前,设置 T3CODE_MOBILE_UPDATES_ENABLED=0,以禁用仓库配置的 Expo OTA 更新源。调试开发客户端 APK 需要 Metro;要验证在无 Expo 开发启动器情况下的冷启动通知点击,则需要打包的 release 构建。

私有构建的 Clerk 登录

Clerk 的原生 Android 登录使用 clerk://<applicationId>.callback。在构建的 publishable key 所选择的 Clerk 实例中,其管理员必须在 原生应用 > 移动 SSO 重定向允许列表 下允许完全匹配的回调。对于开发包,添加:

clerk://com.t3tools.t3code.dev.callback

应用已声明匹配的回调接收器。出现 “redirect url ... does not match an authorized redirect URI” 错误时,需要更改 Clerk 配置;重新构建同一个 APK 无法修复该问题。管理员保存该条目后,请重新打开登录。其他变体请参阅 Android 原生登录重定向。

使用 T3 现有的生产 publishable key 会选择维护者的 Clerk 实例。它不授予更改该实例允许列表的权限。所选包的回调必须已被允许,或由该实例的管理员添加。Android 设备注册和托管交付分别需要下面的 relay 部署。成功的直接配对或 FCM 冒烟测试不能验证托管登录或设备注册。

使用 APP_VARIANT=production 构建会选定 com.t3tools.t3code 及其对应的 Clerk 回调。在 prebuild 和打包期间设置相同的变体,并提供包含该包的 Google services 文件。对于私有二进制文件,请保持禁用 OTA 更新。使用此包的本地签名构建无法更新由维护者签名的官方安装,也无法与之共存;卸载该安装同时会移除其应用本地数据。开发包仍然是独立的应用。

聚焦投递检查

infra/relay/scripts/android-push-smoke.ts 通过生产环境 FCM 客户端实现发送消息,无需配置 relay 的数据库、Clerk 集成或 Cloudflare 队列。它仅验证 Firebase 到设备的投递。

提供一个私有设备 JSON 文件,其中包含应用的原生 FCM token、已注册的 deviceId、已登录的 userId 和 Android packageName。可选的 deepLink 可指向现有会话以进行点击验证。应用必须已注册其本地原生通知处理器并拥有通知权限。在 infra/relay 目录下执行:

vp run push:android:smoke /path/service-account.json /path/device.json running
vp run push:android:smoke /path/service-account.json /path/device.json approval
vp run push:android:smoke /path/service-account.json /path/device.json completed

支持的状态包括 running、approval、input、completed、failed 和 end。Firebase 的接受并不证明设备显示了消息。请检查实际通知、将应用置于后台,并测试通知点击。还要测试关闭通知、禁用持续活动、退出登录、token 轮换,以及应用进程退出后的投递。Android 设置中的强制停止会故意阻止投递,直到应用再次打开。

当应用处于前台时,Android 仅对当前屏幕上的会话抑制提醒,这与 iOS 的通知呈现方式一致;其他会话的提醒仍然会显示。活动卡片在前台仍会更新,并静默保留已完成的结果。请检查:完成操作在所属会话打开时保持静默,在另一个屏幕打开时发出提醒,在应用进入后台后发出提醒,并且重试被抑制的提醒不会在之后显示。这里依据的是接收手机上的应用生命周期和路由,而不是其他客户端上的会话可见性。

启用持续活动后,验证两个会话同时进入 approval/input 状态会产生一条 2 agents need attention 提醒,而两个被观察的活动会话同时完成/失败会产生一条 2 agents finished 提醒。消息正文中列出它们的标题。relay 与 iOS 共享转换选择逻辑,并在工作完成时保留其已投递的基线;再次发布相同状态不得产生新的提醒。分组提醒打开聚合中的优先级会话;单独提醒保留其会话链接。

验证展开的卡片包含五个会话、注意/失败优先级、项目名称和状态。当所有工作完成时,卡片应显示 Agent work completed 或 Agent work failed,失去其持续/提升标志,并在最新显示结果出现 15 分钟后过期。重放不得延长该截止时间。静默运行中的工作使用 relay 的两小时状态生命周期;approval/input 状态使用 24 小时。重新打开同一已登录应用,确认现有提醒和关闭状态仍然存在,冷启动时或在前台至少 60 秒后进行静默聚合重放。还要检查空重放会移除孤立卡片,且超过两分钟的完成事件永远不会发出提醒,即使禁用了持续活动也是如此。

Android 预构建之后,在 apps/mobile/android 目录下运行原生呈现回归测试:

./gradlew :t3-agent-notifications:testDebugUnitTest --tests expo.modules.t3agentnotifications.AgentNotificationsTest

Relay 部署

使用现有 T3 服务进行本地验证

开发 Android 推送时,您无需复制 T3 Connect 的托管基础设施。保留正常的 Clerk 登录和环境连接即可。infra/relay/scripts/android-push-watch.ts 订阅一个配对环境的 shell 流,使用共享的 agent-awareness 投影,并通过新的 FCM 客户端发送更新。它在内存中保存临时状态,无需托管数据库或 Clerk 密钥。

创建一个私有 connection.json,包含 wsUrl(该环境的 /ws URL)和 bearerToken(普通的配对环境访问令牌)。为此 watcher 使用单独的配对凭据。提供上文所述的相同设备文件,然后在 infra/relay 目录下运行:

vp run push:android:watch /path/service-account.json /path/device.json /path/connection.json

Android 原生处理器必须已使用该设备和账户进行配置,并且必须允许通知。原生插桩测试框架可以在测试前配置一次性模拟器;已登录的开发应用会在设备注册期间配置处理器。此 watcher 是一种开发传输通道:它观察配对环境中的所有未归档会话,启用所有提醒类型,不保留持久队列,并且必须持续运行。它不会向现有的托管 relay 注册 Android 设备。托管 relay 需要先应用下面的更改,其通知设置和投递才能端到端工作。

托管投递

现有的 Alchemy 部署默认配置 Cloudflare Workers、投递队列、Hyperdrive、隧道/DNS 资源、PlanetScale Postgres 和 Axiom 可观测性。它需要已启用服务的凭据以及私有 Clerk 配置;仓库中的公共应用设置不授予部署访问权限。在仅限 Android 的开发 relay 中设置 APNS_ENABLED=false,以跳过 Apple 投递及其凭据要求。APNs 默认保持启用。

现有部署账户中的个人阶段

拥有现有 Alchemy 状态和部署凭据访问权限的维护者可以将 Android 更改部署到个人阶段。非生产阶段引用 prod 阶段持有的数据库和 DNS 区域,创建单独的 PlanetScale 分支,并将迁移应用到该分支。因此,个人阶段不是对无关账户的独立部署。

  1. 将 Android 更改应用到具有现有部署凭据的检出副本中。使用 relay README 中描述的现有 Cloudflare、PlanetScale、Axiom、域和 Clerk 配置,创建私有 infra/relay/.env.android-dev。让 CLERK_PUBLISHABLE_KEY、CLERK_SECRET_KEY 和 CLERK_JWT_AUDIENCE 保持在测试客户端使用的同一 Clerk 实例上。
  2. 将 Firebase 服务账户 JSON 添加为 FCM_SERVICE_ACCOUNT,并为此仅限 Android 的阶段设置 APNS_ENABLED=false。保持 RELAY_DOMAIN 未设置,以便部署为个人阶段生成主机名,而不是使用生产主机名。
  3. 在仓库根目录检查部署计划,然后部署同一阶段:

sh vp run --filter t3code-relay deploy --stage dev_ryan_android --env-file .env.android-dev --dry-run vp run --filter t3code-relay deploy --stage dev_ryan_android --env-file .env.android-dev

  1. 将部署后的 relay URL 和匹配的公共 Clerk 配置交给测试者。部署封装器还会把 relay URL 和公共追踪配置写入该检出目录根目录的 .env。使用此 T3CODE_RELAY_URL、现有的 Firebase Android 文件,并禁用 OTA 更新来重新构建私有 APK。如果使用独立的开发包,请按上述说明授权其 Clerk 回调。
  2. 使用相同的 relay URL 配置一台独立的 T3 服务器,并通过新的 relay 链接该测试环境。现有的生产环境 relay 链接不会自动迁移到个人阶段。为测试环境启用活动发布,在手机上启用通知,并验证在手机锁定时,真实的 agent 回合会产生进行中的更新和完成提醒。

维护者可以自行执行部署,只返回公共客户端配置;测试者不需要获取托管或 Clerk 服务器凭据的副本。完全独立的部署需要自己的初始 Cloudflare 栈、PostgreSQL 数据库、Firebase 项目和可供操作者配置的 Clerk 实例。其 Alchemy 部署需要 PlanetScale 和 Axiom 凭据。

使用相同的 relay URL 和 Clerk 公共配置来构建宿主客户端和移动应用。从源码构建的服务器或桌面开发构建可以托管测试环境;请将其 T3 主目录与现有安装分开。仅登录手机并不会链接宿主环境。使用宿主客户端的 T3 Connect 设置来链接它,并启用活动发布。私有 Clerk 实例在使用 t3 connect login 之前,还需要自己的 CLI OAuth 应用;仓库中的生产环境 CLI 客户端 ID 属于维护者的实例。

对于通过 GitHub Actions 进行的部署,请将 FCM_SERVICE_ACCOUNT 添加到 production 环境的 secrets 中。relay 工作流会将其传递给 Alchemy。维护者还必须在原生构建环境中为生产环境 Android 包提供 google-services.json;仅更改 relay 密钥无法将已安装的应用迁移到另一个 Firebase 项目。

Android 投递使用 RelayFcmDeliveryQueue 和一个独立的死信队列。失败的请求会重试;消息在五分钟后过期。发送前,消费者会重新检查设备令牌、当前偏好设置、环境链接和当前线程状态。UNREGISTERED 响应只会使匹配的设备令牌失效。OAuth 令牌缓存在 FCM 服务中,并在授权失败后刷新。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 t3/t3code