From d6f8a1ada1d1d3501f2d11aa6d514965fba00e46 Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Fri, 29 Jan 2021 16:42:52 +0000 Subject: Avoid IME restart for configChanges Handle onConfigurationChanged() in order to prevent restarting InputMethodService everytime. We introduce a new API attribute "configChanges" in InputMethod(attrs.xml) which when declared by IME, will be responsible for handling mentioned configuration changes. Bug: 167948419 Test: atest InputMethodServiceTest Manually: 1. Patch Ie91e7a8e06b80864ef9409031e8543858552d70d to use dual display area. 2. Open applications with editors on both display areas. 3. Attach a debug point for IMS#onConfigurationChanged(). 4. Make sure IMS#resetStateForNewConfiguration() is not called when IME moves between these two identical DisplayAreas Change-Id: Iff88b768c6b06cf5cf1fe9e97ee97f8f78e6f0bd --- .../inputmethodservice/InputMethodService.java | 52 ++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) (limited to 'core/java/android/inputmethodservice/InputMethodService.java') diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7e2be01feb01..03dd3067e64e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -70,6 +70,7 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -131,6 +132,7 @@ import android.widget.TextView; import android.window.WindowMetricsHelper; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodPrivilegedOperations; @@ -513,6 +515,8 @@ public class InputMethodService extends AbstractInputMethodService { private boolean mIsAutomotive; private Handler mHandler; private boolean mImeSurfaceScheduledForRemoval; + private Configuration mLastKnownConfig; + private int mHandledConfigChanges; /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} @@ -588,12 +592,14 @@ 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"); + mHandledConfigChanges = configChanges; mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); updateInputMethodDisplay(displayId); @@ -821,6 +827,9 @@ public class InputMethodService extends AbstractInputMethodService { setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); } final boolean isVisible = isInputViewShown(); + if (isVisible && getResources() != null) { + mLastKnownConfig = getResources().getConfiguration(); + } final boolean visibilityChanged = isVisible != wasVisible; if (resultReceiver != null) { resultReceiver.send(visibilityChanged @@ -1428,10 +1437,37 @@ 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. + *

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(); + if (shouldImeRestartForConfig(newConfig)) { + resetStateForNewConfiguration(); + } + } + + /** + * @return {@code true} if {@link InputMethodService} needs to restart to handle + * .{@link #onConfigurationChanged(Configuration)} + */ + @VisibleForTesting + boolean shouldImeRestartForConfig(@NonNull Configuration newConfig) { + if (mLastKnownConfig == null) { + return true; + } + // If the new config is the same as the config this Service is already running with, + // then don't bother calling resetStateForNewConfiguration. + int diff = mLastKnownConfig.diffPublicOnly(newConfig); + if (diff != 0) { + // remove attrs not-relevant to IME service. + diff &= ActivityInfo.CONFIG_KEYBOARD_HIDDEN; + diff &= ActivityInfo.CONFIG_KEYBOARD; + diff &= ActivityInfo.CONFIG_NAVIGATION; + } + int unhandledDiff = (diff & ~mHandledConfigChanges); + return unhandledDiff != 0; } private void resetStateForNewConfiguration() { @@ -3181,7 +3217,17 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); } } - + + @VisibleForTesting + void setLastKnownConfig(@NonNull Configuration config) { + mLastKnownConfig = config; + } + + @VisibleForTesting + void setHandledConfigChanges(int configChanges) { + mHandledConfigChanges = configChanges; + } + void startExtractingText(boolean inputChanged) { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() -- cgit v1.2.3