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:
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user