Merge "Update size compat restart button surface position after display insests changed" into sc-v2-dev am: 6ee7e8b6b3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16174883

Change-Id: Ia082aa3db3d4e846752d018e9d53ed4f96bb1df5
This commit is contained in:
Tom Natan
2021-11-05 10:24:32 +00:00
committed by Automerger Merge Worker
5 changed files with 172 additions and 18 deletions

View File

@@ -24,11 +24,16 @@ import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.Display; import android.view.Display;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController; 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.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.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue; 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 * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
* activities are in size compatibility mode. * activities are in size compatibility mode.
*/ */
public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener, public class SizeCompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor { DisplayImeController.ImePositionProcessor {
/** Callback for size compat UI interaction. */ /** Callback for size compat UI interaction. */
@@ -58,6 +63,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
/** Whether the IME is shown on display id. */ /** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1); 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. */ /** The showing UIs by task id. */
private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0); private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
@@ -66,6 +75,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
private final Context mContext; private final Context mContext;
private final DisplayController mDisplayController; private final DisplayController mDisplayController;
private final DisplayInsetsController mDisplayInsetsController;
private final DisplayImeController mImeController; private final DisplayImeController mImeController;
private final SyncTransactionQueue mSyncQueue; private final SyncTransactionQueue mSyncQueue;
@@ -76,10 +86,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
public SizeCompatUIController(Context context, public SizeCompatUIController(Context context,
DisplayController displayController, DisplayController displayController,
DisplayInsetsController displayInsetsController,
DisplayImeController imeController, DisplayImeController imeController,
SyncTransactionQueue syncQueue) { SyncTransactionQueue syncQueue) {
mContext = context; mContext = context;
mDisplayController = displayController; mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
mImeController = imeController; mImeController = imeController;
mSyncQueue = syncQueue; mSyncQueue = syncQueue;
mDisplayController.addDisplayWindowListener(this); mDisplayController.addDisplayWindowListener(this);
@@ -114,9 +126,15 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
} }
} }
@Override
public void onDisplayAdded(int displayId) {
addOnInsetsChangedListener(displayId);
}
@Override @Override
public void onDisplayRemoved(int displayId) { public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId); mDisplayContextCache.remove(displayId);
removeOnInsetsChangedListener(displayId);
// Remove all size compat UIs on the removed display. // Remove all size compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>(); 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 @Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
updateDisplayLayout(displayId);
}
private void updateDisplayLayout(int displayId) {
final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId); final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
forAllLayoutsOnDisplay(displayId, layout -> layout.updateDisplayLayout(displayLayout)); 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);
}
}
} }

View File

