Android向けにSKKを作ってみた

Android1.5 preview releaseがリリースされた時、私はサンプルフォルダを覗いてのけ反った。
Android音ゲーが付いてきているではないか!
"JetBoy"と名付けられたその音ゲーは何の前触れもなくそこにあった。
しかし誰も話題にしない。おかしい。
アンドロイドのサンプルに付いてくるゲームはこれで3つ目なのに。しかも音ゲーだ。
これは俺が盛り上げねば!


問題はこのゲームが音ゲーだとわかりにくい事にあると思われる。
起動するとどう見てもシューティングゲームにしか見えない。
ルールのわかりづらさに加え、ゲームとしても面白くない。
見なかったことにした。

AndroidIMEを作る

Android SDK1.5にてsampleに追加されたのは音ゲーだけでなく、IMEのサンプルが付いてきた。
このIMEは何をするわけでもなく、単にAlt+SPACEを押すとAndroidと入力するだけの品物だ。
しかもサンプルのIMEのソフトウェアキーボードにはAltキーがないのでハードウェアキーを使用しなければ動かないというサンプルとしてどうよという出来である。
何の役にも立たない。
しかし、それだけの機能しかない割には細かな部分が実装されていて、まるで何かの実装から無理矢理サンプルを作りだしたような感じがする素敵なものだ。


サンプルには特に実用性はないのだが、いよいよもってAndroidでも正式なIMEAPIが公開されたことに軽い興奮を覚えた。
そしてInputMethodServiceのドキュメントを読んだときには興奮が収まらなくなってきた。
これは凄い。
これは良いものだ。


何がそんなに良いのか?
AndroidIMEには以下の特徴がある。

  • InputMethodServiceを継承したIMEを複数インストールできる
  • IMEは複数のソフトウェアキーボードを実装できる
  • IMEはソフトウェアキーボードとハードウェアキーボードの両方に対応できる
  • IMEAndroidのサービスの1形態であり、入力対象のViewへの入力を全て横取りできる
  • IMEは入力対象のViewの属性を判別することができ、ソフトウェアキーボードの外観を含めた個別の処理を行える
  • IMEは入力対象のアプリケーションを判別でき、アプリケーション個別の実装を持つことができる。
  • IMEバッチ処理を行なうこうことができ、一連の変換動作をアトミックに行なうことが可能
  • ソフトウェアキーボードはXMLを記述するのみで自由にデザインできる
  • ソフトウェアキーボードはプログラマブルで実行中に機能を変更できる
  • ソフトウェアキーボードは連打に対応しており、ポケベル打ち等がすぐ実装できる


つまりAndroidIMEJavaだけで簡単に実装できて、入力対象に対して臨機応変な対応ができ、ソフトウェアキーボードの構成は全く自由で、しかも一つのIMEがソフトウェアキーボードを複数持つことが可能だ。

相変らずAndroidAPIのデザインには惚れ惚れする。


ただし、Androidには残念な点も存在する。
それはこのSDK1.5のIMEの実際の実装には欧米用のLatinと中国語しか存在しないのだ。


Javaの世界では日本語は特別だった。
必ずJapaneseというリンクが常に用意され、日本語だけはドキュメントが最初から用意されていた。
それがAndroidは中国語が優先になっている。
実は前から問題になっていたが、現在のAndroidのフォントは日本語の漢字がまともに表示できない。
一部のコードポイントに対し、中国語の漢字が割り当てられているのだ。
このため日本語のドキュメントをAndroidで正しく表示することは現在では完全に不可能である。
NTTドコモAuといった大企業が既にAndroidの販売を表明している。
まさか、日本語が正しく表示できないような端末を発売することはないと思われるが、それはつまりフォントはAndroidの無料の部分には含まれないということであろう。
そしてこのようなAndroidの中国(台湾含む)重視の姿勢は現在の中国市場規模と台湾が独占する機器製造メーカーのことを考えれば致しかたないのかもしれない。
しかし、日本の転落をAndroidを通してみるとそのショックは随分と大きく感じる。


