Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72779ac9 authored by Bill Lin's avatar Bill Lin
Browse files

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
parent d7912748
Loading
Loading
Loading
Loading
+32 −40
Original line number Original line Diff line number Diff line
@@ -22,14 +22,12 @@ import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSI
import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;


import android.content.Context;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.util.Log;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
import android.window.DisplayAreaOrganizer;
@@ -42,7 +40,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
import com.android.systemui.Dumpable;
import com.android.systemui.wm.DisplayController;
import com.android.systemui.wm.DisplayController;
import com.android.systemui.wm.DisplayLayout;


import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.PrintWriter;
@@ -83,8 +80,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
    @VisibleForTesting
    @VisibleForTesting
    HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
    HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
    private DisplayController mDisplayController;
    private DisplayController mDisplayController;
    private DisplayLayout mDisplayLayout;
    private DisplayInfo mDisplayInfo = new DisplayInfo();
    private OneHandedAnimationController mAnimationController;
    private OneHandedAnimationController mAnimationController;
    private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
    private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
            mSurfaceControlTransactionFactory;
            mSurfaceControlTransactionFactory;
@@ -129,6 +124,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
        switch (msg.what) {
        switch (msg.what) {
            case MSG_RESET_IMMEDIATE:
            case MSG_RESET_IMMEDIATE:
                resetWindowsOffset();
                resetWindowsOffset();
                mDefaultDisplayBounds.set(currentBounds);
                mLastVisualDisplayBounds.set(currentBounds);
                mLastVisualDisplayBounds.set(currentBounds);
                finishOffset(0, TRANSITION_DIRECTION_EXIT);
                finishOffset(0, TRANSITION_DIRECTION_EXIT);
                break;
                break;
@@ -157,9 +153,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
        mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
        mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
        mAnimationController = animationController;
        mAnimationController = animationController;
        mDisplayController = displayController;
        mDisplayController = displayController;
        mDisplayLayout = getDisplayLayout(context);
        mDefaultDisplayBounds.set(getDisplayBounds());
        mDisplayLayout.getStableBounds(mDefaultDisplayBounds);
        mLastVisualDisplayBounds.set(getDisplayBounds());
        mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
        mEnterExitAnimationDurationMs = context.getResources().getInteger(
        mEnterExitAnimationDurationMs = context.getResources().getInteger(
                com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
                com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
        mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
        mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
@@ -175,6 +170,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
            Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
            Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
            return;
            return;
        }
        }
        // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
        mDefaultDisplayBounds.set(getDisplayBounds());
        mDisplayAreaMap.put(displayAreaInfo, leash);
        mDisplayAreaMap.put(displayAreaInfo, leash);
    }
    }