@@ -55,6 +55,7 @@ class SizeCompatUILayout {
private final int mTaskId; private final int mTaskId;
private ShellTaskOrganizer.TaskListener mTaskListener; private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout; private DisplayLayout mDisplayLayout;
private final Rect mStableBounds;
private final int mButtonWidth; private final int mButtonWidth;
private final int mButtonHeight; private final int mButtonHeight;
private final int mPopupOffsetX; private final int mPopupOffsetX;
@@ -89,6 +90,9 @@ class SizeCompatUILayout {
mShouldShowHint = !hasShownHint; mShouldShowHint = !hasShownHint;
mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
mStableBounds = new Rect();
mDisplayLayout.getStableBounds(mStableBounds);
final Resources resources = mContext.getResources(); final Resources resources = mContext.getResources();
mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width); mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height); mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
@@ -173,8 +177,7 @@ class SizeCompatUILayout {
if (!taskConfig.windowConfiguration.getBounds() if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) { .equals(prevTaskConfig.windowConfiguration.getBounds())) {
// Reposition the UI surfaces. // Reposition the UI surfaces.
updateButtonSurfacePosition(); updateAllSurfacePositions();
updateHintSurfacePosition();
} }
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
@@ -190,19 +193,14 @@ class SizeCompatUILayout {
/** Called when display layout changed. */ /** Called when display layout changed. */
void updateDisplayLayout(DisplayLayout displayLayout) { void updateDisplayLayout(DisplayLayout displayLayout) {
if (displayLayout == mDisplayLayout) { final Rect prevStableBounds = mStableBounds;
return;
}
final Rect prevStableBounds = new Rect();
final Rect curStableBounds = new Rect(); final Rect curStableBounds = new Rect();
mDisplayLayout.getStableBounds(prevStableBounds);
displayLayout.getStableBounds(curStableBounds); displayLayout.getStableBounds(curStableBounds);
mDisplayLayout = displayLayout; mDisplayLayout = displayLayout;
if (!prevStableBounds.equals(curStableBounds)) { if (!prevStableBounds.equals(curStableBounds)) {
// Stable bounds changed, update UI surface positions. // Stable bounds changed, update UI surface positions.
updateButtonSurfacePosition(); updateAllSurfacePositions();
updateHintSurfacePosition(); mStableBounds.set(curStableBounds);
} }
} }
@@ -268,6 +266,11 @@ class SizeCompatUILayout {
createSizeCompatHint(); createSizeCompatHint();
} }
private void updateAllSurfacePositions() {
updateButtonSurfacePosition();
updateHintSurfacePosition();
}
@VisibleForTesting @VisibleForTesting
void updateButtonSurfacePosition() { void updateButtonSurfacePosition() {
if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
@@ -290,6 +293,7 @@ class SizeCompatUILayout {
updateSurfacePosition(leash, positionX, positionY); updateSurfacePosition(leash, positionX, positionY);
} }
@VisibleForTesting
void updateHintSurfacePosition() { void updateHintSurfacePosition() {
if (mHint == null || mHintWindowManager == null if (mHint == null || mHintWindowManager == null
|| mHintWindowManager.getSurfaceControl() == null) { || mHintWindowManager.getSurfaceControl() == null) {

View File

@@ -16,11 +16,14 @@
package com.android.wm.shell.sizecompatui; 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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -28,6 +31,8 @@ import static org.mockito.Mockito.verify;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.view.InsetsSource;
import android.view.InsetsState;
import androidx.test.filters.SmallTest; 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.ShellTestCase;
import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController; 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.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.Before; 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.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
@@ -58,12 +67,16 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
private SizeCompatUIController mController; private SizeCompatUIController mController;
private @Mock DisplayController mMockDisplayController; private @Mock DisplayController mMockDisplayController;
private @Mock DisplayInsetsController mMockDisplayInsetsController;
private @Mock DisplayLayout mMockDisplayLayout; private @Mock DisplayLayout mMockDisplayLayout;
private @Mock DisplayImeController mMockImeController; private @Mock DisplayImeController mMockImeController;
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener; private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
private @Mock SyncTransactionQueue mMockSyncQueue; private @Mock SyncTransactionQueue mMockSyncQueue;
private @Mock SizeCompatUILayout mMockLayout; private @Mock SizeCompatUILayout mMockLayout;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@@ -72,7 +85,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId(); doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
doReturn(TASK_ID).when(mMockLayout).getTaskId(); doReturn(TASK_ID).when(mMockLayout).getTaskId();
mController = new SizeCompatUIController(mContext, mMockDisplayController, mController = new SizeCompatUIController(mContext, mMockDisplayController,
mMockImeController, mMockSyncQueue) { mMockDisplayInsetsController, mMockImeController, mMockSyncQueue) {
@Override @Override
SizeCompatUILayout createLayout(Context context, int displayId, int taskId, SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
@@ -113,8 +126,18 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).release(); 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 @Test
public void testOnDisplayRemoved() { public void testOnDisplayRemoved() {
mController.onDisplayAdded(DISPLAY_ID);
final Configuration taskConfig = new Configuration(); final Configuration taskConfig = new Configuration();
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
mMockTaskListener); mMockTaskListener);
@@ -122,9 +145,12 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
mController.onDisplayRemoved(DISPLAY_ID + 1); mController.onDisplayRemoved(DISPLAY_ID + 1);
verify(mMockLayout, never()).release(); verify(mMockLayout, never()).release();
verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
any());
mController.onDisplayRemoved(DISPLAY_ID); mController.onDisplayRemoved(DISPLAY_ID);
verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
verify(mMockLayout).release(); verify(mMockLayout).release();
} }
@@ -144,6 +170,29 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout); 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 @Test
public void testChangeButtonVisibilityOnImeShowHide() { public void testChangeButtonVisibilityOnImeShowHide() {
final Configuration taskConfig = new Configuration(); final Configuration taskConfig = new Configuration();

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell.sizecompatui; 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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -33,6 +35,8 @@ import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.view.DisplayInfo; import android.view.DisplayInfo;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.View; import android.view.View;
@@ -77,7 +81,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
mTaskConfig = new Configuration(); mTaskConfig = new Configuration();
mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
new Configuration(), TASK_ID, mTaskListener, mDisplayLayout, new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownHint */); false /* hasShownHint */);
spyOn(mLayout); spyOn(mLayout);
@@ -176,7 +180,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
displayInfo.logicalWidth = 1000; displayInfo.logicalWidth = 1000;
displayInfo.logicalHeight = 2000; displayInfo.logicalHeight = 2000;
final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo, final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
mContext.getResources(), false, false); mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
mLayout.updateDisplayLayout(displayLayout1); mLayout.updateDisplayLayout(displayLayout1);
verify(mLayout).updateButtonSurfacePosition(); verify(mLayout).updateButtonSurfacePosition();
@@ -185,12 +189,36 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
// No update if the display bounds is the same. // No update if the display bounds is the same.
clearInvocations(mLayout); clearInvocations(mLayout);
final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
mContext.getResources(), false, false); mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
mLayout.updateDisplayLayout(displayLayout2); mLayout.updateDisplayLayout(displayLayout2);
verify(mLayout, never()).updateButtonSurfacePosition(); verify(mLayout, never()).updateButtonSurfacePosition();
verify(mLayout, never()).updateHintSurfacePosition(); 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 @Test
public void testUpdateImeVisibility() { public void testUpdateImeVisibility() {
// Create button if it is not created. // Create button if it is not created.

View File

@@ -156,9 +156,10 @@ public abstract class WMShellBaseModule {
@WMSingleton @WMSingleton
@Provides @Provides
static SizeCompatUIController provideSizeCompatUIController(Context context, static SizeCompatUIController provideSizeCompatUIController(Context context,
DisplayController displayController, DisplayImeController imeController, DisplayController displayController, DisplayInsetsController displayInsetsController,
SyncTransactionQueue syncQueue) { DisplayImeController imeController, SyncTransactionQueue syncQueue) {
return new SizeCompatUIController(context, displayController, imeController, syncQueue); return new SizeCompatUIController(context, displayController, displayInsetsController,
imeController, syncQueue);
} }
@WMSingleton @WMSingleton