Android SKKにまつわるetc.

AndroidSKKを公開してから数日経ちましたが評判が良かったようで驚いています。
はてなスターをいくつも頂きましてありがとうございます。
今だ日本では売ってもいないハード故、ダウンロード数は少いのですが中には実際にインストールしてくださった方もいるようでありがたいことです。


インストールてくださった方
ちょっと、やったこと/思うこと: AndroidでSKK


こっから余談ですが、

>>androidzaurus様、
そうです。JetBoyは音ゲーだったのです。
1.5正式リリースからtoolsディレクトリにjetなるディレクトリが増えておりまして、
使い方の良くわからないMIDIミキサーのようなツールが入ってます。
Python2.5とwxPythonを入れないと使えません。
ひょっとしてJetBoyの曲データを自由に変えられるのではないかと思ったのですが、
今一使い方がわからなすぎるので放ってあります。

ここまで余談


私自信は今だエミュレータのみにて開発しているので正直実機でどのように動くのかまったくわからない点があるのですが、何か問題がありましたらぜひご連絡下さい。


さて、あのまんまSKKのことは忘れようと思っていたのですが、随分と日本語IMEも盛り上がってきたようです。
特にsakamotoさんのMigemize SoftKeyboardにはやられた!という感じです。


splhack: Android 実験版 IME


儂は何を実装しとったんだろう。
adamrockerさんのSimejiが1.5のソフトウェアキーボードに対応した時も先越された!*1とか思ったのですがまだネットワークを使う、使わないの違いがあったので良かったのですが、Migemizeに関しては同じ辞書(笑)使っているだけに一気にSKKの価値がなくなりましたね。
Migemizeのビデオを見て思ったのですが、Googleは今のAPIでもMigemizeのやり方にてOKと思っている節もあるのではないでしょうか。OpenWnnの存在とは矛盾しますが、今のAPIとMigemizeの親和性はかなり高いかなとか思います。
Migemizeは辞書がFileからメモリに読んでいるように読めたのですが、でかい辞書が使えないとなるとデータベースを使っているSKKにも優位性が出てくるかもしれません。辞書のでかさと更新頻度ならSimejiですが、Simejiはネットワークに全てを委ねる必要があるので一長一短が見事にできあがった感じがします。


でもおかげさまでやる気も出ましてマイナーなバグ取りを開始しました。
まだまだ謎の多い、IME APIです。
せっかくのゴールデンウィークをこれ一本で終らしてしまうのは本意ではないのですが、少しでも人様の役に立つソフトにしたいかと思います。


