diff options
| author | Xin Li <delphij@google.com> | 2021-08-14 06:31:03 +0000 |
|---|---|---|
| committer | Xin Li <delphij@google.com> | 2021-08-14 07:23:27 +0000 |
| commit | 773a9dfba73c24974f2305cc5f28a365a77db7a8 (patch) | |
| tree | c17f1b3656c02cd9db89da0199099bdf070a2216 /core/java/android/inputmethodservice | |
| parent | f3da868dad0e2f86e45ac1c3829444e43134f0b2 (diff) | |
| parent | 5c822bf4ea67a3ef0380f0fe9a5c73191ddd50ce (diff) | |
Merge sc-dev-plus-aosp-without-vendor@7634622
Merged-In: I661f5fb6b1f3f6ce4322fab8fa84a7ab364c65b1
Change-Id: I16482b494ff4821016aabbb646e495d616c630ac
Diffstat (limited to 'core/java/android/inputmethodservice')
10 files changed, 444 insertions, 212 deletions
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index d7ca63a54987..3cd13a212a4b 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.app.Service; import android.content.Intent; import android.os.IBinder; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.InputConnection; @@ -193,7 +194,17 @@ public abstract class AbstractInputMethodService extends Service * needed for a new client of the input method. */ public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); - + + /** + * Dumps the internal state of IME to a protocol buffer output stream. + * + * @param proto ProtoOutputStream to dump data to. + * @param icProto {@link InputConnection} call data in proto format. + * @hide + */ + @SuppressWarnings("HiddenAbstractMethod") + public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto); + /** * Implement this to handle {@link android.os.Binder#dump Binder.dump()} * calls on your input method. diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index e9de27456f97..5a517ee0b0eb 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -49,11 +49,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_UPDATE_CURSOR = 95; private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99; private static final int DO_APP_PRIVATE_COMMAND = 100; - private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_FINISH_SESSION = 110; private static final int DO_VIEW_CLICKED = 115; private static final int DO_NOTIFY_IME_HIDDEN = 120; private static final int DO_REMOVE_IME_SURFACE = 130; + private static final int DO_FINISH_INPUT = 140; + @UnsupportedAppUsage HandlerCaller mCaller; @@ -121,10 +122,6 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub args.recycle(); return; } - case DO_TOGGLE_SOFT_INPUT: { - mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2); - return; - } case DO_FINISH_SESSION: { doFinishSession(); return; @@ -141,6 +138,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.removeImeSurface(); return; } + case DO_FINISH_INPUT: { + mInputMethodSession.finishInput(); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -212,16 +213,14 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } @Override - public void toggleSoftInput(int showFlags, int hideFlags) { - mCaller.executeOrSendMessage( - mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags)); - } - - @Override public void finishSession() { mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION)); } + @Override + public void finishInput() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT)); + } private final class ImeInputEventReceiver extends InputEventReceiver implements InputMethodSession.EventCallback { private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>(); diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index a298c856a0fb..9198eb74d1f8 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -37,8 +37,8 @@ import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.InputMethodSubtype; -import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.CancellationGroup; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInlineSuggestionsRequestCallback; @@ -156,8 +156,8 @@ class IInputMethodWrapper extends IInputMethod.Stub } SomeArgs args = (SomeArgs)msg.obj; try { - target.dump((FileDescriptor)args.arg1, - (PrintWriter)args.arg2, (String[])args.arg3); + target.dump((FileDescriptor) args.arg1, + (PrintWriter) args.arg2, (String[]) args.arg3); } catch (RuntimeException e) { ((PrintWriter)args.arg2).println("Exception: " + e); } @@ -171,7 +171,7 @@ class IInputMethodWrapper extends IInputMethod.Stub SomeArgs args = (SomeArgs) msg.obj; try { inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1, - (IInputMethodPrivilegedOperations) args.arg2); + (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3); } finally { args.recycle(); } @@ -200,8 +200,7 @@ class IInputMethodWrapper extends IInputMethod.Stub ic, info, moreArgs.argi1 == 1 /* restarting */, - startInputToken, - moreArgs.argi2 == 1 /* shouldPreRenderIme */); + startInputToken); args.recycle(); moreArgs.recycle(); return; @@ -267,7 +266,7 @@ class IInputMethodWrapper extends IInputMethod.Stub } CountDownLatch latch = new CountDownLatch(1); - mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP, + mCaller.getHandler().sendMessageAtFrontOfQueue(mCaller.obtainMessageOOOO(DO_DUMP, fd, fout, args, latch)); try { if (!latch.await(5, TimeUnit.SECONDS)) { @@ -281,9 +280,10 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void initializeInternal(IBinder token, int displayId, - IInputMethodPrivilegedOperations privOps) { + IInputMethodPrivilegedOperations privOps, int configChanges) { mCaller.executeOrSendMessage( - mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps)); + mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps, + configChanges)); } @BinderThread @@ -327,14 +327,13 @@ class IInputMethodWrapper extends IInputMethod.Stub @Override public void startInput(IBinder startInputToken, IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, - EditorInfo attribute, boolean restarting, boolean shouldPreRenderIme) { + EditorInfo attribute, boolean restarting) { if (mCancellationGroup == null) { Log.e(TAG, "startInput must be called after bindInput."); mCancellationGroup = new CancellationGroup(); } SomeArgs args = SomeArgs.obtain(); args.argi1 = restarting ? 1 : 0; - args.argi2 = shouldPreRenderIme ? 1 : 0; args.argi3 = missingMethods; mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO(DO_START_INPUT, startInputToken, inputContext, attribute, mCancellationGroup, args)); diff --git a/core/java/android/inputmethodservice/ImsConfigurationTracker.java b/core/java/android/inputmethodservice/ImsConfigurationTracker.java new file mode 100644 index 000000000000..3c788884371b --- /dev/null +++ b/core/java/android/inputmethodservice/ImsConfigurationTracker.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.inputmethodservice; + +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Configuration; +import android.content.res.Resources; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +/** + * Helper class that takes care of Configuration change behavior of {@link InputMethodService}. + * Note: this class is public for testing only. Never call any of it's methods for development + * of IMEs. + * @hide + */ +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public final class ImsConfigurationTracker { + + /** + * A constant value that represents {@link Configuration} has changed from the last time + * {@link InputMethodService#onConfigurationChanged(Configuration)} was called. + */ + private static final int CONFIG_CHANGED = -1; + + @Nullable + private Configuration mLastKnownConfig = null; + private int mHandledConfigChanges = 0; + private boolean mInitialized = false; + + /** + * Called from {@link InputMethodService.InputMethodImpl + * #initializeInternal(IBinder, int, IInputMethodPrivilegedOperations, int)} ()} + * @param handledConfigChanges Configuration changes declared handled by IME + * {@link android.R.styleable#InputMethod_configChanges}. + */ + @MainThread + public void onInitialize(int handledConfigChanges) { + Preconditions.checkState(!mInitialized, "onInitialize can be called only once."); + mInitialized = true; + mHandledConfigChanges = handledConfigChanges; + } + + /** + * Called from {@link InputMethodService.InputMethodImpl#onBindInput()} + */ + @MainThread + public void onBindInput(@Nullable Resources resources) { + Preconditions.checkState(mInitialized, + "onBindInput can be called only after onInitialize()."); + if (mLastKnownConfig == null && resources != null) { + mLastKnownConfig = new Configuration(resources.getConfiguration()); + } + } + + /** + * Dynamically set handled configChanges. + * Note: this method is public for testing only. + */ + public void setHandledConfigChanges(int configChanges) { + mHandledConfigChanges = configChanges; + } + + /** + * Called from {@link InputMethodService.InputMethodImpl#onConfigurationChanged(Configuration)}} + */ + @MainThread + public void onConfigurationChanged(@NonNull Configuration newConfig, + @NonNull Runnable resetStateForNewConfigurationRunner) { + if (!mInitialized) { + return; + } + final int diff = mLastKnownConfig != null + ? mLastKnownConfig.diffPublicOnly(newConfig) : CONFIG_CHANGED; + // If the new config is the same as the config this Service is already running with, + // then don't bother calling resetStateForNewConfiguration. + final int unhandledDiff = (diff & ~mHandledConfigChanges); + if (unhandledDiff != 0) { + resetStateForNewConfigurationRunner.run(); + } + if (diff != 0) { + mLastKnownConfig = new Configuration(newConfig); + } + } +} diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java index 90d0ff0a5026..20bf6e0024e3 100644 --- a/core/java/android/inputmethodservice/InlineSuggestionSession.java +++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java @@ -152,7 +152,7 @@ class InlineSuggestionSession { try { mCallback.onInlineSuggestionsSessionInvalidated(); } catch (RemoteException e) { - Log.w(TAG, "onInlineSuggestionsSessionInvalidated() remote exception:" + e); + Log.w(TAG, "onInlineSuggestionsSessionInvalidated() remote exception", e); } if (mResponseCallback != null) { consumeInlineSuggestionsResponse(EMPTY_RESPONSE); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 40c3d87e4f36..881e0cfb58d7 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -16,9 +16,39 @@ package android.inputmethodservice; +import static android.inputmethodservice.InputMethodServiceProto.CANDIDATES_VIEW_STARTED; +import static android.inputmethodservice.InputMethodServiceProto.CANDIDATES_VISIBILITY; +import static android.inputmethodservice.InputMethodServiceProto.CONFIGURATION; +import static android.inputmethodservice.InputMethodServiceProto.DECOR_VIEW_VISIBLE; +import static android.inputmethodservice.InputMethodServiceProto.DECOR_VIEW_WAS_VISIBLE; +import static android.inputmethodservice.InputMethodServiceProto.EXTRACTED_TOKEN; +import static android.inputmethodservice.InputMethodServiceProto.EXTRACT_VIEW_HIDDEN; +import static android.inputmethodservice.InputMethodServiceProto.FULLSCREEN_APPLIED; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_BINDING; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_CONNECTION_CALL; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_EDITOR_INFO; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_STARTED; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_VIEW_STARTED; +import static android.inputmethodservice.InputMethodServiceProto.IN_SHOW_WINDOW; +import static android.inputmethodservice.InputMethodServiceProto.IS_FULLSCREEN; +import static android.inputmethodservice.InputMethodServiceProto.IS_INPUT_VIEW_SHOWN; +import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.CONTENT_TOP_INSETS; +import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.TOUCHABLE_INSETS; +import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.TOUCHABLE_REGION; +import static android.inputmethodservice.InputMethodServiceProto.InsetsProto.VISIBLE_TOP_INSETS; +import static android.inputmethodservice.InputMethodServiceProto.LAST_COMPUTED_INSETS; +import static android.inputmethodservice.InputMethodServiceProto.LAST_SHOW_INPUT_REQUESTED; +import static android.inputmethodservice.InputMethodServiceProto.SETTINGS_OBSERVER; +import static android.inputmethodservice.InputMethodServiceProto.SHOW_INPUT_FLAGS; +import static android.inputmethodservice.InputMethodServiceProto.SHOW_INPUT_REQUESTED; +import static android.inputmethodservice.InputMethodServiceProto.SOFT_INPUT_WINDOW; +import static android.inputmethodservice.InputMethodServiceProto.STATUS_ICON; +import static android.inputmethodservice.InputMethodServiceProto.TOKEN; +import static android.inputmethodservice.InputMethodServiceProto.VIEWS_CREATED; +import static android.inputmethodservice.InputMethodServiceProto.WINDOW_VISIBLE; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -32,8 +62,12 @@ import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; +import android.annotation.UiContext; import android.app.ActivityManager; import android.app.Dialog; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -51,6 +85,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.ResultReceiver; import android.os.SystemClock; +import android.os.Trace; import android.provider.Settings; import android.text.InputType; import android.text.Layout; @@ -59,6 +94,8 @@ import android.text.method.MovementMethod; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; +import android.util.imetracing.ImeTracing; +import android.util.proto.ProtoOutputStream; import android.view.Gravity; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -69,7 +106,6 @@ import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; import android.view.Window; -import android.view.WindowInsets; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; import android.view.WindowManager; @@ -85,6 +121,7 @@ import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InputMethod; +import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceProto; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import android.widget.FrameLayout; @@ -106,6 +143,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Objects; /** * InputMethodService provides a standard implementation of an InputMethod, @@ -259,6 +297,7 @@ import java.util.ArrayList; * @attr ref android.R.styleable#InputMethodService_imeExtractEnterAnimation * @attr ref android.R.styleable#InputMethodService_imeExtractExitAnimation */ +@UiContext public class InputMethodService extends AbstractInputMethodService { static final String TAG = "InputMethodService"; static final boolean DEBUG = false; @@ -371,12 +410,39 @@ public class InputMethodService extends AbstractInputMethodService { private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT; private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING; + /** + * Timeout after which hidden IME surface will be removed from memory + */ + private static final long TIMEOUT_SURFACE_REMOVAL_MILLIS = 5000; + InputMethodManager mImm; private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations(); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) int mTheme = 0; - + + /** + * Finish the {@link InputConnection} when the device becomes + * {@link android.os.PowerManager#isInteractive non-interactive}. + * + * <p> + * If enabled by the current {@link InputMethodService input method}, the current input + * connection will be {@link InputMethodService#onFinishInput finished} whenever the devices + * becomes non-interactive. + * + * <p> + * If not enabled, the current input connection will instead be silently deactivated when the + * devices becomes non-interactive, and an {@link InputMethodService#onFinishInput + * onFinishInput()} {@link InputMethodService#onStartInput onStartInput()} pair is dispatched + * when the device becomes interactive again. + * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id. + LayoutInflater mInflater; TypedArray mThemeAttrs; @UnsupportedAppUsage @@ -388,10 +454,6 @@ public class InputMethodService extends AbstractInputMethodService { 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; @@ -419,6 +481,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean mFullscreenApplied; boolean mIsFullscreen; + private boolean mLastWasInFullscreenMode; @UnsupportedAppUsage View mExtractView; boolean mExtractViewHidden; @@ -449,6 +512,9 @@ public class InputMethodService extends AbstractInputMethodService { private boolean mAutomotiveHideNavBarForKeyboard; private boolean mIsAutomotive; + private Handler mHandler; + private boolean mImeSurfaceScheduledForRemoval; + private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker(); /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} @@ -524,15 +590,18 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public final void initializeInternal(@NonNull IBinder token, int displayId, - IInputMethodPrivilegedOperations privilegedOperations) { + IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) { Log.w(TAG, "The token has already registered, ignore this initialization."); return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal"); + mConfigTracker.onInitialize(configChanges); mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); updateInputMethodDisplay(displayId); attachToken(token); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -571,6 +640,9 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void updateInputMethodDisplay(int displayId) { + if (getDisplayId() == displayId) { + return; + } // Update display for adding IME window to the right display. // TODO(b/111364446) Need to address context lifecycle issue if need to re-create // for update resources & configuration correctly when show soft input @@ -586,6 +658,7 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void bindInput(InputBinding binding) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.bindInput"); mInputBinding = binding; mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding @@ -593,6 +666,8 @@ public class InputMethodService extends AbstractInputMethodService { reportFullscreenMode(); initialize(); onBindInput(); + mConfigTracker.onBindInput(getResources()); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -618,7 +693,9 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void startInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.startInput"); doStartInput(ic, attribute, false); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -628,7 +705,9 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void restartInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput"); doStartInput(ic, attribute, true); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -639,10 +718,8 @@ public class InputMethodService extends AbstractInputMethodService { @Override public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken, boolean shouldPreRenderIme) { - mPrivOps.reportStartInput(startInputToken); - mCanPreRender = shouldPreRenderIme; - if (DEBUG) Log.v(TAG, "Will Pre-render IME: " + mCanPreRender); + @NonNull IBinder startInputToken) { + mPrivOps.reportStartInputAsync(startInputToken); if (restarting) { restartInput(inputConnection, editorInfo); @@ -679,22 +756,17 @@ public class InputMethodService extends AbstractInputMethodService { + " Use requestHideSelf(int) itself"); return; } - final boolean wasVisible = mIsPreRendered - ? mDecorViewVisible && mWindowVisible : isInputViewShown(); + ImeTracing.getInstance().triggerServiceDump( + "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this, + null /* icProto */); + final boolean wasVisible = isInputViewShown(); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); + applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */); - if (mIsPreRendered) { - 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(); + mShowInputFlags = 0; + mShowInputRequested = false; + doHideWindow(); + final boolean isVisible = isInputViewShown(); final boolean visibilityChanged = isVisible != wasVisible; if (resultReceiver != null) { resultReceiver.send(visibilityChanged @@ -702,6 +774,7 @@ public class InputMethodService extends AbstractInputMethodService { : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -733,23 +806,24 @@ public class InputMethodService extends AbstractInputMethodService { + " Use requestShowSelf(int) itself"); return; } - final boolean wasVisible = mIsPreRendered - ? mDecorViewVisible && mWindowVisible : isInputViewShown(); + + if (Trace.isEnabled()) { + Binder.enableTracing(); + } else { + Binder.disableTracing(); + } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput"); + ImeTracing.getInstance().triggerServiceDump( + "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this, + null /* icProto */); + final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { - if (mIsPreRendered) { - if (DEBUG) { - Log.v(TAG, "Making IME window visible"); - } - onPreRenderedWindowVisibilityChanged(true /* setVisible */); - } else { - showWindow(true); - } + showWindow(true); applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */); } - // If user uses hard keyboard, IME button should always be shown. setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); - final boolean isVisible = mIsPreRendered - ? mDecorViewVisible && mWindowVisible : isInputViewShown(); + + final boolean isVisible = isInputViewShown(); final boolean visibilityChanged = isVisible != wasVisible; if (resultReceiver != null) { resultReceiver.send(visibilityChanged @@ -757,6 +831,7 @@ public class InputMethodService extends AbstractInputMethodService { : (wasVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -838,22 +913,51 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(0); } + private void scheduleImeSurfaceRemoval() { + if (mShowInputRequested || mWindowVisible || mWindow == null + || mImeSurfaceScheduledForRemoval) { + return; + } + if (mHandler == null) { + mHandler = new Handler(getMainLooper()); + } + + if (mLastWasInFullscreenMode) { + // Caching surface / delaying surface removal can cause mServedView to detach in certain + // cases in RecyclerView (b/187772544). + // TODO(b/188818557): Re-enable IME surface caching for fullscreen mode once detaching + // view issues is resolved in RecyclerView. + removeImeSurface(); + } else { + mImeSurfaceScheduledForRemoval = true; + mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS); + } + } + private void removeImeSurface() { - if (!mShowInputRequested && !mWindowVisible) { - // hiding a window removes its surface. + // hiding a window removes its surface. + if (mWindow != null) { mWindow.hide(); } + mImeSurfaceScheduledForRemoval = false; + } + + private void cancelImeSurfaceRemoval() { + if (mHandler != null && mImeSurfaceScheduledForRemoval) { + mHandler.removeCallbacksAndMessages(null /* token */); + mImeSurfaceScheduledForRemoval = false; + } } private void setImeWindowStatus(int visibilityFlags, int backDisposition) { - mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); + mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition); } /** Set region of the keyboard to be avoided from back gesture */ private void setImeExclusionRect(int visibleTopInsets) { View rootView = mInputFrame.getRootView(); android.graphics.Insets systemGesture = - rootView.getRootWindowInsets().getInsetsIgnoringVisibility(Type.systemGestures()); + rootView.getRootWindowInsets().getInsets(Type.systemGestures()); ArrayList<Rect> exclusionRects = new ArrayList<>(); exclusionRects.add(new Rect(0, visibleTopInsets, @@ -948,8 +1052,14 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * + * Handles a request to toggle the IME visibility. + * + * @deprecated Starting in {@link Build.VERSION_CODES#S} the system no longer invokes this + * method, instead it explicitly shows or hides the IME. An {@code InputMethodService} + * wishing to toggle its own visibility should instead invoke {@link + * InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf} */ + @Deprecated public void toggleSoftInput(int showFlags, int hideFlags) { InputMethodService.this.onToggleSoftInput(showFlags, hideFlags); } @@ -978,7 +1088,7 @@ public class InputMethodService extends AbstractInputMethodService { * @hide */ public final void removeImeSurface() { - InputMethodService.this.removeImeSurface(); + InputMethodService.this.scheduleImeSurfaceRemoval(); } } @@ -1051,6 +1161,15 @@ public class InputMethodService extends AbstractInputMethodService { * or {@link #TOUCHABLE_INSETS_REGION}. */ public int touchableInsets; + + private void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(CONTENT_TOP_INSETS, contentTopInsets); + proto.write(VISIBLE_TOP_INSETS, visibleTopInsets); + proto.write(TOUCHABLE_INSETS, touchableInsets); + proto.write(TOUCHABLE_REGION, touchableRegion.toString()); + proto.end(token); + } } /** @@ -1191,6 +1310,7 @@ public class InputMethodService extends AbstractInputMethodService { } @Override public void onCreate() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate"); mTheme = Resources.selectSystemTheme(mTheme, getApplicationInfo().targetSdkVersion, android.R.style.Theme_InputMethod, @@ -1201,6 +1321,9 @@ public class InputMethodService extends AbstractInputMethodService { super.onCreate(); mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); mSettingsObserver = SettingsObserver.createAndRegister(this); + // cache preference so we don't have to read ContentProvider when IME is requested to be + // shown the first time (cold start). + mSettingsObserver.shouldShowImeWithHardKeyboard(); mIsAutomotive = isAutomotive(); mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean( @@ -1211,26 +1334,21 @@ public class InputMethodService extends AbstractInputMethodService { // in non-default display. mInflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow"); mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars()); mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM); + mWindow.getWindow().getAttributes().receiveInsetsIgnoringZOrder = true; - // IME layout should always be inset by navigation bar, no matter its current visibility, - // unless automotive requests it. Automotive devices may request the navigation bar to be - // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard) - // in order to maximize the visible screen real estate. When this happens, the IME window - // should animate from the bottom of the screen to reduce the jank that happens from the - // lack of synchronization between the bottom system window and the IME window. + // Automotive devices may request the navigation bar to be hidden when the IME shows up + // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible + // screen real estate. When this happens, the IME window should animate from the bottom of + // the screen to reduce the jank that happens from the lack of synchronization between the + // bottom system window and the IME window. if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) { mWindow.getWindow().setDecorFitsSystemWindows(false); } - mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener( - (v, insets) -> v.onApplyWindowInsets( - new WindowInsets.Builder(insets).setInsets( - navigationBars(), - insets.getInsetsIgnoringVisibility(navigationBars())) - .build())); // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set // by default (but IME developers can opt this out later if they want a new behavior). @@ -1239,10 +1357,12 @@ public class InputMethodService extends AbstractInputMethodService { initViews(); mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); mInlineSuggestionSessionController = new InlineSuggestionSessionController( this::onCreateInlineSuggestionsRequest, this::getHostInputToken, this::onInlineSuggestionsResponse); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1263,6 +1383,7 @@ public class InputMethodService extends AbstractInputMethodService { } void initViews() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initViews"); mInitialized = false; mViewsCreated = false; mShowInputRequested = false; @@ -1272,13 +1393,7 @@ public class InputMethodService extends AbstractInputMethodService { mRootView = mInflater.inflate( com.android.internal.R.layout.input_method, null); mWindow.setContentView(mRootView); - mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer); mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer); - if (Settings.Global.getInt(getContentResolver(), - Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) { - mWindow.getWindow().setWindowAnimations( - com.android.internal.R.style.Animation_InputMethodFancy); - } mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea); mExtractViewHidden = false; mExtractFrame = mRootView.findViewById(android.R.id.extractArea); @@ -1297,6 +1412,7 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesVisibility = getCandidatesHiddenVisibility(); mCandidatesFrame.setVisibility(mCandidatesVisibility); mInputFrame.setVisibility(View.GONE); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @Override public void onDestroy() { @@ -1331,17 +1447,22 @@ public class InputMethodService extends AbstractInputMethodService { * state: {@link #onStartInput} if input is active, and * {@link #onCreateInputView} and {@link #onStartInputView} and related * appropriate functions if the UI is displayed. + * <p>Starting with {@link Build.VERSION_CODES#S}, IMEs can opt into handling configuration + * changes themselves instead of being restarted with + * {@link android.R.styleable#InputMethod_configChanges}. */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - resetStateForNewConfiguration(); + mConfigTracker.onConfigurationChanged(newConfig, this::resetStateForNewConfiguration); } private void resetStateForNewConfiguration() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.resetStateForNewConfiguration"); boolean visible = mDecorViewVisible; int showFlags = mShowInputFlags; boolean showingInput = mShowInputRequested; CompletionInfo[] completions = mCurCompletions; + mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer); initViews(); mInputViewStarted = false; mCandidatesViewStarted = false; @@ -1373,6 +1494,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean showing = onEvaluateInputViewShown(); setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1392,7 +1514,7 @@ public class InputMethodService extends AbstractInputMethodService { public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() { return new InputMethodSessionImpl(); } - + public LayoutInflater getLayoutInflater() { return mInflater; } @@ -1522,7 +1644,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void reportFullscreenMode() { - mPrivOps.reportFullscreenMode(mIsFullscreen); + mPrivOps.reportFullscreenModeAsync(mIsFullscreen); } /** @@ -1534,6 +1656,7 @@ public class InputMethodService extends AbstractInputMethodService { * is currently running in fullscreen mode. */ public void updateFullscreenMode() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.updateFullscreenMode"); boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode(); boolean changed = mLastShowInputRequested != mShowInputRequested; if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { @@ -1572,6 +1695,7 @@ public class InputMethodService extends AbstractInputMethodService { onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested); mLastShowInputRequested = mShowInputRequested; } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1625,8 +1749,12 @@ public class InputMethodService extends AbstractInputMethodService { if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) { return false; } - if (mInputEditorInfo != null - && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) { + if ((mInputEditorInfo != null + && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) + // If app window has portrait orientation, regardless of what display orientation + // is, IME shouldn't use fullscreen-mode. + || (mInputEditorInfo.internalImeOptions + & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) { return false; } return true; @@ -1700,6 +1828,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param outInsets Fill in with the current UI insets. */ public void onComputeInsets(Insets outInsets) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onComputeInsets"); int[] loc = mTmpLocation; if (mInputFrame.getVisibility() == View.VISIBLE) { mInputFrame.getLocationInWindow(loc); @@ -1720,6 +1849,7 @@ public class InputMethodService extends AbstractInputMethodService { outInsets.visibleTopInsets = loc[1]; outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; outInsets.touchableRegion.setEmpty(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1758,7 +1888,7 @@ public class InputMethodService extends AbstractInputMethodService { * applied by {@link #updateInputViewShown()}. */ public boolean isInputViewShown() { - return mCanPreRender ? mWindowVisible : mIsInputViewShown && mDecorViewVisible; + return mDecorViewVisible; } /** @@ -1828,12 +1958,12 @@ public class InputMethodService extends AbstractInputMethodService { public void showStatusIcon(@DrawableRes int iconResId) { mStatusIcon = iconResId; - mPrivOps.updateStatusIcon(getPackageName(), iconResId); + mPrivOps.updateStatusIconAsync(getPackageName(), iconResId); } public void hideStatusIcon() { mStatusIcon = 0; - mPrivOps.updateStatusIcon(null, 0); + mPrivOps.updateStatusIconAsync(null, 0); } /** @@ -2109,12 +2239,14 @@ public class InputMethodService extends AbstractInputMethodService { return; } + ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this, + null /* icProto */); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; - boolean isPreRenderedAndInvisible = mIsPreRendered && !mWindowVisible; final int previousImeWindowStatus = (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown() - ? (isPreRenderedAndInvisible ? IME_INVISIBLE : IME_VISIBLE) : 0); + ? (!mWindowVisible ? IME_INVISIBLE : IME_VISIBLE) : 0); startViews(prepareWindow(showInput)); final int nextImeWindowStatus = mapToImeWindowStatus(); if (previousImeWindowStatus != nextImeWindowStatus) { @@ -2123,14 +2255,7 @@ public class InputMethodService extends AbstractInputMethodService { // 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; - } + mWindowVisible = true; // request draw for the IME surface. // When IME is not pre-rendered, this will actually show the IME. @@ -2138,20 +2263,9 @@ public class InputMethodService extends AbstractInputMethodService { if (DEBUG) Log.v(TAG, "showWindow: draw decorView!"); mWindow.show(); } - maybeNotifyPreRendered(); mDecorViewWasVisible = true; mInShowWindow = false; - } - - /** - * 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()); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -2197,33 +2311,22 @@ public class InputMethodService extends AbstractInputMethodService { if (doShowInput) startExtractingText(false); } - private void onPreRenderedWindowVisibilityChanged(boolean setVisible) { - mWindowVisible = setVisible; - mShowInputFlags = setVisible ? mShowInputFlags : 0; - mShowInputRequested = setVisible; - mDecorViewVisible = setVisible; - if (setVisible) { - onWindowShown(); - } - } - /** - * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when - * {@link ViewRootImpl.sNewInsetsMode} is enabled. + * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}. + * * @param setVisible {@code true} to make it visible, false to hide it. */ private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) { - if (!isVisibilityAppliedUsingInsetsConsumer()) { - return; + ImeTracing.getInstance().triggerServiceDump( + "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this, + null /* icProto */); + if (setVisible) { + cancelImeSurfaceRemoval(); } - mPrivOps.applyImeVisibility(setVisible + mPrivOps.applyImeVisibilityAsync(setVisible ? mCurShowInputToken : mCurHideInputToken, setVisible); } - private boolean isVisibilityAppliedUsingInsetsConsumer() { - return ViewRootImpl.sNewInsetsMode > NEW_INSETS_MODE_NONE; - } - private void finishViews(boolean finishingInput) { if (mInputViewStarted) { if (DEBUG) Log.v(TAG, "CALL: onFinishInputView"); @@ -2244,23 +2347,20 @@ public class InputMethodService extends AbstractInputMethodService { public void hideWindow() { if (DEBUG) Log.v(TAG, "CALL: hideWindow"); - mIsPreRendered = false; + ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this, + null /* icProto */); mWindowVisible = false; finishViews(false /* finishingInput */); if (mDecorViewVisible) { - // When insets API is enabled, it is responsible for client and server side - // visibility of IME window. - if (isVisibilityAppliedUsingInsetsConsumer()) { - if (mInputView != null) { - mInputView.dispatchWindowVisibilityChanged(View.GONE); - } - } else { - mWindow.hide(); + // It is responsible for client and server side visibility of IME window. + if (mInputView != null) { + mInputView.dispatchWindowVisibilityChanged(View.GONE); } mDecorViewVisible = false; onWindowHidden(); mDecorViewWasVisible = false; } + mLastWasInFullscreenMode = mIsFullscreen; updateFullscreenMode(); } @@ -2320,6 +2420,8 @@ public class InputMethodService extends AbstractInputMethodService { void doFinishInput() { if (DEBUG) Log.v(TAG, "CALL: doFinishInput"); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this, + null /* icProto */); finishViews(true /* finishingInput */); if (mInputStarted) { mInlineSuggestionSessionController.notifyOnFinishInput(); @@ -2332,9 +2434,11 @@ public class InputMethodService extends AbstractInputMethodService { } void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { - if (!restarting) { + if (!restarting && mInputStarted) { doFinishInput(); } + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this, + null /* icProto */); mInputStarted = true; mStartedInputConnection = ic; mInputEditorInfo = attribute; @@ -2356,32 +2460,6 @@ 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; } } @@ -2519,6 +2597,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public void requestHideSelf(int flags) { + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this, + null /* icProto */); mPrivOps.hideMySoftInput(flags); } @@ -2531,6 +2611,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public final void requestShowSelf(int flags) { + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this, + null /* icProto */); mPrivOps.showMySoftInput(flags); } @@ -3122,7 +3204,7 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); } } - + void startExtractingText(boolean inputChanged) { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() @@ -3249,7 +3331,7 @@ public class InputMethodService extends AbstractInputMethodService { if (mNotifyUserActionSent) { return; } - mPrivOps.notifyUserAction(); + mPrivOps.notifyUserActionAsync(); mNotifyUserActionSent = true; } } @@ -3281,9 +3363,7 @@ public class InputMethodService extends AbstractInputMethodService { private int mapToImeWindowStatus() { return IME_ACTIVE - | (isInputViewShown() - ? (mCanPreRender ? (mWindowVisible ? IME_VISIBLE : IME_INVISIBLE) - : IME_VISIBLE) : 0); + | (isInputViewShown() ? IME_VISIBLE : 0); } private boolean isAutomotive() { @@ -3318,17 +3398,15 @@ public class InputMethodService extends AbstractInputMethodService { } else { p.println(" mInputEditorInfo: null"); } - + p.println(" mShowInputRequested=" + mShowInputRequested + " mLastShowInputRequested=" + mLastShowInputRequested - + " mCanPreRender=" + mCanPreRender - + " mIsPreRendered=" + mIsPreRendered + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags)); p.println(" mCandidatesVisibility=" + mCandidatesVisibility + " mFullscreenApplied=" + mFullscreenApplied + " mIsFullscreen=" + mIsFullscreen + " mExtractViewHidden=" + mExtractViewHidden); - + if (mExtractedText != null) { p.println(" mExtractedText:"); p.println(" text=" + mExtractedText.text.length() + " chars" @@ -3349,4 +3427,43 @@ public class InputMethodService extends AbstractInputMethodService { + " touchableRegion=" + mTmpInsets.touchableRegion); p.println(" mSettingsObserver=" + mSettingsObserver); } + + /** + * @hide + */ + @Override + public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) { + final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE); + mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW); + proto.write(VIEWS_CREATED, mViewsCreated); + proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible); + proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible); + proto.write(WINDOW_VISIBLE, mWindowVisible); + proto.write(IN_SHOW_WINDOW, mInShowWindow); + proto.write(CONFIGURATION, getResources().getConfiguration().toString()); + proto.write(TOKEN, Objects.toString(mToken)); + proto.write(INPUT_BINDING, Objects.toString(mInputBinding)); + proto.write(INPUT_STARTED, mInputStarted); + proto.write(INPUT_VIEW_STARTED, mInputViewStarted); + proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted); + if (mInputEditorInfo != null) { + mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO); + } + proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested); + proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested); + proto.write(SHOW_INPUT_FLAGS, mShowInputFlags); + proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility); + proto.write(FULLSCREEN_APPLIED, mFullscreenApplied); + proto.write(IS_FULLSCREEN, mIsFullscreen); + proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden); + proto.write(EXTRACTED_TOKEN, mExtractedToken); + proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown); + proto.write(STATUS_ICON, mStatusIcon); + mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS); + proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver)); + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } + proto.end(token); + } } diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java index dbb669be1402..f352f05d0488 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java @@ -23,6 +23,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; import android.view.InputChannel; @@ -38,8 +39,8 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import com.android.internal.annotations.GuardedBy; -import com.android.internal.inputmethod.IMultiClientInputMethodSession; import com.android.internal.inputmethod.CancellationGroup; +import com.android.internal.inputmethod.IMultiClientInputMethodSession; import com.android.internal.os.SomeArgs; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.IInputContext; @@ -256,18 +257,6 @@ final class MultiClientInputMethodClientCallbackAdaptor { } @Override - public void toggleSoftInput(int showFlags, int hideFlags) { - synchronized (mSessionLock) { - if (mCallbackImpl == null || mHandler == null) { - return; - } - mHandler.sendMessage(PooledLambda.obtainMessage( - CallbackImpl::toggleSoftInput, mCallbackImpl, showFlags, - hideFlags)); - } - } - - @Override public void finishSession() { synchronized (mSessionLock) { if (mCallbackImpl == null || mHandler == null) { @@ -303,6 +292,12 @@ final class MultiClientInputMethodClientCallbackAdaptor { // no-op for multi-session reportNotSupported(); } + + @Override + public void finishInput() throws RemoteException { + // no-op for multi-session + reportNotSupported(); + } } private static final class MultiClientInputMethodSessionImpl @@ -412,13 +407,6 @@ final class MultiClientInputMethodClientCallbackAdaptor { mOriginalCallback.onAppPrivateCommand(action, data); } - void toggleSoftInput(int showFlags, int hideFlags) { - if (mFinished) { - return; - } - mOriginalCallback.onToggleSoftInput(showFlags, hideFlags); - } - void finishSession() { if (mFinished) { return; diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java index 4b02085726f1..0a2316508f09 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java @@ -167,16 +167,6 @@ public final class MultiClientInputMethodServiceDelegate { /** * Called when the associated IME client called {@link - * android.view.inputmethod.InputMethodManager#toggleSoftInput(int, int)}. - * - * @param showFlags The flag passed by the client. - * @param hideFlags The flag passed by the client. - * @see android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int) - */ - void onToggleSoftInput(int showFlags, int hideFlags); - - /** - * Called when the associated IME client called {@link * android.view.inputmethod.InputMethodManager#updateCursorAnchorInfo(View, * CursorAnchorInfo)}. * diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java index 6efd03c44b9f..bc0b37ed626d 100644 --- a/core/java/android/inputmethodservice/SoftInputWindow.java +++ b/core/java/android/inputmethodservice/SoftInputWindow.java @@ -16,6 +16,13 @@ package android.inputmethodservice; +import static android.inputmethodservice.SoftInputWindowProto.BOUNDS; +import static android.inputmethodservice.SoftInputWindowProto.GRAVITY; +import static android.inputmethodservice.SoftInputWindowProto.NAME; +import static android.inputmethodservice.SoftInputWindowProto.TAKES_FOCUS; +import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE; +import static android.inputmethodservice.SoftInputWindowProto.WINDOW_TYPE; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; @@ -25,6 +32,7 @@ import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; import android.util.Log; +import android.util.proto.ProtoOutputStream; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; @@ -362,4 +370,15 @@ public class SoftInputWindow extends Dialog { throw new IllegalStateException("Unknown state=" + state); } } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(NAME, mName); + proto.write(WINDOW_TYPE, mWindowType); + proto.write(GRAVITY, mGravity); + proto.write(TAKES_FOCUS, mTakesFocus); + mBounds.dumpDebug(proto, BOUNDS); + proto.write(WINDOW_STATE, mWindowState); + proto.end(token); + } } diff --git a/core/java/android/inputmethodservice/TEST_MAPPING b/core/java/android/inputmethodservice/TEST_MAPPING new file mode 100644 index 000000000000..0ccd75dcbdce --- /dev/null +++ b/core/java/android/inputmethodservice/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/core/java/android/view/inputmethod" + } + ] +} |
