話せば長いんですが、先日来何度も言ってますように、物理キーボードが便利でなんとなくボタンが多い機械が好きな性癖のある諸兄に人気のある Android 端末であるところの Gemini PDA は、グレートブリテン及び北アイルランド連合王国にある Planet Computers という会社の製品なので、キートップの印字が日本語キーボードだろうが、US キーボードだろうが、出荷時には UK キーボードとして設定されており、セットアップ後にユーザーが物理キーボードの設定で然るべきものを選択しなければ実際の入力とキートップが一致しない困ったちゃんなのであります。
さらに、然るべき設定をしても物理的にキーが一般的な109キーボードの半分以下の53個しかないため、Shift Ctrl Alt Fn と組み合わせないと入力できない記号などが存在し、「長音記号が単独で存在しない」「、と。は並んどけよ」「ShiftとCtrlは逆だろ(宗教)」等々ユーザーには不満が色々あったようです。
その筋の方々は rooted な boot.img で Generic.kl を書き換えて自分の好きなキー配置に変更したりしてたようなんですが、なんせ今どきわざわざ rooted とか面倒臭いし、FOTA できなくなるしなんかねえかなぁってところで 2018/6/2 に rooted 不要な手法を公開された方がおりました。
当時まだ出荷待ちだったので、「なるほど、その昔にそんなことがあったなぁ」ぐらいな感じでしたが、実際に届いてから使ってみると、キーボードだけで通知を出せないとか細かい不満が出てきたので自分でも色々調べてみました。
ここまで前振り。
ここから調べたやつ。
公式の説明はこれ
だいたいの書き方はこれなんですが、system に組み込む用で、 User-installable keymaps で使う OVERLAY の説明は見当たりません。
なので、ソースを見ましょう。
まず、使えるキーワード
libs-input-KeyCharacterMap.cpp – platform-frameworks-native – Git at Google
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
switch (mState) {
case STATE_TOP: {
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
if (keywordToken == "type") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseType();
if (status) return status;
} else if (keywordToken == "map") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseMap();
if (status) return status;
} else if (keywordToken == "key") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseKey();
if (status) return status;
} else {
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
return BAD_VALUE;
}
break;
}
type と map と key が使えるということがわかります。
今回 User-installable keymaps なので、 type は OVERLAY 固定です。
map は Generic.kl のような Key layout Files で定義した key を置き換えることができます。
key は kl で定義した Android key code name のキーを押したときにどうなるのか定義します。
そんなこんなで弄った kcm のサンプルがこちら
type OVERLAY
map key 54 META_LEFT
map key 51 SLASH
(中略)
key 1 {
label: '1'
base: '1'
shift: '!'
fn: '~'
}
(中略)
key DEL {
base: fallback DEL
shift: fallback FORWARD_DEL
}
(中略)
key B {
label: 'B'
base: 'b'
shift, capslock: 'B'
fn: replace BRIGHTNESS_DOWN
}
(後略)
この例では、1行目で type で OVERLAY 指定。
次に map で元は SHIFT_RIGHT で定義されている key 54 を META_LEFT に、 COMMA の key 51 を SLASH に再定義しています。これは公式サイトに記述がないやつなので、またソース読みましょう。
長くなるので、コードは貼りませんが、 map key [usage] Label で定義します。usage は USB HID で使うコードなので今回は気にしません。なお、使える Label はここで定義されてます。
User-installable keymaps の map key で Generic.kl を書き換えるのと同様の効果が得られます。キーの定義をまるごと置き換えるためにはこれが一番ラクチンですね。
最後の key は「1」キーの挙動を定義しています。ラベルは「1」、普通に押したとき(base)は「1」、Shift 押しながらでは「!」で、Fn 押しながらでは「~」が出ますよという定義になってます。ここで、base を「2」に書き換えたら「1」を押したら「2」が表示されるようになります。Alt+1を新たに定義したいときは行を追加します。詳しくは先程の公式サイトの説明をご覧ください。
公式サイトでは "fallback" の説明までしかないんですが、ソースを見ると最近 "replace" というのが追加されています。
115f93eeebf7f33b56ed090de70d6e8c733e5d88 – platform-frameworks-native – Git at Google
fallback はアプリケーションに処理されなかったときに実行される処理なので、アプリケーションに横取りされると実行されません。邪魔されないようにしたい場合は replace を使えば邪魔される前に実行されるようにできるということだと思います。たぶん。きっと。
なので、JP キーボードでキーボードの印字通りに「全角半角」を実装するにはこうやります。
key Q {
label: 'q'
base: 'q'
shift, capslock: 'Q'
fn: replace ZENKAKU_HANKAKU
}
ね?簡単でしょ?もっとも、割り当てたところで IME が対応しているかどうかは別のお話です。
ATOK と Google 日本語入力は対応しているけど Gboard は反応しなかったとかそういうやつ。
「英数」と「かな」も割り当てたけど対応してる IME あるの?
ということで、 map key と replace 使えばだいたいなんとかできるぞーというお話でした。
しかし、Gemini PDA 公式の日本語キーボード設定は雑ですね。
map key 100 ZENKAKU_HANKAKU
って書いてますが Generic.kl の key 100 って ALT_RIGHT ですよ?そんなキーないでしょ!
Planet Computers さんもぜひ replace を使いこなしていただければと存じます。
先日お知らせしたオープンベータのやつに上記日本語キーボード用の設定を追加してアップデートしてます。
ソースコードも公開しました。
8796n-GeminiKCM- User-installable keymaps test for Gemini PDA
要望を Play ストアのコメントかなんかでいただければ対応したレイアウトを追加することもあるかもしれません。
現場からは以上です。

コメント
[…] 先日来さんざんキーマップの変更をしてきまして、キートップの表記と実際の入力が一致しない状態になっております。 […]