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:
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -48,4 +48,6 @@ oneway interface IInputMethodSession {
|
||||
void finishSession();
|
||||
|
||||
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
|
||||
|
||||
void notifyImeHidden();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user