Android 熄屏相机控制小工具:屏幕完全关闭后,用音量键控制前后摄像头并拍照。 自用侧载项目,目前仅在一台 Redmi Turbo 4(HyperOS 3 / Android 16)上完成真机验证。
English: Screen-off camera control for Android. A single volume key toggles the front/rear camera;
pressing both volume keys captures a photo. Tested on Redmi Turbo 4 / HyperOS 3 / Android 16, requires Shizuku (no root). Screen-off only; after reboot you must restart Shizuku and arm again.
- 熄屏单击音量上 / 音量下:切换前置 / 后置摄像头(toggle)
- 熄屏同时按音量上 + 音量下:拍照,保存到
DCIM/ScreenOffCam/ - 仅熄屏生效:屏幕亮起时不拦截音量键,正常调节音量
- 按需打开相机:拍照完成后释放相机,减少待机耗电
- 独立反馈开关:拍照震动 / 快门音都有自己的开关
- 通道自愈:Shizuku 被杀或 rish 进程退出后自动重连,并更新常驻通知状态
| 项目 | 状态 |
|---|---|
| 已验证机型 | 红米 Turbo 4(代号 rodin,型号 24129RT7CC) |
| 已验证系统 | HyperOS OS3.0.304.0.WOJCNXM / Android 16 (API 36) |
| compileSdk / targetSdk / minSdk | 36 / 36 / 34 |
| 构建工具 | Gradle 8.14.5、AGP 8.13.2、Kotlin 2.2.20、JDK 17、Build-Tools 36.0.0 |
| 必须依赖 | Shizuku(adb/无线调试模式,shell uid 2000) |
| root / 解锁 BL / Xposed | 不需要 |
| 其他 Android 14+ 机型 | 理论上可移植,但未验证;不同 OEM 的 evdev 节点、锁屏/后台策略可能不同 |
重要限制:目标机是目前唯一验证环境。换机型后,/dev/input 事件、HyperOS/MIUI 的后台冻结策略、
锁屏行为都可能需要重新适配。
- 用户从可见界面点「武装」,启动 camera 类型前台服务 + Partial WakeLock;
- App 通过 Shizuku 的官方
rish客户端,以 shell 身份运行一个常驻getevent, 动态探测带KEY_VOLUMEUP的 evdev 节点并读取音量键事件; ComboDetector做纯逻辑判定:- 单键
DOWN → UP ≤ 800ms,进入 350ms 待确认窗口,无第二颗键则 toggle 前后摄; - 另一颗键最近 DOWN ≤ 300ms 或刚 UP ≤ 350ms,则触发快门;
- 亮屏、长按、重复 DOWN、Deep Doze 无 reader 等场景都有防护;
- 单键
- 触发拍照后
CameraController打开相机 →ImageReader取 JPEG → 写入 MediaStore; FeedbackPlayer按设置触发震动 / 快门音。
更详细的通道设计与实验记录见 docs/:
前置:JDK 17、Android SDK(platform 36 + build-tools 36.0.0)。
cd android
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk- 安装并启动 Shizuku:
- 在 Shizuku 内点「View command」,从电脑
adb或无线调试执行; - 保持 USB 调试 / 开发者选项开启,否则 Shizuku 可能随机停止。
- 在 Shizuku 内点「View command」,从电脑
- 打开「熄屏快门」:
- 允许相机、通知权限;
- 点「Shizuku 授权」并允许;
- 点「澎湃OS 设置引导」,按提示设置:
- 自启动:开启
- 省电策略:无限制(本 app 和 Shizuku 都要)
- 最近任务:锁定后台
- 通知:允许
- 相机「街拍模式」:建议关闭,避免与音量键冲突
- 点「武装」启动 camera 前台服务,确认通知常驻。
- 熄屏后:
- 单击音量上 / 下 → 切换摄像头;
- 同时按音量上 + 下 → 拍照。
重启手机后需要重新做两步:① 按 Shizuku 的「View command」重新启动 Shizuku; ② 回到本 app 点「武装」。camera 前台服务不能开机自启,这是 Android 14+ 的系统限制。
- 音量键不会被吞掉:读取
/dev/input是旁路监听,按音量键会真实改变媒体音量; - 通知不可隐藏:常驻通知是前台服务与 App Standby working set 的锚点;
- 相机占用:武装期间 app 可能短时占用相机,微信/系统相机可能受影响;
- 每次重启需重新武装 + 重新启动 Shizuku;
- 无障碍通道不可用为正式通道:实验证明无障碍在浅熄屏/锁屏可收到音量键, 但 Deep Doze 下收不到,因此最终仍使用 Shizuku;
- 未上架任何应用商店:这是自用侧载工具,商店合规不是设计约束;
- 换机型需要通过
getevent -pl确认音量键 evdev 节点,并实测锁屏/Doze 行为。
screen-off-camera/
├── android/ # Android 应用(Kotlin)
│ └── app/src/main/
│ ├── java/.../ # MainActivity、CameraArmedService、ComboDetector 等
│ ├── res/xml/ # 无障碍实验配置
│ └── assets/shizuku/ # 随包分发的 rish(Apache-2.0)
├── docs/ # 架构、通道设计、真机实验记录、证据
├── scripts/
│ ├── standby-power-test.sh
│ ├── keytest.sh
│ ├── sdk-setup.sh
│ └── sdk-remove.sh
└── tests/ # 无额外依赖的 JVM 纯逻辑测试
纯逻辑测试覆盖 ComboDetector 与 getevent 行解析,不需要 JUnit/第三方依赖:
bash tests/run-tests.sh
# RESULT passed=19 failed=0本项目仅供个人学习与自用侧载,不保证兼容其他机型和系统版本。 使用前请确认你了解相机前台服务、Shizuku/ADB 调试、HyperOS 后台策略带来的隐私与权限影响。
- 本项目代码:Apache License 2.0,见
LICENSE与NOTICE; - 随包分发的
rish/rish_shizuku.dex来自 Shizuku,Apache-2.0; - 依赖
dev.rikka.shizuku:api/provider,Apache-2.0; - 详见
THIRD_PARTY_NOTICES.md。