Titan 2 Elite 键盘折腾记
我其实是黑莓和全键盘手机的爱好者,以前用过黑莓。前阵子偶尔刷到 Unihertz 的 Titan 2 Elite,黑莓风格的实体全键盘,Android 16。我给自己找了个特别正当的理由:做安卓开发总得有台测试机嘛!就这样说服自己冲动了一下,昨天到货了。
问题是,我已经十多年没碰过安卓了。。。好在有 Claude 在旁边协助(好吧,其实它是主力),一路折腾下来,输入法、符号层、终端、语音输入,全都摸了一遍。
今天下午,出现了这么个场面:我电脑的 Claude 用有线连着手机做调试,然后手机上的 Termux 又 ssh 到电脑,然后在这个 ssh 里开着 claude cli,属于嵌套控制了!

说回正事,我最开始只是想把它调成能顺畅打中文、英文和符号的状态。输入法本身(fcitx5-android 加一小段 Lua 符号层)很快就搞定了,真正的麻烦全在终端里:在 Termux 里一按 Alt 就乱套,换到 Termius 里 Fn 层的符号干脆打不出来。折腾着折腾着,我还顺手做了个”按住侧边键说话”的语音输入。这篇就聊聊这套”手机调手机”的调试环境,以及一路踩的坑,值得写一篇,分享给有需要的朋友。
手机 ssh 到 Mac,Mac 再用线控制手机?
我平时的用法是这样的:手机上开 Termux,ssh 到 Mac,在里面跑Claude Code;同时手机用 USB 线插在 Mac 上,adb 是通的。于是 Claude Code 虽然跑在 Mac 上,却能通过 adb 给手机截图、点屏幕、读日志、改设置,而我只是通过手机上的 ssh 窗口跟它说话。
|
1 |
手机 Termux ──ssh(Wi-Fi)──> Mac 上的 Claude Code ──adb(USB)──> 同一台手机 |
好处很直接:改完代码,Claude 当场装到手机上,当场截图看效果,我连电脑都不用坐过去。但这个”自己控制自己”的环,有几个讲究:
1. 别碰手机的网络。我想测”没有网络时语音输入还能不能用”,本来打算开飞行模式,转念一想:飞行模式一开,手机上的 ssh 就断了,我也就跟 Claude 失联了。(自己把自己踢下线,这个画面想想就好笑。)改成只对识别服务这一个 app 断网,用 Android 自带的防火墙链就行:
|
1 2 3 4 5 |
adb shell cmd connectivity set-chain3-enabled true adb shell cmd connectivity set-package-networking-enabled false com.google.android.tts # 测完一定要恢复 adb shell cmd connectivity set-package-networking-enabled true com.google.android.tts adb shell cmd connectivity set-chain3-enabled false |
2. 需要我动手的测试,截屏要放后台跑。我第一次让 Claude 录屏看键盘什么时候弹出来,它把录屏放在前台,我这边看不到任何”开始”的信号,60 秒就这么空过去了。而且我是在手机上跟它说话,测试期间根本没法回消息。后来的做法是:Claude 先把”每 0.7 秒截一张图、文件名带毫秒时间戳”的循环放到后台,告诉我”现在开始操作”,等我做完,它自己对着日志和截图的时间戳分析。
3. ssh 里的 Claude Code 要你重新 /login。我在手机 ssh 里一敲 claude,居然又要我登录。原因是 macOS 上它把登录凭据放在登录钥匙串里,而 ssh 会话没有解锁钥匙串,读不到。在 ssh 会话里解锁一下就好了(密码直接在手机上输,别发给任何人,包括 Claude):
|
1 |
security unlock-keychain ~/Library/Keychains/login.keychain-db |
终端里的键为什么总不对?
Titan 的 Alt+字母是打键帽右上角的小符号(比如 Alt+P 是 @),在普通 app 里一直好使。到了 Termux 里,我按 Alt+P,输入到一半的命令行忽然变成了这样:
|
1 2 |
~ $ ssh lly ← 输入到一半 ~ $ : ← 按了 Alt+P |
退格一下,原来的内容又回来了。我的判断是:终端把 Alt 当 Meta 键,发出去的是 ESC p,被 bash 的 readline 当成了”反向历史搜索”,那个 “:” 就是它的提示符。Termius 里 Alt+V 也一样打不出 ?。所有终端 app 都这么干,毕竟 Emacs 和 readline 的快捷键都靠它。
破局的线索来自 Termux 的源码:它把左 Alt 当 Meta 发 ESC,却把右 Alt 当 AltGr,直接按键盘布局表翻译成字符。Titan 的 Sym 键在 fcitx5 里本来就没人用(fcitx5 根本收不到它),而系统的「Shortcut keys」里能把它改成 Alt。改完之后,Termux 把 Sym 当右 Alt 处理,Sym+P 就打出 @ 了!
(这是个全局设置,Sym 在所有 app 里都变成了第二个 Alt。我同意了,反正它本来就是个空键。)
再说 Fn 层。我把 Fn 改成了 Ctrl,再用 fcitx5 的 Lua 脚本把 Fn+Q 变成 %、Fn+W 变成 ~ 之类(键盘上没有这些符号,还做了个提示小窗,如图)。

