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

Commit 5cf6507e authored by tomnatan's avatar tomnatan Committed by Winson Chung
Browse files

Update size compat restart button surface position after display insests changed

Fix: 204532180
Fix: 182905588
Test: atest WMShellUnitTests:SizeCompatUIControllerTest
Test: atest WMShellUnitTests:SizeCompatUILayoutTest
Change-Id: Icda54db231783f576832b22e4d286fe0524a2088
Merged-In: Icda54db231783f576832b22e4d286fe0524a2088
parent cb5907cc
Loading
Loading
Loading
Loading
+73 −1
Original line number Diff line number Diff line
@@ -24,11 +24,16 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
import android.view.InsetsSourceControl;
import android.view.InsetsState;

import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;

@@ -42,7 +47,7 @@ import java.util.function.Consumer;
 * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
 * activities are in size compatibility mode.
 */
public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener,
public class SizeCompatUIController implements OnDisplaysChangedListener,
        DisplayImeController.ImePositionProcessor {

    /** Callback for size compat UI interaction. */
@@ -58,6 +63,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
    /** Whether the IME is shown on display id. */
    private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);

    /** {@link PerDisplayOnInsetsChangedListener} by display id. */
    private final SparseArray<PerDisplayOnInsetsChangedListener> mOnInsetsChangedListeners =
            new SparseArray<>(0);

    /** The showing UIs by task id. */
    private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);

@@ -66,6 +75,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang

    private final Context mContext;
    private final DisplayController mDisplayController;
    private final DisplayInsetsController mDisplayInsetsController;
    private final DisplayImeController mImeController;
    private final SyncTransactionQueue mSyncQueue;

@@ -76,10 +86,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang

    public SizeCompatUIController(Context context,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            DisplayImeController imeController,
            SyncTransactionQueue syncQueue) {
        mContext = context;
        mDisplayController = displayController;
        mDisplayInsetsController = displayInsetsController;
        mImeController = imeController;
        mSyncQueue = syncQueue;
        mDisplayController.addDisplayWindowListener(this);
@@ -114,9 +126,15 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
        }
    }

    @Override
    public void onDisplayAdded(int displayId) {
        addOnInsetsChangedListener(displayId);
    }

    @Override
    public void onDisplayRemoved(int displayId) {
        mDisplayContextCache.remove(displayId);
        removeOnInsetsChangedListener(displayId);

        // Remove all size compat UIs on the removed display.
        final List<Integer> toRemoveTaskIds = new ArrayList<>();
@@ -126,8 +144,29 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
        }
    }

    private void addOnInsetsChangedListener(int displayId) {
        PerDisplayOnInsetsChangedListener listener = new PerDisplayOnInsetsChangedListener(
                displayId);
        listener.register();
        mOnInsetsChangedListeners.put(displayId, listener);
    }

    private void removeOnInsetsChangedListener(int displayId) {
        PerDisplayOnInsetsChangedListener listener = mOnInsetsChangedListeners.get(displayId);
        if (listener == null) {
            return;
        }
        listener.unregister();
        mOnInsetsChangedListeners.remove(displayId);
    }


    @Override
    public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
        updateDisplayLayout(displayId);
    }

    private void updateDisplayLayout(int displayId) {
        final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
        forAllLayoutsOnDisplay(displayId, layout -> layout.updateDisplayLayout(displayLayout));
    }
