Loading core/java/android/view/inputmethod/InputMethodManager.java +88 −43 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; Loading Loading @@ -199,6 +198,30 @@ public final class InputMethodManager { static final Object mInstanceSync = new Object(); static InputMethodManager mInstance; /** * @hide Flag for IInputMethodManager.windowGainedFocus: a view in * the window has input focus. */ public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0; /** * @hide Flag for IInputMethodManager.windowGainedFocus: the focus * is a text editor. */ public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1; /** * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first * time the window has gotten focus. */ public static final int CONTROL_WINDOW_FIRST = 1<<2; /** * @hide Flag for IInputMethodManager.startInput: this is the first * time the window has gotten focus. */ public static final int CONTROL_START_INITIAL = 1<<8; final IInputMethodManager mService; final Looper mMainLooper; Loading @@ -216,7 +239,7 @@ public final class InputMethodManager { /** * Set whenever this client becomes inactive, to know we need to reset * state with the IME then next time we receive focus. * state with the IME the next time we receive focus. */ boolean mHasBeenInactive = true; Loading @@ -242,11 +265,6 @@ public final class InputMethodManager { * we get around to updating things. */ View mNextServedView; /** * True if we should restart input in the next served view, even if the * view hasn't actually changed from the current serve view. */ boolean mNextServedNeedsStart; /** * This is set when we are in the process of connecting, to determine * when we have actually finished. Loading Loading @@ -331,7 +349,7 @@ public final class InputMethodManager { mCurId = res.id; mBindSequence = res.sequence; } startInputInner(); startInputInner(null, 0, 0, 0); return; } case MSG_UNBIND: { Loading Loading @@ -362,7 +380,7 @@ public final class InputMethodManager { } } if (startInput) { startInputInner(); startInputInner(null, 0, 0, 0); } return; } Loading Loading @@ -957,10 +975,11 @@ public final class InputMethodManager { mServedConnecting = true; } startInputInner(); startInputInner(null, 0, 0, 0); } void startInputInner() { boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode, int windowFlags) { final View view; synchronized (mH) { view = mServedView; Loading @@ -969,7 +988,7 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: view=" + view); if (view == null) { if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); return; return false; } } Loading @@ -982,7 +1001,7 @@ public final class InputMethodManager { // 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; return false; } if (vh.getLooper() != Looper.myLooper()) { // The view is running on a different thread than our own, so Loading @@ -990,10 +1009,10 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); vh.post(new Runnable() { public void run() { startInputInner(); startInputInner(null, 0, 0, 0); } }); return; return false; } // Okay we are now ready to call into the served view and have it Loading @@ -1013,12 +1032,14 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: finished by someone else (view=" + mServedView + " conn=" + mServedConnecting + ")"); return; return false; } // If we already have a text box, then this view is already // connected so we want to restart it. final boolean initial = mCurrentTextBoxAttribute == null; if (mCurrentTextBoxAttribute == null) { controlFlags |= CONTROL_START_INITIAL; } // Hook 'em up and let 'er rip. mCurrentTextBoxAttribute = tba; Loading @@ -1040,9 +1061,17 @@ public final class InputMethodManager { try { if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" + ic + " tba=" + tba + " initial=" + initial); InputBindResult res = mService.startInput(mClient, servedContext, tba, initial, true); + ic + " tba=" + tba + " controlFlags=#" + Integer.toHexString(controlFlags)); InputBindResult res; if (windowGainingFocus != null) { res = mService.windowGainedFocus(mClient, windowGainingFocus, controlFlags, softInputMode, windowFlags, tba, servedContext); } else { res = mService.startInput(mClient, servedContext, tba, controlFlags); } if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res != null) { if (res.id != null) { Loading @@ -1051,7 +1080,7 @@ public final class InputMethodManager { } else if (mCurMethod == null) { // This means there is no input method available. if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); return; return true; } } if (mCurMethod != null && mCompletions != null) { Loading @@ -1064,6 +1093,8 @@ public final class InputMethodManager { Log.w(TAG, "IME died: " + mCurId, e); } } return true; } /** Loading Loading @@ -1139,27 +1170,26 @@ public final class InputMethodManager { * @hide */ public void checkFocus() { if (checkFocusNoStartInput()) { startInputInner(); if (checkFocusNoStartInput(false)) { startInputInner(null, 0, 0, 0); } } private boolean checkFocusNoStartInput() { private boolean checkFocusNoStartInput(boolean forceNewFocus) { // This is called a lot, so short-circuit before locking. if (mServedView == mNextServedView && !mNextServedNeedsStart) { if (mServedView == mNextServedView && !forceNewFocus) { return false; } InputConnection ic = null; synchronized (mH) { if (mServedView == mNextServedView && !mNextServedNeedsStart) { if (mServedView == mNextServedView && !forceNewFocus) { return false; } if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView + " next=" + mNextServedView + " restart=" + mNextServedNeedsStart); + " forceNewFocus=" + forceNewFocus); mNextServedNeedsStart = false; if (mNextServedView == null) { finishInputLocked(); // In this case, we used to have a focused view on the window, Loading Loading @@ -1197,6 +1227,7 @@ public final class InputMethodManager { */ public void onWindowFocus(View rootView, View focusedView, int softInputMode, boolean first, int windowFlags) { boolean forceNewFocus = false; synchronized (mH) { if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView + " softInputMode=" + softInputMode Loading @@ -1205,27 +1236,42 @@ public final class InputMethodManager { if (mHasBeenInactive) { if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); mHasBeenInactive = false; mNextServedNeedsStart = true; forceNewFocus = true; } focusInLocked(focusedView != null ? focusedView : rootView); } boolean startInput = checkFocusNoStartInput(); int controlFlags = 0; if (focusedView != null) { controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS; if (focusedView.onCheckIsTextEditor()) { controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR; } } if (first) { controlFlags |= CONTROL_WINDOW_FIRST; } if (checkFocusNoStartInput(forceNewFocus)) { // We need to restart input on the current focus view. This // should be done in conjunction with telling the system service // about the window gaining focus, to help make the transition // smooth. if (startInputInner(rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) { return; } } // For some reason we didn't do a startInput + windowFocusGain, so // we'll just do a window focus gain and call it a day. synchronized (mH) { try { final boolean isTextEditor = focusedView != null && focusedView.onCheckIsTextEditor(); mService.windowGainedFocus(mClient, rootView.getWindowToken(), focusedView != null, isTextEditor, softInputMode, first, windowFlags); controlFlags, softInputMode, windowFlags, null, null); } catch (RemoteException e) { } } if (startInput) { startInputInner(); } } /** @hide */ Loading Loading @@ -1676,8 +1722,7 @@ public final class InputMethodManager { p.println(" mCurMethod=" + mCurMethod); p.println(" mCurRootView=" + mCurRootView); p.println(" mServedView=" + mServedView); p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart + " mNextServedView=" + mNextServedView); p.println(" mNextServedView=" + mNextServedView); p.println(" mServedConnecting=" + mServedConnecting); if (mCurrentTextBoxAttribute != null) { p.println(" mCurrentTextBoxAttribute:"); Loading core/java/com/android/internal/view/IInputMethodManager.aidl +6 −5 Original line number Diff line number Diff line Loading @@ -43,16 +43,17 @@ interface IInputMethodManager { void removeClient(in IInputMethodClient client); InputBindResult startInput(in IInputMethodClient client, IInputContext inputContext, in EditorInfo attribute, boolean initial, boolean needResult); IInputContext inputContext, in EditorInfo attribute, int controlFlags); void finishInput(in IInputMethodClient client); boolean showSoftInput(in IInputMethodClient client, int flags, in ResultReceiver resultReceiver); boolean hideSoftInput(in IInputMethodClient client, int flags, in ResultReceiver resultReceiver); void windowGainedFocus(in IInputMethodClient client, in IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags); // Report that a window has gained focus. If 'attribute' is non-null, // this will also do a startInput. InputBindResult windowGainedFocus(in IInputMethodClient client, in IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, in EditorInfo attribute, IInputContext inputContext); void showInputMethodPickerFromClient(in IInputMethodClient client); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); Loading services/java/com/android/server/InputMethodManagerService.java +78 −33 Original line number Diff line number Diff line Loading @@ -786,7 +786,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return flags; } InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { InputBindResult attachNewInputLocked(boolean initial) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_BIND_INPUT, mCurMethod, mCurClient.binding)); Loading @@ -804,14 +804,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(getAppShowFlags(), null); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) : null; return new InputBindResult(session.session, mCurId, mCurSeq); } InputBindResult startInputLocked(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, boolean initial, boolean needResult) { IInputContext inputContext, EditorInfo attribute, int controlFlags) { // If no method is currently selected, do nothing. if (mCurMethodId == null) { return mNoBinding; Loading @@ -837,6 +834,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); } InputBindResult startInputUncheckedLocked(ClientState cs, IInputContext inputContext, EditorInfo attribute, int controlFlags) { // If no method is currently selected, do nothing. if (mCurMethodId == null) { return mNoBinding; } if (mCurClient != cs) { // If the client is changing, we need to switch over to the new // one. Loading Loading @@ -867,7 +874,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs.curSession != null) { // Fast case: if we are already connected to the input method, // then just return it. return attachNewInputLocked(initial, needResult); return attachNewInputLocked( (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0); } if (mHaveConnection) { if (mCurMethod != null) { Loading Loading @@ -948,13 +956,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public InputBindResult startInput(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, boolean initial, boolean needResult) { IInputContext inputContext, EditorInfo attribute, int controlFlags) { synchronized (mMethodMap) { final long ident = Binder.clearCallingIdentity(); try { return startInputLocked(client, inputContext, attribute, initial, needResult); return startInputLocked(client, inputContext, attribute, controlFlags); } finally { Binder.restoreCallingIdentity(ident); } Loading Loading @@ -997,7 +1003,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurClient.curSession = new SessionState(mCurClient, method, session); mCurClient.sessionRequested = false; InputBindResult res = attachNewInputLocked(true, true); InputBindResult res = attachNewInputLocked(true); if (res.method != null) { executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO( MSG_BIND_METHOD, mCurClient.client, res)); Loading Loading @@ -1482,36 +1488,45 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override public void windowGainedFocus(IInputMethodClient client, IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags) { public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext) { InputBindResult res = null; long ident = Binder.clearCallingIdentity(); try { synchronized (mMethodMap) { if (DEBUG) Slog.v(TAG, "windowGainedFocus: " + client.asBinder() + " viewHasFocus=" + viewHasFocus + " isTextEditor=" + isTextEditor + " controlFlags=#" + Integer.toHexString(controlFlags) + " softInputMode=#" + Integer.toHexString(softInputMode) + " first=" + first + " flags=#" + Integer.toHexString(windowFlags)); + " windowFlags=#" + Integer.toHexString(windowFlags)); ClientState cs = mClients.get(client.asBinder()); if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (mCurClient == null || client == null || mCurClient.client.asBinder() != client.asBinder()) { try { // We need to check if this is the current client with // focus in the window manager, to allow this call to // be made before input is started in it. if (!mIWindowManager.inputMethodClientHasFocus(client)) { Slog.w(TAG, "Client not active, ignoring focus gain of: " + client); return; if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) { // Check with the window manager to make sure this client actually // has a window with focus. If not, reject. This is thread safe // because if the focus changes some time before or after, the // next client receiving focus that has any interest in input will // be calling through here after that change happens. Slog.w(TAG, "Focus gain on non-focused client " + cs.client + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); return null; } } catch (RemoteException e) { } } if (mCurFocusedWindow == windowToken) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client); return; if (attribute != null) { return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); } return null; } mCurFocusedWindow = windowToken; Loading @@ -1527,6 +1542,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE || mRes.getConfiguration().isLayoutSizeAtLeast( Configuration.SCREENLAYOUT_SIZE_LARGE); final boolean isTextEditor = (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0; // We want to start input before showing the IME, but after closing // it. We want to do this after closing it to help the IME disappear // more quickly (not get stuck behind it initializing itself for the // new focused input, even if its window wants to hide the IME). boolean didStart = false; switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: Loading @@ -1542,12 +1565,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { // There is a focus view, and we are navigating forward // into the window, so show the input window for the user. // We only do this automatically if the window an resize // to accomodate the IME (so what the user sees will give // We only do this automatically if the window can resize // to accommodate the IME (so what the user sees will give // them good context without input information being obscured // by the IME) or if running on a large screen where there // is more room for the target window + IME. if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); didStart = true; } showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } break; Loading @@ -1569,18 +1597,35 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if ((softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); didStart = true; } showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } break; case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: if (DEBUG) Slog.v(TAG, "Window asks to always show input"); if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); didStart = true; } showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); break; } if (!didStart && attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); } } } finally { Binder.restoreCallingIdentity(ident); } return res; } @Override Loading Loading
core/java/android/view/inputmethod/InputMethodManager.java +88 −43 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; Loading Loading @@ -199,6 +198,30 @@ public final class InputMethodManager { static final Object mInstanceSync = new Object(); static InputMethodManager mInstance; /** * @hide Flag for IInputMethodManager.windowGainedFocus: a view in * the window has input focus. */ public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0; /** * @hide Flag for IInputMethodManager.windowGainedFocus: the focus * is a text editor. */ public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1; /** * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first * time the window has gotten focus. */ public static final int CONTROL_WINDOW_FIRST = 1<<2; /** * @hide Flag for IInputMethodManager.startInput: this is the first * time the window has gotten focus. */ public static final int CONTROL_START_INITIAL = 1<<8; final IInputMethodManager mService; final Looper mMainLooper; Loading @@ -216,7 +239,7 @@ public final class InputMethodManager { /** * Set whenever this client becomes inactive, to know we need to reset * state with the IME then next time we receive focus. * state with the IME the next time we receive focus. */ boolean mHasBeenInactive = true; Loading @@ -242,11 +265,6 @@ public final class InputMethodManager { * we get around to updating things. */ View mNextServedView; /** * True if we should restart input in the next served view, even if the * view hasn't actually changed from the current serve view. */ boolean mNextServedNeedsStart; /** * This is set when we are in the process of connecting, to determine * when we have actually finished. Loading Loading @@ -331,7 +349,7 @@ public final class InputMethodManager { mCurId = res.id; mBindSequence = res.sequence; } startInputInner(); startInputInner(null, 0, 0, 0); return; } case MSG_UNBIND: { Loading Loading @@ -362,7 +380,7 @@ public final class InputMethodManager { } } if (startInput) { startInputInner(); startInputInner(null, 0, 0, 0); } return; } Loading Loading @@ -957,10 +975,11 @@ public final class InputMethodManager { mServedConnecting = true; } startInputInner(); startInputInner(null, 0, 0, 0); } void startInputInner() { boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode, int windowFlags) { final View view; synchronized (mH) { view = mServedView; Loading @@ -969,7 +988,7 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: view=" + view); if (view == null) { if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); return; return false; } } Loading @@ -982,7 +1001,7 @@ public final class InputMethodManager { // 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; return false; } if (vh.getLooper() != Looper.myLooper()) { // The view is running on a different thread than our own, so Loading @@ -990,10 +1009,10 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); vh.post(new Runnable() { public void run() { startInputInner(); startInputInner(null, 0, 0, 0); } }); return; return false; } // Okay we are now ready to call into the served view and have it Loading @@ -1013,12 +1032,14 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "Starting input: finished by someone else (view=" + mServedView + " conn=" + mServedConnecting + ")"); return; return false; } // If we already have a text box, then this view is already // connected so we want to restart it. final boolean initial = mCurrentTextBoxAttribute == null; if (mCurrentTextBoxAttribute == null) { controlFlags |= CONTROL_START_INITIAL; } // Hook 'em up and let 'er rip. mCurrentTextBoxAttribute = tba; Loading @@ -1040,9 +1061,17 @@ public final class InputMethodManager { try { if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic=" + ic + " tba=" + tba + " initial=" + initial); InputBindResult res = mService.startInput(mClient, servedContext, tba, initial, true); + ic + " tba=" + tba + " controlFlags=#" + Integer.toHexString(controlFlags)); InputBindResult res; if (windowGainingFocus != null) { res = mService.windowGainedFocus(mClient, windowGainingFocus, controlFlags, softInputMode, windowFlags, tba, servedContext); } else { res = mService.startInput(mClient, servedContext, tba, controlFlags); } if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res != null) { if (res.id != null) { Loading @@ -1051,7 +1080,7 @@ public final class InputMethodManager { } else if (mCurMethod == null) { // This means there is no input method available. if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); return; return true; } } if (mCurMethod != null && mCompletions != null) { Loading @@ -1064,6 +1093,8 @@ public final class InputMethodManager { Log.w(TAG, "IME died: " + mCurId, e); } } return true; } /** Loading Loading @@ -1139,27 +1170,26 @@ public final class InputMethodManager { * @hide */ public void checkFocus() { if (checkFocusNoStartInput()) { startInputInner(); if (checkFocusNoStartInput(false)) { startInputInner(null, 0, 0, 0); } } private boolean checkFocusNoStartInput() { private boolean checkFocusNoStartInput(boolean forceNewFocus) { // This is called a lot, so short-circuit before locking. if (mServedView == mNextServedView && !mNextServedNeedsStart) { if (mServedView == mNextServedView && !forceNewFocus) { return false; } InputConnection ic = null; synchronized (mH) { if (mServedView == mNextServedView && !mNextServedNeedsStart) { if (mServedView == mNextServedView && !forceNewFocus) { return false; } if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView + " next=" + mNextServedView + " restart=" + mNextServedNeedsStart); + " forceNewFocus=" + forceNewFocus); mNextServedNeedsStart = false; if (mNextServedView == null) { finishInputLocked(); // In this case, we used to have a focused view on the window, Loading Loading @@ -1197,6 +1227,7 @@ public final class InputMethodManager { */ public void onWindowFocus(View rootView, View focusedView, int softInputMode, boolean first, int windowFlags) { boolean forceNewFocus = false; synchronized (mH) { if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView + " softInputMode=" + softInputMode Loading @@ -1205,27 +1236,42 @@ public final class InputMethodManager { if (mHasBeenInactive) { if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); mHasBeenInactive = false; mNextServedNeedsStart = true; forceNewFocus = true; } focusInLocked(focusedView != null ? focusedView : rootView); } boolean startInput = checkFocusNoStartInput(); int controlFlags = 0; if (focusedView != null) { controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS; if (focusedView.onCheckIsTextEditor()) { controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR; } } if (first) { controlFlags |= CONTROL_WINDOW_FIRST; } if (checkFocusNoStartInput(forceNewFocus)) { // We need to restart input on the current focus view. This // should be done in conjunction with telling the system service // about the window gaining focus, to help make the transition // smooth. if (startInputInner(rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) { return; } } // For some reason we didn't do a startInput + windowFocusGain, so // we'll just do a window focus gain and call it a day. synchronized (mH) { try { final boolean isTextEditor = focusedView != null && focusedView.onCheckIsTextEditor(); mService.windowGainedFocus(mClient, rootView.getWindowToken(), focusedView != null, isTextEditor, softInputMode, first, windowFlags); controlFlags, softInputMode, windowFlags, null, null); } catch (RemoteException e) { } } if (startInput) { startInputInner(); } } /** @hide */ Loading Loading @@ -1676,8 +1722,7 @@ public final class InputMethodManager { p.println(" mCurMethod=" + mCurMethod); p.println(" mCurRootView=" + mCurRootView); p.println(" mServedView=" + mServedView); p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart + " mNextServedView=" + mNextServedView); p.println(" mNextServedView=" + mNextServedView); p.println(" mServedConnecting=" + mServedConnecting); if (mCurrentTextBoxAttribute != null) { p.println(" mCurrentTextBoxAttribute:"); Loading
core/java/com/android/internal/view/IInputMethodManager.aidl +6 −5 Original line number Diff line number Diff line Loading @@ -43,16 +43,17 @@ interface IInputMethodManager { void removeClient(in IInputMethodClient client); InputBindResult startInput(in IInputMethodClient client, IInputContext inputContext, in EditorInfo attribute, boolean initial, boolean needResult); IInputContext inputContext, in EditorInfo attribute, int controlFlags); void finishInput(in IInputMethodClient client); boolean showSoftInput(in IInputMethodClient client, int flags, in ResultReceiver resultReceiver); boolean hideSoftInput(in IInputMethodClient client, int flags, in ResultReceiver resultReceiver); void windowGainedFocus(in IInputMethodClient client, in IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags); // Report that a window has gained focus. If 'attribute' is non-null, // this will also do a startInput. InputBindResult windowGainedFocus(in IInputMethodClient client, in IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, in EditorInfo attribute, IInputContext inputContext); void showInputMethodPickerFromClient(in IInputMethodClient client); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); Loading
services/java/com/android/server/InputMethodManagerService.java +78 −33 Original line number Diff line number Diff line Loading @@ -786,7 +786,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return flags; } InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { InputBindResult attachNewInputLocked(boolean initial) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( MSG_BIND_INPUT, mCurMethod, mCurClient.binding)); Loading @@ -804,14 +804,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(getAppShowFlags(), null); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) : null; return new InputBindResult(session.session, mCurId, mCurSeq); } InputBindResult startInputLocked(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, boolean initial, boolean needResult) { IInputContext inputContext, EditorInfo attribute, int controlFlags) { // If no method is currently selected, do nothing. if (mCurMethodId == null) { return mNoBinding; Loading @@ -837,6 +834,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (RemoteException e) { } return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); } InputBindResult startInputUncheckedLocked(ClientState cs, IInputContext inputContext, EditorInfo attribute, int controlFlags) { // If no method is currently selected, do nothing. if (mCurMethodId == null) { return mNoBinding; } if (mCurClient != cs) { // If the client is changing, we need to switch over to the new // one. Loading Loading @@ -867,7 +874,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (cs.curSession != null) { // Fast case: if we are already connected to the input method, // then just return it. return attachNewInputLocked(initial, needResult); return attachNewInputLocked( (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0); } if (mHaveConnection) { if (mCurMethod != null) { Loading Loading @@ -948,13 +956,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public InputBindResult startInput(IInputMethodClient client, IInputContext inputContext, EditorInfo attribute, boolean initial, boolean needResult) { IInputContext inputContext, EditorInfo attribute, int controlFlags) { synchronized (mMethodMap) { final long ident = Binder.clearCallingIdentity(); try { return startInputLocked(client, inputContext, attribute, initial, needResult); return startInputLocked(client, inputContext, attribute, controlFlags); } finally { Binder.restoreCallingIdentity(ident); } Loading Loading @@ -997,7 +1003,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurClient.curSession = new SessionState(mCurClient, method, session); mCurClient.sessionRequested = false; InputBindResult res = attachNewInputLocked(true, true); InputBindResult res = attachNewInputLocked(true); if (res.method != null) { executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO( MSG_BIND_METHOD, mCurClient.client, res)); Loading Loading @@ -1482,36 +1488,45 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override public void windowGainedFocus(IInputMethodClient client, IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags) { public InputBindResult windowGainedFocus(IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext) { InputBindResult res = null; long ident = Binder.clearCallingIdentity(); try { synchronized (mMethodMap) { if (DEBUG) Slog.v(TAG, "windowGainedFocus: " + client.asBinder() + " viewHasFocus=" + viewHasFocus + " isTextEditor=" + isTextEditor + " controlFlags=#" + Integer.toHexString(controlFlags) + " softInputMode=#" + Integer.toHexString(softInputMode) + " first=" + first + " flags=#" + Integer.toHexString(windowFlags)); + " windowFlags=#" + Integer.toHexString(windowFlags)); ClientState cs = mClients.get(client.asBinder()); if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); } if (mCurClient == null || client == null || mCurClient.client.asBinder() != client.asBinder()) { try { // We need to check if this is the current client with // focus in the window manager, to allow this call to // be made before input is started in it. if (!mIWindowManager.inputMethodClientHasFocus(client)) { Slog.w(TAG, "Client not active, ignoring focus gain of: " + client); return; if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) { // Check with the window manager to make sure this client actually // has a window with focus. If not, reject. This is thread safe // because if the focus changes some time before or after, the // next client receiving focus that has any interest in input will // be calling through here after that change happens. Slog.w(TAG, "Focus gain on non-focused client " + cs.client + " (uid=" + cs.uid + " pid=" + cs.pid + ")"); return null; } } catch (RemoteException e) { } } if (mCurFocusedWindow == windowToken) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client); return; if (attribute != null) { return startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); } return null; } mCurFocusedWindow = windowToken; Loading @@ -1527,6 +1542,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE || mRes.getConfiguration().isLayoutSizeAtLeast( Configuration.SCREENLAYOUT_SIZE_LARGE); final boolean isTextEditor = (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0; // We want to start input before showing the IME, but after closing // it. We want to do this after closing it to help the IME disappear // more quickly (not get stuck behind it initializing itself for the // new focused input, even if its window wants to hide the IME). boolean didStart = false; switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: Loading @@ -1542,12 +1565,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { // There is a focus view, and we are navigating forward // into the window, so show the input window for the user. // We only do this automatically if the window an resize // to accomodate the IME (so what the user sees will give // We only do this automatically if the window can resize // to accommodate the IME (so what the user sees will give // them good context without input information being obscured // by the IME) or if running on a large screen where there // is more room for the target window + IME. if (DEBUG) Slog.v(TAG, "Unspecified window will show input"); if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); didStart = true; } showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } break; Loading @@ -1569,18 +1597,35 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if ((softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); didStart = true; } showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } break; case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: if (DEBUG) Slog.v(TAG, "Window asks to always show input"); if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); didStart = true; } showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); break; } if (!didStart && attribute != null) { res = startInputUncheckedLocked(cs, inputContext, attribute, controlFlags); } } } finally { Binder.restoreCallingIdentity(ident); } return res; } @Override Loading