diff options
| author | 2017-03-07 00:52:09 -0800 | |
|---|---|---|
| committer | 2017-03-07 00:52:09 -0800 | |
| commit | 5cfc1b4c104471a6f4d0edc8a1b60cbc9a54d78f (patch) | |
| tree | 2f3c4134c3d3ce50d53f8eea6a237015a49f42fb | |
| parent | 25e333cb3ca65dde8d3b448d119180918d2f7356 (diff) | |
Deprecate IMM#showSoftInputUnchecked() part 1
One of non-intuitive behaviors of InputMethodManager#showSoftInput() is
that it always fails if you pass a view that does not window focus. For
example, the following code does not show the software keyboard because
the target window is not yet focused during Activity#onCreate().
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
final EditText editText = new EditText(this);
layout.addView(editText);
final InputMethodManager imm =
getSystemService(InputMethodManager.class);
editText.requestFocus();
// This will be ignored because the target window has not gained
// focus yet.
imm.showSoftInput(editText, 0);
setContentView(layout);
}
Some platform components, however, have worked around by this limitation
by relying on IMM#showSoftInputUnchecked(), which just bypasses internal
IME focus handling flows.
Bypassing standard event handlign flow is indeed problematic, and has
actually contributed to issues such as Bug 35903813 and Bug 31915865
directly or indirectly. In order to make IME focus handling more
deterministic and reliable, IMM#showSoftInputUnchecked() really needs to
be deprecated.
As the initial step to deprecate IMM#showSoftInputUnchecked(), this CL
removes the dependency on it from SearchView. Instead of immediatelly
issuing delayed tasks that call IMM#showSoftInputUnchecked(), this CL
uses View#onCreateInputConnection() as a signal that SearchAutoComplete
now owns IME focus.
Test: Open System Settings and tap the search icon to make sure that
the software keyboard will be shown automatically.
Test: cts-tradefed run cts -m CtsWidgetTestCases --test android.widget.cts.SearchViewTest
Bug: r.android.com/223701
Bug: 36015425
Bug: 31756425
Fixes: 35903813
Change-Id: I20983e4ce1d625e098a8c2335ce75994cfa43235
| -rw-r--r-- | core/java/android/widget/SearchView.java | 101 |
1 files changed, 63 insertions, 38 deletions
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 38221383df3f..519a7dd8be43 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -58,6 +58,7 @@ import android.view.TouchDelegate; import android.view.View; import android.view.ViewConfiguration; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; @@ -159,20 +160,6 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { private SearchableInfo mSearchable; private Bundle mAppSearchData; - /* - * SearchView can be set expanded before the IME is ready to be shown during - * initial UI setup. The show operation is asynchronous to account for this. - */ - private Runnable mShowImeRunnable = new Runnable() { - public void run() { - InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); - - if (imm != null) { - imm.showSoftInputUnchecked(0, null); - } - } - }; - private Runnable mUpdateDrawableStateRunnable = new Runnable() { public void run() { updateFocusedState(); @@ -497,9 +484,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { @Override public void clearFocus() { mClearingFocus = true; - setImeVisibility(false); super.clearFocus(); mSearchSrcTextView.clearFocus(); + mSearchSrcTextView.setImeVisibility(false); mClearingFocus = false; } @@ -967,19 +954,6 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { super.onDetachedFromWindow(); } - private void setImeVisibility(final boolean visible) { - if (visible) { - post(mShowImeRunnable); - } else { - removeCallbacks(mShowImeRunnable); - InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); - - if (imm != null) { - imm.hideSoftInputFromWindow(getWindowToken(), 0); - } - } - } - /** * Called by the SuggestionsAdapter * @hide @@ -1286,7 +1260,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { if (mSearchable != null) { launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString()); } - setImeVisibility(false); + mSearchSrcTextView.setImeVisibility(false); dismissSuggestions(); } } @@ -1311,7 +1285,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { } else { mSearchSrcTextView.setText(""); mSearchSrcTextView.requestFocus(); - setImeVisibility(true); + mSearchSrcTextView.setImeVisibility(true); } } @@ -1319,7 +1293,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { private void onSearchClicked() { updateViewsVisibility(false); mSearchSrcTextView.requestFocus(); - setImeVisibility(true); + mSearchSrcTextView.setImeVisibility(true); if (mOnSearchClickListener != null) { mOnSearchClickListener.onClick(this); } @@ -1477,7 +1451,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { if (mOnSuggestionListener == null || !mOnSuggestionListener.onSuggestionClick(position)) { launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); - setImeVisibility(false); + mSearchSrcTextView.setImeVisibility(false); dismissSuggestions(); return true; } @@ -1910,6 +1884,9 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { private int mThreshold; private SearchView mSearchView; + private boolean mHasPendingShowSoftInputRequest; + final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary(); + public SearchAutoComplete(Context context) { super(context); mThreshold = getThreshold(); @@ -1983,11 +1960,13 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { super.onWindowFocusChanged(hasWindowFocus); if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) { - InputMethodManager inputManager = - getContext().getSystemService(InputMethodManager.class); - inputManager.showSoftInput(this, 0); - // If in landscape mode, then make sure that - // the ime is in front of the dropdown. + // Since InputMethodManager#onPostWindowFocus() will be called after this callback, + // it is a bit too early to call InputMethodManager#showSoftInput() here. We still + // need to wait until the system calls back onCreateInputConnection() to call + // InputMethodManager#showSoftInput(). + mHasPendingShowSoftInputRequest = true; + + // If in landscape mode, then make sure that the ime is in front of the dropdown. if (isLandscapeMode(getContext())) { ensureImeVisible(true); } @@ -2027,7 +2006,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { } if (event.isTracking() && !event.isCanceled()) { mSearchView.clearFocus(); - mSearchView.setImeVisibility(false); + setImeVisibility(false); return true; } } @@ -2051,5 +2030,51 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { }; return 160; } + + /** + * We override {@link View#onCreateInputConnection(EditorInfo)} as a signal to schedule a + * pending {@link InputMethodManager#showSoftInput(View, int)} request (if any). + */ + @Override + public InputConnection onCreateInputConnection(EditorInfo editorInfo) { + final InputConnection ic = super.onCreateInputConnection(editorInfo); + if (mHasPendingShowSoftInputRequest) { + removeCallbacks(mRunShowSoftInputIfNecessary); + post(mRunShowSoftInputIfNecessary); + } + return ic; + } + + private void showSoftInputIfNecessary() { + if (mHasPendingShowSoftInputRequest) { + final InputMethodManager imm = + getContext().getSystemService(InputMethodManager.class); + imm.showSoftInput(this, 0); + mHasPendingShowSoftInputRequest = false; + } + } + + private void setImeVisibility(final boolean visible) { + final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class); + if (!visible) { + mHasPendingShowSoftInputRequest = false; + removeCallbacks(mRunShowSoftInputIfNecessary); + imm.hideSoftInputFromWindow(getWindowToken(), 0); + return; + } + + if (imm.isActive(this)) { + // This means that SearchAutoComplete is already connected to the IME. + // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check. + mHasPendingShowSoftInputRequest = false; + removeCallbacks(mRunShowSoftInputIfNecessary); + imm.showSoftInput(this, 0); + return; + } + + // Otherwise, InputMethodManager#showSoftInput() should be deferred after + // onCreateInputConnection(). + mHasPendingShowSoftInputRequest = true; + } } } |