Fix offset bounds cut off display cutout region
Root cause :
Previous refactoring OneHandedDisplayAreaOrganizer and count on
mDisplayLayout.getStableBounds() for the rotated bounds, however
the bounds was cut off the safe inset(DisplayCutout) region.
Solution :
a. Do not count on DisplayLayout.getStableBounds() API
b. handling bounds by Surface.ROTATION_{N} with below
Rotate policy: 0 <-> 90, 0 <-> 270, 180 <-> 90, 180 <-> 270
| fromRotation | toRotation | rotateBounds |
-------------------------------------------------
| ROTATION_0 | ROTATION_90 | True |
| ROTATION_0 | ROTATION_270 | True |
| ROTATION_180 | ROTATION_90 | True |
| ROTATION_180 | ROTATION_270 | True |
| ROTATION_90 | ROTATION_0 | True |
| ROTATION_90 | ROTATION_180 | True |
| ROTATION_270 | ROTATION_0 | True |
| ROTATION_270 | ROTATION_180 | True |
| Others | False |
c. Reset bounds while register and unregister DisplayAreaOrganizer
Test: atest com.android.systemui.onehanded
Test: atest SystemUITests
Test: atest OneHandedDisplayAreaOrganizerTest
Bug: 160763351
Change-Id: I458edfe77c45320cd0d5b6dba2397b7229bcb048
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 28d7049..16e05f1 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -22,14 +22,12 @@
import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
-import android.view.Display;
-import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -42,7 +40,6 @@
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
import com.android.systemui.wm.DisplayController;
-import com.android.systemui.wm.DisplayLayout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -83,8 +80,6 @@
@VisibleForTesting
HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
private DisplayController mDisplayController;
- private DisplayLayout mDisplayLayout;
- private DisplayInfo mDisplayInfo = new DisplayInfo();
private OneHandedAnimationController mAnimationController;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -129,6 +124,7 @@
switch (msg.what) {
case MSG_RESET_IMMEDIATE:
resetWindowsOffset();
+ mDefaultDisplayBounds.set(currentBounds);
mLastVisualDisplayBounds.set(currentBounds);
finishOffset(0, TRANSITION_DIRECTION_EXIT);
break;
@@ -157,9 +153,8 @@
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
mAnimationController = animationController;
mDisplayController = displayController;
- mDisplayLayout = getDisplayLayout(context);
- mDisplayLayout.getStableBounds(mDefaultDisplayBounds);
- mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
+ mDefaultDisplayBounds.set(getDisplayBounds());
+ mLastVisualDisplayBounds.set(getDisplayBounds());
mEnterExitAnimationDurationMs = context.getResources().getInteger(
com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
@@ -175,6 +170,8 @@
Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
return;
}
+ // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
+ mDefaultDisplayBounds.set(getDisplayBounds());
mDisplayAreaMap.put(displayAreaInfo, leash);
}
@@ -193,26 +190,35 @@
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
- if (mDisplayAreaMap != null) {
- mDisplayAreaMap.clear();
- }
+ resetWindowsOffset();
+
+ // Ensure all cached instance are cleared after resetWindowsOffset
+ mUpdateHandler.post(() -> {
+ if (mDisplayAreaMap != null && !mDisplayAreaMap.isEmpty()) {
+ mDisplayAreaMap.clear();
+ }
+ });
}
/**
- * Handler for display rotation changes.
+ * Handler for display rotation changes by below policy which
+ * handles 90 degree display rotation changes {@link Surface.Rotation}
+ *
*/
- public void onRotateDisplay(Resources res, int toRotation) {
+ public void onRotateDisplay(int fromRotation, int toRotation) {
// Stop one handed without animation and reset cropped size immediately
- final Rect newBounds = new Rect();
- mDisplayLayout.rotateTo(res, toRotation);
- mDisplayLayout.getStableBounds(newBounds);
+ final Rect newBounds = new Rect(mDefaultDisplayBounds);
+ final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = newBounds;
- args.argi1 = 0 /* xOffset */;
- args.argi2 = 0 /* yOffset */;
- args.argi3 = TRANSITION_DIRECTION_EXIT;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+ if (isOrientationDiff) {
+ newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = newBounds;
+ args.argi1 = 0 /* xOffset */;
+ args.argi2 = 0 /* yOffset */;
+ args.argi3 = TRANSITION_DIRECTION_EXIT;
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+ }
}
/**
@@ -314,12 +320,12 @@
}
@Nullable
- private Point getDisplayBounds() {
+ private Rect getDisplayBounds() {
Point realSize = new Point(0, 0);
if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(realSize);
}
- return realSize;
+ return new Rect(0, 0, realSize.x, realSize.y);
}
@VisibleForTesting
@@ -327,16 +333,6 @@
return mUpdateHandler;
}
- private DisplayLayout getDisplayLayout(Context context) {
- final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
- if (display != null) {
- display.getDisplayInfo(mDisplayInfo);
- } else {
- Log.w(TAG, "get DEFAULT_DISPLAY return null");
- }
- return new DisplayLayout(mDisplayInfo, context.getResources(), false, false);
- }
-
/**
* Register transition callback
*/
@@ -368,9 +364,5 @@
pw.println(mLastVisualDisplayBounds);
pw.print(innerPrefix + "getDisplayBounds()=");
pw.println(getDisplayBounds());
- if (mDisplayLayout != null) {
- pw.print(innerPrefix + "mDisplayLayout(w, h)=");
- pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height());
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index 4dacdf3..586761b 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -95,7 +95,7 @@
private final DisplayChangeController.OnDisplayChangingListener mRotationController =
(display, fromRotation, toRotation, wct) -> {
if (mDisplayAreaOrganizer != null) {
- mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
+ mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
}
};
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 07a32a9..7cf351f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -47,8 +47,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -59,7 +59,6 @@
DisplayAreaInfo mDisplayAreaInfo;
Display mDisplay;
- Handler mUpdateHandler;
OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
WindowContainerToken mToken;
@@ -76,6 +75,8 @@
DisplayController mMockDisplayController;
@Mock
SurfaceControl mMockLeash;
+ @Spy
+ Handler mUpdateHandler;
@Before
public void setUp() throws Exception {
@@ -103,7 +104,7 @@
mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
mMockDisplayController,
mMockAnimationController);
- mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
+ mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
}
@Test
@@ -139,21 +140,139 @@
final int xOffSet = 0;
final int yOffSet = 100;
+ TestableLooper.get(this).processAllMessages();
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
assertThat(mUpdateHandler.hasMessages(
- OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isNotNull();
+ OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isEqualTo(true);
}
@Test
- public void testResetImmediately() {
- // To prevent mNativeObject of Surface is null in the test flow
+ public void testRotation_portraitToLandscape() {
when(mMockLeash.isValid()).thenReturn(false);
- mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90);
+ // Rotate 0 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90);
assertThat(mUpdateHandler.hasMessages(
- OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 0 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 180 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 180 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
}
+ @Test
+ public void testRotation_landscapeToPortrait() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 90 -> 0
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 90 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 270 -> 0
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+
+ // Rotate 270 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
+ }
+
+ @Test
+ public void testRotation_portraitToPortrait() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 0 -> 0
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 0 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 180 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 180 -> 180
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+ }
+
+ @Test
+ public void testRotation_landscapeToLandscape() {
+ when(mMockLeash.isValid()).thenReturn(false);
+ // Rotate 90 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 90 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 270 -> 270
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+
+ // Rotate 270 -> 90
+ TestableLooper.get(this).processAllMessages();
+ mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90);
+
+ assertThat(mUpdateHandler.hasMessages(
+ OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
+ }
}