Skip to content

Commit 7663d80

Browse files
author
Dianne Hackborn
committed
Fix issue #6048808: sometimes auto-correct is inactive
My previous change to speed up the time the IME is dismissed was fundamentally flawed. That change basically switched the order the application called the input method manager service from doing startInput() and then windowGainedFocus(), to first windowGainedFocus() and then startInput(). The problem is that the service relies on startInput() being done first, since this is the mechanism to set up the new input focus, and windowGainedFocus() is just updating the IME visibility state after that is done. However, by doing the startInput() first, that means in the case where we are going to hide the IME we must first wait for the IME to re-initialize editing on whatever input has focus in the new window. To address this, the change here tries to find a half-way point between the two. We now do startInput() after windowGainedFocus() only when this will result in the window being hidden. It is not as easy as that, though, because these are calls on to the system service from the application. So being able to do that meant a fair amount of re-arranging of this part of the protocol with the service. Now windowGainedFocus() is called with all of the information also needed for startInput(), and takes care of performing both operations. The client-side code is correspondingly rearranged so that the guts of it where startInput() is called can instead call the windowGainedFocus() entry if appropriate. So... in theory this is safer than the previous change, since it should not be impacting the behavior as much. In practice, however, we are touching and re-arranging a lot more code, and "should" is not a promise. Change-Id: Icb58bef75ef4bf9979f3e2ba88cea20db2e2c3fb
1 parent 566328a commit 7663d80

File tree

3 files changed

+172
-81
lines changed

3 files changed

+172
-81
lines changed

core/java/android/view/inputmethod/InputMethodManager.java

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.android.internal.view.InputBindResult;
2727

