Android FrameworkシリーズのIMF(三)
オリジナル文章、転載は出典を表示してください----
編集ボックスがフォーカスを取得すると、新しいIMEクライアントがcreateされることを知っています.IMFフレームワークはこのイベントをどのように処理しますか.
まずEditTextのコンストラクション関数EditTex->TextView->setText()を呼び出します.
->startInputInner
では、IME部分の流れはどうなっているのでしょうか.
まずconfigure changeのevent呼び出しを受け取るonConfiguration Changed->inputmethodservice.onConfigurationChanged(conf)
doStartInput->initialize->onInitializeInterface->onStartInput->onStartInputView.
onStartInputViewではhandleの様々なIMEのeventを呼び出し、IME自身のonCreate関数をさらに呼び出し、さらに初期化し、receiverのrigisterを行います.
編集ボックスがフォーカスを取得すると、新しいIMEクライアントがcreateされることを知っています.IMFフレームワークはこのイベントをどのように処理しますか.
まずEditTextのコンストラクション関数EditTex->TextView->setText()を呼び出します.
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
->startInputInner
void startInputInner() {
final View view;
synchronized (mH) {
view = mServedView;
// Make sure we have a window token for the served view.
if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
if (view == null) {
if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
return;
}
}
// Now we need to get an input connection from the served view.
// This is complicated in a couple ways: we can't be holding our lock
// when calling out to the view, and we need to make sure we call into
// the view on the same thread that is driving its view hierarchy.
Handler vh = view.getHandler();
if (vh == null) {
// If the view doesn't have a handler, something has changed out
// from under us, so just bail.
if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
return;
}
if (vh.getLooper() != Looper.myLooper()) {
// The view is running on a different thread than our own, so
// we need to reschedule our work for over there.
if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
vh.post(new Runnable() {
public void run() {
startInputInner();
}
});
return;
}
// Okay we are now ready to call into the served view and have it
// do its stuff.
// Life is good: let's hook everything up!
EditorInfo tba = new EditorInfo();
tba.packageName = view.getContext().getPackageName();
tba.fieldId = view.getId();
InputConnection ic = view.onCreateInputConnection(tba); //create InputConnection
if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
synchronized (mH) {
// Now that we are locked again, validate that our state hasn't
// changed.
if (mServedView != view || !mServedConnecting) {
// Something else happened, so abort.
if (DEBUG) Log.v(TAG,
"Starting input: finished by someone else (view="
+ mServedView + " conn=" + mServedConnecting + ")");
return;
}
// If we already have a text box, then this view is already
// connected so we want to restart it.
final boolean initial = mCurrentTextBoxAttribute == null;
// Hook 'em up and let 'er rip.
mCurrentTextBoxAttribute = tba;
mServedConnecting = false;
mServedInputConnection = ic;
IInputContext servedContext;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
mCursorCandStart = -1;
mCursorCandEnd = -1;
mCursorRect.setEmpty();
servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
} else {
servedContext = null;
}
try {
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ ic + " tba=" + tba + " initial=" + initial);
InputBindResult res = mService.startInput(mClient,
servedContext, tba, initial, mCurMethod == null); // IMEservice
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
mBindSequence = res.sequence;
mCurMethod = res.method;
} else {
// This means there is no input method available.
if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
return;
}
}
if (mCurMethod != null && mCompletions != null) {
try {
mCurMethod.displayCompletions(mCompletions);
} catch (RemoteException e) {
}
}
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
}
では、IME部分の流れはどうなっているのでしょうか.
まずconfigure changeのevent呼び出しを受け取るonConfiguration Changed->inputmethodservice.onConfigurationChanged(conf)
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
boolean visible = mWindowVisible;
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
CompletionInfo[] completions = mCurCompletions;
initViews();
mInputViewStarted = false;
mCandidatesViewStarted = false;
if (mInputStarted) {
doStartInput(getCurrentInputConnection(),
getCurrentInputEditorInfo(), true); // startinput, IME input
}
if (visible) {
if (showingInput) {
// If we were last showing the soft keyboard, try to do so again.
if (onShowInputRequested(showFlags, true)) {
showWindow(true);
if (completions != null) {
mCurCompletions = completions;
onDisplayCompletions(completions);
}
} else {
hideWindow();
}
} else if (mCandidatesVisibility == View.VISIBLE) {
// If the candidates are currently visible, make sure the
// window is shown for them.
showWindow(false);
} else {
// Otherwise hide the window.
hideWindow();
}
}
}
doStartInput->initialize->onInitializeInterface->onStartInput->onStartInputView.
onStartInputViewではhandleの様々なIMEのeventを呼び出し、IME自身のonCreate関数をさらに呼び出し、さらに初期化し、receiverのrigisterを行います.