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
This commit is contained in:
Bill Lin
2020-07-08 16:21:18 +08:00
parent d7912748cc
commit 72779ac936
3 changed files with 159 additions and 48 deletions

View File

@@ -22,14 +22,12 @@ import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSI
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 androidx.annotation.VisibleForTesting;
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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
@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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
switch (msg.what) {
case MSG_RESET_IMMEDIATE:
resetWindowsOffset();
mDefaultDisplayBounds.set(currentBounds);
mLastVisualDisplayBounds.set(currentBounds);
finishOffset(0, TRANSITION_DIRECTION_EXIT);
break;
@@ -157,9 +153,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
@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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
}
@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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
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 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
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());
}
}
}

View File

@@ -95,7 +95,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
private final DisplayChangeController.OnDisplayChangingListener mRotationController =
(display, fromRotation, toRotation, wct) -> {
if (mDisplayAreaOrganizer != null) {
mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
}
};

View File

@@ -47,8 +47,8 @@ import org.junit.Before;
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 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
DisplayAreaInfo mDisplayAreaInfo;
Display mDisplay;
Handler mUpdateHandler;
OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
WindowContainerToken mToken;
@@ -76,6 +75,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
DisplayController mMockDisplayController;
@Mock
SurfaceControl mMockLeash;
@Spy
Handler mUpdateHandler;
@Before
public void setUp() throws Exception {
@@ -103,7 +104,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
mMockDisplayController,
mMockAnimationController);
mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
}
@Test
@@ -139,21 +140,139 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
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);
}
}