Let InsetsController control requested visible types

We plan to make the insets source consumer exist only when there is a
corresponding insets source, which means the consumer won't always
exist. This CL makes InsetsController set the requested types on its
own, instead of telling the consumer to do so. Thus, show and hide are
removed from InsetsSourceConsumer. This CL adds onAnimationStateChanged
to the consumer, so that it can apply the requested visibility at the
proper timing.

Bug: 234093736
Test: atest ImeInsetsSourceConsumerTest InsetsAnimationControlImplTest
      InsetsControllerTest InsetsSourceConsumerTest
      WindowInsetsControllerTests CtsInputMethodTestCases
Change-Id: Ib54c75e5fb1188c83074e06abf7af509974af2b1
This commit is contained in:
Tiger
2022-11-30 20:28:28 +08:00
parent e143401903
commit 5e6f95454c
6 changed files with 175 additions and 220 deletions

View File

@@ -16,21 +16,20 @@
package android.view;
import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
import static android.view.ImeInsetsSourceConsumerProto.IS_HIDE_ANIMATION_RUNNING;
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
import static android.view.ImeInsetsSourceConsumerProto.IS_SHOW_REQUESTED_DURING_HIDE_ANIMATION;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.ITYPE_IME;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.inputmethod.ImeTracing;
import java.util.function.Supplier;
/**
@@ -61,6 +60,38 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
super(ITYPE_IME, state, transactionSupplier, controller);
}
@Override
public boolean onAnimationStateChanged(boolean running) {
if (!running) {
ImeTracing.getInstance().triggerClientDump(
"ImeInsetsSourceConsumer#onAnimationFinished",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
final boolean insetsChanged = super.onAnimationStateChanged(running);
final boolean showRequested = (mController.getRequestedVisibleTypes() & getType()) != 0;
if (showRequested) {
onShowRequested();
} else {
mIsRequestedVisibleAwaitingControl = false;
if (!running) {
// Remove IME surface as IME has finished hide animation, if there is no pending
// show request.
if (!mIsShowRequestedDuringHideAnimation) {
notifyHidden();
removeSurface();
}
}
// Here is reached
// (1) before the hide animation starts.
// (2) after the hide animation ends.
// (3) if the IME is not controllable (animationFinished == true in this case).
// We should reset mIsShowRequestedDuringHideAnimation in all cases.
mIsHideAnimationRunning = running;
mIsShowRequestedDuringHideAnimation = false;
}
return insetsChanged;
}
@Override
public void onWindowFocusGained(boolean hasViewFocus) {
super.onWindowFocusGained(hasViewFocus);
@@ -78,36 +109,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
}
@Override
public void show(boolean fromIme) {
super.show(fromIme);
onShowRequested();
}
@Override
public void hide() {
super.hide();
mIsRequestedVisibleAwaitingControl = false;
}
@Override
void hide(boolean animationFinished, @AnimationType int animationType) {
hide();
if (animationFinished) {
// Remove IME surface as IME has finished hide animation, if there is no pending
// show request.
if (!mIsShowRequestedDuringHideAnimation) {
notifyHidden();
removeSurface();
}
}
// This method is called
// (1) before the hide animation starts.
// (2) after the hide animation ends.
// (3) if the IME is not controllable (animationFinished == true in this case).
// We should reset mIsShowRequestedDuringHideAnimation in all cases.
mIsHideAnimationRunning = !animationFinished;
mIsShowRequestedDuringHideAnimation = false;
public boolean applyLocalVisibilityOverride() {
ImeTracing.getInstance().triggerClientDump(
"ImeInsetsSourceConsumer#applyLocalVisibilityOverride",
mController.getHost().getInputMethodManager(), null /* icProto */);
return super.applyLocalVisibilityOverride();
}
/**
@@ -116,6 +122,12 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
*/
@Override
public @ShowResult int requestShow(boolean fromIme) {
if (fromIme) {
ImeTracing.getInstance().triggerClientDump(
"ImeInsetsSourceConsumer#requestShow",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
if (getControl() == null) {
@@ -137,10 +149,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
* IME insets are hidden.
*/
@Override
void notifyHidden() {
private void notifyHidden() {
getImm().notifyImeHidden(mController.getHost().getWindowToken());
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
@Override
@@ -154,11 +164,13 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
@Override
public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl",
mController.getHost().getInputMethodManager(), null /* icProto */);
if (!super.setControl(control, showTypes, hideTypes)) {
return false;
}
if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType());
removeSurface();
}
if (control != null) {
@@ -192,7 +204,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
}
/**
* Called when {@link #show} or {@link InputMethodManager#showSoftInput(View, int)} is called.
* Called when {@link #onAnimationStateChanged(boolean)} or
* {@link InputMethodManager#showSoftInput(View, int)} is called.
*/
public void onShowRequested() {
if (mIsHideAnimationRunning) {

View File

@@ -21,7 +21,6 @@ import static android.view.InsetsControllerProto.CONTROL;
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.toInternalType;
import static android.view.InsetsState.toPublicType;
import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.FIRST;
@@ -742,6 +741,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void updateState(InsetsState newState) {
mState.set(newState, 0 /* types */);
@InsetsType int existingTypes = 0;
@InsetsType int visibleTypes = 0;
@InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
@@ -760,10 +760,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
getSourceConsumer(type).updateSource(source, animationType);
existingTypes |= insetsType;
if (source.isVisible()) {
visibleTypes |= insetsType;
}
}
// If a type doesn't have a source, treat it as visible if it is visible by default.
visibleTypes |= WindowInsets.Type.defaultVisible() & ~existingTypes;
if (mVisibleTypes != visibleTypes) {
if (WindowInsets.Type.hasCompatSystemBars(mVisibleTypes ^ visibleTypes)) {
mCompatSysUiVisibilityStaled = true;
@@ -1103,6 +1108,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
// Basically, we accept the requested visibilities from the upstream callers...
setRequestedVisibleTypes(visible ? types : 0, types);
// However, we might reject the request in some cases, such as delaying showing IME or
// rejecting showing IME.
controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme,
durationMs, interpolator, animationType, layoutInsetsDuringAnimation,
useInsetsAnimationThread, statsToken);
// We are finishing setting the requested visible types. Report them to the server and/or
// the app.
reportRequestedVisibleTypes();
}
private void controlAnimationUncheckedInner(@InsetsType int types,
@Nullable CancellationSignal cancellationSignal,
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
long durationMs, Interpolator interpolator,
@AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION);
if ((types & mTypesBeingCancelled) != 0) {
throw new IllegalStateException("Cannot start a new insets animation of "
@@ -1119,13 +1147,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
&& !mState.getSource(ITYPE_IME).isVisible()) {
// We've requested IMM to show IME, but the IME is not controllable. We need to
// cancel the request.
getSourceConsumer(ITYPE_IME).hide(true, animationType);
setRequestedVisibleTypes(0 /* visibleTypes */, ime());
if (getSourceConsumer(ITYPE_IME).onAnimationStateChanged(false /* running */)) {
notifyVisibilityChanged();
}
}
}
if (types == 0) {
// nothing to animate.
listener.onCancelled(null);
reportRequestedVisibleTypes();
if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
return;
}
@@ -1161,7 +1191,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
});
}
reportRequestedVisibleTypes();
// The requested visibilities should be delayed as well. Otherwise, the server will
// create already visible leashes for us before we play the show animation.
setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
return;
}
@@ -1169,7 +1203,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (typesReady == 0) {
if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
listener.onCancelled(null);
reportRequestedVisibleTypes();
return;
}
@@ -1198,12 +1231,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
} else {
Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
}
if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types, fromIme);
} else {
hideDirectly(types, false /* animationFinished */, animationType, fromIme);
onAnimationStateChanged(types, true /* running */);
if (fromIme) {
switch (animationType) {
case ANIMATION_TYPE_SHOW:
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
break;
case ANIMATION_TYPE_HIDE:
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
break;
}
} else if (animationType == ANIMATION_TYPE_HIDE) {
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
reportRequestedVisibleTypes();
}
// TODO(b/242962223): Make this setter restrictive.
@@ -1228,11 +1269,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean canRun = true;
if (show) {
// Show request
if (fromIme) {
ImeTracing.getInstance().triggerClientDump(
"ImeInsetsSourceConsumer#requestShow", mHost.getInputMethodManager(),
null /* icProto */);
}
switch(consumer.requestShow(fromIme)) {
case ShowResult.SHOW_IMMEDIATELY:
break;
@@ -1246,15 +1282,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// IME cannot be shown (since it didn't have focus), proceed
// with animation of other types.
canRun = false;
// Reject the show request.
setRequestedVisibleTypes(0 /* visibleTypes */, consumer.getType());
break;
}
} else {
// Hide request
// TODO: Move notifyHidden() to beginning of the hide animation
// (when visibility actually changes using hideDirectly()).
if (!fromIme) {
consumer.notifyHidden();
}
}
if (!canRun) {
if (WARN) Log.w(TAG, String.format(
@@ -1266,24 +1298,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (control != null && control.getLeash() != null) {
controls.put(control.getId(), new InsetsSourceControl(control));
typesReady |= consumer.getType();
} else if (animationType == ANIMATION_TYPE_SHOW) {
if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: "
+ fromIme);
// We don't have a control at the moment. However, we still want to update requested
// visibility state such that in case we get control, we can apply show animation.
if (fromIme) {
ImeTracing.getInstance().triggerClientDump(
"InsetsSourceConsumer#show", mHost.getInputMethodManager(),
null /* icProto */);
}
consumer.show(fromIme);
} else if (animationType == ANIMATION_TYPE_HIDE) {
if (fromIme) {
ImeTracing.getInstance().triggerClientDump(
"InsetsSourceConsumer#hide", mHost.getInputMethodManager(),
null /* icProto */);
}
consumer.hide();
}
}
return new Pair<>(typesReady, imeReady);
@@ -1332,6 +1346,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
@Override
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
setRequestedVisibleTypes(shown ? runner.getTypes() : 0, runner.getTypes());
cancelAnimation(runner, false /* invokeCallback */);
if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) {
@@ -1343,15 +1358,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (shown) {
ImeTracker.get().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW);
showDirectly(runner.getTypes(), true /* fromIme */);
ImeTracker.get().onShown(statsToken);
} else {
ImeTracker.get().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_HIDE);
hideDirectly(runner.getTypes(), true /* animationFinished */,
runner.getAnimationType(), true /* fromIme */);
ImeTracker.get().onHidden(statsToken);
}
reportRequestedVisibleTypes();
}
@Override
@@ -1388,28 +1401,31 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
"cancelAnimation of types: %d, animType: %d, host: %s",
control.getTypes(), control.getAnimationType(), mHost.getRootViewTitle()));
}
boolean stateChanged = false;
@InsetsType int removedTypes = 0;
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
if (runningAnimation.runner == control) {
mRunningAnimations.remove(i);
ArraySet<Integer> types = toInternalType(control.getTypes());
for (int j = types.size() - 1; j >= 0; j--) {
if (types.valueAt(j) == ITYPE_IME) {
ImeTracing.getInstance().triggerClientDump(
"InsetsSourceConsumer#notifyAnimationFinished",
mHost.getInputMethodManager(), null /* icProto */);
}
stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished();
}
removedTypes = control.getTypes();
if (invokeCallback) {
dispatchAnimationEnd(runningAnimation.runner.getAnimation());
}
break;
}
}
if (stateChanged) {
mHost.notifyInsetsChanged();
onAnimationStateChanged(removedTypes, false /* running */);
}
private void onAnimationStateChanged(@InsetsType int types, boolean running) {
boolean insetsChanged = false;
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
if ((consumer.getType() & types) != 0) {
insetsChanged |= consumer.onAnimationStateChanged(running);
}
}
if (insetsChanged) {
notifyVisibilityChanged();
}
}
@@ -1472,27 +1488,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return ANIMATION_TYPE_NONE;
}
void setRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void setRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
final @InsetsType int requestedVisibleTypes =
(mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
if (mRequestedVisibleTypes != requestedVisibleTypes) {
if (WindowInsets.Type.hasCompatSystemBars(
mRequestedVisibleTypes ^ requestedVisibleTypes)) {
mCompatSysUiVisibilityStaled = true;
}
mRequestedVisibleTypes = requestedVisibleTypes;
}
}
/**
* Sends the requested visible types to window manager if any of them is changed.
* Called when finishing setting requested visible types or finishing setting controls.
*/
private void reportRequestedVisibleTypes() {
updateCompatSysUiVisibility();
if (mReportedRequestedVisibleTypes != mRequestedVisibleTypes) {
final @InsetsType int diff = mRequestedVisibleTypes ^ mReportedRequestedVisibleTypes;
if (WindowInsets.Type.hasCompatSystemBars(diff)) {
mCompatSysUiVisibilityStaled = true;
}
mReportedRequestedVisibleTypes = mRequestedVisibleTypes;
mHost.updateRequestedVisibleTypes(mReportedRequestedVisibleTypes);
}
updateCompatSysUiVisibility();
}
@VisibleForTesting
@@ -1538,39 +1555,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
!hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
}
private void hideDirectly(@InsetsType int types, boolean animationFinished,
@AnimationType int animationType, boolean fromIme) {
if ((types & ime()) != 0) {
ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly",
mHost.getInputMethodManager(), null /* icProto */);
}
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
}
reportRequestedVisibleTypes();
if (fromIme) {
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
}
}
private void showDirectly(@InsetsType int types, boolean fromIme) {
if ((types & ime()) != 0) {
ImeTracing.getInstance().triggerClientDump("InsetsController#showDirectly",
mHost.getInputMethodManager(), null /* icProto */);
}
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
}
reportRequestedVisibleTypes();
if (fromIme) {
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
}
}
/**
* Cancel on-going animation to show/hide {@link InsetsType}.
*/

