起因:OHO+ 用惯了,小米上没有
三星有个叫 One Hand Operation+ 的东西,圈子里的简称是 OHO+。它是 Good Lock 生态里的一个侧边手势工具:屏幕左右各放一条隐形触钮,从触钮往里滑,能触发你绑定的动作——返回、主屏、截屏、手电筒、打开某个应用,一边 6 种手势,两边 12 种。
这东西用惯了会上瘾。单手就能切应用、拉通知栏、熄屏,大屏手机上特别省事。但它是三星独占,小米上装不了。
于是想把它搬过来。规则树那个项目做完了条件自动化,这次另起一个独立的 App,名字叫边触。
先把原版拉下来看看
既然要复刻,先得知道原版到底做了什么。从三星手机上把 OHO+ 的安装包拉下来(com.samsung.android.sidegesturepad,9.3.13.0,8.5 MB),反编译看清单和类结构。
结果很直接:它是一个系统签名应用。清单里躺着一堆普通应用连写都写不进去的权限:
INJECT_EVENTS 注入按键和触摸
INTERNAL_SYSTEM_WINDOW 开系统级窗口
STATUS_BAR_SERVICE 控制系统栏
FORCE_STOP_PACKAGES 强停应用
MANAGE_ACTIVITY_STACKS 管理任务栈
这些权限的保护级别是 signature,意思是「只有跟系统同一个签名的人才拿得到」。三星给 OHO+ 签的正是这个签名,所以它等于系统自己人。
73 个动作,能做的只有一半多
把它的功能清单完整导出来,一共 73 个可绑定的动作。逐条对着小米的实际情况判了一遍,分成三类:
| 等级 | 数量 | 含义 |
|---|---|---|
| A 直接可做 | 35 | 公开 API 或无障碍就能实现 |
| B 要 root | 28 | 走 svc / cmd / settings |
| C 做不到 | 10 | 依赖 One UI、三星硬件或 Google 服务 |
A 类是主流:返回、主屏、最近任务、通知栏、截屏、锁屏、手电筒、音量、媒体键、打开应用、分屏。这些普通应用就能做。
B 类是系统开关:Wi-Fi、蓝牙、飞行模式、移动数据、NFC、热点、亮度、超时、旋转。要 root。
C 类是真做不了的:侧屏幕面板、防窥模式、圈定即搜、游戏助推器——不是 One UI 专属,就是硬件相关。
所以「两百多个动作全能搬过来」是不成立的,实际能落地的就是这 35 + 28 项,而且 C 类那 10 项搬过来也没意义。
为什么不能直接把 OHO+ 改来用
有人会问:那直接改 OHO+ 让它跑起来不就行了?
不行。那道「检测到不是三星环境就退出」只是最外面一层皮,真正的障碍在下面三层:
- 系统签名。一改 APK,签名就变了,
INJECT_EVENTS这些特权权限全部失效。它能注入按键、能接管导航手势,全靠这个签名。 - 依赖 One UI 和三星 SDK。界面控件、Routines 对接、读写三星自己的设置键,这些在小米上根本没有,删了退出检查也会
NoClassDefFoundError。 - 行为对不上。它接管导航靠的是改三星的导航模式,小米是另一套。
所以改原版是负收益:改动量约等于重写,收益还受限于「没有平台签名」。反而从零写,把能做的做全,工作量更小。
那就自己写一个
边触的实现思路不复杂:
- 左右各一条悬浮窗手柄(
TYPE_APPLICATION_OVERLAY),画成一条半透明圆角竖条 - 每侧 6 种手势:水平、斜上、斜下,各分短滑和长滑
- 短滑与长滑的分界是屏高的 16%,横向位移 ≥ 纵向 1.35 倍算水平,往外滑忽略
- 动作 21 个:返回、主屏、最近任务、上一个应用、通知面板、快捷设置、电源菜单、截屏、锁屏、手电筒、音量 ±、静音、播放/暂停、上一首/下一首、打开应用、分屏、搜索、语音助手、系统设置
「上一个应用」靠无障碍事件记住前台包名历史,重新打开上一个;「截屏」走无障碍的 takeScreenshot;导航类动作走 performGlobalAction。无障碍只订阅窗口切换事件、只读包名,不读界面内容。
手柄的宽度、高度、离边缘距离、透明度、垂直位置都能调,还有应用例外清单(游戏、视频里自动隐藏)。
那堵墙:系统返回手势
做完之后,真正的麻烦才出来。
默认手柄贴在屏幕边缘。结果实测发现:边缘的滑动根本到不了手柄。给手柄加了日志重装,模拟的滑动一个字都没打出来。查窗口层级,一目了然:
Window #1 GestureStubRight ← 系统返回手势的桩
Window #2 GestureStubLeft
Window #3 AntiMistakeTouchView
...
Window #8 EdgePad-right ← 我们的手柄,被压在下面
全面屏手势下,系统在屏幕左右边缘放了一个「手势桩」窗口,它待在系统窗口层,比应用的悬浮窗层高。边缘触摸先被它截走,我们连边都摸不到。
而且这个测试还有个坑:用 adb 的 input swipe 模拟滑动,是到不了悬浮窗的,MIUI 不给 shell 注入的触摸派发到悬浮窗口。最后只能请真手指上场——果然,真手指在边缘滑,触发的是系统返回,不是我们的动作。
结论很硬:普通应用盖不掉系统返回手势。Android 有个 setSystemGestureExclusionRects() 听着像救命稻草,但它只对应用自己的 Activity 窗口有效,悬浮窗不适用。
退路是把手柄往屏幕里挪。实测挪到离边缘 40dp(约 1.3 厘米)后,真手指能稳定触发,振动日志也佐证了手势识别成功。所以边触默认离边缘 40dp,待在系统手势区之外。
代价是:手柄不在「贴边」位置了,手感差一截。这是小米全面屏手势下的物理限制,不是代码能绕过去的。
root 关手势,或者官改
绕不过去的地方,换个思路。这台机器有 KSU root,实测了一下:
settings put secure navigation_mode 0 (关全面屏手势,改三键导航)
→ 手势桩窗口:10 → 0
手势桩一消失,边缘就没人抢了,手柄可以贴边。代价是底部多出三键。
有人提「做个官改 ROM」,把边触签成系统应用。于是真拿到一个小米 17 Pro 的官改包解了一遍:super.img.zst 解压、lpunpack 拆动态分区、解 erofs 文件系统,取出 framework-res.apk 看签名——证书是 CN=MIUI, O=Xiaomi,小米原厂 platform 签名,不是公开的 testkey。
这意思很明确:这类「加 root 去广告」的轻量官改压根不重签 system(重签会炸 Google 服务和系统应用签名),platform 私钥拿不到。所以边触就算塞进 /system/priv-app,也拿不到系统身份。想真正拿到 platform 签名,只剩自编译 AOSP 系 ROM(比如 LineageOS 用 testkey)——为了一个手势工具自己维护 ROM,不成比例。
所以这条路封死了,回到 root 方案,并且把它做成了产品功能:贴边模式。边触现在有个开关,开了就用 root 切三键导航 + 手柄贴边,关了原样还原导航模式和手柄位置。真机验证过:开 navigation_mode 2→0、手势桩 10→0、手柄离边缘 130→0,关掉全部还原。
现状
- 代码:https://github.com/z7ui/EdgePad
- 版本 0.2,21 个动作、每侧 6 种手势、应用例外、手柄外观五参数,外加「贴边模式(需 root)」
- 不联网,不申请存储权限,数据都在本机
- 仓库里附了一份
docs/OHO-对照.md,把三星 73 个动作逐条标了 A/B/C
这项目最大的收获不是那 21 个动作,而是把那堵墙摸清楚了:系统的边缘手势,第三方应用永远抢不过,官改也救不了(除非自编译 ROM)。知道边界在哪,比多写几个动作值钱。
发表回复