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:
Jorim Jaggi
2019-01-02 23:21:49 +01:00
committed by Tiger Huang
parent 2d46334c82
commit 2862047fd7
13 changed files with 417 additions and 99 deletions

View File

@@ -86,7 +86,7 @@ public class InsetsAnimationControlImplTest {
}
@AfterClass
public static void tearDownOnce() throws Exception {
public static void tearDownOnce() {
sInsetsModeSession.close();
}

View File

@@ -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;
}

View File

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

View File

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

View File

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

View 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;
}
}

View File

@@ -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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,7 @@ public class InsetsModeSession implements AutoCloseable {
}
@Override
public void close() throws Exception {
public void close() {
ViewRootImpl.sNewInsetsMode = mOldMode;
}
}