Loading core/java/android/view/InsetsController.java +54 −2 Original line number Diff line number Diff line Loading @@ -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 */ 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) { this(host, (controller, type) -> { if (type == ITYPE_IME) { Loading Loading @@ -599,9 +605,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void updateState(InsetsState newState) { mState.setDisplayFrame(newState.getDisplayFrame()); @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int[] cancelledUserAnimationTypes = {0}; for (int i = newState.getSourcesCount() - 1; i >= 0; 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--) { InsetsSource source = mState.sourceAt(i); Loading @@ -613,6 +633,27 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, 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() { Loading Loading @@ -896,6 +937,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean imeReady = true; for (int i = internalTypes.size() - 1; i >= 0; 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 || animationType == ANIMATION_TYPE_USER; boolean canRun = false; Loading Loading @@ -1283,7 +1333,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @InsetsType int result = 0; for (int i = mSourceConsumers.size() - 1; i >= 0; 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); } } Loading @@ -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 */ private @InsetsType int invokeControllableInsetsChangedListeners() { mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); mLastStartedAnimTypes = 0; @InsetsType int types = calculateControllableTypes(); int size = mControllableInsetsChangedListeners.size(); Loading core/java/android/view/InsetsSource.java +4 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,10 @@ public class InsetsSource implements Parcelable { return mVisible; } public boolean isVisibleFrameEmpty() { return mVisibleFrame != null && mVisibleFrame.isEmpty(); } /** * Calculates the insets this source will cause to a client window. * Loading core/java/android/view/InsetsSourceConsumer.java +3 −3 Original line number Diff line number Diff line Loading @@ -275,9 +275,9 @@ public class InsetsSourceConsumer { } @VisibleForTesting(visibility = PACKAGE) public void updateSource(InsetsSource newSource) { public void updateSource(InsetsSource newSource, @AnimationType int animationType) { 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())) { mPendingFrame = null; mPendingVisibleFrame = null; Loading @@ -286,7 +286,7 @@ public class InsetsSourceConsumer { } // 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); mPendingFrame = new Rect(newSource.getFrame()); mPendingVisibleFrame = newSource.getVisibleFrame() != null Loading core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +4 −14 Original line number Diff line number Diff line Loading @@ -26,13 +26,11 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.app.Instrumentation; import android.content.Context; Loading Loading @@ -135,37 +133,29 @@ public class InsetsSourceConsumerTest { InsetsSourceConsumer consumer = new InsetsSourceConsumer( ITYPE_IME, state, null, controller); when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); InsetsSource source = new InsetsSource(ITYPE_IME); source.setFrame(0, 1, 2, 3); consumer.updateSource(new InsetsSource(source)); when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER); consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_NONE); // While we're animating, updates are delayed 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()); // Finish the animation, now the pending frame should be applied when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); assertTrue(consumer.notifyAnimationFinished()); 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 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()); // Updating with the current frame triggers a different code path, verify this clears // the pending 8, 9, 10, 11 frame: 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()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); } Loading Loading
core/java/android/view/InsetsController.java +54 −2 Original line number Diff line number Diff line Loading @@ -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 */ 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) { this(host, (controller, type) -> { if (type == ITYPE_IME) { Loading Loading @@ -599,9 +605,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void updateState(InsetsState newState) { mState.setDisplayFrame(newState.getDisplayFrame()); @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int[] cancelledUserAnimationTypes = {0}; for (int i = newState.getSourcesCount() - 1; i >= 0; 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--) { InsetsSource source = mState.sourceAt(i); Loading @@ -613,6 +633,27 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, 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() { Loading Loading @@ -896,6 +937,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean imeReady = true; for (int i = internalTypes.size() - 1; i >= 0; 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 || animationType == ANIMATION_TYPE_USER; boolean canRun = false; Loading Loading @@ -1283,7 +1333,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @InsetsType int result = 0; for (int i = mSourceConsumers.size() - 1; i >= 0; 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); } } Loading @@ -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 */ private @InsetsType int invokeControllableInsetsChangedListeners() { mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); mLastStartedAnimTypes = 0; @InsetsType int types = calculateControllableTypes(); int size = mControllableInsetsChangedListeners.size(); Loading
core/java/android/view/InsetsSource.java +4 −0 Original line number Diff line number Diff line Loading @@ -92,6 +92,10 @@ public class InsetsSource implements Parcelable { return mVisible; } public boolean isVisibleFrameEmpty() { return mVisibleFrame != null && mVisibleFrame.isEmpty(); } /** * Calculates the insets this source will cause to a client window. * Loading
core/java/android/view/InsetsSourceConsumer.java +3 −3 Original line number Diff line number Diff line Loading @@ -275,9 +275,9 @@ public class InsetsSourceConsumer { } @VisibleForTesting(visibility = PACKAGE) public void updateSource(InsetsSource newSource) { public void updateSource(InsetsSource newSource, @AnimationType int animationType) { 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())) { mPendingFrame = null; mPendingVisibleFrame = null; Loading @@ -286,7 +286,7 @@ public class InsetsSourceConsumer { } // 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); mPendingFrame = new Rect(newSource.getFrame()); mPendingVisibleFrame = newSource.getVisibleFrame() != null Loading
core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +4 −14 Original line number Diff line number Diff line Loading @@ -26,13 +26,11 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.app.Instrumentation; import android.content.Context; Loading Loading @@ -135,37 +133,29 @@ public class InsetsSourceConsumerTest { InsetsSourceConsumer consumer = new InsetsSourceConsumer( ITYPE_IME, state, null, controller); when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); InsetsSource source = new InsetsSource(ITYPE_IME); source.setFrame(0, 1, 2, 3); consumer.updateSource(new InsetsSource(source)); when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_USER); consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_NONE); // While we're animating, updates are delayed 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()); // Finish the animation, now the pending frame should be applied when(controller.getAnimationType(anyInt())).thenReturn(ANIMATION_TYPE_NONE); assertTrue(consumer.notifyAnimationFinished()); 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 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()); // Updating with the current frame triggers a different code path, verify this clears // the pending 8, 9, 10, 11 frame: 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()); assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame()); } Loading