summaryrefslogtreecommitdiff
path: root/core/java/android/inputmethodservice/InputMethodService.java
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2021-10-07 23:50:15 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-10-07 23:50:15 +0000
commitc03b0fa033117c03430e361d561aa910e95a0478 (patch)
tree9c6aaee5a3023a6c237394b44e06a3fdb46f6747 /core/java/android/inputmethodservice/InputMethodService.java
parent8cc0f40cf250d9c66dc15d0e8bc3a41db9a7cfa1 (diff)
parent531b8f4f2605c44cf73e8421f674a1c7a9c277ff (diff)
Merge "Merge Android 12"
Diffstat (limited to 'core/java/android/inputmethodservice/InputMethodService.java')
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java433
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);
+ }
}