Welcome to FutureAppLaboratory

v=(*^ワ^*)=v

Mozc完全解説01

| Comments

What is mozc?

Mozc is a Japanese Input Method Editor (IME) designed for multi-platform such as Chromium OS, Android, Windows, Mac and Linux. This open-source project originates from Google Japanese Input.

(1) ソースを手に入れよう

ここからダウンロード mozc project from google code

今回使うmozcのバージョンは

1
2
3
4
5
6
7
8
9
MAJOR=1
MINOR=10
BUILD=1390
REVISION=103
ANDROID_VERSION_CODE=1794
FLAG=RELEASE
TARGET_PLATFORM=Android
ANDROID_APPLICATION_ID=org.mozc.android.inputmethod.japanese
ANDROID_SERVICE_NAME=org.mozc.android.inputmethod.japanese.MozcService

android版のビルドはMac非対応なので、vagrantを通してVMのubuntuでビルドします。
こちらのリポジトリを使えば楽です。 https://github.com/niw/mozc

デフォルトでビルドすると、eclipseで開けるプロジェクトは作ってくれないので、手動で作りました。こちらからダウンロードできます。
https://dl.dropboxusercontent.com/u/40868784/mozc_android.zip フォルダ自体とその中のprotobuff, resources_ossをeclipseにインポートすればOK。後はMozcProxyPreferenceActivityというプロジェクトを実行するだけ。

※ ビルドする際に、”SDK does not have any Build Tools installed.“が表示されたら、Build Toolsをダウンロードする必要があります。

1
2
3
4
5
6
$ /usr/bin/curl -L -O 'http://dl.google.com/android/repository/build-tools_r19-linux.zip' && unzip build-tools_r19-linux.zip -d /opt/android/android-sdk-linux/build-tools/ 
$ mv /opt/android/android-sdk-linux/build-tools/android-4.4 /opt/android/android-sdk-linux/build-tools/19.0.0

or

$ /opt/android/android-sdk-linux/tools/android update sdk -u -t build-tools-20.0.0

(2) キーのタッチから、変換候補がもらうまで、簡単な解析をやります

android側のフリックキーボードで「あ」を押し、タッチイベントが発生する。そうすると、以下のコードが順番的に実行されます。

org.mozc.android.inputmethod.japanese.ViewManager:260:onKey
ここでViewのタッチエベントをキャッチする。

org.mozc.android.inputmethod.japanese.MozcService:245:onKeyEvent
メーンサービスでKeyEventを処理する。

1
2
3
4
5
6
7
8
9
@Override
public void onKeyEvent( ProtoCommands.KeyEvent mozcKeyEvent, KeyEventInterface keyEvent, KeyboardSpecification keyboardSpecification, List<? extends TouchEvent> touchEventList) {

...

  sendKeyWithKeyboardSpecification(mozcKeyEvent, keyEvent,
                                   keyboardSpecification, getResources().getConfiguration(),
                                   touchEventList);
}


org.mozc.android.inputmethod.japanese.MozcService:968:sendKeyWithKeyboardSpecification