这套在 Termux 里完全正常,到 Termius 里却一个符号都打不出来。我先怀疑 Termius 把键拦了,就用一个 Lua 探针把 fcitx5 收到的每个键记下来:
|
1 2 3 4 |
sym=0xffe3 states=0x4 release=false ← Ctrl(Fn)按下 sym=0x51 states=0x4 release=false ← Q(带 Ctrl 状态) sym=0x51 states=0x4 release=true sym=0xffe3 states=0x0 release=true |
结果 fcitx5 明明收到了,Lua 也把 % 提交出去了,屏幕上就是没有。再看屏幕:按住 Fn 的时候,Termius 自己底部那一排的 ctrl 按钮会亮起来。我的猜测是它自己读到了 Ctrl 状态,把提交进来的 % 当成了 Ctrl+%,丢掉了(只是猜测,它不开源,没法验证)。设置里的 Experimental Keyboard Support 和 Hotkeys settings 我都试过了,没用。所以结论很朴素:这类小键盘手机,终端选 Termux。
Termux 里的 ⏵⏵ 为什么变成了方块?
Claude Code 底部 “auto mode” 左边本来有两个三角 ⏵⏵,在 Termux 里显示成了两个方块。这是 U+23F5,Termux 默认字体里没有这个字符。
我试了 JetBrains Mono、DejaVu Sans Mono、Hack 的 Nerd Font 版本,都没有。别瞎换,用 fontTools 查一下 cmap 就知道:
|
1 2 3 |
from fontTools.ttLib import TTFont cmap = TTFont("IosevkaNerdFontMono-Regular.ttf").getBestCmap() print(0x23F5 in cmap) # Iosevka 是 True,上面三个都是 False |
字体有了,怎么塞进 Termux 又是一关:它只认 ~/.termux/font.ttf,这个私有目录 adb 碰不到;Android 16 又不让它读 /sdcard/Download(Permission denied,授予读存储权限也没用)。最后的办法有点绕,让 Termux 通过 USB 反向端口转发,从 Mac 上拉:
|
1 2 3 4 5 |
adb reverse tcp:8765 tcp:8765 python3 -m http.server 8765 --bind 127.0.0.1 --directory ./serve # 在 Mac 上,只绑本机 # 然后在 Termux 里: curl -o .termux/font.ttf http://127.0.0.1:8765/font.ttf termux-reload-settings |
换完字体顺便发现:字符变窄了,终端从 82 列变成了 97 列(用 stty size 量的)。这里有个小插曲:Claude Code 自己的 Bash 工具量出来是 80×24,那是假的,它没有真实的 TTY,只是 tput 回退的默认值。

按住一个键说话,字直接打进 Termux
最有意思的部分。Titan 侧面有个 Func1 键,系统支持把它设成 PTT(按住说话)键:按下时发广播 android.intent.action.PTT.down,松开发 PTT.up。我想让它变成语音输入:按住说话,松开后文字出现在光标处,包括 Termux。
怎么把字送进 Termux?Termux 的终端视图不是普通文本框,无障碍服务的”设置文本”对它没用,能把字送进去的只有输入法的 commitText(fcitx5 打中文走的也是这条路)。所以做法是:收到 PTT.down,就让无障碍服务把系统默认输入法临时切成一个我写的、没有界面的输入法,由它负责录音、识别、commitText;松开后再切回 fcitx5。切换默认输入法需要 WRITE_SECURE_SETTINGS 权限,adb 一条命令就能授予。
键盘每次都弹出来。切回 fcitx5 的瞬间,软键盘弹出来了。读 fcitx5-android 的源码才明白:它刚启动时默认认为”在用虚拟键盘”,要等第一次物理按键才切换。我试了几个办法:
1. 切换期间把系统的”有实体键盘时仍显示屏幕键盘”关掉:没用,fcitx5 自己判断,不看这个开关。
2. 切回之前让我的输入法发一次 requestHideSelf:键盘不弹了,但候选窗跑到了屏幕左上角。原因是 fcitx5 订阅光标位置的动作在 onStartInputView 里,被我拦了就没有位置了。
3. 让输入框失去再获得一次焦点:候选窗直接消失了。
4. 最后可用的:用无障碍服务的 SoftKeyboardController,在切换期间把键盘窗口”按住”(SHOW_MODE_HIDDEN),fcitx5 切回稳定两秒后再放开。
另外,这个TTS的引擎,其实是需要联网在云端完成的。
几个坑
1. adb shell input text 往 Termux 里敲命令时,~ 会变成 #,& 会被 shell 吃掉。用相对路径,一条命令一行。
2. PTT.down / PTT.up 是受保护的系统广播,用 adb 自己发一个来模拟,只会得到:
|
1 |
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.PTT.down from pid=..., uid=2000 |
所以这个功能只能真按 Func1 来测。
3. 改 Sym / Fn / Func1 的「Shortcut keys」界面没有桌面图标,设置里搜得到,但点进去落在别的页面,我是用 adb 打开的:adb shell am start -n com.agui.shortcutsettings/.ui.EntryAppActivity。
4. dumpsys input_method 里的 mInputShown=true 不代表软键盘在屏幕上:fcitx5 平时也有一条大约 50 像素高的输入法窗口常驻。判断真键盘得看无障碍窗口的高度,超过 250 像素才算。
最后坦白一下,还有两个毛病没修好:每次说完话 fcitx5 都会回到英文模式,候选窗还会飘在屏幕左上角,要在 Termux 里切一下会话才回到底部。。。现在我的办法就是认了,说完话按一下 Fn+Space。这问题等以后再修吧。。。
依云 在 2026年11月01日 23:12 说:【 】
实体键盘的Android耶,有大量打字需求又不方便使用外接键盘的时候,应该会挺棒的~
话说你在termux里执行termux-setup-storage,应该会问你要存储的权限的,授权之后就能访问/sdcard了。