summaryrefslogtreecommitdiff
path: root/core/java/android/inputmethodservice/InputMethodService.java
diff options
context:
space:
mode:
authorTarandeep Singh <tarandeep@google.com>2018-11-09 18:15:57 +0100
committerJorim Jaggi <jjaggi@google.com>2019-01-25 00:47:21 +0100
commiteadb1392f85f1ac8801018a02c31427801959b1c (patch)
treef4685b80826de22e68be848853c8db67e46d2489 /core/java/android/inputmethodservice/InputMethodService.java
parent75df50dc83f5ffb0b00cdf16f86556ce462df129 (diff)
Pre-render input method (IME transitions 1/n)
Pre-render input method views and window when EditText receives focus. This is a pre-requisite for implementing better IME transitions. Strategy: Once EditText receives focus, startInput is called. If optimization is available, IME views and window (SoftInputWindow) are created and rendered. Until user taps on EditText or showSoftInput() is called, IME window remains invisible. This pre-rendered window is kept around until EditorInfo changes or new connection is started (onStartInput). IME window's visibility will be set using new Insets controller API rather than conventional client-side dialog.show(). Behavior: - This is just IME side preparation CL. No performance improvements yet. - There should be no user perceptible behavior change. - As long as IME developers were following official lifecycle, they shouldn't perceive any behavior change. Availability: This optimization, once fully implemented, will be available when: - Device is not "Low memory" - AND Master flag DebugFlags.FLAG_PRE_RENDER_IME_VIEWS is set. - ViewRootImpl.USE_NEW_INSETS_API is enabled Bug: 118599175 Bug: 111084606 Test: atest CtsInputMethodTestCases Test: atest CtsInputMethodServiceHostTestCases Test: atest ActivityManagerMultiDisplayTests Test: Tested with 4 IMEs and didn't preceive any behavior change. Scenarios tested: 1. With and without hardware keyboard 2. Screen rotation w/ fullscreen mode. 3. split-screen Change-Id: I1a6300fe167eb205ee2b4214a6e270a52ebae062
Diffstat (limited to 'core/java/android/inputmethodservice/InputMethodService.java')
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java245
1 files changed, 170 insertions, 75 deletions
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index d3509d5eb289..301e48c4a990 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -346,6 +346,13 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public static final int IME_VISIBLE = 0x2;
+ /**
+ * @hide
+ * The IME is active and ready with views but set invisible.
+ * This flag cannot be combined with {@link #IME_VISIBLE}.
+ */
+ public static final int IME_INVISIBLE = 0x4;
+
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
@@ -362,10 +369,19 @@ public class InputMethodService extends AbstractInputMethodService {
View mRootView;
SoftInputWindow mWindow;
boolean mInitialized;
- boolean mWindowCreated;
- boolean mWindowVisible;
- boolean mWindowWasVisible;
+ boolean mViewsCreated;
+ // IME views visibility.
+ boolean mDecorViewVisible;
+ boolean mDecorViewWasVisible;
boolean mInShowWindow;
+ // True if pre-rendering of IME views/window is supported.
+ boolean mCanPreRender;
+ // If IME is pre-rendered.
+ boolean mIsPreRendered;
+ // IME window visibility.
+ // Use (mDecorViewVisible && mWindowVisible) to check if IME is visible to the user.
+ boolean mWindowVisible;
+
ViewGroup mFullscreenArea;
FrameLayout mExtractFrame;
FrameLayout mCandidatesFrame;
@@ -552,15 +568,18 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
- public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
+ public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
- @NonNull IBinder startInputToken) {
+ @NonNull IBinder startInputToken, boolean shouldPreRenderIme) {
mPrivOps.reportStartInput(startInputToken);
- // This needs to be dispatched to interface methods rather than doStartInput().
- // Otherwise IME developers who have overridden those interface methods will lose
- // notifications.
- super.dispatchStartInputWithToken(inputConnection, editorInfo, restarting,
- startInputToken);
+ mCanPreRender = shouldPreRenderIme;
+ if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender);
+
+ if (restarting) {
+ restartInput(inputConnection, editorInfo);
+ } else {
+ startInput(inputConnection, editorInfo);
+ }
}
/**
@@ -570,14 +589,27 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
- boolean wasVis = isInputViewShown();
- mShowInputFlags = 0;
- mShowInputRequested = false;
- doHideWindow();
+ final boolean wasVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ if (mIsPreRendered) {
+ // TODO: notify visibility to insets consumer.
+ if (DEBUG) {
+ Log.v(TAG, "Making IME window invisible");
+ }
+ setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+ } else {
+ mShowInputFlags = 0;
+ mShowInputRequested = false;
+ doHideWindow();
+ }
+ final boolean isVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
- resultReceiver.send(wasVis != isInputViewShown()
+ resultReceiver.send(visibilityChanged
? InputMethodManager.RESULT_HIDDEN
- : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
@@ -589,17 +621,28 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "showSoftInput()");
- boolean wasVis = isInputViewShown();
+ final boolean wasVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
- showWindow(true);
+ if (mIsPreRendered) {
+ // TODO: notify visibility to insets consumer.
+ if (DEBUG) {
+ Log.v(TAG, "Making IME window visible");
+ }
+ onPreRenderedWindowVisibilityChanged(true /* setVisible */);
+ } else {
+ showWindow(true);
+ }
}
// If user uses hard keyboard, IME button should always be shown.
- setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
-
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ final boolean isVisible = mIsPreRendered
+ ? mDecorViewVisible && mWindowVisible : isInputViewShown();
+ final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
- resultReceiver.send(wasVis != isInputViewShown()
+ resultReceiver.send(visibilityChanged
? InputMethodManager.RESULT_SHOWN
- : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
@@ -972,7 +1015,7 @@ public class InputMethodService extends AbstractInputMethodService {
void initViews() {
mInitialized = false;
- mWindowCreated = false;
+ mViewsCreated = false;
mShowInputRequested = false;
mShowInputFlags = 0;
@@ -1046,7 +1089,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
private void resetStateForNewConfiguration() {
- boolean visible = mWindowVisible;
+ boolean visible = mDecorViewVisible;
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
CompletionInfo[] completions = mCurCompletions;
@@ -1129,7 +1172,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
mBackDisposition = disposition;
- setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
/**
@@ -1380,7 +1423,7 @@ public class InputMethodService extends AbstractInputMethodService {
mExtractFrame.setVisibility(View.GONE);
}
updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
- if (mWindowWasVisible && mFullscreenArea.getVisibility() != vis) {
+ if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
: com.android.internal.R.styleable.InputMethodService_imeExtractExitAnimation,
@@ -1439,7 +1482,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public void updateInputViewShown() {
boolean isShown = mShowInputRequested && onEvaluateInputViewShown();
- if (mIsInputViewShown != isShown && mWindowVisible) {
+ if (mIsInputViewShown != isShown && mDecorViewVisible) {
mIsInputViewShown = isShown;
mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
if (mInputView == null) {
@@ -1458,14 +1501,14 @@ public class InputMethodService extends AbstractInputMethodService {
public boolean isShowInputRequested() {
return mShowInputRequested;
}
-
+
/**
* Return whether the soft input view is <em>currently</em> shown to the
* user. This is the state that was last determined and
* applied by {@link #updateInputViewShown()}.
*/
public boolean isInputViewShown() {
- return mIsInputViewShown && mWindowVisible;
+ return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible;
}
/**
@@ -1499,7 +1542,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public void setCandidatesViewShown(boolean shown) {
updateCandidatesVisibility(shown);
- if (!mShowInputRequested && mWindowVisible != shown) {
+ if (!mShowInputRequested && mDecorViewVisible != shown) {
// If we are being asked to show the candidates view while the app
// has not asked for the input view to be shown, then we need
// to update whether the window is shown.
@@ -1804,7 +1847,8 @@ public class InputMethodService extends AbstractInputMethodService {
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
- + " mWindowCreated=" + mWindowCreated
+ + " mViewsCreated=" + mViewsCreated
+ + " mDecorViewVisible=" + mDecorViewVisible
+ " mWindowVisible=" + mWindowVisible
+ " mInputStarted=" + mInputStarted
+ " mShowInputFlags=" + mShowInputFlags);
@@ -1814,18 +1858,42 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
- mWindowWasVisible = mWindowVisible;
+ mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
- showWindowInner(showInput);
- mWindowWasVisible = true;
+ boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible;
+ final int previousImeWindowStatus =
+ (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
+ ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
+ startViews(prepareWindow(showInput));
+ final int nextImeWindowStatus = mapToImeWindowStatus();
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
+ }
+
+ // compute visibility
+ onWindowShown();
+ mIsPreRendered = mCanPreRender;
+ if (mIsPreRendered) {
+ onPreRenderedWindowVisibilityChanged(true /* setVisible */);
+ } else {
+ // Pre-rendering not supported.
+ if (DEBUG) Log.d(TAG, "No pre-rendering supported");
+ mWindowVisible = true;
+ }
+
+ // request draw for the IME surface.
+ // When IME is not pre-rendered, this will actually show the IME.
+ if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
+ }
+ mDecorViewWasVisible = true;
mInShowWindow = false;
}
- void showWindowInner(boolean showInput) {
+ private boolean prepareWindow(boolean showInput) {
boolean doShowInput = false;
- final int previousImeWindowStatus =
- (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
- mWindowVisible = true;
+ mDecorViewVisible = true;
if (!mShowInputRequested && mInputStarted && showInput) {
doShowInput = true;
mShowInputRequested = true;
@@ -1836,8 +1904,8 @@ public class InputMethodService extends AbstractInputMethodService {
updateFullscreenMode();
updateInputViewShown();
- if (!mWindowCreated) {
- mWindowCreated = true;
+ if (!mViewsCreated) {
+ mViewsCreated = true;
initialize();
if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
View v = onCreateCandidatesView();
@@ -1846,6 +1914,10 @@ public class InputMethodService extends AbstractInputMethodService {
setCandidatesView(v);
}
}
+ return doShowInput;
+ }
+
+ private void startViews(boolean doShowInput) {
if (mShowInputRequested) {
if (!mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
@@ -1857,29 +1929,26 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, false);
}
-
- if (doShowInput) {
- startExtractingText(false);
- }
+ if (doShowInput) startExtractingText(false);
+ }
- final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
- if (previousImeWindowStatus != nextImeWindowStatus) {
- setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
- }
- if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
- if (DEBUG) Log.v(TAG, "showWindow: showing!");
+ private void onPreRenderedWindowVisibilityChanged(boolean setVisible) {
+ mWindowVisible = setVisible;
+ mShowInputFlags = setVisible ? mShowInputFlags : 0;
+ mShowInputRequested = setVisible;
+ mDecorViewVisible = setVisible;
+ if (setVisible) {
onWindowShown();
- mWindow.show();
}
}
- private void finishViews() {
+ private void finishViews(boolean finishingInput) {
if (mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
- onFinishInputView(false);
+ onFinishInputView(finishingInput);
} else if (mCandidatesViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
- onFinishCandidatesView(false);
+ onFinishCandidatesView(finishingInput);
}
mInputViewStarted = false;
mCandidatesViewStarted = false;
@@ -1891,12 +1960,15 @@ public class InputMethodService extends AbstractInputMethodService {
}
public void hideWindow() {
- finishViews();
- if (mWindowVisible) {
+ if (DEBUG) Log.v(TAG, "CALL: hideWindow");
+ mIsPreRendered = false;
+ mWindowVisible = false;
+ finishViews(false /* finishingInput */);
+ if (mDecorViewVisible) {
mWindow.hide();
- mWindowVisible = false;
+ mDecorViewVisible = false;
onWindowHidden();
- mWindowWasVisible = false;
+ mDecorViewWasVisible = false;
}
updateFullscreenMode();
}
@@ -1956,15 +2028,8 @@ public class InputMethodService extends AbstractInputMethodService {
}
void doFinishInput() {
- if (mInputViewStarted) {
- if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
- onFinishInputView(true);
- } else if (mCandidatesViewStarted) {
- if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
- onFinishCandidatesView(true);
- }
- mInputViewStarted = false;
- mCandidatesViewStarted = false;
+ if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
+ finishViews(true /* finishingInput */);
if (mInputStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
onFinishInput();
@@ -1984,7 +2049,7 @@ public class InputMethodService extends AbstractInputMethodService {
initialize();
if (DEBUG) Log.v(TAG, "CALL: onStartInput");
onStartInput(attribute, restarting);
- if (mWindowVisible) {
+ if (mDecorViewVisible) {
if (mShowInputRequested) {
if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
mInputViewStarted = true;
@@ -1995,6 +2060,31 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesViewStarted = true;
onStartCandidatesView(mInputEditorInfo, restarting);
}
+ } else if (mCanPreRender && mInputEditorInfo != null && mStartedInputConnection != null) {
+ // Pre-render IME views and window when real EditorInfo is available.
+ // pre-render IME window and keep it invisible.
+ if (DEBUG) Log.v(TAG, "Pre-Render IME for " + mInputEditorInfo.fieldName);
+ if (mInShowWindow) {
+ Log.w(TAG, "Re-entrance in to showWindow");
+ return;
+ }
+
+ mDecorViewWasVisible = mDecorViewVisible;
+ mInShowWindow = true;
+ startViews(prepareWindow(true /* showInput */));
+
+ // compute visibility
+ mIsPreRendered = true;
+ onPreRenderedWindowVisibilityChanged(false /* setVisible */);
+
+ // request draw for the IME surface.
+ // When IME is not pre-rendered, this will actually show the IME.
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
+ mDecorViewWasVisible = true;
+ mInShowWindow = false;
+ } else {
+ mIsPreRendered = false;
}
}
@@ -2146,7 +2236,7 @@ public class InputMethodService extends AbstractInputMethodService {
// consume the back key.
if (doIt) requestHideSelf(0);
return true;
- } else if (mWindowVisible) {
+ } else if (mDecorViewVisible) {
if (mCandidatesVisibility == View.VISIBLE) {
// If we are showing candidates even if no input area, then
// hide them.
@@ -2173,7 +2263,6 @@ public class InputMethodService extends AbstractInputMethodService {
return mExtractEditText;
}
-
/**
* Called back when a {@link KeyEvent} is forwarded from the target application.
*
@@ -2886,8 +2975,11 @@ public class InputMethodService extends AbstractInputMethodService {
inputContentInfo.setUriToken(uriToken);
}
- private static int mapToImeWindowStatus(boolean isInputViewShown) {
- return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0);
+ private int mapToImeWindowStatus() {
+ return IME_ACTIVE
+ | (isInputViewShown()
+ ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE)
+ : IME_VISIBLE) : 0);
}
/**
@@ -2897,9 +2989,10 @@ public class InputMethodService extends AbstractInputMethodService {
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method service state for " + this + ":");
- p.println(" mWindowCreated=" + mWindowCreated);
- p.println(" mWindowVisible=" + mWindowVisible
- + " mWindowWasVisible=" + mWindowWasVisible
+ p.println(" mViewsCreated=" + mViewsCreated);
+ p.println(" mDecorViewVisible=" + mDecorViewVisible
+ + " mDecorViewWasVisible=" + mDecorViewWasVisible
+ + " mWindowVisible=" + mWindowVisible
+ " mInShowWindow=" + mInShowWindow);
p.println(" Configuration=" + getResources().getConfiguration());
p.println(" mToken=" + mToken);
@@ -2919,6 +3012,8 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mShowInputRequested=" + mShowInputRequested
+ " mLastShowInputRequested=" + mLastShowInputRequested
+ + " mCanPreRender=" + mCanPreRender
+ + " mIsPreRendered=" + mIsPreRendered
+ " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
p.println(" mCandidatesVisibility=" + mCandidatesVisibility
+ " mFullscreenApplied=" + mFullscreenApplied