Extract remote insets controller out of DisplayImeController

- Moved into DisplayInsetsController so that other shell components
  can listen to changes as well
- Expose the insets to DisplayController's DisplayLayout so that we
  can account for the extra nav bar insets from the task bar

Bug: 182905588
Test: atest DisplayInsetsControllerTest
Change-Id: Ie481282c9a71e02a77ce6b2cf8c2dcf4b8e452c6
This commit is contained in:
Winson Chung
2021-07-27 10:21:19 -07:00
parent c57c02c7d4
commit 0ac518cce9
12 changed files with 623 additions and 123 deletions

View File

@@ -20,7 +20,9 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCR
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
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.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
@@ -39,7 +41,9 @@ import java.util.Optional;
public class ShellInitImpl {
private static final String TAG = ShellInitImpl.class.getSimpleName();
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<BubbleController> mBubblesOptional;
@@ -55,7 +59,10 @@ public class ShellInitImpl {
private final InitImpl mImpl = new InitImpl();
public ShellInitImpl(DisplayImeController displayImeController,
public ShellInitImpl(
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
@@ -68,7 +75,9 @@ public class ShellInitImpl {
Transitions transitions,
StartingWindowController startingWindow,
ShellExecutor mainExecutor) {
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
mDragAndDropController = dragAndDropController;
mShellTaskOrganizer = shellTaskOrganizer;
mBubblesOptional = bubblesOptional;
@@ -88,7 +97,9 @@ public class ShellInitImpl {
}
private void init() {
// Start listening for display changes
// Start listening for display and insets changes
mDisplayController.initialize();
mDisplayInsetsController.initialize();
mDisplayImeController.startMonitorDisplays();
// Setup the shell organizer

View File

@@ -17,6 +17,7 @@
package com.android.wm.shell.common;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.IWindowManager;
@@ -27,6 +28,7 @@ import androidx.annotation.BinderThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -35,14 +37,14 @@ import java.util.ArrayList;
* rotation.
*/
public class DisplayChangeController {
private static final String TAG = DisplayChangeController.class.getSimpleName();
private final ShellExecutor mMainExecutor;
private final IWindowManager mWmService;
private final IDisplayWindowRotationController mControllerImpl;
private final ArrayList<OnDisplayChangingListener> mRotationListener =
new ArrayList<>();
private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
private final CopyOnWriteArrayList<OnDisplayChangingListener> mRotationListener =
new CopyOnWriteArrayList<>();
public DisplayChangeController(IWindowManager wmService, ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
@@ -59,34 +61,26 @@ public class DisplayChangeController {
* Adds a display rotation controller.
*/
public void addRotationListener(OnDisplayChangingListener listener) {
synchronized (mRotationListener) {
mRotationListener.add(listener);
}
mRotationListener.add(listener);
}
/**
* Removes a display rotation controller.
*/
public void removeRotationListener(OnDisplayChangingListener listener) {
synchronized (mRotationListener) {
mRotationListener.remove(listener);
}
mRotationListener.remove(listener);
}
private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
IDisplayWindowRotationCallback callback) {
WindowContainerTransaction t = new WindowContainerTransaction();
synchronized (mRotationListener) {
mTmpListeners.clear();
// Make a local copy in case the handlers add/remove themselves.
mTmpListeners.addAll(mRotationListener);
}
for (OnDisplayChangingListener c : mTmpListeners) {
for (OnDisplayChangingListener c : mRotationListener) {
c.onRotateDisplay(displayId, fromRotation, toRotation, t);
}
try {
callback.continueRotateDisplay(toRotation, t);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to continue rotation", e);
}
}

View File

@@ -26,6 +26,7 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
import android.view.InsetsState;
import androidx.annotation.BinderThread;
@@ -52,14 +53,6 @@ public class DisplayController {
private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
/**
* Gets a display by id from DisplayManager.
*/
public Display getDisplay(int displayId) {
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
return displayManager.getDisplay(displayId);
}
public DisplayController(Context context, IWindowManager wmService,
ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
@@ -67,13 +60,27 @@ public class DisplayController {
mWmService = wmService;
mChangeController = new DisplayChangeController(mWmService, mainExecutor);
mDisplayContainerListener = new DisplayWindowListenerImpl();
}
/**
* Initializes the window listener.
*/
public void initialize() {
try {
mWmService.registerDisplayWindowListener(mDisplayContainerListener);
} catch (RemoteException e) {
throw new RuntimeException("Unable to register hierarchy listener");
throw new RuntimeException("Unable to register display controller");
}
}
/**
* Gets a display by id from DisplayManager.
*/
public Display getDisplay(int displayId) {
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
return displayManager.getDisplay(displayId);
}
/**
* Gets the DisplayLayout associated with a display.
*/
@@ -90,6 +97,16 @@ public class DisplayController {
return r != null ? r.mContext : null;
}
/**
* Updates the insets for a given display.
*/
public void updateDisplayInsets(int displayId, InsetsState state) {
final DisplayRecord r = mDisplays.get(displayId);
if (r != null) {
r.setInsets(state);
}
}
/**
* Add a display window-container listener. It will get notified whenever a display's
* configuration changes or when displays are added/removed from the WM hierarchy.
@@ -134,17 +151,18 @@ public class DisplayController {
if (mDisplays.get(displayId) != null) {
return;
}
Display display = getDisplay(displayId);
final Display display = getDisplay(displayId);
if (display == null) {
// It's likely that the display is private to some app and thus not
// accessible by system-ui.
return;
}
DisplayRecord record = new DisplayRecord();
record.mDisplayId = displayId;
record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
final Context context = (displayId == Display.DEFAULT_DISPLAY)
? mContext
: mContext.createDisplayContext(display);
record.mDisplayLayout = new DisplayLayout(record.mContext, display);
final DisplayRecord record = new DisplayRecord(displayId);
record.setDisplayLayout(context, new DisplayLayout(context, display));
mDisplays.put(displayId, record);
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
@@ -154,24 +172,23 @@ public class DisplayController {
private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
synchronized (mDisplays) {
DisplayRecord dr = mDisplays.get(displayId);
final DisplayRecord dr = mDisplays.get(displayId);
if (dr == null) {
Slog.w(TAG, "Skipping Display Configuration change on non-added"
+ " display.");
return;
}
Display display = getDisplay(displayId);
final Display display = getDisplay(displayId);
if (display == null) {
Slog.w(TAG, "Skipping Display Configuration change on invalid"
+ " display. It may have been removed.");
return;
}
Context perDisplayContext = mContext;
if (displayId != Display.DEFAULT_DISPLAY) {
perDisplayContext = mContext.createDisplayContext(display);
}
dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
final Context perDisplayContext = (displayId == Display.DEFAULT_DISPLAY)
? mContext
: mContext.createDisplayContext(display);
final Context context = perDisplayContext.createConfigurationContext(newConfig);
dr.setDisplayLayout(context, new DisplayLayout(context, display));
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
displayId, newConfig);
@@ -219,9 +236,25 @@ public class DisplayController {
}
private static class DisplayRecord {
int mDisplayId;
Context mContext;
DisplayLayout mDisplayLayout;
private int mDisplayId;
private Context mContext;
private DisplayLayout mDisplayLayout;
private InsetsState mInsetsState = new InsetsState();
private DisplayRecord(int displayId) {
mDisplayId = displayId;
}
private void setDisplayLayout(Context context, DisplayLayout displayLayout) {
mContext = context;
mDisplayLayout = displayLayout;
mDisplayLayout.setInsets(mContext.getResources(), mInsetsState);
}
private void setInsets(InsetsState state) {
mInsetsState = state;
mDisplayLayout.setInsets(mContext.getResources(), state);
}
}
@BinderThread

View File

@@ -69,14 +69,17 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
protected final Executor mMainExecutor;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
private final DisplayInsetsController mDisplayInsetsController;
private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
public DisplayImeController(IWindowManager wmService, DisplayController displayController,
DisplayInsetsController displayInsetsController,
Executor mainExecutor, TransactionPool transactionPool) {
mWmService = wmService;
mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
mMainExecutor = mainExecutor;
mTransactionPool = transactionPool;
}
@@ -110,11 +113,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void onDisplayRemoved(int displayId) {
try {
mWmService.setDisplayWindowInsetsController(displayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
PerDisplay pd = mImePerDisplay.get(displayId);
if (pd == null) {
return;
}
pd.unregister();
mImePerDisplay.remove(displayId);
}
@@ -196,12 +199,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
/** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
public class PerDisplay {
public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
protected final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
new DisplayWindowInsetsControllerImpl();
InsetsSourceControl mImeSourceControl = null;
int mAnimationDirection = DIRECTION_NONE;
ValueAnimator mAnimation = null;
@@ -216,14 +217,15 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
public void register() {
try {
mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
}
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
}
protected void insetsChanged(InsetsState insetsState) {
public void unregister() {
mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
}
@Override
public void insetsChanged(InsetsState insetsState) {
if (mInsetsState.equals(insetsState)) {
return;
}
@@ -241,8 +243,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
@Override
@VisibleForTesting
protected void insetsControlChanged(InsetsState insetsState,
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
insetsChanged(insetsState);
InsetsSourceControl imeSourceControl = null;
@@ -303,7 +306,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
protected void showInsets(int types, boolean fromIme) {
@Override
public void showInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
@@ -311,8 +315,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
startAnimation(true /* show */, false /* forceRestart */);
}
protected void hideInsets(int types, boolean fromIme) {
@Override
public void hideInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
@@ -320,6 +324,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
startAnimation(false /* show */, false /* forceRestart */);
}
@Override
public void topFocusedWindowChanged(String packageName) {
// Do nothing
}
@@ -493,47 +498,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
dispatchVisibilityChanged(mDisplayId, isShowing);
}
}
@VisibleForTesting
@BinderThread
public class DisplayWindowInsetsControllerImpl
extends IDisplayWindowInsetsController.Stub {
@Override
public void topFocusedWindowChanged(String packageName) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.topFocusedWindowChanged(packageName);
});
}
@Override
public void insetsChanged(InsetsState insetsState) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.insetsChanged(insetsState);
});
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.insetsControlChanged(insetsState, activeControls);
});
}
@Override
public void showInsets(int types, boolean fromIme) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.showInsets(types, fromIme);
});
}
@Override
public void hideInsets(int types, boolean fromIme) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.hideInsets(types, fromIme);
});
}
}
}
void removeImeSurface() {

View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.common;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import androidx.annotation.BinderThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Manages insets from the core.
*/
public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayInsetsController";
private final IWindowManager mWmService;
private final ShellExecutor mMainExecutor;
private final DisplayController mDisplayController;
private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>();
private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners =
new SparseArray<>();
public DisplayInsetsController(IWindowManager wmService, DisplayController displayController,
ShellExecutor mainExecutor) {
mWmService = wmService;
mDisplayController = displayController;
mMainExecutor = mainExecutor;
}
/**
* Starts listening for insets for each display.
**/
public void initialize() {
mDisplayController.addDisplayWindowListener(this);
}
/**
* Adds a callback to listen for insets changes for a particular display. Note that the
* listener will not be updated with the existing state of the insets on that display.
*/
public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
if (listeners == null) {
listeners = new CopyOnWriteArrayList<>();
mListeners.put(displayId, listeners);
}
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
/**
* Removes a callback listening for insets changes from a particular display.
*/
public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
if (listeners == null) {
return;
}
listeners.remove(listener);
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
pd.register();
mInsetsPerDisplay.put(displayId, pd);
}
@Override
public void onDisplayRemoved(int displayId) {
PerDisplay pd = mInsetsPerDisplay.get(displayId);
if (pd == null) {
return;
}
pd.unregister();
mInsetsPerDisplay.remove(displayId);
}
/**
* An implementation of {@link IDisplayWindowInsetsController} for a given display id.
**/
public class PerDisplay {
private final int mDisplayId;
private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
new DisplayWindowInsetsControllerImpl();
public PerDisplay(int displayId) {
mDisplayId = displayId;
}
public void register() {
try {
mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
}
}
public void unregister() {
try {
mWmService.setDisplayWindowInsetsController(mDisplayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId);
}
}
private void insetsChanged(InsetsState insetsState) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
mDisplayController.updateDisplayInsets(mDisplayId, insetsState);
for (OnInsetsChangedListener listener : listeners) {
listener.insetsChanged(insetsState);
}
}
private void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
listener.insetsControlChanged(insetsState, activeControls);
}
}
private void showInsets(int types, boolean fromIme) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
listener.showInsets(types, fromIme);
}
}
private void hideInsets(int types, boolean fromIme) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
listener.hideInsets(types, fromIme);
}
}
private void topFocusedWindowChanged(String packageName) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
listener.topFocusedWindowChanged(packageName);
}
}
@BinderThread
private class DisplayWindowInsetsControllerImpl
extends IDisplayWindowInsetsController.Stub {
@Override
public void topFocusedWindowChanged(String packageName) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.topFocusedWindowChanged(packageName);
});
}
@Override
public void insetsChanged(InsetsState insetsState) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.insetsChanged(insetsState);
});
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.insetsControlChanged(insetsState, activeControls);
});
}
@Override
public void showInsets(int types, boolean fromIme) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.showInsets(types, fromIme);
});
}
@Override
public void hideInsets(int types, boolean fromIme) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.hideInsets(types, fromIme);
});
}
}
}
/**
* Gets notified whenever the insets change.
*
* @see IDisplayWindowInsetsController
*/
@ShellMainThread
public interface OnInsetsChangedListener {
/**
* Called when top focused window changes to determine whether or not to take over insets
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
* @param packageName: Passes the top package name
*/
void topFocusedWindowChanged(String packageName);
/**
* Called when the window insets configuration has changed.
*/
void insetsChanged(InsetsState insetsState);
/**
* Called when this window retrieved control over a specified set of insets sources.
*/
void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls);
/**
* Called when a set of insets source window should be shown by policy.
*
* @param types internal insets types (WindowInsets.Type.InsetsType) to show
* @param fromIme true if this request originated from IME (InputMethodService).
*/
void showInsets(int types, boolean fromIme);
/**
* Called when a set of insets source window should be hidden by policy.
*
* @param types internal insets types (WindowInsets.Type.InsetsType) to hide
* @param fromIme true if this request originated from IME (InputMethodService).
*/
void hideInsets(int types, boolean fromIme);
}
}

