Merge "Disable user animations on insets whose visible frame is empty" into rvc-dev am: ae9ce9716d am: 07d77fce08 am: ad8e892660

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11754098

Change-Id: I60bd235db536df23e29cd9958892265b0cafa00d
This commit is contained in:
Tiger Huang
2020-06-16 09:47:30 +00:00
committed by Automerger Merge Worker
4 changed files with 65 additions and 19 deletions

View File

@@ -485,6 +485,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
/** Set of inset types for which an animation was started since last resetting this field */ /** Set of inset types for which an animation was started since last resetting this field */
private @InsetsType int mLastStartedAnimTypes; private @InsetsType int mLastStartedAnimTypes;
/** Set of inset types which cannot be controlled by the user animation */
private @InsetsType int mLastDisabledUserAnimationInsetsTypes;
private Runnable mInvokeControllableInsetsChangedListeners =
this::invokeControllableInsetsChangedListeners;
public InsetsController(Host host) { public InsetsController(Host host) {
this(host, (controller, type) -> { this(host, (controller, type) -> {
if (type == ITYPE_IME) { if (type == ITYPE_IME) {
@@ -599,9 +605,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void updateState(InsetsState newState) { private void updateState(InsetsState newState) {
mState.setDisplayFrame(newState.getDisplayFrame()); mState.setDisplayFrame(newState.getDisplayFrame());
@InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
for (int i = newState.getSourcesCount() - 1; i >= 0; i--) { for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
InsetsSource source = newState.sourceAt(i); InsetsSource source = newState.sourceAt(i);
getSourceConsumer(source.getType()).updateSource(source); @InternalInsetsType int internalInsetsType = source.getType();
@AnimationType int animationType = getAnimationType(internalInsetsType);
if (source.isVisibleFrameEmpty()) {
@InsetsType int insetsType = toPublicType(internalInsetsType);
// The user animation is not allowed when visible frame is empty.
disabledUserAnimationTypes |= insetsType;
if (animationType == ANIMATION_TYPE_USER) {
// Existing user animation needs to be cancelled.
animationType = ANIMATION_TYPE_NONE;
cancelledUserAnimationTypes[0] |= insetsType;
}
}
getSourceConsumer(internalInsetsType).updateSource(source, animationType);
} }
for (int i = mState.getSourcesCount() - 1; i >= 0; i--) { for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
InsetsSource source = mState.sourceAt(i); InsetsSource source = mState.sourceAt(i);
@@ -613,6 +633,27 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
mFrame.right, mFrame.top + mCaptionInsetsHeight)); mFrame.right, mFrame.top + mCaptionInsetsHeight));
} }
updateDisabledUserAnimationTypes(disabledUserAnimationTypes);
if (cancelledUserAnimationTypes[0] != 0) {
mHandler.post(() -> show(cancelledUserAnimationTypes[0]));
}
}
private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) {
@InsetsType int diff = mLastDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes;
if (diff != 0) {
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
if (consumer.getControl() != null && (toPublicType(consumer.mType) & diff) != 0) {
mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
mHandler.post(mInvokeControllableInsetsChangedListeners);
break;
}
}
mLastDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes;
}
} }
private boolean captionInsetsUnchanged() { private boolean captionInsetsUnchanged() {
@@ -896,6 +937,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean imeReady = true; boolean imeReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) { for (int i = internalTypes.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (animationType == ANIMATION_TYPE_USER) {
final InsetsSource source = mState.peekSource(consumer.getType());
if (source != null && source.isVisibleFrameEmpty()) {
if (WARN) Log.w(TAG, String.format(
"collectSourceControls can't run user animation for type: %s",
InsetsState.typeToString(consumer.getType())));
continue;
}
}
boolean show = animationType == ANIMATION_TYPE_SHOW boolean show = animationType == ANIMATION_TYPE_SHOW
|| animationType == ANIMATION_TYPE_USER; || animationType == ANIMATION_TYPE_USER;
boolean canRun = false; boolean canRun = false;
@@ -1283,7 +1333,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@InsetsType int result = 0; @InsetsType int result = 0;
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
if (consumer.getControl() != null) { InsetsSource source = mState.peekSource(consumer.mType);
if (consumer.getControl() != null && source != null && !source.isVisibleFrameEmpty()) {
result |= toPublicType(consumer.mType); result |= toPublicType(consumer.mType);
} }
} }
@@ -1294,6 +1345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
* @return The types that are now animating due to a listener invoking control/show/hide * @return The types that are now animating due to a listener invoking control/show/hide
*/ */
private @InsetsType int invokeControllableInsetsChangedListeners() { private @InsetsType int invokeControllableInsetsChangedListeners() {
mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
mLastStartedAnimTypes = 0; mLastStartedAnimTypes = 0;
@InsetsType int types = calculateControllableTypes(); @InsetsType int types = calculateControllableTypes();
int size = mControllableInsetsChangedListeners.size(); int size = mControllableInsetsChangedListeners.size();

View File

@@ -92,6 +92,10 @@ public class InsetsSource implements Parcelable {
return mVisible; return mVisible;
} }
public boolean isVisibleFrameEmpty() {
return mVisibleFrame != null && mVisibleFrame.isEmpty();
}
/** /**
* Calculates the insets this source will cause to a client window. * Calculates the insets this source will cause to a client window.
* *

View File

@@ -275,9 +275,9 @@ public class InsetsSourceConsumer {
} }
@VisibleForTesting(visibility = PACKAGE) @VisibleForTesting(visibility = PACKAGE)
public void updateSource(InsetsSource newSource) { public void updateSource(InsetsSource newSource, @AnimationType int animationType) {
InsetsSource source = mState.peekSource(mType); InsetsSource source = mState.peekSource(mType);
if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE if (source == null || animationType == ANIMATION_TYPE_NONE
|| source.getFrame().equals(newSource.getFrame())) { || source.getFrame().equals(newSource.getFrame())) {
mPendingFrame = null; mPendingFrame = null;
mPendingVisibleFrame = null; mPendingVisibleFrame = null;
@@ -286,7 +286,7 @@ public class InsetsSourceConsumer {
} }
// Frame is changing while animating. Keep note of the new frame but keep existing frame // Frame is changing while animating. Keep note of the new frame but keep existing frame
// until animaition is finished. // until animation is finished.
newSource = new InsetsSource(newSource); newSource = new InsetsSource(newSource);
mPendingFrame = new Rect(newSource.getFrame()); mPendingFrame = new Rect(newSource.getFrame());
mPendingVisibleFrame = newSource.getVisibleFrame() != null mPendingVisibleFrame = newSource.getVisibleFrame() != null

View File

@@ -26,13 +26,11 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.Instrumentation; import android.app.Instrumentation;
import android.content.Context; import android.content.Context;
@@ -135,37 +133,29 @@ public class InsetsSourceConsumerTest {
InsetsSourceConsumer consumer = new InsetsSourceConsumer( InsetsSourceConsumer consumer = new InsetsSourceConsumer(
ITYPE_IME, state, null, controller); ITYPE_IME, state, null, controller);
when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE);
InsetsSource source = new InsetsSource(ITYPE_IME); InsetsSource source = new InsetsSource(ITYPE_IME);
source.setFrame(0, 1, 2, 3); source.setFrame(0, 1, 2, 3);
consumer.updateSource(new InsetsSource(source)); consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_NONE);
when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER);
// While we're animating, updates are delayed // While we're animating, updates are delayed
source.setFrame(4, 5, 6, 7); source.setFrame(4, 5, 6, 7);
consumer.updateSource(new InsetsSource(source)); consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame()); assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame());
// Finish the animation, now the pending frame should be applied // Finish the animation, now the pending frame should be applied
when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE);
assertTrue(consumer.notifyAnimationFinished()); assertTrue(consumer.notifyAnimationFinished());
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER);
// Animating again, updates are delayed // Animating again, updates are delayed
source.setFrame(8, 9, 10, 11); source.setFrame(8, 9, 10, 11);
consumer.updateSource(new InsetsSource(source)); consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
// Updating with the current frame triggers a different code path, verify this clears // Updating with the current frame triggers a different code path, verify this clears
// the pending 8, 9, 10, 11 frame: // the pending 8, 9, 10, 11 frame:
source.setFrame(4, 5, 6, 7); source.setFrame(4, 5, 6, 7);
consumer.updateSource(new InsetsSource(source)); consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE);
assertFalse(consumer.notifyAnimationFinished()); assertFalse(consumer.notifyAnimationFinished());
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
} }