Loading core/java/android/view/InsetsController.java +11 −7 Original line number Diff line number Diff line Loading @@ -737,15 +737,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void onIdMatch(InsetsSource source1, InsetsSource source2) { final @InsetsType int type = source1.getType(); if ((type & Type.systemBars()) == 0 final Rect frame1 = source1.getFrame(); final Rect frame2 = source2.getFrame(); if (!source1.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING) || !source2.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING) || !source1.isVisible() || !source2.isVisible() || source1.getFrame().equals(source2.getFrame()) || frame1.equals(frame2) || frame1.isEmpty() || frame2.isEmpty() || !(Rect.intersects(mFrame, source1.getFrame()) || Rect.intersects(mFrame, source2.getFrame()))) { return; } mTypes |= type; mTypes |= source1.getType(); if (mToState == null) { mToState = new InsetsState(); } Loading Loading @@ -877,7 +879,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return false; } if (DEBUG) Log.d(TAG, "onStateChanged: " + state); mLastDispatchedState.set(state, true /* copySources */); final InsetsState lastState = new InsetsState(mState, true /* copySources */); updateState(state); Loading @@ -888,10 +889,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation true /* excludesInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); if (lastState.getDisplayFrame().equals(mState.getDisplayFrame())) { InsetsState.traverse(lastState, mState, mStartResizingAnimationIfNeeded); if (mLastDispatchedState.getDisplayFrame().equals(state.getDisplayFrame())) { // Here compares the raw states instead of the overridden ones because we don't want // to animate an insets source that its mServerVisible is false. InsetsState.traverse(mLastDispatchedState, state, mStartResizingAnimationIfNeeded); } } mLastDispatchedState.set(state, true /* copySources */); return true; } Loading core/java/android/view/InsetsSource.java +9 −0 Original line number Diff line number Diff line Loading @@ -99,11 +99,17 @@ public class InsetsSource implements Parcelable { */ public static final int FLAG_FORCE_CONSUMING = 1 << 2; /** * Controls whether the insets source will play an animation when resizing. */ public static final int FLAG_ANIMATE_RESIZING = 1 << 3; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_SUPPRESS_SCRIM, FLAG_INSETS_ROUNDED_CORNER, FLAG_FORCE_CONSUMING, FLAG_ANIMATE_RESIZING, }) public @interface Flags {} Loading Loading @@ -540,6 +546,9 @@ public class InsetsSource implements Parcelable { if ((flags & FLAG_FORCE_CONSUMING) != 0) { joiner.add("FORCE_CONSUMING"); } if ((flags & FLAG_ANIMATE_RESIZING) != 0) { joiner.add("ANIMATE_RESIZING"); } return joiner.toString(); } Loading core/tests/coretests/src/android/view/InsetsControllerTest.java +63 −19 Original line number Diff line number Diff line Loading @@ -21,12 +21,11 @@ import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_RESIZE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.AnimationType; import static android.view.InsetsSource.FLAG_ANIMATE_RESIZING; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED; import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY; import static android.view.ViewRootImpl.CAPTION_ON_SHELL; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.defaultVisible; Loading Loading @@ -671,36 +670,81 @@ public class InsetsControllerTest { } @Test public void testResizeAnimation_insetsTypes() { for (int i = 0; i < SIZE; i++) { final @InsetsType int type = 1 << i; final @AnimationType int expectedAnimationType = (type & systemBars()) != 0 ? ANIMATION_TYPE_RESIZE : ANIMATION_TYPE_NONE; doTestResizeAnimation_insetsTypes(type, expectedAnimationType); } public void testResizeAnimation_withFlagAnimateResizing() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final int id = ID_NAVIGATION_BAR; final @InsetsType int type = navigationBars(); final InsetsState state1 = new InsetsState(); state1.getOrCreateSource(id, type) .setVisible(true) .setFrame(0, 0, 500, 50) .setFlags(FLAG_ANIMATE_RESIZING, FLAG_ANIMATE_RESIZING); final InsetsState state2 = new InsetsState(state1, true /* copySources */); state2.peekSource(id).setFrame(0, 0, 500, 60); // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals("There must not be resize animation.", ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Changing frame of the source with FLAG_ANIMATE_RESIZING will cause the resize // animation. mController.onStateChanged(state2); assertEquals("There must be resize animation.", ANIMATION_TYPE_RESIZE, mController.getAnimationType(type)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } private void doTestResizeAnimation_insetsTypes(@InsetsType int type, @AnimationType int expectedAnimationType) { final int id = type; @Test public void testResizeAnimation_withoutFlagAnimateResizing() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final int id = ID_STATUS_BAR; final @InsetsType int type = statusBars(); final InsetsState state1 = new InsetsState(); state1.getOrCreateSource(id, type).setVisible(true).setFrame(0, 0, 500, 50); state1.getOrCreateSource(id, type) .setVisible(true) .setFrame(0, 0, 500, 50) .setFlags(0, FLAG_ANIMATE_RESIZING); final InsetsState state2 = new InsetsState(state1, true /* copySources */); state2.peekSource(id).setFrame(0, 0, 500, 60); final String message = "Animation type of " + WindowInsets.Type.toString(type) + ":"; final String message = "There must not be resize animation."; // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Changing frame of the source without FLAG_ANIMATE_RESIZING must not cause the resize // animation. mController.onStateChanged(state2); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @Test public void testResizeAnimation_sourceFrame() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final int id = ID_STATUS_BAR; final @InsetsType int type = statusBars(); final InsetsState state1 = new InsetsState(); state1.setDisplayFrame(new Rect(0, 0, 500, 1000)); state1.getOrCreateSource(id, type).setFrame(0, 0, 500, 50); final InsetsState state2 = new InsetsState(state1, true /* copySources */); state2.setDisplayFrame(state1.getDisplayFrame()); state2.peekSource(id).setFrame(0, 0, 500, 0); final String message = "There must not be resize animation."; // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Changing frame might cause the resize animation. This depends on the insets type. // Changing frame won't cause the resize animation if the new frame is empty. mController.onStateChanged(state2); assertEquals(message, expectedAnimationType, mController.getAnimationType(type)); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Cancel the existing animations for the next iteration. mController.cancelExistingAnimations(); // Changing frame won't cause the resize animation if the existing frame is empty. mController.onStateChanged(state1); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); Loading Loading
core/java/android/view/InsetsController.java +11 −7 Original line number Diff line number Diff line Loading @@ -737,15 +737,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @Override public void onIdMatch(InsetsSource source1, InsetsSource source2) { final @InsetsType int type = source1.getType(); if ((type & Type.systemBars()) == 0 final Rect frame1 = source1.getFrame(); final Rect frame2 = source2.getFrame(); if (!source1.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING) || !source2.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING) || !source1.isVisible() || !source2.isVisible() || source1.getFrame().equals(source2.getFrame()) || frame1.equals(frame2) || frame1.isEmpty() || frame2.isEmpty() || !(Rect.intersects(mFrame, source1.getFrame()) || Rect.intersects(mFrame, source2.getFrame()))) { return; } mTypes |= type; mTypes |= source1.getType(); if (mToState == null) { mToState = new InsetsState(); } Loading Loading @@ -877,7 +879,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return false; } if (DEBUG) Log.d(TAG, "onStateChanged: " + state); mLastDispatchedState.set(state, true /* copySources */); final InsetsState lastState = new InsetsState(mState, true /* copySources */); updateState(state); Loading @@ -888,10 +889,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation true /* excludesInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); if (lastState.getDisplayFrame().equals(mState.getDisplayFrame())) { InsetsState.traverse(lastState, mState, mStartResizingAnimationIfNeeded); if (mLastDispatchedState.getDisplayFrame().equals(state.getDisplayFrame())) { // Here compares the raw states instead of the overridden ones because we don't want // to animate an insets source that its mServerVisible is false. InsetsState.traverse(mLastDispatchedState, state, mStartResizingAnimationIfNeeded); } } mLastDispatchedState.set(state, true /* copySources */); return true; } Loading
core/java/android/view/InsetsSource.java +9 −0 Original line number Diff line number Diff line Loading @@ -99,11 +99,17 @@ public class InsetsSource implements Parcelable { */ public static final int FLAG_FORCE_CONSUMING = 1 << 2; /** * Controls whether the insets source will play an animation when resizing. */ public static final int FLAG_ANIMATE_RESIZING = 1 << 3; @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_SUPPRESS_SCRIM, FLAG_INSETS_ROUNDED_CORNER, FLAG_FORCE_CONSUMING, FLAG_ANIMATE_RESIZING, }) public @interface Flags {} Loading Loading @@ -540,6 +546,9 @@ public class InsetsSource implements Parcelable { if ((flags & FLAG_FORCE_CONSUMING) != 0) { joiner.add("FORCE_CONSUMING"); } if ((flags & FLAG_ANIMATE_RESIZING) != 0) { joiner.add("ANIMATE_RESIZING"); } return joiner.toString(); } Loading
core/tests/coretests/src/android/view/InsetsControllerTest.java +63 −19 Original line number Diff line number Diff line Loading @@ -21,12 +21,11 @@ import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_RESIZE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.AnimationType; import static android.view.InsetsSource.FLAG_ANIMATE_RESIZING; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED; import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY; import static android.view.ViewRootImpl.CAPTION_ON_SHELL; import static android.view.WindowInsets.Type.SIZE; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.defaultVisible; Loading Loading @@ -671,36 +670,81 @@ public class InsetsControllerTest { } @Test public void testResizeAnimation_insetsTypes() { for (int i = 0; i < SIZE; i++) { final @InsetsType int type = 1 << i; final @AnimationType int expectedAnimationType = (type & systemBars()) != 0 ? ANIMATION_TYPE_RESIZE : ANIMATION_TYPE_NONE; doTestResizeAnimation_insetsTypes(type, expectedAnimationType); } public void testResizeAnimation_withFlagAnimateResizing() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final int id = ID_NAVIGATION_BAR; final @InsetsType int type = navigationBars(); final InsetsState state1 = new InsetsState(); state1.getOrCreateSource(id, type) .setVisible(true) .setFrame(0, 0, 500, 50) .setFlags(FLAG_ANIMATE_RESIZING, FLAG_ANIMATE_RESIZING); final InsetsState state2 = new InsetsState(state1, true /* copySources */); state2.peekSource(id).setFrame(0, 0, 500, 60); // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals("There must not be resize animation.", ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Changing frame of the source with FLAG_ANIMATE_RESIZING will cause the resize // animation. mController.onStateChanged(state2); assertEquals("There must be resize animation.", ANIMATION_TYPE_RESIZE, mController.getAnimationType(type)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } private void doTestResizeAnimation_insetsTypes(@InsetsType int type, @AnimationType int expectedAnimationType) { final int id = type; @Test public void testResizeAnimation_withoutFlagAnimateResizing() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final int id = ID_STATUS_BAR; final @InsetsType int type = statusBars(); final InsetsState state1 = new InsetsState(); state1.getOrCreateSource(id, type).setVisible(true).setFrame(0, 0, 500, 50); state1.getOrCreateSource(id, type) .setVisible(true) .setFrame(0, 0, 500, 50) .setFlags(0, FLAG_ANIMATE_RESIZING); final InsetsState state2 = new InsetsState(state1, true /* copySources */); state2.peekSource(id).setFrame(0, 0, 500, 60); final String message = "Animation type of " + WindowInsets.Type.toString(type) + ":"; final String message = "There must not be resize animation."; // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Changing frame of the source without FLAG_ANIMATE_RESIZING must not cause the resize // animation. mController.onStateChanged(state2); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @Test public void testResizeAnimation_sourceFrame() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { final int id = ID_STATUS_BAR; final @InsetsType int type = statusBars(); final InsetsState state1 = new InsetsState(); state1.setDisplayFrame(new Rect(0, 0, 500, 1000)); state1.getOrCreateSource(id, type).setFrame(0, 0, 500, 50); final InsetsState state2 = new InsetsState(state1, true /* copySources */); state2.setDisplayFrame(state1.getDisplayFrame()); state2.peekSource(id).setFrame(0, 0, 500, 0); final String message = "There must not be resize animation."; // New insets source won't cause the resize animation. mController.onStateChanged(state1); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Changing frame might cause the resize animation. This depends on the insets type. // Changing frame won't cause the resize animation if the new frame is empty. mController.onStateChanged(state2); assertEquals(message, expectedAnimationType, mController.getAnimationType(type)); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); // Cancel the existing animations for the next iteration. mController.cancelExistingAnimations(); // Changing frame won't cause the resize animation if the existing frame is empty. mController.onStateChanged(state1); assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); Loading