Window Manager Flag Migration (2/n)
Adds policy to decide when focused window can control bars. And also generalizes control target for: a) Normal case: The focused window is the control target. b) Transient bar case: The control target is a special object in WM that controls the transient animation. Bug: 118118435 Test: atest InsetsPolicyTest InsetsStateControllerTest WindowStateTests Change-Id: I4819d85eec745d56abe23dbca0905979da854e5e
This commit is contained in:
@@ -86,7 +86,7 @@ public class InsetsAnimationControlImplTest {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownOnce() throws Exception {
|
||||
public static void tearDownOnce() {
|
||||
sInsetsModeSession.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.View;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
@@ -89,6 +90,10 @@ public class BarController {
|
||||
}
|
||||
|
||||
void setWindow(WindowState win) {
|
||||
if (ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL) {
|
||||
// BarController gets replaced with InsetsPolicy in the full insets mode.
|
||||
return;
|
||||
}
|
||||
mWin = win;
|
||||
}
|
||||
|
||||
|
||||
@@ -549,6 +549,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
private final PointerEventDispatcher mPointerEventDispatcher;
|
||||
|
||||
private final InsetsStateController mInsetsStateController;
|
||||
private final InsetsPolicy mInsetsPolicy;
|
||||
|
||||
/** @see #getParentWindow() */
|
||||
private WindowState mParentWindow;
|
||||
@@ -968,6 +969,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
mWmService.mAnimator.addDisplayLocked(mDisplayId);
|
||||
mInputMonitor = new InputMonitor(service, mDisplayId);
|
||||
mInsetsStateController = new InsetsStateController(this);
|
||||
mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
|
||||
}
|
||||
|
||||
boolean isReady() {
|
||||
@@ -1126,6 +1128,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
return mInsetsStateController;
|
||||
}
|
||||
|
||||
InsetsPolicy getInsetsPolicy() {
|
||||
return mInsetsPolicy;
|
||||
}
|
||||
|
||||
@Surface.Rotation
|
||||
int getRotation() {
|
||||
return mDisplayRotation.getRotation();
|
||||
|
||||
@@ -1029,6 +1029,14 @@ public class DisplayPolicy {
|
||||
displayFrames.mDisplayCutoutSafe.top);
|
||||
}
|
||||
|
||||
WindowState getStatusBar() {
|
||||
return mStatusBar;
|
||||
}
|
||||
|
||||
WindowState getNavigationBar() {
|
||||
return mNavigationBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control the animation to run when a window's state changes. Return a
|
||||
* non-0 number to force the animation to a specific resource ID, or 0
|
||||
@@ -3108,8 +3116,7 @@ public class DisplayPolicy {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(
|
||||
mTopFullscreenOpaqueWindowState);
|
||||
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
|
||||
|
||||
int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
|
||||
& ~mResettingSystemUiFlags
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.server.wm;
|
||||
|
||||
/**
|
||||
* Generalization of an object that can control insets state.
|
||||
*/
|
||||
interface InsetsControlTarget {
|
||||
void notifyInsetsControlChanged();
|
||||
}
|
||||
103
services/core/java/com/android/server/wm/InsetsPolicy.java
Normal file
103
services/core/java/com/android/server/wm/InsetsPolicy.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Policy that implements who gets control over the windows generating insets.
|
||||
*/
|
||||
class InsetsPolicy {
|
||||
|
||||
private final InsetsStateController mStateController;
|
||||
private final DisplayContent mDisplayContent;
|
||||
private final DisplayPolicy mPolicy;
|
||||
|
||||
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
|
||||
mStateController = stateController;
|
||||
mDisplayContent = displayContent;
|
||||
mPolicy = displayContent.getDisplayPolicy();
|
||||
}
|
||||
|
||||
/** Updates the target which can control system bars. */
|
||||
void updateBarControlTarget(@Nullable WindowState focusedWin) {
|
||||
mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin),
|
||||
getNavControlTarget(focusedWin));
|
||||
}
|
||||
|
||||
private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) {
|
||||
if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) {
|
||||
return null;
|
||||
}
|
||||
return focusedWin;
|
||||
}
|
||||
|
||||
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
|
||||
if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) {
|
||||
return null;
|
||||
}
|
||||
return focusedWin;
|
||||
}
|
||||
|
||||
private boolean isStatusBarForciblyVisible() {
|
||||
final WindowState statusBar = mPolicy.getStatusBar();
|
||||
if (statusBar == null) {
|
||||
return false;
|
||||
}
|
||||
final int privateFlags = statusBar.mAttrs.privateFlags;
|
||||
|
||||
// TODO: Pretend to the app that it's still able to control it?
|
||||
if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
|
||||
return true;
|
||||
}
|
||||
if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isNavBarForciblyVisible() {
|
||||
final WindowState statusBar = mPolicy.getStatusBar();
|
||||
if (statusBar == null) {
|
||||
return false;
|
||||
}
|
||||
if ((statusBar.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean areSystemBarsForciblyVisible() {
|
||||
final boolean isDockedStackVisible =
|
||||
mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
|
||||
final boolean isFreeformStackVisible =
|
||||
mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM);
|
||||
final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
|
||||
|
||||
// We need to force system bars when the docked stack is visible, when the freeform stack
|
||||
// is visible but also when we are resizing for the transitions when docked stack
|
||||
// visibility changes.
|
||||
return isDockedStackVisible || isFreeformStackVisible || isResizing;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class InsetsSourceProvider {
|
||||
private final DisplayContent mDisplayContent;
|
||||
private final InsetsStateController mStateController;
|
||||
private @Nullable InsetsSourceControl mControl;
|
||||
private @Nullable WindowState mControllingWin;
|
||||
private @Nullable InsetsControlTarget mControlTarget;
|
||||
private @Nullable ControlAdapter mAdapter;
|
||||
private WindowState mWin;
|
||||
private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
|
||||
@@ -119,8 +119,8 @@ class InsetsSourceProvider {
|
||||
mSource.setFrame(new Rect());
|
||||
} else {
|
||||
mWin.setInsetProvider(this);
|
||||
if (mControllingWin != null) {
|
||||
updateControlForTarget(mControllingWin, true /* force */);
|
||||
if (mControlTarget != null) {
|
||||
updateControlForTarget(mControlTarget, true /* force */);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,19 +143,19 @@ class InsetsSourceProvider {
|
||||
if (mControl != null) {
|
||||
final Rect frame = mWin.getWindowFrames().mFrame;
|
||||
if (mControl.setSurfacePosition(frame.left, frame.top)) {
|
||||
mStateController.notifyControlChanged(mControllingWin);
|
||||
mStateController.notifyControlChanged(mControlTarget);
|
||||
}
|
||||
}
|
||||
setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
|
||||
&& !mWin.mGivenInsetsPending);
|
||||
}
|
||||
|
||||
void updateControlForTarget(@Nullable WindowState target, boolean force) {
|
||||
void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
|
||||
if (mWin == null) {
|
||||
mControllingWin = target;
|
||||
mControlTarget = target;
|
||||
return;
|
||||
}
|
||||
if (target == mControllingWin && !force) {
|
||||
if (target == mControlTarget && !force) {
|
||||
return;
|
||||
}
|
||||
if (target == null) {
|
||||
@@ -167,13 +167,13 @@ class InsetsSourceProvider {
|
||||
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
|
||||
mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
|
||||
!mClientVisible /* hidden */);
|
||||
mControllingWin = target;
|
||||
mControlTarget = target;
|
||||
mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash,
|
||||
new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
|
||||
}
|
||||
|
||||
boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
|
||||
if (mControllingWin != caller || modifiedSource.isVisible() == mClientVisible) {
|
||||
if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
|
||||
return false;
|
||||
}
|
||||
setClientVisible(modifiedSource.isVisible());
|
||||
@@ -232,10 +232,10 @@ class InsetsSourceProvider {
|
||||
@Override
|
||||
public void onAnimationCancelled(SurfaceControl animationLeash) {
|
||||
if (mAdapter == this) {
|
||||
mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
|
||||
mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
|
||||
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
|
||||
mControl = null;
|
||||
mControllingWin = null;
|
||||
mControlTarget = null;
|
||||
mAdapter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.InsetsState.InternalInsetType;
|
||||
import static android.view.InsetsState.TYPE_IME;
|
||||
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
|
||||
import static android.view.InsetsState.TYPE_TOP_BAR;
|
||||
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
|
||||
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
|
||||
import static android.view.ViewRootImpl.sNewInsetsMode;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -31,7 +29,6 @@ import android.util.SparseArray;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.ViewRootImpl;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
@@ -46,10 +43,11 @@ class InsetsStateController {
|
||||
private final InsetsState mState = new InsetsState();
|
||||
private final DisplayContent mDisplayContent;
|
||||
|
||||
private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
|
||||
private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>();
|
||||
private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>();
|
||||
private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>();
|
||||
private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
|
||||
private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
|
||||
new ArrayMap<>();
|
||||
private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
|
||||
private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
|
||||
|
||||
private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
|
||||
if (w.isVisible()) {
|
||||
@@ -87,15 +85,15 @@ class InsetsStateController {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
|
||||
ArrayList<Integer> controlled = mWinControlTypeMap.get(target);
|
||||
@Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) {
|
||||
ArrayList<Integer> controlled = mControlTargetTypeMap.get(target);
|
||||
if (controlled == null) {
|
||||
return null;
|
||||
}
|
||||
final int size = controlled.size();
|
||||
final InsetsSourceControl[] result = new InsetsSourceControl[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
result[i] = mControllers.get(controlled.get(i)).getControl();
|
||||
result[i] = mProviders.get(controlled.get(i)).getControl();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -103,8 +101,8 @@ class InsetsStateController {
|
||||
/**
|
||||
* @return The provider of a specific type.
|
||||
*/
|
||||
InsetsSourceProvider getSourceProvider(int type) {
|
||||
return mControllers.computeIfAbsent(type,
|
||||
InsetsSourceProvider getSourceProvider(@InternalInsetType int type) {
|
||||
return mProviders.computeIfAbsent(type,
|
||||
key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
|
||||
}
|
||||
|
||||
@@ -113,8 +111,8 @@ class InsetsStateController {
|
||||
*/
|
||||
void onPostLayout() {
|
||||
mState.setDisplayFrame(mDisplayContent.getBounds());
|
||||
for (int i = mControllers.size() - 1; i>= 0; i--) {
|
||||
mControllers.valueAt(i).onPostLayout();
|
||||
for (int i = mProviders.size() - 1; i >= 0; i--) {
|
||||
mProviders.valueAt(i).onPostLayout();
|
||||
}
|
||||
if (!mLastState.equals(mState)) {
|
||||
mLastState.set(mState, true /* copySources */);
|
||||
@@ -126,7 +124,7 @@ class InsetsStateController {
|
||||
boolean changed = false;
|
||||
for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
|
||||
final InsetsSource source = state.sourceAt(i);
|
||||
final InsetsSourceProvider provider = mControllers.get(source.getType());
|
||||
final InsetsSourceProvider provider = mProviders.get(source.getType());
|
||||
if (provider == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -137,75 +135,77 @@ class InsetsStateController {
|
||||
}
|
||||
}
|
||||
|
||||
void onImeTargetChanged(@Nullable WindowState imeTarget) {
|
||||
void onImeTargetChanged(@Nullable InsetsControlTarget imeTarget) {
|
||||
onControlChanged(TYPE_IME, imeTarget);
|
||||
notifyPendingInsetsControlChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the top opaque fullscreen window that is able to control the system bars changes.
|
||||
* Called when the focused window that is able to control the system bars changes.
|
||||
*
|
||||
* @param controllingWindow The window that is now able to control the system bars appearance
|
||||
* and visibility.
|
||||
* @param topControlling The target that is now able to control the top bar appearance
|
||||
* and visibility.
|
||||
* @param navControlling The target that is now able to control the nav bar appearance
|
||||
* and visibility.
|
||||
*/
|
||||
void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
|
||||
// TODO: Apply policy that determines whether controllingWindow is able to control system
|
||||
// bars
|
||||
|
||||
// TODO: Depending on the form factor, mapping is different
|
||||
onControlChanged(TYPE_TOP_BAR, controllingWindow);
|
||||
onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
|
||||
void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling,
|
||||
@Nullable InsetsControlTarget navControlling) {
|
||||
onControlChanged(TYPE_TOP_BAR, topControlling);
|
||||
onControlChanged(TYPE_NAVIGATION_BAR, navControlling);
|
||||
notifyPendingInsetsControlChanged();
|
||||
}
|
||||
|
||||
void notifyControlRevoked(@NonNull WindowState previousControllingWin,
|
||||
void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget,
|
||||
InsetsSourceProvider provider) {
|
||||
removeFromControlMaps(previousControllingWin, provider.getSource().getType());
|
||||
removeFromControlMaps(previousControlTarget, provider.getSource().getType());
|
||||
}
|
||||
|
||||
private void onControlChanged(int type, @Nullable WindowState win) {
|
||||
final WindowState previous = mTypeWinControlMap.get(type);
|
||||
if (win == previous) {
|
||||
private void onControlChanged(@InternalInsetType int type,
|
||||
@Nullable InsetsControlTarget target) {
|
||||
final InsetsControlTarget previous = mTypeControlTargetMap.get(type);
|
||||
if (target == previous) {
|
||||
return;
|
||||
}
|
||||
final InsetsSourceProvider controller = getSourceProvider(type);
|
||||
if (controller == null) {
|
||||
final InsetsSourceProvider provider = getSourceProvider(type);
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
if (!controller.isControllable()) {
|
||||
if (!provider.isControllable()) {
|
||||
return;
|
||||
}
|
||||
controller.updateControlForTarget(win, false /* force */);
|
||||
provider.updateControlForTarget(target, false /* force */);
|
||||
if (previous != null) {
|
||||
removeFromControlMaps(previous, type);
|
||||
mPendingControlChanged.add(previous);
|
||||
}
|
||||
if (win != null) {
|
||||
addToControlMaps(win, type);
|
||||
mPendingControlChanged.add(win);
|
||||
if (target != null) {
|
||||
addToControlMaps(target, type);
|
||||
mPendingControlChanged.add(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromControlMaps(@NonNull WindowState win, int type) {
|
||||
final ArrayList<Integer> array = mWinControlTypeMap.get(win);
|
||||
private void removeFromControlMaps(@NonNull InsetsControlTarget target,
|
||||
@InternalInsetType int type) {
|
||||
final ArrayList<Integer> array = mControlTargetTypeMap.get(target);
|
||||
if (array == null) {
|
||||
return;
|
||||
}
|
||||
array.remove((Integer) type);
|
||||
if (array.isEmpty()) {
|
||||
mWinControlTypeMap.remove(win);
|
||||
mControlTargetTypeMap.remove(target);
|
||||
}
|
||||
mTypeWinControlMap.remove(type);
|
||||
mTypeControlTargetMap.remove(type);
|
||||
}
|
||||
|
||||
private void addToControlMaps(@NonNull WindowState win, int type) {
|
||||
final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win,
|
||||
private void addToControlMaps(@NonNull InsetsControlTarget target,
|
||||
@InternalInsetType int type) {
|
||||
final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target,
|
||||
key -> new ArrayList<>());
|
||||
array.add(type);
|
||||
mTypeWinControlMap.put(type, win);
|
||||
mTypeControlTargetMap.put(type, target);
|
||||
}
|
||||
|
||||
void notifyControlChanged(WindowState target) {
|
||||
void notifyControlChanged(InsetsControlTarget target) {
|
||||
mPendingControlChanged.add(target);
|
||||
notifyPendingInsetsControlChanged();
|
||||
}
|
||||
@@ -216,8 +216,8 @@ class InsetsStateController {
|
||||
}
|
||||
mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
|
||||
for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
|
||||
final WindowState controllingWin = mPendingControlChanged.valueAt(i);
|
||||
controllingWin.notifyInsetsControlChanged();
|
||||
final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
|
||||
controlTarget.notifyInsetsControlChanged();
|
||||
}
|
||||
mPendingControlChanged.clear();
|
||||
});
|
||||
@@ -231,10 +231,10 @@ class InsetsStateController {
|
||||
pw.println(prefix + "WindowInsetsStateController");
|
||||
mState.dump(prefix + " ", pw);
|
||||
pw.println(prefix + " " + "Control map:");
|
||||
for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
|
||||
for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) {
|
||||
pw.print(prefix + " ");
|
||||
pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
|
||||
+ mTypeWinControlMap.valueAt(i));
|
||||
pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> "
|
||||
+ mTypeControlTargetMap.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,8 @@ import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/** A window in the window manager. */
|
||||
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
|
||||
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
|
||||
InsetsControlTarget {
|
||||
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;
|
||||
|
||||
// The minimal size of a window within the usable area of the freeform stack.
|
||||
@@ -3329,7 +3330,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
}
|
||||
}
|
||||
|
||||
void notifyInsetsControlChanged() {
|
||||
@Override
|
||||
public void notifyInsetsControlChanged() {
|
||||
final InsetsStateController stateController =
|
||||
getDisplayContent().getInsetsStateController();
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.test.InsetsModeSession;
|
||||
|
||||
import androidx.test.filters.FlakyTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
|
||||
@Presubmit
|
||||
@RunWith(WindowTestRunner.class)
|
||||
public class InsetsPolicyTest extends WindowTestsBase {
|
||||
private static InsetsModeSession sInsetsModeSession;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpOnce() {
|
||||
// To let the insets provider control the insets visibility, the insets mode has to be
|
||||
// NEW_INSETS_MODE_FULL.
|
||||
sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownOnce() {
|
||||
sInsetsModeSession.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_regular() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar");
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
|
||||
|
||||
// The app can control both system bars.
|
||||
assertNotNull(controls);
|
||||
assertEquals(2, controls.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_dockedStackVisible() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar");
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
final WindowState win = createWindowOnStack(null, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
|
||||
ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
|
||||
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
|
||||
|
||||
// The app must not control any system bars.
|
||||
assertNull(controls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_freeformStackVisible() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar");
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
final WindowState win = createWindowOnStack(null, WINDOWING_MODE_FREEFORM,
|
||||
ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
|
||||
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
|
||||
|
||||
// The app must not control any bars.
|
||||
assertNull(controls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_dockedDividerControllerResizing() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar");
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
mDisplayContent.getDockedDividerController().setResizing(true);
|
||||
|
||||
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
|
||||
|
||||
// The app must not control any system bars.
|
||||
assertNull(controls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_keyguard() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |= PRIVATE_FLAG_KEYGUARD;
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
|
||||
|
||||
// The app must not control the top bar.
|
||||
assertNotNull(controls);
|
||||
assertEquals(1, controls.length);
|
||||
}
|
||||
|
||||
// TODO: adjust this test if we pretend to the app that it's still able to control it.
|
||||
@Test
|
||||
public void testControlsForDispatch_forceStatusBarVisibleTransparent() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |=
|
||||
PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
|
||||
|
||||
// The app must not control the top bar.
|
||||
assertNotNull(controls);
|
||||
assertEquals(1, controls.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_statusBarForceShowNavigation() {
|
||||
addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |=
|
||||
PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
|
||||
|
||||
// The app must not control the navigation bar.
|
||||
assertNotNull(controls);
|
||||
assertEquals(1, controls.length);
|
||||
}
|
||||
|
||||
private WindowState addWindow(int type, String name) {
|
||||
final WindowState win = createWindow(null, type, name);
|
||||
mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
|
||||
return win;
|
||||
}
|
||||
|
||||
private InsetsSourceControl[] addAppWindowAndGetControlsForDispatch() {
|
||||
return addWindowAndGetControlsForDispatch(addWindow(TYPE_APPLICATION, "app"));
|
||||
}
|
||||
|
||||
private InsetsSourceControl[] addWindowAndGetControlsForDispatch(WindowState win) {
|
||||
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
|
||||
return mDisplayContent.getInsetsStateController().getControlsForDispatch(win);
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,15 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.test.InsetsModeSession;
|
||||
|
||||
import androidx.test.filters.FlakyTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -37,90 +39,91 @@ import androidx.test.filters.SmallTest;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
|
||||
@Presubmit
|
||||
@RunWith(WindowTestRunner.class)
|
||||
public class InsetsStateControllerTest extends WindowTestsBase {
|
||||
private static int sPreviousNewInsetsMode;
|
||||
private static InsetsModeSession sInsetsModeSession;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpOnce() {
|
||||
// TODO: Make use of SettingsSession when it becomes feasible for this.
|
||||
sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode;
|
||||
// To let the insets provider control the insets visibility, the insets mode has to be
|
||||
// NEW_INSETS_MODE_FULL.
|
||||
ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
|
||||
sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownOnce() {
|
||||
ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode;
|
||||
sInsetsModeSession.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@FlakyTest(bugId = 131005232)
|
||||
public void testStripForDispatch_notOwn() {
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
|
||||
topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
|
||||
assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
|
||||
}
|
||||
|
||||
@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
|
||||
@Test
|
||||
public void testStripForDispatch_own() {
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
|
||||
mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
|
||||
.setWindow(topBar, null);
|
||||
topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
|
||||
assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar));
|
||||
final InsetsState state = getController().getInsetsForDispatch(topBar);
|
||||
for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
|
||||
final InsetsSource source = state.sourceAt(i);
|
||||
assertNotEquals(TYPE_TOP_BAR, source.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
|
||||
@Test
|
||||
public void testStripForDispatch_navBar() {
|
||||
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
|
||||
final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
|
||||
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
|
||||
getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
|
||||
getController().getSourceProvider(TYPE_IME).setWindow(ime, null);
|
||||
assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar));
|
||||
assertEquals(0, getController().getInsetsForDispatch(navBar).getSourcesCount());
|
||||
}
|
||||
|
||||
@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
|
||||
@Test
|
||||
public void testBarControllingWinChanged() {
|
||||
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
|
||||
getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
|
||||
getController().onBarControllingWindowChanged(app);
|
||||
getController().onBarControlTargetChanged(app, app);
|
||||
InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
|
||||
assertEquals(2, controls.length);
|
||||
}
|
||||
|
||||
@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
|
||||
@Test
|
||||
public void testControlRevoked() {
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
|
||||
getController().onBarControllingWindowChanged(app);
|
||||
getController().onBarControlTargetChanged(app, null);
|
||||
assertNotNull(getController().getControlsForDispatch(app));
|
||||
getController().onBarControllingWindowChanged(null);
|
||||
getController().onBarControlTargetChanged(null, null);
|
||||
assertNull(getController().getControlsForDispatch(app));
|
||||
}
|
||||
|
||||
@FlakyTest(bugId = 124088319)
|
||||
@Test
|
||||
public void testControlRevoked_animation() {
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
|
||||
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
|
||||
getController().onBarControllingWindowChanged(app);
|
||||
getController().onBarControlTargetChanged(app, null);
|
||||
assertNotNull(getController().getControlsForDispatch(app));
|
||||
topBar.cancelAnimation();
|
||||
assertNull(getController().getControlsForDispatch(app));
|
||||
|
||||
@@ -403,7 +403,7 @@ public class WindowStateTests extends WindowTestsBase {
|
||||
assertTrue(topBar.isVisible());
|
||||
mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
|
||||
.setWindow(topBar, null);
|
||||
mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app);
|
||||
mDisplayContent.getInsetsStateController().onBarControlTargetChanged(app, app);
|
||||
mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
|
||||
.onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
|
||||
waitUntilHandlersIdle();
|
||||
|
||||
@@ -31,7 +31,7 @@ public class InsetsModeSession implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
public void close() {
|
||||
ViewRootImpl.sNewInsetsMode = mOldMode;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user