@@ -219,4 +258,37 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
            }
        }
    }

    /** An implementation of {@link OnInsetsChangedListener} for a given display id. */
    private class PerDisplayOnInsetsChangedListener implements OnInsetsChangedListener {
        final int mDisplayId;
        final InsetsState mInsetsState = new InsetsState();

        PerDisplayOnInsetsChangedListener(int displayId) {
            mDisplayId = displayId;
        }

        void register() {
            mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
        }

        void unregister() {
            mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
        }

        @Override
        public void insetsChanged(InsetsState insetsState) {
            if (mInsetsState.equals(insetsState)) {
                return;
            }
            mInsetsState.set(insetsState);
            updateDisplayLayout(mDisplayId);
        }

        @Override
        public void insetsControlChanged(InsetsState insetsState,
                InsetsSourceControl[] activeControls) {
            insetsChanged(insetsState);
        }
    }
}
+14 −10
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ class SizeCompatUILayout {
    private final int mTaskId;
    private ShellTaskOrganizer.TaskListener mTaskListener;
    private DisplayLayout mDisplayLayout;
    private final Rect mStableBounds;
    private final int mButtonWidth;
    private final int mButtonHeight;
    private final int mPopupOffsetX;
@@ -89,6 +90,9 @@ class SizeCompatUILayout {
        mShouldShowHint = !hasShownHint;
        mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);

        mStableBounds = new Rect();
        mDisplayLayout.getStableBounds(mStableBounds);

        final Resources resources = mContext.getResources();
        mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
        mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
@@ -173,8 +177,7 @@ class SizeCompatUILayout {
        if (!taskConfig.windowConfiguration.getBounds()
                .equals(prevTaskConfig.windowConfiguration.getBounds())) {
            // Reposition the UI surfaces.
            updateButtonSurfacePosition();
            updateHintSurfacePosition();
            updateAllSurfacePositions();
        }

        if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
@@ -190,19 +193,14 @@ class SizeCompatUILayout {

    /** Called when display layout changed. */
    void updateDisplayLayout(DisplayLayout displayLayout) {
        if (displayLayout == mDisplayLayout) {
            return;
        }

        final Rect prevStableBounds = new Rect();
        final Rect prevStableBounds = mStableBounds;
        final Rect curStableBounds = new Rect();
        mDisplayLayout.getStableBounds(prevStableBounds);
        displayLayout.getStableBounds(curStableBounds);
        mDisplayLayout = displayLayout;
        if (!prevStableBounds.equals(curStableBounds)) {
            // Stable bounds changed, update UI surface positions.
            updateButtonSurfacePosition();
            updateHintSurfacePosition();
            updateAllSurfacePositions();
            mStableBounds.set(curStableBounds);
        }
    }

@@ -268,6 +266,11 @@ class SizeCompatUILayout {
        createSizeCompatHint();
    }

    private void updateAllSurfacePositions() {
        updateButtonSurfacePosition();
        updateHintSurfacePosition();
    }

    @VisibleForTesting
    void updateButtonSurfacePosition() {
        if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
@@ -290,6 +293,7 @@ class SizeCompatUILayout {
        updateSurfacePosition(leash, positionX, positionY);
    }

    @VisibleForTesting
    void updateHintSurfacePosition() {
        if (mHint == null || mHintWindowManager == null
                || mHintWindowManager.getSurfaceControl() == null) {
+50 −1
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package com.android.wm.shell.sizecompatui;

import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -28,6 +31,8 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.view.InsetsSource;
import android.view.InsetsState;

import androidx.test.filters.SmallTest;

@@ -35,12 +40,16 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -58,12 +67,16 @@ public class SizeCompatUIControllerTest extends ShellTestCase {

    private SizeCompatUIController mController;
    private @Mock DisplayController mMockDisplayController;
    private @Mock DisplayInsetsController mMockDisplayInsetsController;
    private @Mock DisplayLayout mMockDisplayLayout;
    private @Mock DisplayImeController mMockImeController;
    private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
    private @Mock SyncTransactionQueue mMockSyncQueue;
    private @Mock SizeCompatUILayout mMockLayout;

    @Captor
    ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
@@ -72,7 +85,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
        doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
        doReturn(TASK_ID).when(mMockLayout).getTaskId();
        mController = new SizeCompatUIController(mContext, mMockDisplayController,
                mMockImeController, mMockSyncQueue) {
                mMockDisplayInsetsController, mMockImeController, mMockSyncQueue) {
            @Override
            SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
                    Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
@@ -113,8 +126,18 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
        verify(mMockLayout).release();
    }

    @Test
    public void testOnDisplayAdded() {
        mController.onDisplayAdded(DISPLAY_ID);
        mController.onDisplayAdded(DISPLAY_ID + 1);

        verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID), any());
        verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID + 1), any());
    }

    @Test
    public void testOnDisplayRemoved() {
        mController.onDisplayAdded(DISPLAY_ID);
        final Configuration taskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                mMockTaskListener);
@@ -122,9 +145,12 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
        mController.onDisplayRemoved(DISPLAY_ID + 1);

        verify(mMockLayout, never()).release();
        verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
                any());

        mController.onDisplayRemoved(DISPLAY_ID);

        verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
        verify(mMockLayout).release();
    }

@@ -144,6 +170,29 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
        verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
    }

    @Test
    public void testInsetsChanged() {
        mController.onDisplayAdded(DISPLAY_ID);
        final Configuration taskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                mMockTaskListener);
        InsetsState insetsState = new InsetsState();
        InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
        insetsSource.setFrame(0, 0, 1000, 1000);
        insetsState.addSource(insetsSource);

        verify(mMockDisplayInsetsController).addInsetsChangedListener(eq(DISPLAY_ID),
                mOnInsetsChangedListenerCaptor.capture());
        mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);

        verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);

        // No update if the insets state is the same.
        clearInvocations(mMockLayout);
        mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
        verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
    }

    @Test
    public void testChangeButtonVisibilityOnImeShowHide() {
        final Configuration taskConfig = new Configuration();
+31 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.wm.shell.sizecompatui;

import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;

import static org.junit.Assert.assertFalse;
@@ -33,6 +35,8 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.view.DisplayInfo;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;

@@ -77,7 +81,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
        mTaskConfig = new Configuration();

        mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
                new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
                new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
                false /* hasShownHint */);

        spyOn(mLayout);
