Merge "Avoid IME restart for configChanges" into sc-dev
This commit is contained in:
@@ -51921,6 +51921,7 @@ package android.view.inputmethod {
|
|||||||
method public int describeContents();
|
method public int describeContents();
|
||||||
method public void dump(android.util.Printer, String);
|
method public void dump(android.util.Printer, String);
|
||||||
method public android.content.ComponentName getComponent();
|
method public android.content.ComponentName getComponent();
|
||||||
|
method public int getConfigChanges();
|
||||||
method public String getId();
|
method public String getId();
|
||||||
method public int getIsDefaultResourceId();
|
method public int getIsDefaultResourceId();
|
||||||
method public String getPackageName();
|
method public String getPackageName();
|
||||||
|
|||||||
@@ -2899,6 +2899,10 @@ package android.view.inputmethod {
|
|||||||
method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
|
method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class InputMethodInfo implements android.os.Parcelable {
|
||||||
|
ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int);
|
||||||
|
}
|
||||||
|
|
||||||
public final class InputMethodManager {
|
public final class InputMethodManager {
|
||||||
method public int getDisplayId();
|
method public int getDisplayId();
|
||||||
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
|
method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
|
|||||||
SomeArgs args = (SomeArgs) msg.obj;
|
SomeArgs args = (SomeArgs) msg.obj;
|
||||||
try {
|
try {
|
||||||
inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
|
inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
|
||||||
(IInputMethodPrivilegedOperations) args.arg2);
|
(IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
|
||||||
} finally {
|
} finally {
|
||||||
args.recycle();
|
args.recycle();
|
||||||
}
|
}
|
||||||
@@ -280,9 +280,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
|
|||||||
@BinderThread
|
@BinderThread
|
||||||
@Override
|
@Override
|
||||||
public void initializeInternal(IBinder token, int displayId,
|
public void initializeInternal(IBinder token, int displayId,
|
||||||
IInputMethodPrivilegedOperations privOps) {
|
IInputMethodPrivilegedOperations privOps, int configChanges) {
|
||||||
mCaller.executeOrSendMessage(
|
mCaller.executeOrSendMessage(
|
||||||
mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps));
|
mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
|
||||||
|
configChanges));
|
||||||
}
|
}
|
||||||
|
|
||||||
@BinderThread
|
@BinderThread
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -513,6 +513,7 @@ public class InputMethodService extends AbstractInputMethodService {
|
|||||||
private boolean mIsAutomotive;
|
private boolean mIsAutomotive;
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private boolean mImeSurfaceScheduledForRemoval;
|
private boolean mImeSurfaceScheduledForRemoval;
|
||||||
|
private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
|
* An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
|
||||||
@@ -588,12 +589,13 @@ public class InputMethodService extends AbstractInputMethodService {
|
|||||||
@MainThread
|
@MainThread
|
||||||
@Override
|
@Override
|
||||||
public final void initializeInternal(@NonNull IBinder token, int displayId,
|
public final void initializeInternal(@NonNull IBinder token, int displayId,
|
||||||
IInputMethodPrivilegedOperations privilegedOperations) {
|
IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
|
||||||
if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
|
if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
|
||||||
Log.w(TAG, "The token has already registered, ignore this initialization.");
|
Log.w(TAG, "The token has already registered, ignore this initialization.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
|
||||||
|
mConfigTracker.onInitialize(configChanges);
|
||||||
mPrivOps.set(privilegedOperations);
|
mPrivOps.set(privilegedOperations);
|
||||||
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
|
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
|
||||||
updateInputMethodDisplay(displayId);
|
updateInputMethodDisplay(displayId);
|
||||||
@@ -663,6 +665,7 @@ public class InputMethodService extends AbstractInputMethodService {
|
|||||||
reportFullscreenMode();
|
reportFullscreenMode();
|
||||||
initialize();
|
initialize();
|
||||||
onBindInput();
|
onBindInput();
|
||||||
|
mConfigTracker.onBindInput(getResources());
|
||||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1428,10 +1431,13 @@ public class InputMethodService extends AbstractInputMethodService {
|
|||||||
* state: {@link #onStartInput} if input is active, and
|
* state: {@link #onStartInput} if input is active, and
|
||||||
* {@link #onCreateInputView} and {@link #onStartInputView} and related
|
* {@link #onCreateInputView} and {@link #onStartInputView} and related
|
||||||
* appropriate functions if the UI is displayed.
|
* 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) {
|
@Override public void onConfigurationChanged(Configuration newConfig) {
|
||||||
super.onConfigurationChanged(newConfig);
|
super.onConfigurationChanged(newConfig);
|
||||||
resetStateForNewConfiguration();
|
mConfigTracker.onConfigurationChanged(newConfig, this::resetStateForNewConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetStateForNewConfiguration() {
|
private void resetStateForNewConfiguration() {
|
||||||
@@ -3181,7 +3187,7 @@ public class InputMethodService extends AbstractInputMethodService {
|
|||||||
requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
|
requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void startExtractingText(boolean inputChanged) {
|
void startExtractingText(boolean inputChanged) {
|
||||||
final ExtractEditText eet = mExtractEditText;
|
final ExtractEditText eet = mExtractEditText;
|
||||||
if (eet != null && getCurrentInputStarted()
|
if (eet != null && getCurrentInputStarted()
|
||||||
|
|||||||
@@ -101,11 +101,12 @@ public interface InputMethod {
|
|||||||
* @param privilegedOperations IPC endpoint to do some privileged
|
* @param privilegedOperations IPC endpoint to do some privileged
|
||||||
* operations that are allowed only to the
|
* operations that are allowed only to the
|
||||||
* current IME.
|
* current IME.
|
||||||
|
* @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@MainThread
|
@MainThread
|
||||||
default void initializeInternal(IBinder token, int displayId,
|
default void initializeInternal(IBinder token, int displayId,
|
||||||
IInputMethodPrivilegedOperations privilegedOperations) {
|
IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
|
||||||
updateInputMethodDisplay(displayId);
|
updateInputMethodDisplay(displayId);
|
||||||
attachToken(token);
|
attachToken(token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,19 +18,23 @@ package android.view.inputmethod;
|
|||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.annotation.TestApi;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.Resources.NotFoundException;
|
import android.content.res.Resources.NotFoundException;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
@@ -62,6 +66,7 @@ import java.util.List;
|
|||||||
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
|
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
|
||||||
* @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
|
* @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
|
||||||
* @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
|
* @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
|
||||||
|
* @attr ref android.R.styleable#InputMethod_configChanges
|
||||||
*/
|
*/
|
||||||
public final class InputMethodInfo implements Parcelable {
|
public final class InputMethodInfo implements Parcelable {
|
||||||
static final String TAG = "InputMethodInfo";
|
static final String TAG = "InputMethodInfo";
|
||||||
@@ -129,6 +134,12 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
private final boolean mShowInInputMethodPicker;
|
private final boolean mShowInInputMethodPicker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag for configurations IME assumes the responsibility for handling in
|
||||||
|
* {@link InputMethodService#onConfigurationChanged(Configuration)}}.
|
||||||
|
*/
|
||||||
|
private final int mHandledConfigChanges;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
|
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
|
||||||
* @return a unique ID to be returned by {@link #getId()}. We have used
|
* @return a unique ID to be returned by {@link #getId()}. We have used
|
||||||
@@ -221,6 +232,8 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
|
com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
|
||||||
showInInputMethodPicker = sa.getBoolean(
|
showInInputMethodPicker = sa.getBoolean(
|
||||||
com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true);
|
com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true);
|
||||||
|
mHandledConfigChanges = sa.getInt(
|
||||||
|
com.android.internal.R.styleable.InputMethod_configChanges, 0);
|
||||||
sa.recycle();
|
sa.recycle();
|
||||||
|
|
||||||
final int depth = parser.getDepth();
|
final int depth = parser.getDepth();
|
||||||
@@ -309,6 +322,7 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
mIsVrOnly = source.readBoolean();
|
mIsVrOnly = source.readBoolean();
|
||||||
mService = ResolveInfo.CREATOR.createFromParcel(source);
|
mService = ResolveInfo.CREATOR.createFromParcel(source);
|
||||||
mSubtypes = new InputMethodSubtypeArray(source);
|
mSubtypes = new InputMethodSubtypeArray(source);
|
||||||
|
mHandledConfigChanges = source.readInt();
|
||||||
mForceDefault = false;
|
mForceDefault = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +334,22 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
|
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
|
||||||
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
|
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
|
||||||
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
|
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
|
||||||
false /* inlineSuggestionsEnabled */, false /* isVrOnly */);
|
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
|
||||||
|
0 /* handledConfigChanges */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary API for creating a built-in input method for test.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@TestApi
|
||||||
|
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
|
||||||
|
@NonNull CharSequence label, @NonNull String settingsActivity,
|
||||||
|
int handledConfigChanges) {
|
||||||
|
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
|
||||||
|
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
|
||||||
|
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
|
||||||
|
false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -332,7 +361,7 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
boolean forceDefault) {
|
boolean forceDefault) {
|
||||||
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
|
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
|
||||||
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
|
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
|
||||||
false /* isVrOnly */);
|
false /* isVrOnly */, 0 /* handledconfigChanges */);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -343,7 +372,8 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
|
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
|
||||||
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
|
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
|
||||||
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
|
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
|
||||||
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly);
|
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
|
||||||
|
0 /* handledConfigChanges */);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -353,7 +383,7 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
|
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
|
||||||
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
|
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
|
||||||
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
|
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
|
||||||
boolean isVrOnly) {
|
boolean isVrOnly, int handledConfigChanges) {
|
||||||
final ServiceInfo si = ri.serviceInfo;
|
final ServiceInfo si = ri.serviceInfo;
|
||||||
mService = ri;
|
mService = ri;
|
||||||
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
|
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
|
||||||
@@ -367,6 +397,7 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
mSuppressesSpellChecker = false;
|
mSuppressesSpellChecker = false;
|
||||||
mShowInInputMethodPicker = true;
|
mShowInInputMethodPicker = true;
|
||||||
mIsVrOnly = isVrOnly;
|
mIsVrOnly = isVrOnly;
|
||||||
|
mHandledConfigChanges = handledConfigChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
|
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
|
||||||
@@ -513,6 +544,17 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bit mask of kinds of configuration changes that this IME
|
||||||
|
* can handle itself (without being restarted by the system).
|
||||||
|
*
|
||||||
|
* @attr ref android.R.styleable#InputMethod_configChanges
|
||||||
|
*/
|
||||||
|
@ActivityInfo.Config
|
||||||
|
public int getConfigChanges() {
|
||||||
|
return mHandledConfigChanges;
|
||||||
|
}
|
||||||
|
|
||||||
public void dump(Printer pw, String prefix) {
|
public void dump(Printer pw, String prefix) {
|
||||||
pw.println(prefix + "mId=" + mId
|
pw.println(prefix + "mId=" + mId
|
||||||
+ " mSettingsActivityName=" + mSettingsActivityName
|
+ " mSettingsActivityName=" + mSettingsActivityName
|
||||||
@@ -622,6 +664,7 @@ public final class InputMethodInfo implements Parcelable {
|
|||||||
dest.writeBoolean(mIsVrOnly);
|
dest.writeBoolean(mIsVrOnly);
|
||||||
mService.writeToParcel(dest, flags);
|
mService.writeToParcel(dest, flags);
|
||||||
mSubtypes.writeToParcel(dest);
|
mSubtypes.writeToParcel(dest);
|
||||||
|
dest.writeInt(mHandledConfigChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
|
|||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
oneway interface IInputMethod {
|
oneway interface IInputMethod {
|
||||||
void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
|
void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
|
||||||
|
int configChanges);
|
||||||
|
|
||||||
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
|
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
|
||||||
in IInlineSuggestionsRequestCallback cb);
|
in IInlineSuggestionsRequestCallback cb);
|
||||||
|
|||||||
@@ -3585,6 +3585,16 @@
|
|||||||
Note: This functions as a hint to the system, which may choose to ignore this
|
Note: This functions as a hint to the system, which may choose to ignore this
|
||||||
preference in certain situations or in future releases.-->
|
preference in certain situations or in future releases.-->
|
||||||
<attr name="showInInputMethodPicker" format="boolean" />
|
<attr name="showInInputMethodPicker" format="boolean" />
|
||||||
|
<!-- Specify one or more configuration changes that the IME will handle itself. If not
|
||||||
|
specified, the IME will be restarted if any of these configuration changes happen in
|
||||||
|
the system. Otherwise, the IME will remain running and its
|
||||||
|
{@link android.inputmethodservice.InputMethodService#onConfigurationChanged}
|
||||||
|
method is called with the new configuration.
|
||||||
|
<p>Note that all of these configuration changes can impact the
|
||||||
|
resource values seen by the application, so you will generally need
|
||||||
|
to re-retrieve all resources (including view layouts, drawables, etc)
|
||||||
|
to correctly handle any configuration change.-->
|
||||||
|
<attr name="configChanges" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
|
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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 static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ImsConfigurationTrackerTest {
|
||||||
|
private ImsConfigurationTracker mImsConfigTracker;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws TimeoutException {
|
||||||
|
mContext = getInstrumentation().getContext();
|
||||||
|
mImsConfigTracker = new ImsConfigurationTracker();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldImeRestart() throws Exception {
|
||||||
|
Configuration config = mContext.getResources().getConfiguration();
|
||||||
|
mImsConfigTracker.onInitialize(0 /* handledConfigChanges */);
|
||||||
|
mImsConfigTracker.onBindInput(mContext.getResources());
|
||||||
|
Configuration newConfig = new Configuration(config);
|
||||||
|
|
||||||
|
final AtomicBoolean didReset = new AtomicBoolean();
|
||||||
|
Runnable resetStateRunner = () -> didReset.set(true);
|
||||||
|
|
||||||
|
mImsConfigTracker.onConfigurationChanged(newConfig, resetStateRunner);
|
||||||
|
assertFalse("IME shouldn't restart if config hasn't changed",
|
||||||
|
didReset.get());
|
||||||
|
|
||||||
|
// Screen density changed but IME doesn't handle configChanges
|
||||||
|
newConfig.densityDpi = 99;
|
||||||
|
mImsConfigTracker.onConfigurationChanged(newConfig, resetStateRunner);
|
||||||
|
assertTrue("IME should restart for unhandled configChanges",
|
||||||
|
didReset.get());
|
||||||
|
|
||||||
|
didReset.set(false);
|
||||||
|
// opt-in IME to handle density config changes.
|
||||||
|
mImsConfigTracker.setHandledConfigChanges(ActivityInfo.CONFIG_DENSITY);
|
||||||
|
mImsConfigTracker.onConfigurationChanged(newConfig, resetStateRunner);
|
||||||
|
assertFalse("IME shouldn't restart since it handles configChanges",
|
||||||
|
didReset.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2623,8 +2623,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
|||||||
}
|
}
|
||||||
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
|
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
|
||||||
// Dispatch display id for InputMethodService to update context display.
|
// Dispatch display id for InputMethodService to update context display.
|
||||||
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
|
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
|
||||||
MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken));
|
MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
|
||||||
|
mMethodMap.get(mCurMethodId).getConfigChanges()));
|
||||||
scheduleNotifyImeUidToAudioService(mCurMethodUid);
|
scheduleNotifyImeUidToAudioService(mCurMethodUid);
|
||||||
if (mCurClient != null) {
|
if (mCurClient != null) {
|
||||||
clearClientSessionLocked(mCurClient);
|
clearClientSessionLocked(mCurClient);
|
||||||
@@ -4477,7 +4478,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
|||||||
}
|
}
|
||||||
final IBinder token = (IBinder) args.arg2;
|
final IBinder token = (IBinder) args.arg2;
|
||||||
((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
|
((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
|
||||||
new InputMethodPrivilegedOperationsImpl(this, token));
|
new InputMethodPrivilegedOperationsImpl(this, token),
|
||||||
|
(int) args.arg3);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
}
|
}
|
||||||
args.recycle();
|
args.recycle();
|
||||||
|
|||||||
Reference in New Issue
Block a user