2828
import android.content.Context;
29-
import android.content.pm.PackageManager;
3029
import android.graphics.Rect;
3130
import android.os.Bundle;
3231
import android.os.Handler;
@@ -198,7 +197,31 @@ public final class InputMethodManager {
198197

199198
static final Object mInstanceSync = new Object();
200199
static InputMethodManager mInstance;
201-
200+
201+
/**
202+
* @hide Flag for IInputMethodManager.windowGainedFocus: a view in
203+
* the window has input focus.
204+
*/
205+
public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0;
206+
207+
/**
208+
* @hide Flag for IInputMethodManager.windowGainedFocus: the focus
209+
* is a text editor.
210+
*/
211+
public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1;
212+
213+
/**
214+
* @hide Flag for IInputMethodManager.windowGainedFocus: this is the first
215+
* time the window has gotten focus.
216+
*/
217+
public static final int CONTROL_WINDOW_FIRST = 1<<2;
218+
219+
/**
220+
* @hide Flag for IInputMethodManager.startInput: this is the first
221+
* time the window has gotten focus.
222+
*/
223+
public static final int CONTROL_START_INITIAL = 1<<8;
224+
202225
final IInputMethodManager mService;
203226
final Looper mMainLooper;
204227

@@ -216,7 +239,7 @@ public final class InputMethodManager {
216239

217240
/**
218241
* Set whenever this client becomes inactive, to know we need to reset
219-
* state with the IME then next time we receive focus.
242+
* state with the IME the next time we receive focus.
220243
*/
221244
boolean mHasBeenInactive = true;
222245

@@ -242,11 +265,6 @@ public final class InputMethodManager {
242265
* we get around to updating things.
243266
*/
244267
View mNextServedView;
245-
/**
246-
* True if we should restart input in the next served view, even if the
247-
* view hasn't actually changed from the current serve view.
248-
*/
249-
boolean mNextServedNeedsStart;
250268
/**
251269
* This is set when we are in the process of connecting, to determine
252270
* when we have actually finished.
@@ -331,7 +349,7 @@ public void handleMessage(Message msg) {
331349
mCurId = res.id;
332350
mBindSequence = res.sequence;
333351
}
334-
startInputInner();
352+
startInputInner(null, 0, 0, 0);
335353
return;
336354
}
337355
case MSG_UNBIND: {
@@ -362,7 +380,7 @@ public void handleMessage(Message msg) {
362380
}
363381
}
364382
if (startInput) {
365-
startInputInner();
383+
startInputInner(null, 0, 0, 0);
366384
}
367385
return;
368386
}
@@ -952,10 +970,11 @@ public void restartInput(View view) {
952970
mServedConnecting = true;
953971
}
954972

955-
startInputInner();
973+
startInputInner(null, 0, 0, 0);
956974
}
957975

958-
void startInputInner() {
976+
boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
977+
int windowFlags) {
959978
final View view;
960979
synchronized (mH) {
961980
view = mServedView;
@@ -964,7 +983,7 @@ void startInputInner() {
964983
if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
965984
if (view == null) {
966985
if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
967-
return;
986+
return false;
968987
}
969988
}
970989

@@ -977,18 +996,18 @@ void startInputInner() {
977996
// If the view doesn't have a handler, something has changed out
978997
// from under us, so just bail.
979998
if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
980-
return;
999+
return false;
9811000
}
9821001
if (vh.getLooper() != Looper.myLooper()) {
9831002
// The view is running on a different thread than our own, so
9841003
// we need to reschedule our work for over there.
9851004
if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
9861005
vh.post(new Runnable() {
9871006
public void run() {
988-
startInputInner();
1007+
startInputInner(null, 0, 0, 0);
9891008
}
9901009
});
991-
return;
1010+
return false;
9921011
}
9931012

9941013
// Okay we are now ready to call into the served view and have it
@@ -1008,12 +1027,14 @@ public void run() {
10081027
if (DEBUG) Log.v(TAG,
10091028
"Starting input: finished by someone else (view="
10101029
+ mServedView + " conn=" + mServedConnecting + ")");
1011-
return;
1030+
return false;
10121031
}
1013-
1032+
10141033
// If we already have a text box, then this view is already
10151034
// connected so we want to restart it.
1016-
final boolean initial = mCurrentTextBoxAttribute == null;
1035+
if (mCurrentTextBoxAttribute == null) {
1036+
controlFlags |= CONTROL_START_INITIAL;
1037+
}
10171038

10181039
// Hook 'em up and let 'er rip.
10191040
mCurrentTextBoxAttribute = tba;
@@ -1033,9 +1054,17 @@ public void run() {
10331054

10341055
try {
10351056
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
1036-
+ ic + " tba=" + tba + " initial=" + initial);
1037-
InputBindResult res = mService.startInput(mClient,
1038-
servedContext, tba, initial, true);
1057+
+ ic + " tba=" + tba + " controlFlags=#"
1058+
+ Integer.toHexString(controlFlags));
1059+
InputBindResult res;
1060+
if (windowGainingFocus != null) {
1061+
res = mService.windowGainedFocus(mClient, windowGainingFocus,
1062+
controlFlags, softInputMode, windowFlags,
1063+
tba, servedContext);
1064+
} else {
1065+
res = mService.startInput(mClient,
1066+
servedContext, tba, controlFlags);
1067+
}
10391068
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
10401069
if (res != null) {
10411070
if (res.id != null) {
@@ -1044,7 +1073,7 @@ public void run() {
10441073
} else if (mCurMethod == null) {
10451074
// This means there is no input method available.
10461075
if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
1047-
return;
1076+
return true;
10481077
}
10491078
}
10501079
if (mCurMethod != null && mCompletions != null) {
@@ -1057,6 +1086,8 @@ public void run() {
10571086
Log.w(TAG, "IME died: " + mCurId, e);
10581087
}
10591088
}
1089+
1090+
return true;
10601091
}
10611092

10621093
/**
@@ -1133,27 +1164,26 @@ void scheduleCheckFocusLocked(View view) {
11331164
* @hide
11341165
*/
11351166
public void checkFocus() {
1136-
if (checkFocusNoStartInput()) {
1137-
startInputInner();
1167+
if (checkFocusNoStartInput(false)) {
1168+
startInputInner(null, 0, 0, 0);
11381169
}
11391170
}
11401171

1141-
private boolean checkFocusNoStartInput() {
1172+
private boolean checkFocusNoStartInput(boolean forceNewFocus) {
11421173
// This is called a lot, so short-circuit before locking.
1143-
if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1174+
if (mServedView == mNextServedView && !forceNewFocus) {
11441175
return false;
11451176
}
11461177

11471178
InputConnection ic = null;
11481179
synchronized (mH) {
1149-
if (mServedView == mNextServedView && !mNextServedNeedsStart) {
1180+
if (mServedView == mNextServedView && !forceNewFocus) {
11501181
return false;
11511182
}
11521183
if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
11531184
+ " next=" + mNextServedView
1154-
+ " restart=" + mNextServedNeedsStart);
1185+
+ " forceNewFocus=" + forceNewFocus);
11551186

1156-
mNextServedNeedsStart = false;
11571187
if (mNextServedView == null) {
11581188
finishInputLocked();
11591189
// In this case, we used to have a focused view on the window,
@@ -1184,13 +1214,14 @@ void closeCurrentInput() {
11841214
} catch (RemoteException e) {
11851215
}
11861216
}
1187-
1217+
11881218
/**
11891219
* Called by ViewAncestor when its window gets input focus.
11901220
* @hide
11911221
*/
11921222
public void onWindowFocus(View rootView, View focusedView, int softInputMode,
11931223
boolean first, int windowFlags) {
1224+
boolean forceNewFocus = false;
11941225
synchronized (mH) {
11951226
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
11961227
+ " softInputMode=" + softInputMode
@@ -1199,27 +1230,42 @@ public void onWindowFocus(View rootView, View focusedView, int softInputMode,
11991230
if (mHasBeenInactive) {
12001231
if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
12011232
mHasBeenInactive = false;
1202-
mNextServedNeedsStart = true;
1233+
forceNewFocus = true;
12031234
}
12041235
focusInLocked(focusedView != null ? focusedView : rootView);
12051236
}
1237+
1238+
int controlFlags = 0;
1239+
if (focusedView != null) {
1240+
controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
1241+
if (focusedView.onCheckIsTextEditor()) {
1242+
controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
1243+
}
1244+
}
1245+
if (first) {
1246+
controlFlags |= CONTROL_WINDOW_FIRST;
1247+
}
12061248

1207-
boolean startInput = checkFocusNoStartInput();
1249+
if (checkFocusNoStartInput(forceNewFocus)) {
1250+
// We need to restart input on the current focus view. This
1251+
// should be done in conjunction with telling the system service
1252+
// about the window gaining focus, to help make the transition
1253+
// smooth.
1254+
if (startInputInner(rootView.getWindowToken(),
1255+
controlFlags, softInputMode, windowFlags)) {
1256+
return;
1257+
}
1258+
}
12081259

1260+
// For some reason we didn't do a startInput + windowFocusGain, so
1261+
// we'll just do a window focus gain and call it a day.
12091262
synchronized (mH) {
12101263
try {
1211-
final boolean isTextEditor = focusedView != null &&
1212-
focusedView.onCheckIsTextEditor();
12131264
mService.windowGainedFocus(mClient, rootView.getWindowToken(),
1214-
focusedView != null, isTextEditor, softInputMode, first,
1215-
windowFlags);
1265+
controlFlags, softInputMode, windowFlags, null, null);
12161266
} catch (RemoteException e) {
12171267
}
12181268
}
1219-
1220-
if (startInput) {
1221-
startInputInner();
1222-
}
12231269
}
12241270

12251271
/** @hide */
@@ -1649,8 +1695,7 @@ void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
16491695
p.println(" mCurMethod=" + mCurMethod);
16501696
p.println(" mCurRootView=" + mCurRootView);
16511697
p.println(" mServedView=" + mServedView);
1652-
p.println(" mNextServedNeedsStart=" + mNextServedNeedsStart
1653-
+ " mNextServedView=" + mNextServedView);
1698+
p.println(" mNextServedView=" + mNextServedView);
16541699
p.println(" mServedConnecting=" + mServedConnecting);
16551700
if (mCurrentTextBoxAttribute != null) {
16561701
p.println(" mCurrentTextBoxAttribute:");

core/java/com/android/internal/view/IInputMethodManager.aidl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,17 @@ interface IInputMethodManager {
4343
void removeClient(in IInputMethodClient client);
4444

4545
InputBindResult startInput(in IInputMethodClient client,
46-
IInputContext inputContext, in EditorInfo attribute,
47-
boolean initial, boolean needResult);
46+
IInputContext inputContext, in EditorInfo attribute, int controlFlags);
4847
void finishInput(in IInputMethodClient client);
4948
boolean showSoftInput(in IInputMethodClient client, int flags,
5049
in ResultReceiver resultReceiver);
5150
boolean hideSoftInput(in IInputMethodClient client, int flags,
5251
in ResultReceiver resultReceiver);
53-
void windowGainedFocus(in IInputMethodClient client, in IBinder windowToken,
54-
boolean viewHasFocus, boolean isTextEditor,
55-
int softInputMode, boolean first, int windowFlags);
52+
// Report that a window has gained focus. If 'attribute' is non-null,
53+
// this will also do a startInput.
54+
InputBindResult windowGainedFocus(in IInputMethodClient client, in IBinder windowToken,
55+
int controlFlags, int softInputMode, int windowFlags,
56+
in EditorInfo attribute, IInputContext inputContext);
5657

5758
void showInputMethodPickerFromClient(in IInputMethodClient client);
5859
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);

0 commit comments

Comments
 (0)