1
2
3
4
5
6
7
  /**
   * Sends mozcKeyEvent and/or Request to mozc server.
   *
   * This skips to send request if the given keyboard specification is
   * same as before.
   */
  boolean sendKeyWithKeyboardSpecification( ...


org.mozc.android.inputmethod.japanese.session.SessionExecutor:626:sendKey

1
2
3
4
5
6
7
8
9
  /**
   * Sends {@code SEND_KEY} command to the server asynchronously.
   */
  public void sendKey(ProtoCommands.KeyEvent mozcKeyEvent, KeyEventInterface triggeringKeyEvent, List<? extends TouchEvent> touchEventList, EvaluationCallback callback) {

    ...

    evaluateAsynchronously(inputBuilder, triggeringKeyEvent, callback);
  }


org.mozc.android.inputmethod.japanese.session.SessionExecutor:612:evaluateAsynchronously
asyncなので、handlerに渡します。

1
2
3
4
5
6
  void evaluateAsynchronously(Input.Builder inputBuilder, KeyEventInterface triggeringKeyEvent, EvaluationCallback callback) {

    ...

    handler.sendMessage(handler.obtainMessage(type, context));
  }


org.mozc.android.inputmethod.japanese.session.SessionExecutor:300:handlerMessage
SessionExecutorの中のExecutorMainCallbackがmessageを取り、処理する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  /**
   * A core implementation of evaluation executing process.
   *
   * <p>This class takes messages from the UI thread. By using {@link SessionHandler},
   * it evaluates the {@link Input} in a message, and then returns the result with notifying
   * the UI thread if necessary.
   * All evaluations should be done with this class in order to keep evaluation in the incoming
   * order.
   * Package private for testing purpose.
   */
  static class ExecutorMainCallback implements Handler.Callback {

    ...

    @Override
    public boolean handleMessage(Message message) {
      // Dispatch the message.
      switch (message.what) {
        case INITIALIZE_SESSION_HANDLER:
          sessionHandler.initialize(Context.class.cast(message.obj));
          break;
        case DELETE_SESSION:
          deleteSession();
          break;
        case EVALUATE_ASYNCHRONOUSLY:
        case EVALUATE_KEYEVENT_ASYNCHRONOUSLY:
          evaluateAsynchronously(
              AsynchronousEvaluationContext.class.cast(message.obj), message.getTarget());
    ...


org.mozc.android.inputmethod.japanese.session.SessionExecutor:459

1
  context.outCommand = evaluate(inputBuilder.build());


org.mozc.android.inputmethod.japanese.session.SessionExecutor:333
ここで、JNIを通してnative変換エンジンと通信する。(同期で)

1
2
3
4
5
private Command evaluate(Input input) {
    ...
    Command outCommand = sessionHandler.evalCommand(inCommand);
    ...
}


org.mozc.android.inputmethod.japanese.session.LocalSessionHandler:100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  @Override
  public Command evalCommand(Command command) {
    byte[] inBytes = command.toByteArray();
    byte[] outBytes = null;
    outBytes = MozcJNI.evalCommand(inBytes);
    try {
      return Command.parseFrom(outBytes);
    } catch (InvalidProtocolBufferException e) {
      MozcLog.w("InvalidProtocolBufferException is thrown."
          + "We can do nothing so just return default instance.");
      MozcLog.w(e.toString());
      return Command.getDefaultInstance();
    }
  }

ここでのCommand.parseFrom(outBytes)をデーバッグすると、以下の入力、出力、変換候補リストが分かります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  input {
    type: SEND_KEY
    id: 6381052470309579002
    key {
        key_code: 49
      }
    touch_events {
        source_id: 2
        stroke {
              action: TOUCH_DOWN
              x: 0.30168644
              y: 0.16023746
              timestamp: 0
            }
        stroke {
              action: TOUCH_UP
              x: 0.30168644
              y: 0.16023746
              timestamp: 25
            }
      }
  }
  output {
    id: 6381052470309579002
    mode: HIRAGANA
    consumed: true
    preedit {
      cursor: 1
      Segment {
            annotation: UNDERLINE
            value: "\343\201\204"
            value_length: 1
            key: "\343\201\204"
          }
     }
     candidates {
      size: 151
      Candidate {
            index: 0
            value: "\343\201\204\343\202\215"
            annotation {
                    description: "Suffix"
                  }
            id: 0
          }
      Candidate {
            index: 1
            value: "\343\201\204"
            annotation {
                    description: "Realtime"
                  }
            id: 1
          }
      Candidate {
            index: 2
            value: "\350\203\203"
            annotation {
                    description: "Realtime"
                  }
            id: 2
          }
      Candidate {
            index: 3
            value: "\343\201\203"
            annotation {
                    description:
                "\346\215\250\343\201\246\344\273\256\345\220\215
                    \345\260\217\346\233\270\343\201\215\346\226\207\345\255\227"
                  }
            id: 3
          }

      ... // 省略

      Candidate {
            index: 8
            value: "\344\275\215"
            annotation {
                    description: "Realtime"
                  }
            id: 8
          }
      position: 0
      category: SUGGESTION
      display_type: MAIN
      footer {
            label:
          "Tab\343\202\255\343\203\274\343\201\247\351\201\270\346\212\236"
          }
    }
  status {
      activated: true
      mode: HIRAGANA
      comeback_mode: HIRAGANA
    }
  all_candidate_words {
      candidates {
            id: 0
            index: 0
            key: "\343\201\204\343\202\215"
            value: "\343\201\204\343\202\215"
            annotation {
                    description: "Suffix"
                  }
          }
      candidates {
            id: 1
            index: 1
            value: "\343\201\204"
            annotation {
                    description: "Realtime"
                  }
          }
      candidates {
            id: 2
            index: 2
            value: "\350\203\203"
            annotation {
                    description: "Realtime"
                  }
          }
      candidates {
            id: 3
            index: 3
            key: "\343\201\203"
            value: "\343\201\203"
            annotation {
                    description:
                "\346\215\250\343\201\246\344\273\256\345\220\215
                    \345\260\217\346\233\270\343\201\215\346\226\207\345\255\227"
                  }
          }

      ... // 省略

第一回目はここまで。(^O^)/

Comments