diff options
6 files changed, 98 insertions, 17 deletions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 500701de7f23..ce3022ed791d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1672,12 +1672,20 @@ public interface WindowManager extends ViewManager { * Visibility state for {@link #softInputMode}: please show the soft * input area when normally appropriate (when the user is navigating * forward to your window). + * + * <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag + * is ignored unless there is a focused view that returns {@code true} from + * {@link View#isInEditMode()} when the window is focused.</p> */ public static final int SOFT_INPUT_STATE_VISIBLE = 4; /** * Visibility state for {@link #softInputMode}: please always make the * soft input area visible when this window receives input focus. + * + * <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag + * is ignored unless there is a focused view that returns {@code true} from + * {@link View#isInEditMode()} when the window is focused.</p> */ public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3cd8d4a2417d..9f033d72167f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1333,7 +1333,8 @@ public final class InputMethodManager { + Integer.toHexString(controlFlags)); final InputBindResult res = mService.startInputOrWindowGainedFocus( startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode, - windowFlags, tba, servedContext, missingMethodFlags); + windowFlags, tba, servedContext, missingMethodFlags, + view.getContext().getApplicationInfo().targetSdkVersion); if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res != null) { if (res.id != null) { @@ -1588,7 +1589,8 @@ public final class InputMethodManager { mService.startInputOrWindowGainedFocus( InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null, - null, 0 /* missingMethodFlags */); + null, 0 /* missingMethodFlags */, + rootView.getContext().getApplicationInfo().targetSdkVersion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 57efae61a9c6..d3b4dbf1bd82 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -16,6 +16,9 @@ package com.android.internal.inputmethod; +import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR; +import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -26,6 +29,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.os.Build; import android.os.LocaleList; import android.os.RemoteException; import android.provider.Settings; @@ -33,6 +37,7 @@ import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import android.util.Pair; import android.util.Printer; import android.util.Slog; @@ -1510,4 +1515,20 @@ public class InputMethodUtils { } return locales; } + + public static boolean isSoftInputModeStateVisibleAllowed( + int targetSdkVersion, int controlFlags) { + if (targetSdkVersion < Build.VERSION_CODES.P) { + // for compatibility. + return true; + } + if ((controlFlags & CONTROL_WINDOW_VIEW_HAS_FOCUS) == 0) { + return false; + } + if ((controlFlags & CONTROL_WINDOW_IS_TEXT_EDITOR) == 0) { + return false; + } + return true; + } + } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 1fd5564773b1..ac03d74632fa 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -61,7 +61,8 @@ interface IInputMethodManager { in IInputMethodClient client, in IBinder windowToken, int controlFlags, /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, in EditorInfo attribute, IInputContext inputContext, - /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags); + /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags, + int unverifiedTargetSdkVersion); void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java index 33cd328a6d80..e6ac68294564 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java @@ -16,6 +16,9 @@ package com.android.internal.inputmethod; +import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR; +import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.not; @@ -31,6 +34,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.os.Build; import android.os.LocaleList; import android.os.Parcel; import android.support.test.InstrumentationRegistry; @@ -1410,4 +1414,29 @@ public class InputMethodUtilsTest { assertEquals(new Locale("a b c"), InputMethodUtils.constructLocaleFromString("a b c")); assertEquals(new Locale("en-US"), InputMethodUtils.constructLocaleFromString("en-US")); } + + @Test + public void testIsSoftInputModeStateVisibleAllowed() { + // On pre-P devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are always + // allowed, regardless of the focused view state. + assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( + Build.VERSION_CODES.O_MR1, 0)); + assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( + Build.VERSION_CODES.O_MR1, CONTROL_WINDOW_VIEW_HAS_FOCUS)); + assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( + Build.VERSION_CODES.O_MR1, + CONTROL_WINDOW_VIEW_HAS_FOCUS | CONTROL_WINDOW_IS_TEXT_EDITOR)); + + // On P+ devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are allowed only + // when there is a focused View and its View#onCheckIsTextEditor() returns true. + assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed( + Build.VERSION_CODES.P, 0)); + assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed( + Build.VERSION_CODES.P, CONTROL_WINDOW_VIEW_HAS_FOCUS)); + assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( + Build.VERSION_CODES.P, + CONTROL_WINDOW_VIEW_HAS_FOCUS | CONTROL_WINDOW_IS_TEXT_EDITOR)); + + } + } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 5fed93b1b0b1..25c3fe42f795 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -93,6 +93,7 @@ import android.hardware.input.InputManagerInternal; import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Environment; @@ -2723,10 +2724,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub /* @InputMethodClient.StartInputReason */ final int startInputReason, IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext, - /* @InputConnectionInspector.missingMethods */ final int missingMethods) { + /* @InputConnectionInspector.missingMethods */ final int missingMethods, + int unverifiedTargetSdkVersion) { if (windowToken != null) { return windowGainedFocus(startInputReason, client, windowToken, controlFlags, - softInputMode, windowFlags, attribute, inputContext, missingMethods); + softInputMode, windowFlags, attribute, inputContext, missingMethods, + unverifiedTargetSdkVersion); } else { return startInput(startInputReason, client, inputContext, missingMethods, attribute, controlFlags); @@ -2738,7 +2741,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IInputMethodClient client, IBinder windowToken, int controlFlags, /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext, - /* @InputConnectionInspector.missingMethods */ final int missingMethods) { + /* @InputConnectionInspector.missingMethods */ final int missingMethods, + int unverifiedTargetSdkVersion) { // Needs to check the validity before clearing calling identity final boolean calledFromValidUser = calledFromValidUser(); InputBindResult res = null; @@ -2754,7 +2758,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " attribute=" + attribute + " controlFlags=#" + Integer.toHexString(controlFlags) + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode) - + " windowFlags=#" + Integer.toHexString(windowFlags)); + + " windowFlags=#" + Integer.toHexString(windowFlags) + + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion); ClientState cs = mClients.get(client.asBinder()); if (cs == null) { @@ -2868,22 +2873,37 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if ((softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { if (DEBUG) Slog.v(TAG, "Window asks to show input going forward"); - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, - missingMethods, attribute, controlFlags, startInputReason); - didStart = true; + if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( + unverifiedTargetSdkVersion, controlFlags)) { + if (attribute != null) { + res = startInputUncheckedLocked(cs, inputContext, + missingMethods, attribute, controlFlags, + startInputReason); + didStart = true; + } + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + } else { + Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" + + " there is no focused view that also returns true from" + + " View#onCheckIsTextEditor()"); } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); } break; case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: if (DEBUG) Slog.v(TAG, "Window asks to always show input"); - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, - attribute, controlFlags, startInputReason); - didStart = true; + if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( + unverifiedTargetSdkVersion, controlFlags)) { + if (attribute != null) { + res = startInputUncheckedLocked(cs, inputContext, missingMethods, + attribute, controlFlags, startInputReason); + didStart = true; + } + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + } else { + Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + + " there is no focused view that also returns true from" + + " View#onCheckIsTextEditor()"); } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); break; } |