diff --git a/core/api/current.txt b/core/api/current.txt index 282a4c355d2d3..330089dcc6d4b 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 9a706673583bd..d7c18462077d6 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 @@ -124,6 +125,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. */ @@ -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 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); @@ -653,6 +675,15 @@ public final class InputMethodInfo implements Parcelable { return mInlineSuggestionsEnabled; } + /** + * 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. */ @@ -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 fca2bd15787d1..3a2401a970094 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3644,6 +3644,14 @@ + + + + + + diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java index 8718b954474af..e7d7d640324d0 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 @@ -84,6 +86,18 @@ public class InputMethodInfoTest { assertThat(clone.isInlineSuggestionsEnabled(), is(true)); } + @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 76ee728fdb07a..e0fa67f64c4c7 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 7068ed13376f1..0b7e39136feb3 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); } }