@@ -176,7 +180,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
        displayInfo.logicalWidth = 1000;
        displayInfo.logicalHeight = 2000;
        final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
                mContext.getResources(), false, false);
                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);

        mLayout.updateDisplayLayout(displayLayout1);
        verify(mLayout).updateButtonSurfacePosition();
@@ -185,12 +189,36 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
        // No update if the display bounds is the same.
        clearInvocations(mLayout);
        final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
                mContext.getResources(), false, false);
                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
        mLayout.updateDisplayLayout(displayLayout2);
        verify(mLayout, never()).updateButtonSurfacePosition();
        verify(mLayout, never()).updateHintSurfacePosition();
    }

    @Test
    public void testUpdateDisplayLayoutInsets() {
        final DisplayInfo displayInfo = new DisplayInfo();
        displayInfo.logicalWidth = 1000;
        displayInfo.logicalHeight = 2000;
        final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
                mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);

        mLayout.updateDisplayLayout(displayLayout);
        verify(mLayout).updateButtonSurfacePosition();
        verify(mLayout).updateHintSurfacePosition();

        // Update if the insets change on the existing display layout
        clearInvocations(mLayout);
        InsetsState insetsState = new InsetsState();
        InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
        insetsSource.setFrame(0, 0, 1000, 1000);
        insetsState.addSource(insetsSource);
        displayLayout.setInsets(mContext.getResources(), insetsState);
        mLayout.updateDisplayLayout(displayLayout);
        verify(mLayout).updateButtonSurfacePosition();
        verify(mLayout).updateHintSurfacePosition();
    }

    @Test
    public void testUpdateImeVisibility() {
        // Create button if it is not created.
+4 −3
Original line number Diff line number Diff line
@@ -151,9 +151,10 @@ public abstract class WMShellBaseModule {
    @WMSingleton
    @Provides
    static SizeCompatUIController provideSizeCompatUIController(Context context,
            DisplayController displayController, DisplayImeController imeController,
            SyncTransactionQueue syncQueue) {
        return new SizeCompatUIController(context, displayController, imeController, syncQueue);
            DisplayController displayController, DisplayInsetsController displayInsetsController,
            DisplayImeController imeController, SyncTransactionQueue syncQueue) {
        return new SizeCompatUIController(context, displayController, displayInsetsController,
                imeController, syncQueue);
    }

    @WMSingleton