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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
* <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();
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user