Fix SHOW_IMPLICIT and SHOW_EXPLICIT handling

This fixes a bug introduced in the refactor from [1], where
SHOW_IMPLICIT and SHOW_EXPLICIT requests would not be handled correctly,
as they represent the same underlying bit flag value.

IntDefs were added to help understand which set of flags to use
based on call site. Requests originating from InputMethodManager
(and a few from InputMethodService) must utilise InputMethodManager
flags (i.e SHOW_IMPLICIT). When reaching the ImeVisibilityStateComputer,
these are translated to InputMethodService flags (i.e. SHOW_EXPLICIT).

Additonally, this documents and tests that `ImeVisibilityStateComputer`
keeps the strongest state set until a hide request (i.e. an implicit
show request just after a forced / explicit show request, without
any hide requests in between, will still have the
forced / explicit state set).

[1]: Id1115ceb951e4bb0361a32b824d966cc70b7d132

Bug: 289188559
Test: atest DefaultImeVisibilityApplierTest ImeVisibilityStateComputerTest
  com.android.inputmethodservice.InputMethodServiceTest
Change-Id: I0fde7ea4cec99e739eb4541cc206daa191661689
This commit is contained in:
Cosmin Băieș
2023-06-28 12:37:18 +02:00
parent e6ac183848
commit fed109a5ee
21 changed files with 611 additions and 233 deletions

View File

@@ -433,7 +433,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
int flags, ResultReceiver resultReceiver) {
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
flags, showInputToken, resultReceiver, statsToken));

View File

