Link InsetsController to IME (IME transitons 4/n)

With this change, InsetsController.show/hide now links to IME. This also
takes care of animating IME along with other types.
Insets API are reactive i.e. they remain in sync with state of IME.

Test: atest InsetsControllerTest
Test: atest ImeInsetsConsumerTest
Bug: 118118435

Change-Id: Ib3997487bd19351d1d23bc70173fc9bdfd23a704
This commit is contained in:
Tarandeep Singh
2019-01-29 18:09:15 -08:00
parent 215929b6f2
commit 46d59f0ecb
10 changed files with 435 additions and 40 deletions

View File

@@ -51,6 +51,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
private static final int DO_TOGGLE_SOFT_INPUT = 105;
private static final int DO_FINISH_SESSION = 110;
private static final int DO_VIEW_CLICKED = 115;
private static final int DO_NOTIFY_IME_HIDDEN = 120;
HandlerCaller mCaller;
InputMethodSession mInputMethodSession;
@@ -129,6 +130,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
mInputMethodSession.viewClicked(msg.arg1 == 1);
return;
}
case DO_NOTIFY_IME_HIDDEN: {
mInputMethodSession.notifyImeHidden();
return;
}
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -171,6 +176,11 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
}
@Override
public void notifyImeHidden() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_IME_HIDDEN));
}
@Override
public void updateCursor(Rect newCursor) {
mCaller.executeOrSendMessage(

View File

@@ -592,7 +592,6 @@ public class InputMethodService extends AbstractInputMethodService {
final boolean wasVisible = mIsPreRendered
? mDecorViewVisible && mWindowVisible : isInputViewShown();
if (mIsPreRendered) {
// TODO: notify visibility to insets consumer.
if (DEBUG) {
Log.v(TAG, "Making IME window invisible");
}
@@ -658,6 +657,11 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
private void notifyImeHidden() {
setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
onPreRenderedWindowVisibilityChanged(false /* setVisible */);
}
private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
}
@@ -760,6 +764,14 @@ public class InputMethodService extends AbstractInputMethodService {
}
InputMethodService.this.onUpdateCursorAnchorInfo(info);
}
/**
* Notify IME that window is hidden.
* @hide
*/
public final void notifyImeHidden() {
InputMethodService.this.notifyImeHidden();
}
}
/**

View File

@@ -290,6 +290,12 @@ final class MultiClientInputMethodClientCallbackAdaptor {
CallbackImpl::updateCursorAnchorInfo, mCallbackImpl, info));
}
}
@Override
public final void notifyImeHidden() {
// no-op for multi-session since IME is responsible controlling navigation bar buttons.
reportNotSupported();
}
}
private static final class MultiClientInputMethodSessionImpl

View File

@@ -18,10 +18,10 @@ package android.view;
import static android.view.InsetsState.TYPE_IME;
import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.text.TextUtils;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
@@ -73,11 +73,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
return;
}
if (setVisible) {
mController.show(Type.IME);
} else {
mController.hide(Type.IME);
}
mController.applyImeVisibility(setVisible);
}
@Override
@@ -91,6 +87,30 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
mHasWindowFocus = false;
}
/**
* Request {@link InputMethodManager} to show the IME.
* @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
*/
@Override
@ShowResult int requestShow(boolean fromIme) {
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
if (fromIme) {
return ShowResult.SHOW_IMMEDIATELY;
}
return getImm().requestImeShow(null /* resultReceiver */)
? ShowResult.SHOW_DELAYED : ShowResult.SHOW_FAILED;
}
/**
* Notify {@link InputMethodService} that IME window is hidden.
*/
@Override
void notifyHidden() {
getImm().notifyImeHidden();
}
private boolean isDummyOrEmptyEditor(EditorInfo info) {
// TODO(b/123044812): Handle dummy input gracefully in IME Insets API
return info == null || (info.fieldId <= 0 && info.inputType <= 0);

View File

@@ -29,10 +29,13 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Property;
import android.util.SparseArray;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetType;
import com.android.internal.annotations.VisibleForTesting;
@@ -51,6 +54,7 @@ public class InsetsController implements WindowInsetsController {
private static final int DIRECTION_NONE = 0;
private static final int DIRECTION_SHOW = 1;
private static final int DIRECTION_HIDE = 2;
@IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
private @interface AnimationDirection{}
@@ -103,6 +107,8 @@ public class InsetsController implements WindowInsetsController {
private ObjectAnimator mAnimator;
private @AnimationDirection int mAnimationDirection;
private int mPendingTypesToShow;
public InsetsController(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
mAnimCallback = () -> {
@@ -193,6 +199,12 @@ public class InsetsController implements WindowInsetsController {
@Override
public void show(@InsetType int types) {
show(types, false /* fromIme */);
}
private void show(@InsetType int types, boolean fromIme) {
// TODO: Support a ResultReceiver for IME.
// TODO(b/123718661): Make show() work for multi-session IME.
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -201,15 +213,18 @@ public class InsetsController implements WindowInsetsController {
// Only one animator (with multiple InsetType) can run at a time.
// previous one should be cancelled for simplicity.
cancelExistingAnimation();
} else if (consumer.isVisible() || mAnimationDirection == DIRECTION_SHOW) {
// no-op: already shown or animating in.
} else if (consumer.isVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already shown or animating in (because window visibility is
// applied before starting animation).
// TODO: When we have more than one types: handle specific case when
// show animation is going on, but the current type is not becoming visible.
continue;
}
typesReady |= InsetsState.toPublicType(consumer.getType());
}
applyAnimation(typesReady, true /* show */);
applyAnimation(typesReady, true /* show */, fromIme);
}
@Override
@@ -220,35 +235,114 @@ public class InsetsController implements WindowInsetsController {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (mAnimationDirection == DIRECTION_SHOW) {
cancelExistingAnimation();
} else if (!consumer.isVisible() || mAnimationDirection == DIRECTION_HIDE) {
} else if (!consumer.isVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already hidden or animating out.
continue;
}
typesReady |= InsetsState.toPublicType(consumer.getType());
}
applyAnimation(typesReady, false /* show */);
applyAnimation(typesReady, false /* show */, false /* fromIme */);
}
@Override
public void controlWindowInsetsAnimation(@InsetType int types,
WindowInsetsAnimationControlListener listener) {
controlWindowInsetsAnimation(types, listener, false /* fromIme */);
}
private void controlWindowInsetsAnimation(@InsetType int types,
WindowInsetsAnimationControlListener listener, boolean fromIme) {
if (types == 0) {
// nothing to animate.
return;
}
// TODO: Check whether we already have a controller.
final ArraySet<Integer> internalTypes = mState.toInternalType(types);
final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
Pair<Integer, Boolean> typesReadyPair = collectConsumers(fromIme, internalTypes, consumers);
int typesReady = typesReadyPair.first;
boolean isReady = typesReadyPair.second;
if (!isReady) {
// IME isn't ready, all requested types would be shown once IME is ready.
mPendingTypesToShow = typesReady;
// TODO: listener for pending types.
return;
}
// pending types from previous request.
typesReady = collectPendingConsumers(typesReady, consumers);
if (typesReady == 0) {
listener.onCancelled();
return;
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
mFrame, mState, listener, typesReady,
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
mAnimationControls.add(controller);
}
/**
* @return Pair of (types ready to animate, is ready to animate).
*/
private Pair<Integer, Boolean> collectConsumers(boolean fromIme,
ArraySet<Integer> internalTypes, SparseArray<InsetsSourceConsumer> consumers) {
int typesReady = 0;
boolean isReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (consumer.getControl() != null) {
if (!consumer.isVisible()) {
// Show request
switch(consumer.requestShow(fromIme)) {
case ShowResult.SHOW_IMMEDIATELY:
typesReady |= InsetsState.toPublicType(TYPE_IME);
break;
case ShowResult.SHOW_DELAYED:
isReady = false;
break;
case ShowResult.SHOW_FAILED:
// IME cannot be shown (since it didn't have focus), proceed
// with animation of other types.
if (mPendingTypesToShow != 0) {
// remove IME from pending because view no longer has focus.
mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
}
break;
}
} else {
// Hide request
// TODO: Move notifyHidden() to beginning of the hide animation
// (when visibility actually changes using hideDirectly()).
consumer.notifyHidden();
typesReady |= InsetsState.toPublicType(consumer.getType());
}
consumers.put(consumer.getType(), consumer);
} else {
// TODO: Let calling app know it's not possible, or wait
// TODO: Remove it from types
}
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
mFrame, mState, listener, types,
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
mAnimationControls.add(controller);
return new Pair<>(typesReady, isReady);
}
private int collectPendingConsumers(@InsetType int typesReady,
SparseArray<InsetsSourceConsumer> consumers) {
if (mPendingTypesToShow != 0) {
typesReady |= mPendingTypesToShow;
final ArraySet<Integer> internalTypes = mState.toInternalType(mPendingTypesToShow);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
consumers.put(consumer.getType(), consumer);
}
mPendingTypesToShow = 0;
}
return typesReady;
}
private void applyLocalVisibilityOverride() {
@@ -293,6 +387,19 @@ public class InsetsController implements WindowInsetsController {
return mViewRoot;
}
/**
* Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
* @hide
*/
@VisibleForTesting
public void applyImeVisibility(boolean setVisible) {
if (setVisible) {
show(Type.IME, true /* fromIme */);
} else {
hide(Type.IME);
}
}
private InsetsSourceConsumer createConsumerOfType(int type) {
if (type == TYPE_IME) {
return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
@@ -321,7 +428,7 @@ public class InsetsController implements WindowInsetsController {
}
}
private void applyAnimation(@InsetType final int types, boolean show) {
private void applyAnimation(@InsetType final int types, boolean show, boolean fromIme) {
if (types == 0) {
// nothing to animate.
return;
@@ -366,7 +473,7 @@ public class InsetsController implements WindowInsetsController {
// TODO: Instead of clearing this here, properly wire up
// InsetsAnimationControlImpl.finish() to remove this from mAnimationControls.
mAnimationControls.clear();
controlWindowInsetsAnimation(types, listener);
controlWindowInsetsAnimation(types, listener, fromIme);
}
private void hideDirectly(@InsetType int types) {

View File

@@ -16,12 +16,15 @@
package android.view;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.view.InsetsState.InternalInsetType;
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
/**
@@ -30,6 +33,25 @@ import java.util.function.Supplier;
*/
public class InsetsSourceConsumer {
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.SHOW_DELAYED, ShowResult.SHOW_FAILED})
@interface ShowResult {
/**
* Window type is ready to be shown, will be shown immidiately.
*/
int SHOW_IMMEDIATELY = 0;
/**
* Result will be delayed. Window needs to be prepared or request is not from controller.
* Request will be delegated to controller and may or may not be shown.
*/
int SHOW_DELAYED = 1;
/**
* Window will not be shown because one of the conditions couldn't be met.
* (e.g. in IME's case, when no editor is focused.)
*/
int SHOW_FAILED = 2;
}
protected final InsetsController mController;
protected boolean mVisible;
private final Supplier<Transaction> mTransactionSupplier;
@@ -104,6 +126,25 @@ public class InsetsSourceConsumer {
return mVisible;
}
/**
* Request to show current window type.
*
* @param fromController {@code true} if request is coming from controller.
* (e.g. in IME case, controller is
* {@link android.inputmethodservice.InputMethodService}).
* @return @see {@link ShowResult}.
*/
@ShowResult int requestShow(boolean fromController) {
return ShowResult.SHOW_IMMEDIATELY;
}
/**
* Notify listeners that window is now hidden.
*/
void notifyHidden() {
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
private void setVisible(boolean visible) {
if (mVisible == visible) {
return;

View File

@@ -1886,6 +1886,36 @@ public final class InputMethodManager {
}
}
/**
* Call showSoftInput with currently focused view.
* @return {@code true} if IME can be shown.
* @hide
*/
public boolean requestImeShow(ResultReceiver resultReceiver) {
synchronized (mH) {
if (mServedView == null) {
return false;
}
showSoftInput(mServedView, 0 /* flags */, resultReceiver);
return true;
}
}
/**
* Notify IME directly that it is no longer visible.
* @hide
*/
public void notifyImeHidden() {
synchronized (mH) {
try {
if (mCurMethod != null) {
mCurMethod.notifyImeHidden();
}
} catch (RemoteException re) {
}
}
}
/**
* Report the current selection range.
*

View File

@@ -184,4 +184,11 @@ public interface InputMethodSession {
* insertion point and composition string.
*/
public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo);
/**
* Notifies {@link android.inputmethodservice.InputMethodService} that IME has been
* hidden from user.
* @hide
*/
public void notifyImeHidden();
}

View File

@@ -48,4 +48,6 @@ oneway interface IInputMethodSession {
void finishSession();
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
void notifyImeHidden();
}

View File

@@ -75,6 +75,7 @@ public class InsetsControllerTest {
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
rect, rect);
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
@@ -95,6 +96,186 @@ public class InsetsControllerTest {
@Test
public void testAnimationEndState() {
InsetsSourceControl[] controls = prepareControls();
InsetsSourceControl navBar = controls[0];
InsetsSourceControl topBar = controls[1];
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.show(Type.all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
// no focused view, no IME.
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
mController.hide(Type.all());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
mController.show(Type.ime());
mController.cancelExistingAnimation();
// no focused view, no IME.
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
public void testApplyImeVisibility() {
final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash, new Point());
InsetsSourceControl[] controls = new InsetsSourceControl[3];
controls[0] = ime;
mController.onControlsChanged(controls);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.applyImeVisibility(true);
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
mController.applyImeVisibility(false);
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
public void testShowHideSelectively() {
InsetsSourceControl[] controls = prepareControls();
InsetsSourceControl navBar = controls[0];
InsetsSourceControl topBar = controls[1];
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
int types = Type.sideBars() | Type.systemBars();
// test show select types.
mController.show(types);
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
// test hide all
mController.hide(types);
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
public void testShowHideSingle() {
InsetsSourceControl[] controls = prepareControls();
InsetsSourceControl navBar = controls[0];
InsetsSourceControl topBar = controls[1];
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
int types = Type.sideBars() | Type.systemBars();
// test show select types.
mController.show(types);
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
// test hide all
mController.hide(Type.all());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
// test single show
mController.show(Type.sideBars());
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
// test single hide
mController.hide(Type.sideBars());
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
public void testShowHideMultiple() {
InsetsSourceControl[] controls = prepareControls();
InsetsSourceControl navBar = controls[0];
InsetsSourceControl topBar = controls[1];
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// start two animations and see if previous is cancelled and final state is reached.
mController.show(Type.sideBars());
mController.show(Type.systemBars());
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
mController.hide(Type.sideBars());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
int types = Type.sideBars() | Type.systemBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.sideBars());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
public void testShowMultipleHideOneByOne() {
InsetsSourceControl[] controls = prepareControls();
InsetsSourceControl navBar = controls[0];
InsetsSourceControl topBar = controls[1];
InsetsSourceControl ime = controls[2];
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
int types = Type.sideBars() | Type.systemBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.sideBars());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
private InsetsSourceControl[] prepareControls() {
final InsetsSourceControl navBar = new InsetsSourceControl(TYPE_NAVIGATION_BAR, mLeash,
new Point());
final InsetsSourceControl topBar = new InsetsSourceControl(TYPE_TOP_BAR, mLeash,
@@ -106,27 +287,6 @@ public class InsetsControllerTest {
controls[1] = topBar;
controls[2] = ime;
mController.onControlsChanged(controls);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.show(Type.all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
mController.hide(Type.all());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
mController.show(Type.ime());
mController.cancelExistingAnimation();
assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
mController.hide(Type.ime());
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
});
return controls;
}
}