さてさて、で先日公開したときにはLatinIMEのソースがどこにあるのかわからなかったのですが、公式Blogから直リンクされていました(笑


http://android.git.kernel.org/?p=platform/packages/inputmethods/LatinIME.git;a=tree

(ちょっと戻るとピンインのソースもありますので参考になります。)


真面目に読んでいないのバレバレなのですが、そちらからソースをパチって(Apache2.0なので無問題)SKKでも電話番号入力ではテンキー表示になるようにしました。(res/drawableのライセンスが判らないのですがApache2.0で良いのでしょうか?)
で、本家のLatinのソースを読んで思ったのですが、これソフトキーボードにしか対応していませんね。
onKeydown()を読むと親クラスのsuperに渡しているのでアルファベットだけなら別に問題なく処理されるのでしょうから良いのでしょうが、サンプルのDeadKeyの処理とかないし、onKeyに渡すとかしていないのでcomplicationとか働かないのではないでしょうか?試せませんが。


LatinIMEのソースを読んで驚いたのは辞書の取扱はC++のJNIで書かれていたことです。
C++と言ってもCしか使っていないように見えましたが。
どうもXMLにて初期辞書を提供し、バイナリに書き換えて利用するようです。


なぜ辞書の扱いがJNIなのか私にも本当の理由は良くわかりませんが、何となく想像するにパフォーマンスの問題ではないでしょうか。
SimejiやMigemizeを見た後にキー入力の補完をsqlite3でやってみようと思ったのですが、以下のselect文では非常に遅いことがわかりました。


aで始まる文字列を取得する

select key from dictionary where key like 'a%';


どうもsqlite3をAndroidJavaで用いる場合、db.queryは一瞬で終わるのですが、実際に最初の値をCursorから受けとる時点でSQLが実行され(?)その時点で5秒ルールを越える時間が経ってしまい、強制終了ダイアログが「EditorViewを持つアプリに対して」実行されてしまいます。

辞書のテーブルは以下のように作りました。

create table if not exists dictionary (key text primary key asc not null unique,
 value text not null);


この状態でも、keyに対し明示的にcreate indexを行なっても実行時間に変わりはありませんでした。
最新のsqlite3ではSELECT文にINDEXED BYにてindexを指定できるようですが、Androidのsqlite3では実行できませんでした。
またLIKEをGLOBに変更しても駄目でした。
LIMITにて結果を10個程度に制限するとやっと実用的な50msec程度の時間で実行できますが、それも2文字までです。
3文字目から再び4秒以上の時間がかかってしまいます。
またLIMITを付けた場合にはORDER BYを付けたとしても、辞書の最初のn個の要素が取得できません。


どうも色々と推測の域を出ないのですが、現在のAndroidエミュレータ上にてSQLのqueryを行なう場合に、ディクショナリーがでかく、結果の行数が多くなるような検索を行なう場合、さらに検索対象の列の型がtextの場合に比較対象が3文字以上である場合に恐しい検索時間がかかってしまうようです。
これを解決するためにはLatinIMEのC++の辞書の使い方を理解するか、migemizeのようにメモリにキーだけでも全部入れるか、別にランダムアクセスファイルを実装するか何か考えなくてはならないようです。


そんなわけでマイナーアップデートのリリースは一旦おあずけとなりました。


メタキーの問題


LatinIMEのソースをつらつらと読んでいるとKeyboardViewではlongPressを拾えることがわかりました。
これは即採用です。ソフトウェアキーボードは表示領域の狭さ故にキーをどのように置くかに悩んでしまっていたのですが、列を増やさずともロングプレスを別処理として書けばキーの機能を増やせそうです。
XMLでKeyCodeを複数しておけば連打すると勝手に複数種類の文字入力に対応してくれるのですが、SKKでは'/'などのモード切替のキーが画面に表示されないため、連打のシーケンスにモード切替文字を入れてしまうと本文がどんどん削られてしまうという問題が発生しておりました。


面白いのはLatinIMEにはチュートリアルが存在するようです。
どうもEditorView側のprivateImeOptionsに"com.android.setupwizard:ShowTutorial"が設定されていて初めて利用できるようです。どうもこれはSetupWizardに追加されるようです。
またデフォルトのキーボードにはスマイリーのキー等が設定されて遊んでいます。
通常英語を含むどの言語を選んでもそのロケールにて上書きされてしまうのでこれを見るのは中々至難の技のようですが、このような遊びが残っている部分がGoogleらしくて良いのではないでしょうか。



IME APIのサンプルではtranslateKeyDownというメソッドにてメタキーのプレスを処理しています。
この処理の中で用いているMetakeyKeyListenerの有効性が今一良くわかりません。
MetaStateの状態をLogに書き出すようにしたのですが、ハードキーレベルではKeyboardViewのMetaキーがロックされませんね。
またSYMキーの取扱が微妙で、KeyEventではSYMキーに対するキーコードは63になっているのですが、エミュレータ上のSYMキーを押すと84(SEARCH)が入力されるようでして、現在エミュレータではSYMキーが動きません。
またエミュレータを用いる場合に、PC上のハードキーはシフトキーしかハードキー入力されず、PCのALTキーはエミュレータにハードキーのALTキーとして入力されません。(Windowsの場合)
次にPCのハードキーを用いる場合にSHIFT単体では入力が行われないのでソフトキーのように短時間でSHIFT2回でCAPSロックという技は使えません。この辺が実機だとどうなるのでしょうか?


エミュレータのハードウェアキーはロングプレスができずキーリピートが発生してしまうようです。


ハードキーのメタキーの処理が悩みどころです。
translateKeyDownではメタキーの処理をMetaKeyListenerのstaticメソッドにて処理していますが、なんだかロックの仕方がわかりません。ロックされません。
サンプルではソフトキーのシフトキー入力に対してはhandleShift()が呼ばれていますが、ハードキーに対してはonKeyDown、translateKeyDownでも対応が行われていないようです。
またMetaKeyListenerのドキュメントですがほとんど記述されていません。
これは正式リリースされたにも関わらず、ドキュメントの無いAPIは実装が済んでいない法則からしてこのクラスは完成していません。
事実、clearMetaKeyState(View view, Editable content, int states)などはViewの引数を捨てています。なんじゃこれ。

 293     public void clearMetaKeyState(View view, Editable content, int states) {
 294         clearMetaKeyState(content, states);
 295     }

EditorViewのカーソルは素直に実装されているようでカーソルの上と下にメタの状態が現われているようです。
どうも上がAltで下がシフトのようですが、ここらへんんは文書化されているのでしょうか?
どうもカーソルの上下に/\\/が付くのですが、それが黒塗りになることもあるようです。
どっちがロックでしょう?


KeyboarViewにも今のところisShifted()はあってもisAlted()とかisSysmed()とかはないようです。
つまりViewからはALTとSYMの状態は取れず、mMetaStateからしか取れないようですが、LatinIMEは実装されておらず、またmMetaStateはKeyboardViewと違いCAPの状態にならないように感じます。
Altキーの同時押しはKeyCodeに直接影響を与えているようにも見えますが、どうもログ取ってもルールが良くわかりません。

ここらせん、私がわかっていないのか、ドキュメントがどっかにあるのか、実装がまだまだなのか、どれかだと思います。

*1:それどころかSimejiのビデオを見てネットワークによる変換でもこんなに速いものかと愕然とした