summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yohei Yukawa <yukawa@google.com> 2017-03-07 00:52:09 -0800
committer Yohei Yukawa <yukawa@google.com> 2017-03-07 00:52:09 -0800
commit5cfc1b4c104471a6f4d0edc8a1b60cbc9a54d78f (patch)
tree2f3c4134c3d3ce50d53f8eea6a237015a49f42fb
parent25e333cb3ca65dde8d3b448d119180918d2f7356 (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.java101
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;
+ }
}
}