View File

@@ -25,7 +25,6 @@ import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.getDefaultVisibility;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -40,7 +39,6 @@ import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.ImeTracing;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -116,10 +114,6 @@ public class InsetsSourceConsumer {
*/
public boolean setControl(@Nullable InsetsSourceControl control,
@InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
if (mInternalType == ITYPE_IME) {
ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
if (Objects.equals(mSourceControl, control)) {
if (mSourceControl != null && mSourceControl != control) {
mSourceControl.release(SurfaceControl::release);
@@ -210,22 +204,27 @@ public class InsetsSourceConsumer {
return mInternalType;
}
@VisibleForTesting
public void show(boolean fromIme) {
if (DEBUG) Log.d(TAG, String.format("Call show() for type: %s fromIme: %b ",
InsetsState.typeToString(mInternalType), fromIme));
setRequestedVisible(true);
}
/**
* Called right after the animation is started or finished.
*/
@VisibleForTesting(visibility = PACKAGE)
public boolean onAnimationStateChanged(boolean running) {
boolean insetsChanged = false;
if (!running && mPendingFrame != null) {
InsetsSource source = mState.getSource(mInternalType);
source.setFrame(mPendingFrame);
source.setVisibleFrame(mPendingVisibleFrame);
mPendingFrame = null;
mPendingVisibleFrame = null;
insetsChanged = true;
}
@VisibleForTesting
public void hide() {
if (DEBUG) Log.d(TAG, String.format("Call hide for %s on %s",
InsetsState.typeToString(mInternalType), mController.getHost().getRootViewTitle()));
setRequestedVisible(false);
}
void hide(boolean animationFinished, @AnimationType int animationType) {
hide();
// We apply the visibility override after the animation is started. We don't do this before
// that because we need to know the initial insets state while creating the animation.
// We also need to apply the override after the animation is finished because the requested
// visibility can be set when finishing the user animation.
insetsChanged |= applyLocalVisibilityOverride();
return insetsChanged;
}
/**
@@ -247,19 +246,14 @@ public class InsetsSourceConsumer {
return mHasViewFocusWhenWindowFocusGain;
}
boolean applyLocalVisibilityOverride() {
@VisibleForTesting(visibility = PACKAGE)
public boolean applyLocalVisibilityOverride() {
final InsetsSource source = mState.peekSource(mInternalType);
final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(
mInternalType);
final boolean hasControl = mSourceControl != null;
final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
if (mInternalType == ITYPE_IME) {
ImeTracing.getInstance().triggerClientDump(
"InsetsSourceConsumer#applyLocalVisibilityOverride",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
// If we don't have control, we are not able to change the visibility.
if (!hasControl) {
if (DEBUG) Log.d(TAG, "applyLocalVisibilityOverride: No control in "
@@ -298,13 +292,6 @@ public class InsetsSourceConsumer {
public void onPerceptible(boolean perceptible) {
}
/**
* Notify listeners that window is now hidden.
*/
void notifyHidden() {
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
/**
* Remove surface on which this consumer type is drawn.
*/
@@ -336,31 +323,6 @@ public class InsetsSourceConsumer {
if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
}
@VisibleForTesting(visibility = PACKAGE)
public boolean notifyAnimationFinished() {
if (mPendingFrame != null) {
InsetsSource source = mState.getSource(mInternalType);
source.setFrame(mPendingFrame);
source.setVisibleFrame(mPendingVisibleFrame);
mPendingFrame = null;
mPendingVisibleFrame = null;
return true;
}
return false;
}
/**
* Sets requested visibility from the client, regardless of whether we are able to control it at
* the moment.
*/
protected void setRequestedVisible(boolean requestedVisible) {
mController.setRequestedVisibleTypes(requestedVisible ? mType : 0, mType);
if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
}
}
private void applyRequestedVisibilityToControl() {
if (mSourceControl == null || mSourceControl.getLeash() == null) {
return;

View File

@@ -102,7 +102,8 @@ public class InsetsAnimationControlImplTest {
new InsetsSourceControl(ITYPE_NAVIGATION_BAR, WindowInsets.Type.navigationBars(),
mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)),
new int[1], new int[1]);
navConsumer.hide();
mMockController.setRequestedVisibleTypes(0, WindowInsets.Type.navigationBars());
navConsumer.applyLocalVisibilityOverride();
SparseArray<InsetsSourceControl> controls = new SparseArray<>();
controls.put(ITYPE_STATUS_BAR, topConsumer.getControl());

View File

@@ -132,17 +132,10 @@ public class InsetsControllerTest {
private boolean mImeRequestedShow;
@Override
public void show(boolean fromIme) {
super.show(fromIme);
if (fromIme) {
mImeRequestedShow = true;
}
}
@Override
public int requestShow(boolean fromController) {
if (fromController || mImeRequestedShow) {
mImeRequestedShow = true;
return SHOW_IMMEDIATELY;
} else {
return IME_SHOW_DELAYED;
@@ -815,10 +808,10 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
prepareControls();
// Hiding visible system bars should only causes insets change once for each bar.
// Calling to hide system bars once should only cause insets change once.
clearInvocations(mTestHost);
mController.hide(statusBars() | navigationBars());
verify(mTestHost, times(2)).notifyInsetsChanged();
verify(mTestHost, times(1)).notifyInsetsChanged();
// Sending the same insets state should not cause insets change.
// This simulates the callback from server after hiding system bars.
@@ -826,10 +819,10 @@ public class InsetsControllerTest {
mController.onStateChanged(mController.getState());
verify(mTestHost, never()).notifyInsetsChanged();
// Showing invisible system bars should only causes insets change once for each bar.
// Calling to show system bars once should only cause insets change once.
clearInvocations(mTestHost);
mController.show(statusBars() | navigationBars());
verify(mTestHost, times(2)).notifyInsetsChanged();
verify(mTestHost, times(1)).notifyInsetsChanged();
// Sending the same insets state should not cause insets change.
// This simulates the callback from server after showing system bars.
@@ -912,7 +905,7 @@ public class InsetsControllerTest {
assertNull(imeInsetsConsumer.getControl());
// Verify IME requested visibility should be updated to IME consumer from controller.
mController.show(ime());
mController.show(ime(), true /* fromIme */, null /* statsToken */);
assertTrue(isRequestedVisible(mController, ime()));
mController.hide(ime());

View File

@@ -29,7 +29,6 @@ import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -119,30 +118,33 @@ public class InsetsSourceConsumerTest {
}
@Test
public void testHide() {
public void testOnAnimationStateChanged_requestedInvisible() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mConsumer.hide();
mController.setRequestedVisibleTypes(0 /* visibleTypes */, mSpyInsetsSource.getType());
mConsumer.onAnimationStateChanged(false /* running */);
verify(mSpyInsetsSource).setVisible(eq(false));
});
}
@Test
public void testShow() {
public void testOnAnimationStateChanged_requestedVisible() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Insets source starts out visible
mConsumer.hide();
mConsumer.show(false /* fromIme */);
final int type = mSpyInsetsSource.getType();
mController.setRequestedVisibleTypes(0 /* visibleTypes */, type);
mConsumer.onAnimationStateChanged(false /* running */);
mController.setRequestedVisibleTypes(type, type);
mConsumer.onAnimationStateChanged(false /* running */);
verify(mSpyInsetsSource).setVisible(eq(false));
verify(mSpyInsetsSource).setVisible(eq(true));
});
}
@Test
public void testPendingStates() {
InsetsState state = new InsetsState();
InsetsController controller = mock(InsetsController.class);
InsetsController controller = new InsetsController(new ViewRootInsetsControllerHost(
mViewRoot));
InsetsSourceConsumer consumer = new InsetsSourceConsumer(
ITYPE_IME, state, null, controller);
@@ -156,7 +158,7 @@ public class InsetsSourceConsumerTest {
assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame());
// Finish the animation, now the pending frame should be applied
assertTrue(consumer.notifyAnimationFinished());
assertTrue(consumer.onAnimationStateChanged(false /* running */));
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
// Animating again, updates are delayed
@@ -169,7 +171,7 @@ public class InsetsSourceConsumerTest {
source.setFrame(4, 5, 6, 7);
consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
assertFalse(consumer.notifyAnimationFinished());
assertFalse(consumer.onAnimationStateChanged(false /* running */));
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
}
@@ -178,7 +180,7 @@ public class InsetsSourceConsumerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mConsumer.setControl(null, new int[1], new int[1]);
reset(mMockTransaction);
mConsumer.hide();
mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
verifyZeroInteractions(mMockTransaction);
int[] hideTypes = new int[1];
mConsumer.setControl(
@@ -193,7 +195,7 @@ public class InsetsSourceConsumerTest {
@Test
public void testRestore_noAnimation() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mConsumer.hide();
mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
mConsumer.setControl(null, new int[1], new int[1]);
reset(mMockTransaction);
verifyZeroInteractions(mMockTransaction);