From 8d04bcbc43d669644910c681389b504ce1296109 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 29 May 2020 18:01:04 +0200 Subject: [PATCH] Insets: allow controlling insets as long as the window is covering in the relevant direction Fixes: 154745615 Test: InsetsStateTest Change-Id: I666a7eee82c15f0c5594c0acbd1a4f0186bcdc01 --- core/java/android/view/InsetsController.java | 14 +------ core/java/android/view/InsetsState.java | 38 +++++++++++++++++++ .../src/android/view/InsetsStateTest.java | 22 +++++++++++ 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 758062f41428b..5505651e7f487 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -754,7 +754,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType) { - if (!checkDisplayFramesForControlling()) { + if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) { listener.onCancelled(null); return; } @@ -764,13 +764,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation false /* useInsetsAnimationThread */); } - private boolean checkDisplayFramesForControlling() { - - // If the frame of our window doesn't span the entire display, the control API makes very - // little sense, as we don't deal with negative insets. So just cancel immediately. - return mState.getDisplayFrame().equals(mFrame); - } - private void controlAnimationUnchecked(@InsetsType int types, @Nullable CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, @@ -1208,9 +1201,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } private @InsetsType int calculateControllableTypes() { - if (!checkDisplayFramesForControlling()) { - return 0; - } @InsetsType int result = 0; for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); @@ -1218,7 +1208,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation result |= toPublicType(consumer.mType); } } - return result; + return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame); } /** diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 9896aa4fe214b..f77593155e4c6 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -238,6 +238,44 @@ public class InsetsState implements Parcelable { return insets.toRect(); } + /** + * Calculate which insets *cannot* be controlled, because the frame does not cover the + * respective side of the inset. + * + * If the frame of our window doesn't cover the entire inset, the control API makes very + * little sense, as we don't deal with negative insets. + */ + @InsetsType + public int calculateUncontrollableInsetsFromFrame(Rect frame) { + int blocked = 0; + for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { + InsetsSource source = mSources.get(type); + if (source == null) { + continue; + } + if (!canControlSide(frame, getInsetSide( + source.calculateInsets(frame, true /* ignoreVisibility */)))) { + blocked |= toPublicType(type); + } + } + return blocked; + } + + private boolean canControlSide(Rect frame, int side) { + switch (side) { + case ISIDE_LEFT: + case ISIDE_RIGHT: + return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right; + case ISIDE_TOP: + case ISIDE_BOTTOM: + return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom; + case ISIDE_FLOATING: + return true; + default: + return false; + } + } + private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap) { diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 2884777fc9976..acbaa1d46ae29 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -25,6 +25,8 @@ import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; import static android.view.WindowInsets.Type.ime; +import static android.view.WindowInsets.Type.navigationBars; +import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; @@ -307,6 +309,26 @@ public class InsetsStateTest { } } + @Test + public void testCalculateUncontrollableInsets() throws Exception { + try (InsetsModeSession session = new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 200, 300)); + mState.getSource(ITYPE_IME).setVisible(true); + mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(100, 0, 200, 300)); + mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true); + + mState.setDisplayFrame(new Rect(0, 0, 200, 300)); + assertEquals(0, + mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 0, 200, 300))); + assertEquals(statusBars() | ime(), + mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 50, 200, 250))); + assertEquals(navigationBars(), + mState.calculateUncontrollableInsetsFromFrame(new Rect(50, 0, 150, 300))); + } + } + private void assertEqualsAndHashCode() { assertEquals(mState, mState2); assertEquals(mState.hashCode(), mState2.hashCode());