さて、悪い話ばかりでもない。
実は既にOpenWnnがAndroidに移植されており、Androidの公式gitに登録されてい"た"。
何かあったのかしれないが、アクセスはできなくなったらしい。
また復活しているかもしれないが、とりあえずOpenWnnは用意されている。
そして恐らく各メーカーはATOKを始めとした独自実装を用意してくると思われる。


今回、AndroidIME APIにとても感動したのでこれは早速何か実装せねばなるまいと思った。
そこで大昔からずっと使い続けているSKKと呼ばれる入力メソッドをAndroidに実装することにした。

SKK

SKKとは"Simple Kana to Kanji conversion program"のことであり、佐藤雅彦氏(現京都大学教授)によって 1987 年に設計、開発された Emacs 用の日本語入力プログラムである。
一部に非常に人気があり、WindowsMacX WindowIMEにも移植され多くの人に今でも使われている。
この文章ももちろんSKKにて入力している。
詳しくはこちらのサイトを参照してほしい。


SKK Open Lab
SKK Openlab - トップ


筆者も大昔にUNIX上のEmacsで体験していらい、WindowsMacSKKで統一している。
SKKの何がそんなに魅了するのか?
それはSKKを使うとコンピュータの推論に多くの程度付き合わずに済むからである。


既存の日本語IMEは長文を要素解析をして変換を行なう。
このため最近のATOK等は文章の前後の意味まで解析を行った上で変換を行なうが一定の変換ミスはどうしても防ぎきることができない。
SKKの特徴は多文節変換を完全に捨ててしまい、シンプルな単文節変換に絞ったことにある。
しかし、ただ単文節の変換を行っても誤変換は防ぎきれない。
SKKは単語や動詞の送り仮名の切れ目をキー入力にて全て指定できる。
このためユーザーの考えた通りの文章を入力していくことができ、誤変換を修正するというプロセスを極力省くことができる。
またSKKは標準で英日変換に対応している。このため外来語をローマ字で入力する必要がないため英語のスペルに強くなるという利点がある。これは個人的に非常に大きな利点だ。

AndroidSKKの使用方法

今回他所様の影響もあって初めて動画を用意してみた。
ソフトウェアキーボードを使うのが筋なのだが、面倒なのでハードウェアキーボードにてバリバリ入力している。
実機を持っていらっしゃる方はぜひソフトウェアキーボードにて暇を潰してみてほしい。


動画を見て頂くとわかると思うが、日本語の入力は基本的にローマ字入力になる。
SKKは入力にviのようなモードがある。
最初は平仮名になっているのでローマ字で平仮名を入力する。
漢字の入力開始にはシフトキーを用いる。例えば「名前」と入力するには「Namae」と最初の"な"を大文字のNで初める。
変換の開始はスペースキーだ。スペースキーを連続で押すと同じ読みの複数の変換候補を辿ることができる。
変換の確定はスペースキー以外だ。次の入力を開始すれば自動的に確定するのでキー入力の打鍵数が少いのもSKKの特徴である。
片仮名を入力するには'q'を押す。漢字変換モードで'q'を押すとそれまでの入力を一変でカナに変換することができる。
英語の入力は'/'で開始する。ここでスペースを押すと辞書に登録されていれば英単語をカナに変換することができる。
英数モードに入るには小文字のLを押す。全角の英数モードに入るには大文字のLだ。それらのモードから抜けるには最下段左端のキーボードをクローズするキーを押す。
Enterキーを押すと無変換で英文字列が入力できる。
最後に漢字が単語でなく送り仮名を用いる場合、送り仮名の開始にて再びシフトキーを用いる。例えば"打つ"と入力する場合には"UTu"と入力する。漢字候補は送り仮名が決定した時に表示される。このため"打って"は"UTte"まで入力して初めて変換候補が表示される。

インストール

AndroidSKKのインストールには二つの手順が必要である。
一つはソースをダウンロードしapkをビルドしてインストールする。
もう一つは辞書のインストールである。

ソースと辞書は以下のGoogle codeにて公開している。
http://code.google.com/p/android-apps-by-minghai/