View File

@@ -25,6 +25,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON
import static android.util.RotationUtils.rotateBounds;
import static android.util.RotationUtils.rotateInsets;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -44,7 +45,10 @@ import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.Surface;
import android.view.WindowInsets;
import com.android.internal.R;
@@ -85,6 +89,7 @@ public class DisplayLayout {
private boolean mAllowSeamlessRotationDespiteNavBarMoving = false;
private boolean mNavigationBarCanMove = false;
private boolean mReverseDefaultRotation = false;
private InsetsState mInsetsState = new InsetsState();
@Override
public boolean equals(Object o) {
@@ -105,7 +110,8 @@ public class DisplayLayout {
== other.mAllowSeamlessRotationDespiteNavBarMoving
&& mNavigationBarCanMove == other.mNavigationBarCanMove
&& mReverseDefaultRotation == other.mReverseDefaultRotation
&& mNavBarFrameHeight == other.mNavBarFrameHeight;
&& mNavBarFrameHeight == other.mNavBarFrameHeight
&& Objects.equals(mInsetsState, other.mInsetsState);
}
@Override
@@ -113,7 +119,7 @@ public class DisplayLayout {
return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
mNavigationBarCanMove, mReverseDefaultRotation);
mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState);
}
/**
@@ -164,6 +170,7 @@ public class DisplayLayout {
mNavBarFrameHeight = dl.mNavBarFrameHeight;
mNonDecorInsets.set(dl.mNonDecorInsets);
mStableInsets.set(dl.mStableInsets);
mInsetsState.set(dl.mInsetsState, true /* copySources */);
}
private void init(DisplayInfo info, Resources res, boolean hasNavigationBar,
@@ -183,9 +190,17 @@ public class DisplayLayout {
recalcInsets(res);
}
/**
* Updates the current insets.
*/
public void setInsets(Resources res, InsetsState state) {
mInsetsState = state;
recalcInsets(res);
}
private void recalcInsets(Resources res) {
computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mUiMode, mNonDecorInsets,
mHasNavigationBar);
computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mInsetsState, mUiMode,
mNonDecorInsets, mHasNavigationBar);
mStableInsets.set(mNonDecorInsets);
if (mHasStatusBar) {
convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
@@ -259,7 +274,7 @@ public class DisplayLayout {
return mWidth > mHeight;
}
/** Get the navbar frame height (used by ime). */
/** Get the navbar frame (or window) height (used by ime). */
public int navBarFrameHeight() {
return mNavBarFrameHeight;
}
@@ -328,21 +343,29 @@ public class DisplayLayout {
* @param outInsets the insets to return
*/
static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth,
int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets,
boolean hasNavigationBar) {
int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
Rect outInsets, boolean hasNavigationBar) {
outInsets.setEmpty();
// Only navigation bar
if (hasNavigationBar) {
final InsetsSource extraNavBar = insetsState.getSource(ITYPE_EXTRA_NAVIGATION_BAR);
final boolean hasExtraNav = extraNavBar != null && extraNavBar.isVisible();
int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation);
int navBarSize =
getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode);
if (position == NAV_BAR_BOTTOM) {
outInsets.bottom = navBarSize;
outInsets.bottom = hasExtraNav
? Math.max(navBarSize, extraNavBar.getFrame().height())
: navBarSize;
} else if (position == NAV_BAR_RIGHT) {
outInsets.right = navBarSize;
outInsets.right = hasExtraNav
? Math.max(navBarSize, extraNavBar.getFrame().width())
: navBarSize;
} else if (position == NAV_BAR_LEFT) {
outInsets.left = navBarSize;
outInsets.left = hasExtraNav
? Math.max(navBarSize, extraNavBar.getFrame().width())
: navBarSize;
}
}
@@ -364,13 +387,13 @@ public class DisplayLayout {
* @param outInsets the insets to return
*/
static void computeStableInsets(Resources res, int displayRotation, int displayWidth,
int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets,
boolean hasNavigationBar, boolean hasStatusBar) {
int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
Rect outInsets, boolean hasNavigationBar, boolean hasStatusBar) {
outInsets.setEmpty();
// Navigation bar and status bar.
computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout,
uiMode, outInsets, hasNavigationBar);
insetsState, uiMode, outInsets, hasNavigationBar);
convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight,
hasStatusBar);
}