@@ -607,6 +607,7 @@ public class InputMethodService extends AbstractInputMethodService {
InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
@InputMethod.ShowFlags
int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
@@ -931,8 +932,9 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
public void showSoftInputWithToken(@InputMethod.ShowFlags int flags,
ResultReceiver resultReceiver, IBinder showInputToken,
@Nullable ImeTracker.Token statsToken) {
mSystemCallingShowSoftInput = true;
mCurShowInputToken = showInputToken;
mCurStatsToken = statsToken;
@@ -950,7 +952,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
ImeTracker.forLogging().onProgress(
mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
if (DEBUG) Log.v(TAG, "showSoftInput()");
@@ -1321,7 +1323,8 @@ public class InputMethodService extends AbstractInputMethodService {
* InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
*/
@Deprecated
public void toggleSoftInput(int showFlags, int hideFlags) {
public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
@InputMethodManager.HideFlags int hideFlags) {
InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
}
@@ -2793,18 +2796,16 @@ public class InputMethodService extends AbstractInputMethodService {
* {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
* and the current configuration to decide whether the input view should
* be shown at this point.
*
* @param flags Provides additional information about the show request,
* as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
*
* @param configChange This is true if we are re-showing due to a
* configuration change.
* @return Returns true to indicate that the window should be shown.
*/
public boolean onShowInputRequested(int flags, boolean configChange) {
public boolean onShowInputRequested(@InputMethod.ShowFlags int flags, boolean configChange) {
if (!onEvaluateInputViewShown()) {
return false;
}
if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
if ((flags & InputMethod.SHOW_EXPLICIT) == 0) {
if (!configChange && onEvaluateFullscreenMode() && !isInputViewShown()) {
// Don't show if this is not explicitly requested by the user and
// the input method is fullscreen unless it is already shown. That
@@ -2830,14 +2831,14 @@ public class InputMethodService extends AbstractInputMethodService {
* exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
* to have this method to ensure that those internal states are always updated no matter how
* {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
* @param flags Provides additional information about the show request,
* as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
*
* @param configChange This is true if we are re-showing due to a
* configuration change.
* @return Returns true to indicate that the window should be shown.
* @see #onShowInputRequested(int, boolean)
*/
private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
private boolean dispatchOnShowInputRequested(@InputMethod.ShowFlags int flags,
boolean configChange) {
final boolean result = onShowInputRequested(flags, configChange);
mInlineSuggestionSessionController.notifyOnShowInputRequested(result);
if (result) {
@@ -3270,16 +3271,13 @@ public class InputMethodService extends AbstractInputMethodService {
*
* The input method will continue running, but the user can no longer use it to generate input
* by touching the screen.
*
* @see InputMethodManager#HIDE_IMPLICIT_ONLY
* @see InputMethodManager#HIDE_NOT_ALWAYS
* @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
public void requestHideSelf(@InputMethodManager.HideFlags int flags) {
requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME);
}
private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) {
private void requestHideSelf(@InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason) {
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
null /* icProto */);
mPrivOps.hideMySoftInput(flags, reason);
@@ -3288,12 +3286,8 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Show the input method's soft input area, so the user sees the input method window and can
* interact with it.
*
* @see InputMethodManager#SHOW_IMPLICIT
* @see InputMethodManager#SHOW_FORCED
* @param flags Provides additional operating flags.
*/
public final void requestShowSelf(int flags) {
public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) {
ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
null /* icProto */);
mPrivOps.showMySoftInput(flags);
@@ -3453,7 +3447,8 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Handle a request by the system to toggle the soft input area.
*/
private void onToggleSoftInput(int showFlags, int hideFlags) {
private void onToggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
@InputMethodManager.HideFlags int hideFlags) {
if (DEBUG) Log.v(TAG, "toggleSoftInput()");
if (isInputViewShown()) {
requestHideSelf(

View File

@@ -295,8 +295,8 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
@Nullable ResultReceiver resultReceiver,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {
@@ -312,7 +312,7 @@ final class IInputMethodManagerGlobalInvoker {
@AnyThread
static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
@Nullable ImeTracker.Token statsToken, int flags,
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
@Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final IInputMethodManager service = getService();
if (service == null) {

View File

@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,6 +37,8 @@ import com.android.internal.inputmethod.IInputMethod;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -269,6 +272,14 @@ public interface InputMethod {
*/
@MainThread
public void revokeSession(InputMethodSession session);
/** @hide */
@IntDef(flag = true, prefix = { "SHOW_" }, value = {
SHOW_EXPLICIT,
SHOW_FORCED,
})
@Retention(RetentionPolicy.SOURCE)
@interface ShowFlags {}
/**
* Flag for {@link #showSoftInput}: this show has been explicitly
@@ -288,8 +299,6 @@ public interface InputMethod {
/**
* Request that any soft input part of the input method be shown to the user.
*
* @param flags Provides additional information about the show request.
* Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
* The result code should be
@@ -304,7 +313,7 @@ public interface InputMethod {
* @hide
*/
@MainThread
public default void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver,
IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
showSoftInput(flags, resultReceiver);
}
@@ -312,8 +321,6 @@ public interface InputMethod {
/**
* Request that any soft input part of the input method be shown to the user.
*
* @param flags Provides additional information about the show request.
* Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
* The result code should be
@@ -323,11 +330,12 @@ public interface InputMethod {
* {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
*/
@MainThread
public void showSoftInput(int flags, ResultReceiver resultReceiver);
public void showSoftInput(@ShowFlags int flags, ResultReceiver resultReceiver);
/**
* Request that any soft input part of the input method be hidden from the user.
* @param flags Provides additional information about the show request.
*
* @param flags Provides additional information about the hide request.
* Currently always 0.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.
@@ -350,7 +358,8 @@ public interface InputMethod {
/**
* Request that any soft input part of the input method be hidden from the user.
* @param flags Provides additional information about the show request.
*
* @param flags Provides additional information about the hide request.
* Currently always 0.
* @param resultReceiver The client requesting the show may wish to
* be told the impact of their request, which should be supplied here.

View File

@@ -39,6 +39,7 @@ import android.Manifest;
import android.annotation.DisplayContext;
import android.annotation.DrawableRes;
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -121,6 +122,8 @@ import com.android.internal.view.IInputMethodManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
@@ -2004,6 +2007,14 @@ public final class InputMethodManager {
}
}
/** @hide */
@IntDef(flag = true, prefix = { "SHOW_" }, value = {
SHOW_IMPLICIT,
SHOW_FORCED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShowFlags {}
/**
* Flag for {@link #showSoftInput} to indicate that this is an implicit
* request to show the input window, not as the result of a direct request
@@ -2035,10 +2046,8 @@ public final class InputMethodManager {
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #SHOW_IMPLICIT} bit set.
*/
public boolean showSoftInput(View view, int flags) {
public boolean showSoftInput(View view, @ShowFlags int flags) {
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
@@ -2101,21 +2110,20 @@ public final class InputMethodManager {
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #SHOW_IMPLICIT} bit set.
* @param resultReceiver If non-null, this will be called by the IME when
* it has processed your request to tell you what it has done. The result
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, null /* statsToken */, flags, resultReceiver,
SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken,
@ShowFlags int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if (statsToken == null) {
statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason);
@@ -2169,7 +2177,7 @@ public final class InputMethodManager {
*/
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499)
public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) {
synchronized (mH) {
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow(
null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
@@ -2200,6 +2208,14 @@ public final class InputMethodManager {
}
}
/** @hide */
@IntDef(flag = true, prefix = { "HIDE_" }, value = {
HIDE_IMPLICIT_ONLY,
HIDE_NOT_ALWAYS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface HideFlags {}
/**
* Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)}
* to indicate that the soft input window should only be hidden if it was not explicitly shown
@@ -2221,10 +2237,8 @@ public final class InputMethodManager {
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
}
@@ -2246,21 +2260,19 @@ public final class InputMethodManager {
*
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
* @param resultReceiver If non-null, this will be called by the IME when
* it has processed your request to tell you what it has done. The result
* code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
* {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
* {@link #RESULT_HIDDEN}.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
return hideSoftInputFromWindow(windowToken, flags, resultReceiver,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
}
private boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
null /* component */, Process.myUid(),
@@ -2463,12 +2475,6 @@ public final class InputMethodManager {
* If not the input window will be displayed.
* @param windowToken The token of the window that is making the request,
* as returned by {@link View#getWindowToken() View.getWindowToken()}.
* @param showFlags Provides additional operating flags. May be
* 0 or have the {@link #SHOW_IMPLICIT},
* {@link #SHOW_FORCED} bit set.
* @param hideFlags Provides additional operating flags. May be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
*
* @deprecated Use {@link #showSoftInput(View, int)} or
* {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead.
@@ -2477,7 +2483,8 @@ public final class InputMethodManager {
* has an effect if the calling app is the current IME focus.
*/
@Deprecated
public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
public void toggleSoftInputFromWindow(IBinder windowToken, @ShowFlags int showFlags,
@HideFlags int hideFlags) {
ImeTracing.getInstance().triggerClientDump(
"InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this,
null /* icProto */);
@@ -2495,12 +2502,6 @@ public final class InputMethodManager {
*
* If the input window is already displayed, it gets hidden.
* If not the input window will be displayed.
* @param showFlags Provides additional operating flags. May be
* 0 or have the {@link #SHOW_IMPLICIT},
* {@link #SHOW_FORCED} bit set.
* @param hideFlags Provides additional operating flags. May be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
*
* @deprecated Use {@link #showSoftInput(View, int)} or
* {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead.
@@ -2509,7 +2510,7 @@ public final class InputMethodManager {
* has an effect if the calling app is the current IME focus.
*/
@Deprecated
public void toggleSoftInput(int showFlags, int hideFlags) {
public void toggleSoftInput(@ShowFlags int showFlags, @HideFlags int hideFlags) {
ImeTracing.getInstance().triggerClientDump(
"InputMethodManager#toggleSoftInput", InputMethodManager.this,
null /* icProto */);
@@ -3522,15 +3523,12 @@ public final class InputMethodManager {
* @param token Supplies the identifying token given to an input method
* when it was started, which allows it to perform this operation on
* itself.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
* @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was
* intended for IME developers who should be accessing APIs through the service. APIs in this
* class are intended for app developers interacting with the IME.
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) {
InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(
flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION);
}
@@ -3544,15 +3542,12 @@ public final class InputMethodManager {
* @param token Supplies the identifying token given to an input method
* when it was started, which allows it to perform this operation on
* itself.
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #SHOW_IMPLICIT} or
* {@link #SHOW_FORCED} bit set.
* @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was
* intended for IME developers who should be accessing APIs through the service. APIs in this
* class are intended for app developers interacting with the IME.
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) {
InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
}

View File

@@ -169,12 +169,6 @@ public interface InputMethodSession {
/**
* Toggle the soft input window.
* Applications can toggle the state of the soft input window.
* @param showFlags Provides additional operating flags. May be
* 0 or have the {@link InputMethodManager#SHOW_IMPLICIT},
* {@link InputMethodManager#SHOW_FORCED} bit set.
* @param hideFlags Provides additional operating flags. May be
* 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY},
* {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
*
* @deprecated Starting in {@link android.os.Build.VERSION_CODES#S} the system no longer invokes
* this method, instead it explicitly shows or hides the IME. An {@code InputMethodService}
@@ -182,7 +176,8 @@ public interface InputMethodSession {
* InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
*/
@Deprecated
public void toggleSoftInput(int showFlags, int hideFlags);
public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
@InputMethodManager.HideFlags int hideFlags);
/**
* This method is called when the cursor and/or the character position relevant to text input

View File

@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
@@ -253,13 +254,11 @@ public final class InputMethodPrivilegedOperations {
/**
* Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)}
*
* @param flags additional operating flags
* @param reason the reason to hide soft input
* @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
* @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS
*/
@AnyThread
public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason) {
public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
@@ -275,13 +274,9 @@ public final class InputMethodPrivilegedOperations {
/**
* Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)}
*
* @param flags additional operating flags
* @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
* @see android.view.inputmethod.InputMethodManager#SHOW_FORCED
*/
@AnyThread
public void showMySoftInput(int flags) {
public void showMySoftInput(@InputMethodManager.ShowFlags int flags) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;

View File

@@ -37,6 +37,7 @@ import android.os.ResultReceiver;
import android.util.EventLog;
import android.util.Slog;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.GuardedBy;
@@ -75,7 +76,8 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
@GuardedBy("ImfLock.class")
@Override
public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
if (curMethod != null) {
if (DEBUG) {

View File

@@ -30,6 +30,7 @@ import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
@@ -198,8 +199,8 @@ final class IInputMethodInvoker {
// TODO(b/192412909): Convert this back to void method
@AnyThread
boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, int flags,
ResultReceiver resultReceiver) {
boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
try {
mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver);
} catch (RemoteException e) {

View File

@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethod;
import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -34,13 +35,13 @@ interface ImeVisibilityApplier {
*
* @param showInputToken A token that represents the requester to show IME.
* @param statsToken A token that tracks the progress of an IME request.
* @param showFlags Provides additional operating flags to show IME.
* @param resultReceiver If non-null, this will be called back to the caller when
* it has processed request to tell what it has done.
* @param reason The reason for requesting to show IME.
*/
default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {}
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {}
/**
* Performs hiding IME to the given window

View File

@@ -221,17 +221,21 @@ public final class ImeVisibilityStateComputer {
/**
* Called when {@link InputMethodManagerService} is processing the show IME request.
* @param statsToken The token for tracking this show request
* @param showFlags The additional operation flags to indicate whether this show request mode is
* implicit or explicit.
* @return {@code true} when the computer has proceed this show request operation.
*
* @param statsToken The token for tracking this show request.
* @return {@code true} when the show request can proceed.
*/
boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, int showFlags) {
boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
@InputMethodManager.ShowFlags int showFlags) {
if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
return false;
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
// We only "set" the state corresponding to the flags, as this will be reset
// in clearImeShowFlags during a hide request.
// Thus, we keep the strongest values set (e.g. an implicit show right after
// an explicit show will still be considered explicit, likewise for forced).
if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) {
mRequestedShowExplicitly = true;
mShowForced = true;
@@ -243,12 +247,12 @@ public final class ImeVisibilityStateComputer {
/**
* Called when {@link InputMethodManagerService} is processing the hide IME request.
* @param statsToken The token for tracking this hide request
* @param hideFlags The additional operation flags to indicate whether this hide request mode is
* implicit or explicit.
* @return {@code true} when the computer has proceed this hide request operations.
*
* @param statsToken The token for tracking this hide request.
* @return {@code true} when the hide request can proceed.
*/
boolean canHideIme(@NonNull ImeTracker.Token statsToken, int hideFlags) {
boolean canHideIme(@NonNull ImeTracker.Token statsToken,
@InputMethodManager.HideFlags int hideFlags) {
if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mRequestedShowExplicitly || mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -264,13 +268,31 @@ public final class ImeVisibilityStateComputer {
return true;
}
int getImeShowFlags() {
/**
* Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags}
* to {@link InputMethod.ShowFlags}.
*/
@InputMethod.ShowFlags
int getShowFlagsForInputMethodServiceOnly() {
int flags = 0;
if (mShowForced) {
flags |= InputMethod.SHOW_FORCED | InputMethod.SHOW_EXPLICIT;
} else if (mRequestedShowExplicitly) {
flags |= InputMethod.SHOW_EXPLICIT;
} else {
}
return flags;
}
/**
* Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags}
* to {@link InputMethodManager.ShowFlags}.
*/
@InputMethodManager.ShowFlags
int getShowFlags() {
int flags = 0;
if (mShowForced) {
flags |= InputMethodManager.SHOW_FORCED;
} else if (!mRequestedShowExplicitly) {
flags |= InputMethodManager.SHOW_IMPLICIT;
}
return flags;

View File

@@ -2462,7 +2462,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final ImeTracker.Token statsToken = mCurStatsToken;
mCurStatsToken = null;
showCurrentInputLocked(mCurFocusedWindow, statsToken,
mVisibilityStateComputer.getImeShowFlags(),
mVisibilityStateComputer.getShowFlags(),
null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
@@ -3398,8 +3398,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, int flags, int lastClickTooType,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickTooType, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3572,15 +3573,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
@InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
return showCurrentInputLocked(windowToken, statsToken, flags,
MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
}
@GuardedBy("ImfLock.class")
private boolean showCurrentInputLocked(IBinder windowToken,
@Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
@Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
int lastClickToolType, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
statsToken = createStatsTokenForFocusedClient(true /* show */,
@@ -3611,7 +3614,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
curMethod.updateEditorToolType(lastClickToolType);
}
mVisibilityApplier.performShowIme(windowToken, statsToken,
mVisibilityStateComputer.getImeShowFlags(), resultReceiver, reason);
mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
resultReceiver, reason);
mVisibilityStateComputer.setInputShown(true);
return true;
} else {
@@ -3623,8 +3627,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
@Nullable ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
@Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput");
@@ -3654,7 +3658,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
@InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
// Create statsToken is none exists.
if (statsToken == null) {
statsToken = createStatsTokenForFocusedClient(false /* show */,
@@ -4841,7 +4846,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags,
private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
@@ -4863,7 +4868,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
private void showMySoftInput(@NonNull IBinder token, int flags) {
private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
synchronized (ImfLock.class) {
@@ -6822,8 +6827,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason,
AndroidFuture future /* T=Void */) {
public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
@SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {
@@ -6836,7 +6841,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void showMySoftInput(int flags, AndroidFuture future /* T=Void */) {
public void showMySoftInput(@InputMethodManager.ShowFlags int flags,
AndroidFuture future /* T=Void */) {
@SuppressWarnings("unchecked")
final AndroidFuture<Void> typedFuture = future;
try {

View File

@@ -17,7 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.inputmethodtests">
<uses-sdk android:targetSdkVersion="31" />
<queries>
<intent>
<action android:name="android.view.InputMethod" />

View File

@@ -9,5 +9,16 @@
{"exclude-annotation": "org.junit.Ignore"}
]
}
],
"postsubmit": [
{
"name": "FrameworksImeTests",
"options": [
{"include-filter": "com.android.inputmethodservice"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
}
]
}

View File

@@ -18,8 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.inputmethod.imetests">
<uses-sdk android:targetSdkVersion="31" />
<!-- Permissions required for granting and logging -->
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>

View File

@@ -20,6 +20,8 @@ import static com.android.compatibility.common.util.SystemUtil.eventually;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.app.Instrumentation;
import android.content.Context;
import android.content.res.Configuration;
@@ -45,6 +47,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -56,9 +59,9 @@ public class InputMethodServiceTest {
private static final String EDIT_TEXT_DESC = "Input box";
private static final long TIMEOUT_IN_SECONDS = 3;
private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
"settings put secure show_ime_with_hard_keyboard 1";
"settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 1";
private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
"settings put secure show_ime_with_hard_keyboard 0";
"settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 0";
private Instrumentation mInstrumentation;
private UiDevice mUiDevice;
@@ -82,29 +85,19 @@ public class InputMethodServiceTest {
mUiDevice.freezeRotation();
mUiDevice.setOrientationNatural();
// Waits for input binding ready.
eventually(
() -> {
mInputMethodService =
InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting();
assertThat(mInputMethodService).isNotNull();
eventually(() -> {
mInputMethodService =
InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting();
assertThat(mInputMethodService).isNotNull();
// The editor won't bring up keyboard by default.
assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse();
});
// Save the original value of show_ime_with_hard_keyboard in Settings.
// The editor won't bring up keyboard by default.
assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse();
});
// Save the original value of show_ime_with_hard_keyboard from Settings.
mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt(
mInputMethodService.getContentResolver(),
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
// Disable showing Ime with hard keyboard because it is the precondition the for most test
// cases
if (mShowImeWithHardKeyboardEnabled) {
executeShellCommand(DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
}
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_NOKEYS;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
}
@After
@@ -112,82 +105,141 @@ public class InputMethodServiceTest {
mUiDevice.unfreezeRotation();
executeShellCommand("ime disable " + mInputMethodId);
// Change back the original value of show_ime_with_hard_keyboard in Settings.
executeShellCommand(mShowImeWithHardKeyboardEnabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
executeShellCommand(mShowImeWithHardKeyboardEnabled
? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
: DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
}
/**
* This checks that the IME can be shown and hidden by user actions
* (i.e. tapping on an EditText, tapping the Home button).
*/
@Test
public void testShowHideKeyboard_byUserAction() throws InterruptedException {
public void testShowHideKeyboard_byUserAction() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
// Performs click on editor box to bring up the soft keyboard.
Log.i(TAG, "Click on EditText.");
verifyInputViewStatus(() -> clickOnEditorText(), true /* inputViewStarted */);
// Press back key to hide soft keyboard.
Log.i(TAG, "Press back");
verifyInputViewStatus(
() -> assertThat(mUiDevice.pressHome()).isTrue(), false /* inputViewStarted */);
() -> clickOnEditorText(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// Press home key to hide soft keyboard.
Log.i(TAG, "Press home");
verifyInputViewStatus(
() -> assertThat(mUiDevice.pressHome()).isTrue(),
true /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
/**
* This checks that the IME can be shown and hidden using the WindowInsetsController APIs.
*/
@Test
public void testShowHideKeyboard_byApi() throws InterruptedException {
public void testShowHideKeyboard_byApi() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
// Triggers to show IME via public API.
verifyInputViewStatus(
() -> assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// Triggers to hide IME via public API.
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(),
() -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(),
true /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
/**
* This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
*/
@Test
public void testShowHideSelf() throws InterruptedException {
// IME requests to show itself without any flags: expect shown.
public void testShowHideSelf() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
// IME request to show itself without any flags, expect shown.
Log.i(TAG, "Call IMS#requestShowSelf(0)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestShowSelf(0), true /* inputViewStarted */);
() -> mInputMethodService.requestShowSelf(0 /* flags */),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// IME requests to hide itself with flag: HIDE_IMPLICIT_ONLY, expect not hide (shown).
// IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect not hide (shown).
Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY),
false /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// IME request to hide itself without any flags: expect hidden.
// IME request to hide itself without any flags, expect hidden.
Log.i(TAG, "Call IMS#requestHideSelf(0)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestHideSelf(0), false /* inputViewStarted */);
() -> mInputMethodService.requestHideSelf(0 /* flags */),
true /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
// IME request to show itself with flag SHOW_IMPLICIT: expect shown.
// IME request to show itself with flag SHOW_IMPLICIT, expect shown.
Log.i(TAG, "Call IMS#requestShowSelf(InputMethodManager.SHOW_IMPLICIT)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// IME request to hide itself with flag: HIDE_IMPLICIT_ONLY, expect hidden.
// IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect hidden.
Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
verifyInputViewStatusOnMainSync(
() -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY),
true /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
/**
* This checks the return value of IMS#onEvaluateInputViewShown,
* when show_ime_with_hard_keyboard is enabled.
*/
@Test
public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception {
executeShellCommand(ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
mInstrumentation.waitForIdleSync();
setShowImeWithHardKeyboard(true /* enabled */);
// Simulate connecting a hard keyboard
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_NO;
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_NOKEYS;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_NO;
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
}
/**
* This checks the return value of IMSonEvaluateInputViewShown,
* when show_ime_with_hard_keyboard is disabled.
*/
@Test
public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() {
public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
@@ -196,6 +248,8 @@ public class InputMethodServiceTest {
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_NOKEYS;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_NO;
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
mInputMethodService.getResources().getConfiguration().keyboard =
@@ -205,149 +259,386 @@ public class InputMethodServiceTest {
eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
}
/**
* This checks that any (implicit or explicit) show request,
* when IMS#onEvaluateInputViewShown returns false, results in the IME not being shown.
*/
@Test
public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception {
// Simulate connecting a hard keyboard
setShowImeWithHardKeyboard(false /* enabled */);
// Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_NO;
// When InputMethodService#onEvaluateInputViewShown() returns false, the Ime should not be
// shown no matter what the show flag is.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
false /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
false /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
/**
* This checks that an explicit show request results in the IME being shown.
*/
@Test
public void testShowSoftInputExplicitly() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
// When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the
// Ime should be shown.
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
/**
* This checks that an implicit show request results in the IME being shown.
*/
@Test
public void testShowSoftInputImplicitly() throws Exception {
// When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, the
// Ime should be shown.
setShowImeWithHardKeyboard(true /* enabled */);
// When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT,
// the IME should be shown.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
/**
* This checks that an explicit show request when the IME is not previously shown,
* and it should be shown in fullscreen mode, results in the IME being shown.
*/
@Test
public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
// When keyboard is off, InputMethodService#onEvaluateInputViewShown returns true, flag is
// IMPLICIT and InputMethodService#onEvaluateFullScreenMode returns true, the Ime should not
// be shown.
public void testShowSoftInputExplicitly_fullScreenMode() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
// Set orientation landscape to enable fullscreen mode.
setOrientation(2);
eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse());
// Wait for the TestActivity to be recreated
// Wait for the TestActivity to be recreated.
eventually(() ->
assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity));
// Get the new TestActivity
// Get the new TestActivity.
mActivity = TestActivity.getLastCreatedInstance();
assertThat(mActivity).isNotNull();
InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
// Wait for the new EditText to be served by InputMethodManager
eventually(() ->
assertThat(imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
// Wait for the new EditText to be served by InputMethodManager.
eventually(() -> assertThat(
imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
false /* inputViewStarted */);
mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
/**
* This checks that an implicit show request when the IME is not previously shown,
* and it should be shown in fullscreen mode, results in the IME not being shown.
*/
@Test
public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
// Set orientation landscape to enable fullscreen mode.
setOrientation(2);
eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse());
// Wait for the TestActivity to be recreated.
eventually(() ->
assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity));
// Get the new TestActivity.
mActivity = TestActivity.getLastCreatedInstance();
assertThat(mActivity).isNotNull();
InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
// Wait for the new EditText to be served by InputMethodManager.
eventually(() -> assertThat(
imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
false /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
/**
* This checks that an explicit show request when a hard keyboard is connected,
* results in the IME being shown.
*/
@Test
public void testShowSoftInputExplicitly_withHardKeyboard() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
// Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
/**
* This checks that an implicit show request when a hard keyboard is connected,
* results in the IME not being shown.
*/
@Test
public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
// Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
// When connecting to a hard keyboard and the flag is IMPLICIT, the Ime should not be shown.
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
false /* expected */,
false /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
/**
* This checks that an explicit show request followed by connecting a hard keyboard
* and a configuration change, still results in the IME being shown.
*/
@Test
public void testConfigurationChanged_withKeyboardShownExplicitly() throws InterruptedException {
public void testShowSoftInputExplicitly_thenConfigurationChanged() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
// Start with no hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_NOKEYS;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
verifyInputViewStatusOnMainSync(
() -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
// Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
mInputMethodService.getResources().getConfiguration().orientation =
Configuration.ORIENTATION_LANDSCAPE;
verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
mInputMethodService.getResources().getConfiguration()),
true /* inputViewStarted */);
}
assertThat(mInputMethodService.isInputViewShown()).isTrue();
@Test
public void testConfigurationChanged_withKeyboardShownImplicitly() throws InterruptedException {
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
true /* inputViewStarted */);
// Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
mInputMethodService.getResources().getConfiguration().orientation =
Configuration.ORIENTATION_LANDSCAPE;
// Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
// Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
mInputMethodService.getResources().getConfiguration().orientation =
Configuration.ORIENTATION_LANDSCAPE;
verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
mInputMethodService.getResources().getConfiguration()),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
/**
* This checks that an implicit show request followed by connecting a hard keyboard
* and a configuration change, does not trigger IMS#onFinishInputView,
* but results in the IME being hidden.
*/
@Test
public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
// Start with no hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_NOKEYS;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.HARDKEYBOARDHIDDEN_YES;
// Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
mInputMethodService.getResources().getConfiguration().orientation =
Configuration.ORIENTATION_LANDSCAPE;
// Normally, IMS#onFinishInputView will be called when finishing the input view by the user.
// But if IMS#hideWindow is called when receiving a new configuration change, we don't
// expect that it's user-driven to finish the lifecycle of input view with
// IMS#onFinishInputView, because the input view will be re-initialized according to the
// last mShowSoftRequested state. So in this case we treat the input view is still alive.
// last #mShowInputRequested state. So in this case we treat the input view as still alive.
verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
mInputMethodService.getResources().getConfiguration()),
mInputMethodService.getResources().getConfiguration()),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isFalse();
}
private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted)
throws InterruptedException {
verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/);
/**
* This checks that an explicit show request directly followed by an implicit show request,
* while a hardware keyboard is connected, still results in the IME being shown
* (i.e. the implicit show request is treated as explicit).
*/
@Test
public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard()
throws Exception {
setShowImeWithHardKeyboard(false /* enabled */);
// Simulate connecting a hard keyboard.
mInputMethodService.getResources().getConfiguration().keyboard =
Configuration.KEYBOARD_QWERTY;
mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
Configuration.HARDKEYBOARDHIDDEN_YES;
// Explicit show request.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// Implicit show request.
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
false /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
// Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
// This should now consider the implicit show request, but keep the state from the
// explicit show request, and thus not hide the keyboard.
verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
mInputMethodService.getResources().getConfiguration()),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
private void verifyInputViewStatusOnMainSync(Runnable runnable, boolean inputViewStarted)
throws InterruptedException {
verifyInputViewStatusInternal(runnable, inputViewStarted, true /*runOnMainSync*/);
/**
* This checks that a forced show request directly followed by an explicit show request,
* and then a hide not always request, still results in the IME being shown
* (i.e. the explicit show request retains the forced state).
*/
@Test
public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways()
throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_FORCED)).isTrue(),
true /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
verifyInputViewStatusOnMainSync(() -> assertThat(
mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
false /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
verifyInputViewStatusOnMainSync(() ->
mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS),
false /* expected */,
true /* inputViewStarted */);
assertThat(mInputMethodService.isInputViewShown()).isTrue();
}
/**
* This checks that the IME fullscreen mode state is updated after changing orientation.
*/
@Test
public void testFullScreenMode() throws Exception {
setShowImeWithHardKeyboard(true /* enabled */);
Log.i(TAG, "Set orientation natural");
verifyFullscreenMode(() -> setOrientation(0),
false /* expected */,
true /* orientationPortrait */);
Log.i(TAG, "Set orientation left");
verifyFullscreenMode(() -> setOrientation(1),
true /* expected */,
false /* orientationPortrait */);
Log.i(TAG, "Set orientation right");
verifyFullscreenMode(() -> setOrientation(2),
false /* expected */,
false /* orientationPortrait */);
}
private void verifyInputViewStatus(
Runnable runnable, boolean expected, boolean inputViewStarted)
throws InterruptedException {
verifyInputViewStatusInternal(runnable, expected, inputViewStarted,
false /* runOnMainSync */);
}
private void verifyInputViewStatusOnMainSync(
Runnable runnable, boolean expected, boolean inputViewStarted)
throws InterruptedException {
verifyInputViewStatusInternal(runnable, expected, inputViewStarted,
true /* runOnMainSync */);
}
/**
* Verifies the status of the Input View after executing the given runnable.
*
* @param runnable the runnable to execute for showing or hiding the IME.
* @param expected whether the runnable is expected to trigger the signal.
* @param inputViewStarted the expected state of the Input View after executing the runnable.
* @param runOnMainSync whether to execute the runnable on the main thread.
*/
private void verifyInputViewStatusInternal(
Runnable runnable, boolean inputViewStarted, boolean runOnMainSync)
Runnable runnable, boolean expected, boolean inputViewStarted, boolean runOnMainSync)
throws InterruptedException {
CountDownLatch signal = new CountDownLatch(1);
mInputMethodService.setCountDownLatchForTesting(signal);
// Runnable to trigger onStartInputView()/ onFinishInputView()
// Runnable to trigger onStartInputView() / onFinishInputView() / onConfigurationChanged()
if (runOnMainSync) {
mInstrumentation.runOnMainSync(runnable);
} else {
runnable.run();
}
// Waits for onStartInputView() to finish.
mInstrumentation.waitForIdleSync();
signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
if (expected && !completed) {
fail("Timed out waiting for"
+ " onStartInputView() / onFinishInputView() / onConfigurationChanged()");
} else if (!expected && completed) {
fail("Unexpected call"
+ " onStartInputView() / onFinishInputView() / onConfigurationChanged()");
}
// Input is not finished.
assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
assertThat(mInputMethodService.getCurrentInputViewStarted()).isEqualTo(inputViewStarted);
}
@Test
public void testFullScreenMode() throws Exception {
Log.i(TAG, "Set orientation natural");
verifyFullscreenMode(() -> setOrientation(0), true /* orientationPortrait */);
Log.i(TAG, "Set orientation left");
verifyFullscreenMode(() -> setOrientation(1), false /* orientationPortrait */);
Log.i(TAG, "Set orientation right");
verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */);
}
private void setOrientation(int orientation) {
// Simple wrapper for catching RemoteException.
try {
@@ -366,7 +657,15 @@ public class InputMethodServiceTest {
}
}
private void verifyFullscreenMode(Runnable runnable, boolean orientationPortrait)
/**
* Verifies the IME fullscreen mode state after executing the given runnable.
*
* @param runnable the runnable to execute for setting the orientation.
* @param expected whether the runnable is expected to trigger the signal.
* @param orientationPortrait whether the orientation is expected to be portrait.
*/
private void verifyFullscreenMode(
Runnable runnable, boolean expected, boolean orientationPortrait)
throws InterruptedException {
CountDownLatch signal = new CountDownLatch(1);
mInputMethodService.setCountDownLatchForTesting(signal);
@@ -379,7 +678,12 @@ public class InputMethodServiceTest {
}
// Waits for onConfigurationChanged() to finish.
mInstrumentation.waitForIdleSync();
signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
if (expected && !completed) {
fail("Timed out waiting for onConfigurationChanged()");
} else if (!expected && completed) {
fail("Unexpected call onConfigurationChanged()");
}
clickOnEditorText();
eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isTrue());
@@ -416,7 +720,21 @@ public class InputMethodServiceTest {
return mTargetPackageName + "/" + INPUT_METHOD_SERVICE_NAME;
}
private String executeShellCommand(String cmd) throws Exception {
/**
* Sets the value of show_ime_with_hard_keyboard, only if it is different to the default value.
*
* @param enabled the value to be set.
*/
private void setShowImeWithHardKeyboard(boolean enabled) throws IOException {
if (mShowImeWithHardKeyboardEnabled != enabled) {
executeShellCommand(enabled
? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
: DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
mInstrumentation.waitForIdleSync();
}
}
private String executeShellCommand(String cmd) throws IOException {
Log.i(TAG, "Run command: " + cmd);
return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
.executeShellCommand(cmd);

View File

@@ -40,7 +40,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Display;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -77,9 +76,9 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
public void testPerformShowIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performShowIme(new Binder() /* showInputToken */,
null /* statsToken */, InputMethodManager.SHOW_IMPLICIT, null, SHOW_SOFT_INPUT);
null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT);
}
verifyShowSoftInput(false, true, InputMethodManager.SHOW_IMPLICIT);
verifyShowSoftInput(false, true, 0 /* showFlags */);
}
@Test
@@ -126,7 +125,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe
@Test
public void testApplyImeVisibility_showImeImplicit() throws Exception {
mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT);
verifyShowSoftInput(true, true, InputMethodManager.SHOW_IMPLICIT);
verifyShowSoftInput(true, true, 0 /* showFlags */);
}
@Test

View File

@@ -106,7 +106,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
@Test
public void testRequestImeVisibility_showExplicit() {
initImeTargetWindowState(mWindowToken);
boolean res = mComputer.onImeShowFlags(null, 0 /* show explicit */);
boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */);
mComputer.requestImeVisibility(mWindowToken, res);
final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -118,6 +118,34 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes
assertThat(mComputer.mRequestedShowExplicitly).isTrue();
}
/**
* This checks that the state after an explicit show request does not get reset during
* a subsequent implicit show request, without an intermediary hide request.
*/
@Test
public void testRequestImeVisibility_showExplicit_thenShowImplicit() {
initImeTargetWindowState(mWindowToken);
mComputer.onImeShowFlags(null, 0 /* showFlags */);
assertThat(mComputer.mRequestedShowExplicitly).isTrue();
mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
assertThat(mComputer.mRequestedShowExplicitly).isTrue();
}
/**
* This checks that the state after a forced show request does not get reset during
* a subsequent explicit show request, without an intermediary hide request.
*/
@Test
public void testRequestImeVisibility_showForced_thenShowExplicit() {
initImeTargetWindowState(mWindowToken);
mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED);
assertThat(mComputer.mShowForced).isTrue();
mComputer.onImeShowFlags(null, 0 /* showFlags */);
assertThat(mComputer.mShowForced).isTrue();
}
@Test
public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() {
// Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy

View File

@@ -19,6 +19,7 @@ package com.android.server.inputmethod;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -162,7 +163,10 @@ public class InputMethodBindingControllerTest extends InputMethodManagerServiceT
assertThat(mBindingController.getCurToken()).isNotNull();
}
// Wait for onServiceConnected()
mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
boolean completed = mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
if (!completed) {
fail("Timed out waiting for onServiceConnected()");
}
// Verify onServiceConnected() is called and bound successfully.
synchronized (ImfLock.class) {

View File

@@ -43,6 +43,8 @@ android_test_helper_app {
},
export_package_resources: true,
sdk_version: "current",
certificate: "platform",
}
android_library {

View File

@@ -18,8 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.apps.inputmethod.simpleime">
<uses-sdk android:targetSdkVersion="31" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:debuggable="true"