ビルド後のインストールの方法と設定の方法はadamrocker氏のsimejiに関するページを参照して下さい。
http://www.adamrocker.com/blog/253/simeji-for-android-sdk-15.html


辞書はSKKのLサイズ辞書をsqlite3のデータベースに変換したものだ。
ソース上はハードコードしているのでSoftKeyboard.javaの以下の行を任意で変更してほしい。

    static final String DICTIONARY = "/sdcard/skk_dict.db";


今回辞書にsqlite3を用いたのはファイルの線形探索では鬼のように時間が掛って実用には全く不向きだったためである。
辞書変換アプリ、"DicMaker"もアップしておいたので適当に使用して頂きたい。


辞書のサイズは解凍すると12Mになる。
本体に入れるのは迷うサイズになるのでSDCARD必須と考えている。


なお、Android SDK1.5には実は辞書機構が存在している。
ただし、今回IMEAPIのドキュメントを読んでも辞書の使い方がさっぱりわからなかった。
また辞書の操作がSettings → Locale & Text → User Dictionaryから行えるのだが、この辞書がよく理解できなかった。
この辞書は単語しか登録できず、変換前のテキストと変換後のテキストを登録するようにはできていない。
このため今回は純正の辞書には一切頼らないことにした。


もし辞書について、APIや使用法をご存知ならぜひコメント等にて教えて頂きたい。


実装の解説

IMEはサービスである。
ライフタイムはオフィシャルにて図示されているのでそちらを参照してほしい。


Android Developers Blog: Creating an Input Method
Android Developers Blog: On-screen Input Methods


さて、実装のメインはonKeyDownとonKeyの2つの関数に要約される。
ハードウェアキーを押すとonKeydownが、ソフトウェアキーを押すとonKeyがイベント駆動で呼び出される。


ハードウェアキーとソフトウェアキーでは使用されるコードが異なる。
ハードはキーコードが用いられ、ソフトはUNiCODEのコードポイントが利用される。
その中を取り持つのがtranslateKeyDownである。
またハードはシフトキーなどのメタキーと普通のキーの2つのイベントが発生し、それを結合する必要がある。
ソフトはKeyboardViewがステートを保持しており、プログラムがやはり結合する必要がある。


今回、漢字変換を行なうため、SKKIMEを一つの有限状態遷移機械と見なし、モードを用意した。それがmInputModeだ。
本来ならモード毎に処理を完全に分離したほうが、実装量は増えるかもしれないがソースの保守性が良くなり推奨されるのだが、サンプルプログラムに上書きで書いている内にif文だらけの酷いプログラムになってしまった。
長いメソッドのコードの塊が順依存にて動いているので万が一コードを読んでみようと思った人がいたらどうか覚悟して読んでもらいたい。


入力中の文字や、変換候補を表示するのはCandidateViewだ。
これはただの裸のViewを継承しているまさにViewでしかない。
つまり何も制約されていないので通常Viewでできることならいくらでも好きな設計ができると思われる。
今回は1行しか画面表示していないが、極めれば元SONYの増井さんのPOBoxの用な表示領域が広く選択肢が多いViewも実装できるのではないかと思われる。

http://pub.cozmixng.org/~the-rwiki/rw-cgi.rb?cmd=view;name=POBox

今回、CandidateViewはサンプルに従い2つの利用方法を行っている。
一つはローマ字入力中の入力済みアルファベットの表示。
もう一つは漢字変換時の変換候補の表示である。
実はこれは両方とも同じメソッド、setSuggestionsにて行われている。
つまり候補が一つしかない状態の表示ということだ。
これは無駄が多いので本来なら直したほうが良い。


CandidateViewはただのViewなので候補間の区切りの線も全部自前でCanvasに書いている。
指でスクロールできるので候補が多い場合にはスクロールしたほうが良いかもしれない。
これにはListViewのような慣性が今のところ効かないので修正をリクエストしたい。
今はただのViewなので全部自前で実装しなければならない。
候補を指でタッチするとCandidateView側にてイベント駆動が起こり、onTouchEvent(MotionEvent me)が呼ばれる。
この中でSoftwareKeyboardのpickSuggestionManually(mSelectedIndex)が呼ばれることにより変換が確定されている。


