diff options
8 files changed, 111 insertions, 17 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 282a4c355d2d..330089dcc6d4 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1453,6 +1453,7 @@ package android { field public static final int supportsAssist = 16844016; // 0x10104f0 field public static final int supportsBatteryGameMode; field public static final int supportsInlineSuggestions = 16844301; // 0x101060d + field public static final int supportsInlineSuggestionsWithTouchExploration; field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1 field public static final int supportsLocalInteraction = 16844047; // 0x101050f field public static final int supportsMultipleDisplays = 16844182; // 0x1010596 diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 9a706673583b..d7c18462077d 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -64,6 +64,7 @@ import java.util.List; * @attr ref android.R.styleable#InputMethod_isDefault * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions + * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestionsWithTouchExploration * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker * @attr ref android.R.styleable#InputMethod_configChanges @@ -125,6 +126,11 @@ public final class InputMethodInfo implements Parcelable { private final boolean mInlineSuggestionsEnabled; /** + * The flag whether this IME supports inline suggestions when touch exploration is enabled. + */ + private final boolean mSupportsInlineSuggestionsWithTouchExploration; + + /** * The flag whether this IME suppresses spell checker. */ private final boolean mSuppressesSpellChecker; @@ -189,6 +195,7 @@ public final class InputMethodInfo implements Parcelable { boolean isAuxIme = true; boolean supportsSwitchingToNextInputMethod = false; // false as default boolean inlineSuggestionsEnabled = false; // false as default + boolean supportsInlineSuggestionsWithTouchExploration = false; // false as default boolean suppressesSpellChecker = false; // false as default boolean showInInputMethodPicker = true; // true as default mForceDefault = false; @@ -234,6 +241,9 @@ public final class InputMethodInfo implements Parcelable { false); inlineSuggestionsEnabled = sa.getBoolean( com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false); + supportsInlineSuggestionsWithTouchExploration = sa.getBoolean( + com.android.internal.R.styleable + .InputMethod_supportsInlineSuggestionsWithTouchExploration, false); suppressesSpellChecker = sa.getBoolean( com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false); showInInputMethodPicker = sa.getBoolean( @@ -314,6 +324,8 @@ public final class InputMethodInfo implements Parcelable { mIsAuxIme = isAuxIme; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; mInlineSuggestionsEnabled = inlineSuggestionsEnabled; + mSupportsInlineSuggestionsWithTouchExploration = + supportsInlineSuggestionsWithTouchExploration; mSuppressesSpellChecker = suppressesSpellChecker; mShowInInputMethodPicker = showInInputMethodPicker; mIsVrOnly = isVrOnly; @@ -326,6 +338,7 @@ public final class InputMethodInfo implements Parcelable { mIsAuxIme = source.readInt() == 1; mSupportsSwitchingToNextInputMethod = source.readInt() == 1; mInlineSuggestionsEnabled = source.readInt() == 1; + mSupportsInlineSuggestionsWithTouchExploration = source.readInt() == 1; mSuppressesSpellChecker = source.readBoolean(); mShowInInputMethodPicker = source.readBoolean(); mIsVrOnly = source.readBoolean(); @@ -345,7 +358,8 @@ public final class InputMethodInfo implements Parcelable { settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */, - 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */); + 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */, + false /* inlineSuggestionsEnabled */); } /** @@ -360,7 +374,8 @@ public final class InputMethodInfo implements Parcelable { settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges, - false /* supportsStylusHandwriting */); + false /* supportsStylusHandwriting */, + false /* inlineSuggestionsEnabled */); } /** @@ -373,7 +388,8 @@ public final class InputMethodInfo implements Parcelable { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */, 0 /* handledconfigChanges */, - false /* supportsStylusHandwriting */); + false /* supportsStylusHandwriting */, + false /* inlineSuggestionsEnabled */); } /** @@ -385,7 +401,8 @@ public final class InputMethodInfo implements Parcelable { boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) { this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly, - 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */); + 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */, + false /* inlineSuggestionsEnabled */); } /** @@ -395,7 +412,8 @@ public final class InputMethodInfo implements Parcelable { public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled, - boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting) { + boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting, + boolean supportsInlineSuggestionsWithTouchExploration) { final ServiceInfo si = ri.serviceInfo; mService = ri; mId = new ComponentName(si.packageName, si.name).flattenToShortString(); @@ -406,6 +424,8 @@ public final class InputMethodInfo implements Parcelable { mForceDefault = forceDefault; mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; mInlineSuggestionsEnabled = inlineSuggestionsEnabled; + mSupportsInlineSuggestionsWithTouchExploration = + supportsInlineSuggestionsWithTouchExploration; mSuppressesSpellChecker = false; mShowInInputMethodPicker = true; mIsVrOnly = isVrOnly; @@ -583,6 +603,8 @@ public final class InputMethodInfo implements Parcelable { + " mIsVrOnly=" + mIsVrOnly + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled + + " mSupportsInlineSuggestionsWithTouchExploration=" + + mSupportsInlineSuggestionsWithTouchExploration + " mSuppressesSpellChecker=" + mSuppressesSpellChecker + " mShowInInputMethodPicker=" + mShowInInputMethodPicker + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting); @@ -654,6 +676,15 @@ public final class InputMethodInfo implements Parcelable { } /** + * Returns {@code true} if this input method supports inline suggestions when touch exploration + * is enabled. + * @hide + */ + public boolean supportsInlineSuggestionsWithTouchExploration() { + return mSupportsInlineSuggestionsWithTouchExploration; + } + + /** * Return {@code true} if this input method suppresses spell checker. */ public boolean suppressesSpellChecker() { @@ -683,6 +714,7 @@ public final class InputMethodInfo implements Parcelable { dest.writeInt(mIsAuxIme ? 1 : 0); dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0); dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0); + dest.writeInt(mSupportsInlineSuggestionsWithTouchExploration ? 1 : 0); dest.writeBoolean(mSuppressesSpellChecker); dest.writeBoolean(mShowInInputMethodPicker); dest.writeBoolean(mIsVrOnly); diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index fca2bd15787d..3a2401a97009 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3644,6 +3644,14 @@ <attr name="__removed2" format="boolean" /> <!-- Specifies whether the IME supports showing inline suggestions. --> <attr name="supportsInlineSuggestions" format="boolean" /> + <!-- Specifies whether the IME supports showing inline suggestions when touch + exploration is enabled. This does nothing if supportsInlineSuggestions is false. + The default value is false and most IMEs should not set this + to true since the older menu-style Autofill works better with touch exploration. + This attribute should be set to true in special situations, such as if this is an + accessibility-focused IME which blocks user interaction with the app window while the + IME is displayed. --> + <attr name="supportsInlineSuggestionsWithTouchExploration" format="boolean" /> <!-- Specifies whether the IME suppresses system spell checker. The default value is false. If an IME sets this attribute to true, the system spell checker will be disabled while the IME has an diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 9fa5f78fef15..3beb4b2f7ee3 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3280,6 +3280,7 @@ <public name="knownActivityEmbeddingCerts" /> <public name="intro" /> <public name="enableOnBackInvokedCallback" /> + <public name="supportsInlineSuggestionsWithTouchExploration" /> </staging-public-group> <staging-public-group type="id" first-id="0x01de0000"> diff --git a/core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml b/core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml new file mode 100644 index 000000000000..34402089b47d --- /dev/null +++ b/core/tests/coretests/res/xml/ime_meta_inline_suggestions_with_touch_exploration.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2022 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. + --> + +<input-method + xmlns:android="http://schemas.android.com/apk/res/android" + android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity" + android:supportsInlineSuggestionsWithTouchExploration="true" +> + <subtype + android:label="subtype1" + android:imeSubtypeLocale="en_US" + android:imeSubtypeMode="keyboard" /> +</input-method> diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java index 8718b954474a..e7d7d640324d 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java @@ -55,11 +55,13 @@ public class InputMethodInfoTest { assertThat(imi.supportsSwitchingToNextInputMethod(), is(false)); assertThat(imi.isInlineSuggestionsEnabled(), is(false)); + assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false)); final InputMethodInfo clone = cloneViaParcel(imi); assertThat(clone.supportsSwitchingToNextInputMethod(), is(false)); assertThat(imi.isInlineSuggestionsEnabled(), is(false)); + assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(false)); } @Test @@ -85,6 +87,18 @@ public class InputMethodInfoTest { } @Test + public void testInlineSuggestionsEnabledWithTouchExploration() throws Exception { + final InputMethodInfo imi = + buildInputMethodForTest(R.xml.ime_meta_inline_suggestions_with_touch_exploration); + + assertThat(imi.supportsInlineSuggestionsWithTouchExploration(), is(true)); + + final InputMethodInfo clone = cloneViaParcel(imi); + + assertThat(clone.supportsInlineSuggestionsWithTouchExploration(), is(true)); + } + + @Test public void testIsVrOnly() throws Exception { final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_vr_only); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 76ee728fdb07..e0fa67f64c4c 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -103,7 +103,6 @@ import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; import android.view.KeyEvent; -import android.view.accessibility.AccessibilityManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.SmartSuggestionMode; @@ -367,8 +366,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable private ClientSuggestionsSession mClientSuggestionsSession; - private final AccessibilityManager mAccessibilityManager; - // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a // new one per Session. private final BroadcastReceiver mDelayedFillBroadcastReceiver = @@ -518,10 +515,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - // If a11y touch exploration is enabled, then we do not send an inline fill request - // to the regular af service, because dropdown UI is easier to use. - if (mPendingInlineSuggestionsRequest.isServiceSupported() - && !mAccessibilityManager.isTouchExplorationEnabled()) { + if (mPendingInlineSuggestionsRequest.isServiceSupported()) { mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), @@ -1064,7 +1058,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mRemoteFillService = serviceComponentName == null ? null : new RemoteFillService(context, serviceComponentName, userId, this, bindInstantServiceAllowed); - mAccessibilityManager = AccessibilityManager.getInstance(context); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 7068ed13376f..0b7e39136feb 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -130,6 +130,7 @@ import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import android.view.accessibility.AccessibilityManager; import android.view.autofill.AutofillId; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InlineSuggestionsRequest; @@ -287,6 +288,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private final InputMethodMenuController mMenuController; private final InputMethodBindingController mBindingController; + // TODO(b/219056452): Use AccessibilityManagerInternal instead. + private final AccessibilityManager mAccessibilityManager; + /** * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}. * @@ -1627,6 +1631,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mUserManager = mContext.getSystemService(UserManager.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); + mAccessibilityManager = AccessibilityManager.getInstance(context); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); mPlatformCompat = IPlatformCompat.Stub.asInterface( @@ -1995,12 +2000,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId, - InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) { + InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback, + boolean touchExplorationEnabled) { final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked()); try { IInputMethodInvoker curMethod = getCurMethodLocked(); - if (userId == mSettings.getCurrentUserId() && imi != null - && imi.isInlineSuggestionsEnabled() && curMethod != null) { + if (userId == mSettings.getCurrentUserId() && curMethod != null + && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) { final IInlineSuggestionsRequestCallback callbackImpl = new InlineSuggestionsRequestCallbackDecorator(callback, imi.getPackageName(), mCurTokenDisplayId, getCurTokenLocked(), @@ -2014,6 +2020,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } + private static boolean isInlineSuggestionsEnabled(InputMethodInfo imi, + boolean touchExplorationEnabled) { + return imi.isInlineSuggestionsEnabled() + && (!touchExplorationEnabled + || imi.supportsInlineSuggestionsWithTouchExploration()); + } + /** * The decorator which validates the host package name in the * {@link InlineSuggestionsRequest} argument to make sure it matches the IME package name. @@ -5197,8 +5210,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onCreateInlineSuggestionsRequest(@UserIdInt int userId, InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) { + // Get the device global touch exploration state before lock to avoid deadlock. + boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); + synchronized (ImfLock.class) { - onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb); + onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb, + touchExplorationEnabled); } } |