diff options
| author | Yohei Yukawa <yukawa@google.com> | 2021-03-31 02:28:22 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2021-03-31 02:28:22 +0000 |
| commit | 12e4fde7935ef83760a45478f58eb80c49620cd9 (patch) | |
| tree | 49f315a569f627c9d8cc5fa10b6c9eeb8033bfb2 /core/java/android/inputmethodservice | |
| parent | 7d058d62e5bfc2893e297ece9094665d628127c2 (diff) | |
| parent | 71ab752fc59e3db45e12d6ed9c922eb1ffca84a7 (diff) | |
Merge "Avoid IME restart for configChanges" into sc-dev
Diffstat (limited to 'core/java/android/inputmethodservice')
3 files changed, 115 insertions, 6 deletions
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 5cfcd667632b..9198eb74d1f8 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -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(); } @@ -280,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 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/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7e2be01feb01..bf016124da31 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -513,6 +513,7 @@ public class InputMethodService extends AbstractInputMethodService { 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} @@ -588,12 +589,13 @@ 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); @@ -663,6 +665,7 @@ public class InputMethodService extends AbstractInputMethodService { reportFullscreenMode(); initialize(); onBindInput(); + mConfigTracker.onBindInput(getResources()); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -1428,10 +1431,13 @@ 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() { @@ -3181,7 +3187,7 @@ public class InputMethodService extends AbstractInputMethodService { requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS); } } - + void startExtractingText(boolean inputChanged) { final ExtractEditText eet = mExtractEditText; if (eet != null && getCurrentInputStarted() |