漢字変換はsqlite3を用いて辞書から候補を見つけているだけである。
AndroidのDBの取扱はDBの作成がアプリ毎に決まったディレクトリにしか作成できない。
これはセキュリティ上の理由である。
このため辞書DBはsqlite3のコマンドを直に叩いて作成した。
まず、sqlite3の引数に存在しないdb名を与えると新しいdbを作成する。
次にcreate tableにてkeyとvalueの2つの列だけが存在するテーブルを作成する。
その後、DicMakerにて辞書ファイルをsqlite3のデータベースに全て書き込んだ。
Lサイズの辞書だと変換に約1時間かかった。

InputConnection

IMEの作成において一番理解が必要なのはInputConnectionである。
これはInputMethodService上にてgetCurrentInputConnection()にて取得できる、現在の入力対処となっているEditorへの操作を担当するクラスである。


InputConnectionにおいて大切な概念はComposingだ。
Androidでは入力途中にて確定していない文字列はComposingTextとして扱われる。
これはEditorのView上にて下線が付いた状態で表示される。
IMEではInputConnectionに対して確定していない文字列をInputConnection.setComposingTextにて表示する。
これの凄いところは現在、Editor上にどのような表示がなされていようとsetComposingTextを行なうことで、変換候補の表示を全て一発で更新できることだ。
漢字変換が確定されるまではひたすらsetComposingTextにて下線付きの変換候補表示を行い、変換が決定したとき初めてcommitTextを用いることで実際の入力が確定される。


このとき、実はEditor側から色々なイベントがInputMethodService側に対して駆動される。
しかし、現在正直自分はそれらを理解していない。
この部分が実装で一番苦労した点であり、より細かなドキュメントがあるのならばぜひ教えて頂きたい部分である。


現在、AndroidAPIはcomposingを主体に実装されており、日本語の変換という操作を本当に意識しているのかわからない。
それは現在のUserDictionaryの実装にも現われていると感じている。
将来、AndroidIME APIは大きな変更か拡張があると予想される。
その部分は日本人に大きな影響があろうだろう。


OpenWnnの実装を行っている人達はこの現状をどうお考えになられているのであろうか?
よろしければこっそり内情を教えて頂きたいなと思う。

今後の展開

今回、実はまだ辞書登録が実装されていない。
SKKの利点は辞書にないものはその場ですぐに登録できることにある。
これはsqlite3に対してinsertを実行するだけなのでそのうち対応したい。


またSKKは変換を確定したとき、その変換文字列に対する確定した候補を次の変換時の第一候補にするのであるが、その部分が実装されていない。
これは既存の辞書エントリを更新するだけなので実は簡単なのだがそのうち実装したい。


また最近のSKKは数詞に対する変換の機能が拡張されているようだ。
しかし古いSKKしか知らない私は辞書の#で始まる項目の使い方すら知らないためその部分は今後の拡張予定としたい。


AndroidIME APIはpreferenceをSettingsに自動的に追加できる機能があるのだが、今回まだそれを実装できていない。
それはできるだけ早く実装したい。


またバグは非常に多いと思われるのでもし見つけた方がいらしたらぜひこちらにコメントをお寄せ頂きたい。

最後に


AndroidIME APIは素晴しい。本当に良いものだ。
そして奥が深く、正直自分はまだその全てを理解できているとは思わない。
また恐らく未完成であるので今後、必ず変更が行われるだろう。
それでも良い。このAPIを知ってとりあえず自分の好きなメソッドを実装してほしい。
Javaで簡単にIMEが実装できる。こんなに嬉しことはない。
ぜひ皆さんに挑戦してほしい。


オフィシャルgitのcupcakeのソースに中国語のIME、LATINのIMEを見つけた方はぜひコメントに場所をお知らせください。
私はとりあえず電話番号にはオフィシャルのソフトウェアキーボードを実装したいです。