View File

@@ -58,7 +58,7 @@ public class DisplayImeControllerTest {
mT = mock(SurfaceControl.Transaction.class);
mMock = mock(IInputMethodManager.class);
mExecutor = spy(Runnable::run);
mPerDisplay = new DisplayImeController(null, null, mExecutor, new TransactionPool() {
mPerDisplay = new DisplayImeController(null, null, null, mExecutor, new TransactionPool() {
@Override
public SurfaceControl.Transaction acquire() {
return mT;

View File

@@ -0,0 +1,193 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.common;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.TestShellExecutor;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
public class DisplayInsetsControllerTest {
private static final int SECOND_DISPLAY = DEFAULT_DISPLAY + 10;
@Mock
private IWindowManager mWm;
@Mock
private DisplayController mDisplayController;
private DisplayInsetsController mController;
private SparseArray<IDisplayWindowInsetsController> mInsetsControllersByDisplayId;
private TestShellExecutor mExecutor;
private ArgumentCaptor<Integer> mDisplayIdCaptor;
private ArgumentCaptor<IDisplayWindowInsetsController> mInsetsControllerCaptor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mExecutor = new TestShellExecutor();
mInsetsControllersByDisplayId = new SparseArray<>();
mDisplayIdCaptor = ArgumentCaptor.forClass(Integer.class);
mInsetsControllerCaptor = ArgumentCaptor.forClass(IDisplayWindowInsetsController.class);
mController = new DisplayInsetsController(mWm, mDisplayController, mExecutor);
addDisplay(DEFAULT_DISPLAY);
}
@Test
public void testOnDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
throws RemoteException {
addDisplay(SECOND_DISPLAY);
verify(mWm).setDisplayWindowInsetsController(eq(SECOND_DISPLAY), notNull());
}
@Test
public void testOnDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
throws RemoteException {
addDisplay(SECOND_DISPLAY);
removeDisplay(SECOND_DISPLAY);
verify(mWm).setDisplayWindowInsetsController(SECOND_DISPLAY, null);
}
@Test
public void testPerDisplayListenerCallback() throws RemoteException {
TrackedListener defaultListener = new TrackedListener();
TrackedListener secondListener = new TrackedListener();
addDisplay(SECOND_DISPLAY);
mController.addInsetsChangedListener(DEFAULT_DISPLAY, defaultListener);
mController.addInsetsChangedListener(SECOND_DISPLAY, secondListener);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).topFocusedWindowChanged(null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false);
mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false);
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
assertTrue(defaultListener.insetsChangedCount == 1);
assertTrue(defaultListener.insetsControlChangedCount == 1);
assertTrue(defaultListener.showInsetsCount == 1);
assertTrue(defaultListener.hideInsetsCount == 1);
assertTrue(secondListener.topFocusedWindowChangedCount == 0);
assertTrue(secondListener.insetsChangedCount == 0);
assertTrue(secondListener.insetsControlChangedCount == 0);
assertTrue(secondListener.showInsetsCount == 0);
assertTrue(secondListener.hideInsetsCount == 0);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).topFocusedWindowChanged(null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false);
mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false);
mExecutor.flushAll();
assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
assertTrue(defaultListener.insetsChangedCount == 1);
assertTrue(defaultListener.insetsControlChangedCount == 1);
assertTrue(defaultListener.showInsetsCount == 1);
assertTrue(defaultListener.hideInsetsCount == 1);
assertTrue(secondListener.topFocusedWindowChangedCount == 1);
assertTrue(secondListener.insetsChangedCount == 1);
assertTrue(secondListener.insetsControlChangedCount == 1);
assertTrue(secondListener.showInsetsCount == 1);
assertTrue(secondListener.hideInsetsCount == 1);
}
private void addDisplay(int displayId) throws RemoteException {
mController.onDisplayAdded(displayId);
verify(mWm, times(mInsetsControllersByDisplayId.size() + 1))
.setDisplayWindowInsetsController(mDisplayIdCaptor.capture(),
mInsetsControllerCaptor.capture());
List<Integer> displayIds = mDisplayIdCaptor.getAllValues();
List<IDisplayWindowInsetsController> insetsControllers =
mInsetsControllerCaptor.getAllValues();
for (int i = 0; i < displayIds.size(); i++) {
mInsetsControllersByDisplayId.put(displayIds.get(i), insetsControllers.get(i));
}
}
private void removeDisplay(int displayId) {
mController.onDisplayRemoved(displayId);
mInsetsControllersByDisplayId.remove(displayId);
}
private static class TrackedListener implements
DisplayInsetsController.OnInsetsChangedListener {
int topFocusedWindowChangedCount = 0;
int insetsChangedCount = 0;
int insetsControlChangedCount = 0;
int showInsetsCount = 0;
int hideInsetsCount = 0;
@Override
public void topFocusedWindowChanged(String packageName) {
topFocusedWindowChangedCount++;
}
@Override
public void insetsChanged(InsetsState insetsState) {
insetsChangedCount++;
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
insetsControlChangedCount++;
}
@Override
public void showInsets(int types, boolean fromIme) {
showInsetsCount++;
}
@Override
public void hideInsets(int types, boolean fromIme) {
hideInsetsCount++;
}
}
}

View File

@@ -685,6 +685,7 @@ public class ShellTransitionTests {
// No remote stuff happening, so this can't be hit
}
DisplayController out = new DisplayController(mContext, mockWM, mMainExecutor);
out.initialize();
try {
displayListener[0].onDisplayAdded(DEFAULT_DISPLAY);
mMainExecutor.flushAll();

View File

@@ -25,6 +25,7 @@ import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
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.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -58,10 +59,10 @@ public class TvWMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
return new DisplayImeController(wmService, displayController, mainExecutor,
transactionPool);
DisplayController displayController, DisplayInsetsController displayInsetsController,
@ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) {
return new DisplayImeController(wmService, displayController, displayInsetsController,
mainExecutor, transactionPool);
}
//

View File

@@ -44,6 +44,7 @@ import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
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.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
@@ -108,6 +109,14 @@ public abstract class WMShellBaseModule {
return new DisplayController(context, wmService, mainExecutor);
}
@WMSingleton
@Provides
static DisplayInsetsController provideDisplayInsetsController( IWindowManager wmService,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor) {
return new DisplayInsetsController(wmService, displayController, mainExecutor);
}
@WMSingleton
@Provides
static DisplayLayout provideDisplayLayout() {
@@ -452,7 +461,9 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController,
static ShellInitImpl provideShellInitImpl(DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
@@ -465,7 +476,9 @@ public abstract class WMShellBaseModule {
Transitions transitions,
StartingWindowController startingWindow,
@ShellMainThread ShellExecutor mainExecutor) {
return new ShellInitImpl(displayImeController,
return new ShellInitImpl(displayController,
displayImeController,
displayInsetsController,
dragAndDropController,
shellTaskOrganizer,
bubblesOptional,

View File

@@ -28,6 +28,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
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.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -83,10 +84,11 @@ public class WMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
DisplayController displayController, DisplayInsetsController displayInsetsController,
@ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
return new DisplayImeController(wmService, displayController, mainExecutor,
transactionPool);
return new DisplayImeController(wmService, displayController, displayInsetsController,
mainExecutor, transactionPool);
}
//