Fix InsetsState#calculateUncontrollableInsetsFromFrame

If a type of insets doesn't provide any insets to a window, the previous
logic would think the type is controllable. This is wrong.

If a type of insets provides the entire inset to a window, the previous
logic would think the type is not controllable when the width or height
doesn't match the display frame. This doesn't make much sense as well.

This CL fixes the issues mentioned above and also refines
WindowInsetsTests. It displays the name (string) of the controllable
types, not the value (number).

Fix: 234426300
Test: WindowInsetsTests
Test: atest InsetsControllerTest
Change-Id: Ice71db61cc4963c6d8489096bec9c295d58bfd78
This commit is contained in:
Tiger Huang
2022-05-31 20:05:40 +08:00
parent 2e83cb0c22
commit ea97f83f8f
5 changed files with 70 additions and 29 deletions

View File

@@ -404,27 +404,20 @@ public class InsetsState implements Parcelable {
if (source == null) {
continue;
}
if (!canControlSide(frame, getInsetSide(
source.calculateInsets(frame, true /* ignoreVisibility */)))) {
if (!canControlSource(frame, source)) {
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 static boolean canControlSource(Rect frame, InsetsSource source) {
final Insets insets = source.calculateInsets(frame, true /* ignoreVisibility */);
final Rect sourceFrame = source.getFrame();
final int sourceWidth = sourceFrame.width();
final int sourceHeight = sourceFrame.height();
return insets.left == sourceWidth || insets.right == sourceWidth
|| insets.top == sourceHeight || insets.bottom == sourceHeight;
}
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,

View File

@@ -214,19 +214,23 @@ public class InsetsControllerTest {
}
@Test
public void testFrameDoesntMatchDisplay() {
mController.onFrameChanged(new Rect(0, 0, 100, 100));
mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
InsetsSourceControl control =
new InsetsSourceControl(
ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0));
mController.onControlsChanged(new InsetsSourceControl[] { control });
public void testFrameDoesntOverlapWithInsets() {
WindowInsetsAnimationControlListener controlListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
new CancellationSignal(), controlListener);
mController.addOnControllableInsetsChangedListener(
(controller, typeMask) -> assertEquals(0, typeMask));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// The frame doesn't overlap with status bar.
mController.onFrameChanged(new Rect(0, 10, 100, 100));
InsetsSourceControl control =
new InsetsSourceControl(
ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0));
mController.onControlsChanged(new InsetsSourceControl[]{control});
mController.controlWindowInsetsAnimation(0, 0 /* durationMs */,
new LinearInterpolator(),
new CancellationSignal(), controlListener);
mController.addOnControllableInsetsChangedListener(
(controller, typeMask) -> assertEquals(0, typeMask));
});
verify(controlListener).onCancelled(null);
verify(controlListener, never()).onReady(any(), anyInt());
}

View File

@@ -88,7 +88,7 @@
<TextView
android:id="@+id/textViewControllableInsets"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp" />

View File

@@ -22,7 +22,7 @@
<!-- The item positions should match the flag values respectively. -->
<string-array name="behaviors">
<item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
<item>BEHAVIOR_SHOW_BARS_BY_TOUCH (deprecated)</item>
<item>BEHAVIOR_DEFAULT</item>
<item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
</string-array>

View File

@@ -83,7 +83,51 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn
final View contentView = findViewById(R.id.content);
contentView.setOnApplyWindowInsetsListener(this);
contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
(c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
(c, types) -> mTextControllableInsets.setText(
"ControllableInsetsTypes:\n" + insetsTypesToString(types)));
}
private static String insetsTypesToString(int types) {
if (types == 0) {
return "none";
}
final StringBuilder sb = new StringBuilder();
if ((types & Type.statusBars()) != 0) {
types &= ~Type.statusBars();
sb.append("statusBars ");
}
if ((types & Type.navigationBars()) != 0) {
types &= ~Type.navigationBars();
sb.append("navigationBars ");
}
if ((types & Type.captionBar()) != 0) {
types &= ~Type.captionBar();
sb.append("captionBar ");
}
if ((types & Type.ime()) != 0) {
types &= ~Type.ime();
sb.append("ime ");
}
if ((types & Type.systemGestures()) != 0) {
types &= ~Type.systemGestures();
sb.append("systemGestures ");
}
if ((types & Type.mandatorySystemGestures()) != 0) {
types &= ~Type.mandatorySystemGestures();
sb.append("mandatorySystemGestures ");
}
if ((types & Type.tappableElement()) != 0) {
types &= ~Type.tappableElement();
sb.append("tappableElement ");
}
if ((types & Type.displayCutout()) != 0) {
types &= ~Type.displayCutout();
sb.append("displayCutout ");
}
if (types != 0) {
sb.append("unknownTypes:").append(types);
}
return sb.toString();
}
@Override