@@ -193,20 +190,28 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
    @Override
    @Override
    public void unregisterOrganizer() {
    public void unregisterOrganizer() {
        super.unregisterOrganizer();
        super.unregisterOrganizer();
        if (mDisplayAreaMap != null) {
        resetWindowsOffset();

        // Ensure all cached instance are cleared after resetWindowsOffset
        mUpdateHandler.post(() -> {
            if (mDisplayAreaMap != null && !mDisplayAreaMap.isEmpty()) {
                mDisplayAreaMap.clear();
                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
        // Stop one handed without animation and reset cropped size immediately
        final Rect newBounds = new Rect();
        final Rect newBounds = new Rect(mDefaultDisplayBounds);
        mDisplayLayout.rotateTo(res, toRotation);
        final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
        mDisplayLayout.getStableBounds(newBounds);


        if (isOrientationDiff) {
            newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
            SomeArgs args = SomeArgs.obtain();
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = newBounds;
            args.arg1 = newBounds;
            args.argi1 = 0 /* xOffset */;
            args.argi1 = 0 /* xOffset */;
@@ -214,6 +219,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
            args.argi3 = TRANSITION_DIRECTION_EXIT;
            args.argi3 = TRANSITION_DIRECTION_EXIT;
            mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
            mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
        }
        }
    }


    /**
    /**
     * Offset the windows by a given offset on Y-axis, triggered also from screen rotation.
     * Offset the windows by a given offset on Y-axis, triggered also from screen rotation.
@@ -314,12 +320,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
    }
    }


    @Nullable
    @Nullable
    private Point getDisplayBounds() {
    private Rect getDisplayBounds() {
        Point realSize = new Point(0, 0);
        Point realSize = new Point(0, 0);
        if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
        if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
            mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(realSize);
            mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(realSize);
        }
        }
        return realSize;
        return new Rect(0, 0, realSize.x, realSize.y);
    }
    }


    @VisibleForTesting
    @VisibleForTesting
@@ -327,16 +333,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
        return mUpdateHandler;
        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
     * Register transition callback
     */
     */
@@ -368,9 +364,5 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implemen
        pw.println(mLastVisualDisplayBounds);
        pw.println(mLastVisualDisplayBounds);
        pw.print(innerPrefix + "getDisplayBounds()=");
        pw.print(innerPrefix + "getDisplayBounds()=");
        pw.println(getDisplayBounds());
        pw.println(getDisplayBounds());
        if (mDisplayLayout != null) {
            pw.print(innerPrefix + "mDisplayLayout(w, h)=");
            pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height());
        }
    }
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -95,7 +95,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable {
    private final DisplayChangeController.OnDisplayChangingListener mRotationController =
    private final DisplayChangeController.OnDisplayChangingListener mRotationController =
            (display, fromRotation, toRotation, wct) -> {
            (display, fromRotation, toRotation, wct) -> {
                if (mDisplayAreaOrganizer != null) {
                if (mDisplayAreaOrganizer != null) {
                    mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
                    mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
                }
                }
            };
            };


+127 −8
Original line number Original line Diff line number Diff line
@@ -47,8 +47,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;


@SmallTest
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidTestingRunner.class)
@@ -59,7 +59,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {


    DisplayAreaInfo mDisplayAreaInfo;
    DisplayAreaInfo mDisplayAreaInfo;
    Display mDisplay;
    Display mDisplay;
    Handler mUpdateHandler;
    OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
    OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
    OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
    OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
    WindowContainerToken mToken;
    WindowContainerToken mToken;
@@ -76,6 +75,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
    DisplayController mMockDisplayController;
    DisplayController mMockDisplayController;
    @Mock
    @Mock
    SurfaceControl mMockLeash;
    SurfaceControl mMockLeash;
    @Spy
    Handler mUpdateHandler;


    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
@@ -103,7 +104,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
        mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
        mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
                mMockDisplayController,
                mMockDisplayController,
                mMockAnimationController);
                mMockAnimationController);
        mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
        mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
    }
    }


    @Test
    @Test
@@ -139,21 +140,139 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
        final int xOffSet = 0;
        final int xOffSet = 0;
        final int yOffSet = 100;
        final int yOffSet = 100;


        TestableLooper.get(this).processAllMessages();
        mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
        mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
        mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
        mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);


        assertThat(mUpdateHandler.hasMessages(
        assertThat(mUpdateHandler.hasMessages(
                OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isNotNull();
                OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isEqualTo(true);
    }

    @Test
    public void testRotation_portraitToLandscape() {
        when(mMockLeash.isValid()).thenReturn(false);
        // Rotate 0 -> 90
        TestableLooper.get(this).processAllMessages();
        mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90);

        assertThat(mUpdateHandler.hasMessages(
                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
    @Test
    public void testResetImmediately() {
    public void testRotation_landscapeToPortrait() {
        // To prevent mNativeObject of Surface is null in the test flow
        when(mMockLeash.isValid()).thenReturn(false);
        when(mMockLeash.isValid()).thenReturn(false);
        mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90);
        // 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(
        assertThat(mUpdateHandler.hasMessages(
                OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
                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);
    }
}
}