diff options
| author | Xin Li <delphij@google.com> | 2019-09-05 16:53:23 +0000 |
|---|---|---|
| committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2019-09-05 16:53:23 +0000 |
| commit | d191463bb0a528d3dc97a21b85ad83374b27c239 (patch) | |
| tree | 425b31516b5d8b1530a6fcf90a9661d5878f08b4 /core/java/android/inputmethodservice/InputMethodService.java | |
| parent | 3f275ca900aac74865a2f83eeda36a064661ff80 (diff) | |
| parent | e199ca954dff7fdfb06e6280695d34a957ed5efc (diff) | |
Merge "DO NOT MERGE - Merge Android 10 into master"
Diffstat (limited to 'core/java/android/inputmethodservice/InputMethodService.java')
| -rw-r--r-- | core/java/android/inputmethodservice/InputMethodService.java | 518 |
1 files changed, 362 insertions, 156 deletions
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 049e5af56019..82d4d1d10d7e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -16,12 +16,14 @@ package android.inputmethodservice; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.annotation.AnyThread; import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.IntDef; @@ -80,6 +82,12 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.inputmethod.IInputContentUriToken; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; +import com.android.internal.inputmethod.InputMethodPrivilegedOperations; +import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -338,12 +346,20 @@ 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; InputMethodManager mImm; - + private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations(); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) int mTheme = 0; @@ -353,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; @@ -372,19 +397,6 @@ public class InputMethodService extends AbstractInputMethodService { InputConnection mStartedInputConnection; EditorInfo mInputEditorInfo; - /** - * A token to keep tracking the last IPC that triggered - * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If - * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from - * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged. - * - * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to - * disentangle event flows for various purposes such as better window animation and providing - * fine-grained debugging information.</p> - */ - @Nullable - private IBinder mStartInputToken; - int mShowInputFlags; boolean mShowInputRequested; boolean mLastShowInputRequested; @@ -411,13 +423,9 @@ public class InputMethodService extends AbstractInputMethodService { @BackDispositionMode int mBackDisposition; - /** - * {@code true} when the previous IME had non-empty inset at the bottom of the screen and we - * have not shown our own window yet. In this situation, the previous inset continues to be - * shown as an empty region until it is explicitly updated. Basically we can trigger the update - * by calling 1) {@code mWindow.show()} or 2) {@link #clearInsetOfPreviousIme()}. - */ - boolean mShouldClearInsetOfPreviousIme; + private Object mLock = new Object(); + @GuardedBy("mLock") + private boolean mNotifyUserActionSent; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) final Insets mTmpInsets = new Insets(); @@ -460,13 +468,49 @@ public class InputMethodService extends AbstractInputMethodService { public class InputMethodImpl extends AbstractInputMethodImpl { /** * {@inheritDoc} + * @hide + */ + @MainThread + @Override + public final void initializeInternal(@NonNull IBinder token, int displayId, + IInputMethodPrivilegedOperations privilegedOperations) { + if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) { + Log.w(TAG, "The token has already registered, ignore this initialization."); + return; + } + mPrivOps.set(privilegedOperations); + InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); + updateInputMethodDisplay(displayId); + attachToken(token); + } + + /** + * {@inheritDoc} */ @MainThread @Override public void attachToken(IBinder token) { - if (mToken == null) { - mToken = token; - mWindow.setToken(token); + if (mToken != null) { + throw new IllegalStateException( + "attachToken() must be called at most once. token=" + token); + } + mToken = token; + mWindow.setToken(token); + } + + /** + * {@inheritDoc} + * @hide + */ + @MainThread + @Override + public void updateInputMethodDisplay(int displayId) { + // Update display for adding IME window to the right display. + if (displayId != DEFAULT_DISPLAY) { + // TODO(b/111364446) Need to address context lifecycle issue if need to re-create + // for update resources & configuration correctly when show soft input + // in non-default display. + updateDisplay(displayId); } } @@ -482,9 +526,7 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding + " ic=" + mInputConnection); - if (mImm != null && mToken != null) { - mImm.reportFullscreenMode(mToken, mIsFullscreen); - } + reportFullscreenMode(); initialize(); onBindInput(); } @@ -530,16 +572,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) { - mStartInputToken = startInputToken; + @NonNull IBinder startInputToken, boolean shouldPreRenderIme) { + mPrivOps.reportStartInput(startInputToken); + mCanPreRender = shouldPreRenderIme; + if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender); - // 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); + if (restarting) { + restartInput(inputConnection, editorInfo); + } else { + startInput(inputConnection, editorInfo); + } } /** @@ -549,15 +593,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(); - clearInsetOfPreviousIme(); + final boolean wasVisible = mIsPreRendered + ? mDecorViewVisible && mWindowVisible : isInputViewShown(); + if (mIsPreRendered) { + if (DEBUG) { + Log.v(TAG, "Making IME window invisible"); + } + setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition); + applyVisibilityInInsetsConsumer(false /* setVisible */); + 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); } } @@ -569,18 +625,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) { + if (DEBUG) { + Log.v(TAG, "Making IME window visible"); + } + applyVisibilityInInsetsConsumer(true /* setVisible */); + onPreRenderedWindowVisibilityChanged(true /* setVisible */); + } else { + showWindow(true); + } } - clearInsetOfPreviousIme(); // If user uses hard keyboard, IME button should always be shown. - mImm.setImeWindowStatus(mToken, mStartInputToken, - 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); } } @@ -591,10 +657,19 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void changeInputMethodSubtype(InputMethodSubtype subtype) { - onCurrentInputMethodSubtypeChanged(subtype); + dispatchOnCurrentInputMethodSubtypeChanged(subtype); } } + private void notifyImeHidden() { + setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition); + onPreRenderedWindowVisibilityChanged(false /* setVisible */); + } + + private void setImeWindowStatus(int visibilityFlags, int backDisposition) { + mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); + } + /** * Concrete implementation of * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides @@ -693,6 +768,14 @@ public class InputMethodService extends AbstractInputMethodService { } InputMethodService.this.onUpdateCursorAnchorInfo(info); } + + /** + * Notify IME that window is hidden. + * @hide + */ + public final void notifyImeHidden() { + InputMethodService.this.notifyImeHidden(); + } } /** @@ -914,9 +997,9 @@ public class InputMethodService extends AbstractInputMethodService { super.onCreate(); mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mSettingsObserver = SettingsObserver.createAndRegister(this); - // If the previous IME has occupied non-empty inset in the screen, we need to decide whether - // we continue to use the same size of the inset or update it - mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0); + // TODO(b/111364446) Need to address context lifecycle issue if need to re-create + // for update resources & configuration correctly when show soft input + // in non-default display. mInflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, @@ -949,7 +1032,7 @@ public class InputMethodService extends AbstractInputMethodService { void initViews() { mInitialized = false; - mWindowCreated = false; + mViewsCreated = false; mShowInputRequested = false; mShowInputFlags = 0; @@ -994,6 +1077,11 @@ public class InputMethodService extends AbstractInputMethodService { mSettingsObserver.unregister(); mSettingsObserver = null; } + if (mToken != null) { + // This is completely optional, but allows us to show more explicit error messages + // when IME developers are doing something unsupported. + InputMethodPrivilegedOperationsRegistry.remove(mToken); + } } /** @@ -1018,7 +1106,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void resetStateForNewConfiguration() { - boolean visible = mWindowVisible; + boolean visible = mDecorViewVisible; int showFlags = mShowInputFlags; boolean showingInput = mShowInputRequested; CompletionInfo[] completions = mCurCompletions; @@ -1051,8 +1139,7 @@ public class InputMethodService extends AbstractInputMethodService { } // If user uses hard keyboard, IME button should always be shown. boolean showing = onEvaluateInputViewShown(); - mImm.setImeWindowStatus(mToken, mStartInputToken, - IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); + setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); } } @@ -1102,8 +1189,7 @@ public class InputMethodService extends AbstractInputMethodService { return; } mBackDisposition = disposition; - mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()), - mBackDisposition); + setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); } /** @@ -1166,7 +1252,7 @@ public class InputMethodService extends AbstractInputMethodService { * used input method and subtype. */ public final boolean switchToPreviousInputMethod() { - return mImm.switchToPreviousInputMethodInternal(mToken); + return mPrivOps.switchToPreviousInputMethod(); } /** @@ -1178,7 +1264,7 @@ public class InputMethodService extends AbstractInputMethodService { * input method and subtype. */ public final boolean switchToNextInputMethod(boolean onlyCurrentIme) { - return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme); + return mPrivOps.switchToNextInputMethod(onlyCurrentIme); } /** @@ -1191,7 +1277,7 @@ public class InputMethodService extends AbstractInputMethodService { * between IMEs and subtypes. */ public final boolean shouldOfferSwitchingToNextInputMethod() { - return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken); + return mPrivOps.shouldOfferSwitchingToNextInputMethod(); } public boolean getCurrentInputStarted() { @@ -1201,7 +1287,11 @@ public class InputMethodService extends AbstractInputMethodService { public EditorInfo getCurrentInputEditorInfo() { return mInputEditorInfo; } - + + private void reportFullscreenMode() { + mPrivOps.reportFullscreenMode(mIsFullscreen); + } + /** * Re-evaluate whether the input method should be running in fullscreen * mode, and update its UI if this has changed since the last time it @@ -1216,9 +1306,7 @@ public class InputMethodService extends AbstractInputMethodService { if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { changed = true; mIsFullscreen = isFullscreen; - if (mImm != null && mToken != null) { - mImm.reportFullscreenMode(mToken, mIsFullscreen); - } + reportFullscreenMode(); mFullscreenApplied = true; initialize(); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) @@ -1352,7 +1440,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, @@ -1411,7 +1499,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) { @@ -1430,14 +1518,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; } /** @@ -1471,7 +1559,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. @@ -1507,12 +1595,12 @@ public class InputMethodService extends AbstractInputMethodService { public void showStatusIcon(@DrawableRes int iconResId) { mStatusIcon = iconResId; - mImm.showStatusIconInternal(mToken, getPackageName(), iconResId); + mPrivOps.updateStatusIcon(getPackageName(), iconResId); } public void hideStatusIcon() { mStatusIcon = 0; - mImm.hideStatusIconInternal(mToken); + mPrivOps.updateStatusIcon(null, 0); } /** @@ -1523,7 +1611,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param id Unique identifier of the new input method to start. */ public void switchInputMethod(String id) { - mImm.setInputMethodInternal(mToken, id); + mPrivOps.setInputMethod(id); } /** @@ -1535,7 +1623,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param subtype The new subtype of the new input method to be switched to. */ public final void switchInputMethod(String id, InputMethodSubtype subtype) { - mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype); + mPrivOps.setInputMethodAndSubtype(id, subtype); } public void setExtractView(View view) { @@ -1776,7 +1864,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); @@ -1786,18 +1875,55 @@ 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(); + } + maybeNotifyPreRendered(); + mDecorViewWasVisible = true; mInShowWindow = false; } - void showWindowInner(boolean showInput) { + /** + * Notify {@link android.view.ImeInsetsSourceConsumer} if IME has been pre-rendered + * for current EditorInfo, when pre-rendering is enabled. + */ + private void maybeNotifyPreRendered() { + if (!mCanPreRender || !mIsPreRendered) { + return; + } + mPrivOps.reportPreRendered(getCurrentInputEditorInfo()); + } + + + 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; @@ -1808,8 +1934,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(); @@ -1818,6 +1944,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"); @@ -1829,50 +1959,58 @@ 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) { - mImm.setImeWindowStatus(mToken, mStartInputToken, 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(); - // Put here rather than in onWindowShown() in case people forget to call - // super.onWindowShown(). - mShouldClearInsetOfPreviousIme = false; } } - private void finishViews() { + /** + * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when + * pre-rendering is enabled. + * @param setVisible {@code true} to make it visible, false to hide it. + */ + private void applyVisibilityInInsetsConsumer(boolean setVisible) { + if (!mIsPreRendered) { + return; + } + mPrivOps.applyImeVisibility(setVisible); + } + + 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; } private void doHideWindow() { - mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition); + setImeWindowStatus(0, mBackDisposition); hideWindow(); } 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(); } @@ -1895,19 +2033,6 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * Reset the inset occupied the previous IME when and only when - * {@link #mShouldClearInsetOfPreviousIme} is {@code true}. - */ - private void clearInsetOfPreviousIme() { - if (DEBUG) Log.v(TAG, "clearInsetOfPreviousIme() " - + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); - if (!mShouldClearInsetOfPreviousIme) return; - - mImm.clearLastInputMethodWindowForTransition(mToken); - mShouldClearInsetOfPreviousIme = false; - } - - /** * Called when a new client has bound to the input method. This * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} * and {@link #onFinishInput()} calls as the user navigates through its @@ -1945,15 +2070,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(); @@ -1973,7 +2091,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; @@ -1984,6 +2102,32 @@ 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(); + maybeNotifyPreRendered(); + mDecorViewWasVisible = true; + mInShowWindow = false; + } else { + mIsPreRendered = false; } } @@ -2076,7 +2220,14 @@ public class InputMethodService extends AbstractInputMethodService { * protocol, so applications with custom text editing written before this method appeared will * not call to inform the IME of this interaction. * @param focusChanged true if the user changed the focused view by this click. + * @see InputMethodManager#viewClicked(View) + * @deprecated The method may not be called for composite {@link View} that works as a giant + * "Canvas", which can host its own UI hierarchy and sub focus state. + * {@link android.webkit.WebView} is a good example. Application / IME developers + * should not rely on this method. If your goal is just being notified when an + * on-going input is interrupted, simply monitor {@link #onFinishInput()}. */ + @Deprecated public void onViewClicked(boolean focusChanged) { // Intentionally empty } @@ -2114,7 +2265,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public void requestHideSelf(int flags) { - mImm.hideSoftInputFromInputMethodInternal(mToken, flags); + mPrivOps.hideMySoftInput(flags); } /** @@ -2126,7 +2277,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public final void requestShowSelf(int flags) { - mImm.showSoftInputFromInputMethodInternal(mToken, flags); + mPrivOps.showMySoftInput(flags); } private boolean handleBack(boolean doIt) { @@ -2135,7 +2286,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. @@ -2162,7 +2313,6 @@ public class InputMethodService extends AbstractInputMethodService { return mExtractEditText; } - /** * Called back when a {@link KeyEvent} is forwarded from the target application. * @@ -2768,6 +2918,13 @@ public class InputMethodService extends AbstractInputMethodService { } } + private void dispatchOnCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) { + synchronized (mLock) { + mNotifyUserActionSent = false; + } + onCurrentInputMethodSubtypeChanged(newSubtype); + } + // TODO: Handle the subtype change event /** * Called when the subtype was changed. @@ -2786,18 +2943,22 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * @return The recommended height of the input method window. - * An IME author can get the last input method's height as the recommended height - * by calling this in - * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}. - * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME - * switching by using this value as a visible inset height. It's efficient for the smooth - * transition between different IMEs. However, note that this may return 0 (or possibly - * unexpectedly low height). You should thus avoid relying on the return value of this method - * all the time. Please make sure to use a reasonable height for the IME. + * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual + * semantics has never been well defined. + * + * <p>Note that the previous document clearly mentioned that this method could return {@code 0} + * at any time for whatever reason. Now this method is just always returning {@code 0}.</p> + * + * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method + * always returns {@code 0} + * @deprecated the actual behavior of this method has never been well defined. You cannot use + * this method in a reliable and predictable way */ + @Deprecated public int getInputMethodWindowRecommendedHeight() { - return mImm.getInputMethodWindowVisibleHeight(); + Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0." + + " Do not use this method."); + return 0; } /** @@ -2820,11 +2981,55 @@ public class InputMethodService extends AbstractInputMethodService { if (getCurrentInputConnection() != inputConnection) { return; } - mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo()); + exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo()); + } + + /** + * {@inheritDoc} + * @hide + */ + @AnyThread + @Override + public final void notifyUserActionIfNecessary() { + synchronized (mLock) { + if (mNotifyUserActionSent) { + return; + } + mPrivOps.notifyUserAction(); + mNotifyUserActionSent = true; + } + } + + /** + * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access + * permission to the content. + * + * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, + * InputConnection)} for details.</p> + * + * @param inputContentInfo Content to be temporarily exposed from the input method to the + * application. + * This cannot be {@code null}. + * @param editorInfo The editor that receives {@link InputContentInfo}. + */ + private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo, + @NonNull EditorInfo editorInfo) { + final Uri contentUri = inputContentInfo.getContentUri(); + final IInputContentUriToken uriToken = + mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName); + if (uriToken == null) { + Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString() + + " packageName=" + editorInfo.packageName); + return; + } + 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); } /** @@ -2834,9 +3039,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); @@ -2846,7 +3052,6 @@ public class InputMethodService extends AbstractInputMethodService { p.println(" mInputStarted=" + mInputStarted + " mInputViewStarted=" + mInputViewStarted + " mCandidatesViewStarted=" + mCandidatesViewStarted); - p.println(" mStartInputToken=" + mStartInputToken); if (mInputEditorInfo != null) { p.println(" mInputEditorInfo:"); @@ -2857,6 +3062,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 @@ -2881,7 +3088,6 @@ public class InputMethodService extends AbstractInputMethodService { + " visibleTopInsets=" + mTmpInsets.visibleTopInsets + " touchableInsets=" + mTmpInsets.touchableInsets + " touchableRegion=" + mTmpInsets.touchableRegion); - p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); p.println(" mSettingsObserver=" + mSettingsObserver); } } |
