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
This commit is contained in:
tomnatan
2021-11-01 17:33:13 +00:00
committed by Winson Chung
parent a913e1f4e8
commit e048e69651
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.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);
}
}
}

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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.

View File

@@ -156,9 +156,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