diff options
| author | Xin Li <delphij@google.com> | 2021-10-07 23:50:15 +0000 |
|---|---|---|
| committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-10-07 23:50:15 +0000 |
| commit | c03b0fa033117c03430e361d561aa910e95a0478 (patch) | |
| tree | 9c6aaee5a3023a6c237394b44e06a3fdb46f6747 /core/java/android/inputmethodservice/InputMethodService.java | |
| parent | 8cc0f40cf250d9c66dc15d0e8bc3a41db9a7cfa1 (diff) | |
| parent | 531b8f4f2605c44cf73e8421f674a1c7a9c277ff (diff) | |
Merge "Merge Android 12"
Diffstat (limited to 'core/java/android/inputmethodservice/InputMethodService.java')
| -rw-r--r-- | core/java/android/inputmethodservice/InputMethodService.java | 433 |
1 files changed, 275 insertions, 158 deletions
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); + } } |
