Merge "Support for WM.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR."
This commit is contained in:
committed by
Android (Google) Code Review
commit
a674fafb62
@@ -1422,7 +1422,7 @@ public interface WindowManager extends ViewManager {
|
||||
* this window is visible.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
|
||||
@RequiresPermission(permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
|
||||
public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
|
||||
|
||||
/**
|
||||
@@ -1442,6 +1442,15 @@ public interface WindowManager extends ViewManager {
|
||||
@RequiresPermission(permission.DEVICE_POWER)
|
||||
public static final int PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN = 0x00200000;
|
||||
|
||||
/**
|
||||
* Flag to indicate that this window should be considered a screen decoration similar to the
|
||||
* nav bar and status bar. This will cause this window to affect the window insets reported
|
||||
* to other windows when it is visible.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(permission.STATUS_BAR_SERVICE)
|
||||
public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000;
|
||||
|
||||
/**
|
||||
* Control flags that are private to the platform.
|
||||
* @hide
|
||||
@@ -1526,7 +1535,11 @@ public interface WindowManager extends ViewManager {
|
||||
@ViewDebug.FlagToString(
|
||||
mask = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
|
||||
equals = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
|
||||
name = "ACQUIRES_SLEEP_TOKEN")
|
||||
name = "ACQUIRES_SLEEP_TOKEN"),
|
||||
@ViewDebug.FlagToString(
|
||||
mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
|
||||
equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
|
||||
name = "IS_SCREEN_DECOR")
|
||||
})
|
||||
@TestApi
|
||||
public int privateFlags;
|
||||
|
||||
@@ -758,7 +758,8 @@ public interface WindowManagerPolicy {
|
||||
* @param attrs The window layout parameters to be modified. These values
|
||||
* are modified in-place.
|
||||
*/
|
||||
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs);
|
||||
public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
|
||||
boolean hasStatusBarServicePermission);
|
||||
|
||||
/**
|
||||
* After the window manager has computed the current configuration based
|
||||
@@ -1172,13 +1173,13 @@ public interface WindowManagerPolicy {
|
||||
/**
|
||||
* Called when layout of the windows is about to start.
|
||||
*
|
||||
* @param isDefaultDisplay true if window is on {@link Display#DEFAULT_DISPLAY}.
|
||||
* @param displayId Id of the display we are doing layout on.
|
||||
* @param displayWidth The current full width of the screen.
|
||||
* @param displayHeight The current full height of the screen.
|
||||
* @param displayRotation The current rotation being applied to the base window.
|
||||
* @param uiMode The current uiMode in configuration.
|
||||
*/
|
||||
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
|
||||
public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
|
||||
int displayRotation, int uiMode);
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,6 +66,7 @@ import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
|
||||
@@ -198,6 +199,7 @@ import android.service.dreams.IDreamManager;
|
||||
import android.service.vr.IPersistentVrStateCallbacks;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
@@ -456,6 +458,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
private AccessibilityShortcutController mAccessibilityShortcutController;
|
||||
|
||||
boolean mSafeMode;
|
||||
private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
|
||||
WindowState mStatusBar = null;
|
||||
int mStatusBarHeight;
|
||||
WindowState mNavigationBar = null;
|
||||
@@ -2613,7 +2616,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
|
||||
public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
|
||||
boolean hasStatusBarServicePermission) {
|
||||
|
||||
final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
|
||||
if (mScreenDecorWindows.contains(win)) {
|
||||
if (!isScreenDecor) {
|
||||
// No longer has the flag set, so remove from the set.
|
||||
mScreenDecorWindows.remove(win);
|
||||
}
|
||||
} else if (isScreenDecor && hasStatusBarServicePermission) {
|
||||
mScreenDecorWindows.add(win);
|
||||
}
|
||||
|
||||
switch (attrs.type) {
|
||||
case TYPE_SYSTEM_OVERLAY:
|
||||
case TYPE_SECURE_SYSTEM_OVERLAY:
|
||||
@@ -3074,6 +3089,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
*/
|
||||
@Override
|
||||
public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
|
||||
|
||||
if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.STATUS_BAR_SERVICE,
|
||||
"PhoneWindowManager");
|
||||
mScreenDecorWindows.add(win);
|
||||
}
|
||||
|
||||
switch (attrs.type) {
|
||||
case TYPE_STATUS_BAR:
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
@@ -3125,6 +3148,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
mNavigationBar = null;
|
||||
mNavigationBarController.setWindow(null);
|
||||
}
|
||||
mScreenDecorWindows.remove(win);
|
||||
}
|
||||
|
||||
static final boolean PRINT_ANIM = false;
|
||||
@@ -4395,8 +4419,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
|
||||
int displayRotation, int uiMode) {
|
||||
public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
|
||||
int displayRotation, int uiMode) {
|
||||
final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY;
|
||||
mDisplayRotation = displayRotation;
|
||||
final int overscanLeft, overscanTop, overscanRight, overscanBottom;
|
||||
if (isDefaultDisplay) {
|
||||
@@ -4524,6 +4549,71 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
updateSystemUiVisibilityLw();
|
||||
}
|
||||
}
|
||||
layoutScreenDecorWindows(displayId, displayWidth, displayHeight, pf, df, dcf);
|
||||
}
|
||||
|
||||
private void layoutScreenDecorWindows(int displayId, int displayWidth, int displayHeight,
|
||||
Rect pf, Rect df, Rect dcf) {
|
||||
if (mScreenDecorWindows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
|
||||
final WindowState w = mScreenDecorWindows.valueAt(i);
|
||||
if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
|
||||
// Skip if not on the same display or not visible.
|
||||
continue;
|
||||
}
|
||||
|
||||
w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
|
||||
df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
|
||||
df /* stableFrame */, df /* outsetFrame */);
|
||||
final Rect frame = w.getFrameLw();
|
||||
|
||||
if (frame.left <= 0 && frame.top <= 0) {
|
||||
// Docked at left or top.
|
||||
if (frame.bottom >= displayHeight) {
|
||||
// Docked left.
|
||||
mDockLeft = Math.max(frame.right, mDockLeft);
|
||||
} else if (frame.right >= displayWidth ) {
|
||||
// Docked top.
|
||||
mDockTop = Math.max(frame.bottom, mDockTop);
|
||||
} else {
|
||||
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
|
||||
+ " not docked on left or top of display. frame=" + frame
|
||||
+ " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
|
||||
}
|
||||
} else if (frame.right >= displayWidth && frame.bottom >= displayHeight) {
|
||||
// Docked at right or bottom.
|
||||
if (frame.top <= 0) {
|
||||
// Docked right.
|
||||
mDockRight = Math.min(frame.left, mDockRight);
|
||||
} else if (frame.left <= 0) {
|
||||
// Docked bottom.
|
||||
mDockBottom = Math.min(frame.top, mDockBottom);
|
||||
} else {
|
||||
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
|
||||
+ " not docked on right or bottom" + " of display. frame=" + frame
|
||||
+ " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
|
||||
}
|
||||
} else {
|
||||
// Screen decor windows are required to be docked on one of the sides of the screen.
|
||||
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
|
||||
+ " not docked on one of the sides of the display. frame=" + frame
|
||||
+ " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
|
||||
}
|
||||
}
|
||||
|
||||
mContentTop = mSystemTop = mVoiceContentTop = mCurTop = mRestrictedScreenTop = mDockTop;
|
||||
mContentLeft = mSystemLeft = mVoiceContentLeft = mCurLeft = mRestrictedScreenLeft
|
||||
= mRestrictedOverscanScreenLeft = mDockLeft;
|
||||
mContentBottom = mSystemBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
|
||||
mContentRight = mSystemRight = mVoiceContentRight = mCurRight = mDockRight;
|
||||
|
||||
mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
|
||||
mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
|
||||
mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
|
||||
mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
|
||||
}
|
||||
|
||||
private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
|
||||
@@ -4823,9 +4913,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void layoutWindowLw(WindowState win, WindowState attached) {
|
||||
// We've already done the navigation bar and status bar. If the status bar can receive
|
||||
// input, we need to layout it again to accomodate for the IME window.
|
||||
if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
|
||||
// We've already done the navigation bar, status bar, and all screen decor windows. If the
|
||||
// status bar can receive input, we need to layout it again to accommodate for the IME
|
||||
// window.
|
||||
if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
|
||||
|| mScreenDecorWindows.contains(win)) {
|
||||
return;
|
||||
}
|
||||
final WindowManager.LayoutParams attrs = win.getAttrs();
|
||||
@@ -4864,6 +4956,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
|
||||
if (!isDefaultDisplay) {
|
||||
// TODO: Need to fix this and above to take into account decor windows.
|
||||
if (attached != null) {
|
||||
// If this window is attached to another, our display
|
||||
// frame is the same as the one we are attached to.
|
||||
|
||||
@@ -2877,8 +2877,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
|
||||
}
|
||||
|
||||
mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation,
|
||||
getConfiguration().uiMode);
|
||||
mService.mPolicy.beginLayoutLw(mDisplayId, dw, dh, mRotation, getConfiguration().uiMode);
|
||||
if (isDefaultDisplay) {
|
||||
// Not needed on non-default displays.
|
||||
mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
|
||||
|
||||
@@ -1320,7 +1320,10 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
|
||||
}
|
||||
|
||||
mPolicy.adjustWindowParamsLw(win.mAttrs);
|
||||
final boolean hasStatusBarServicePermission =
|
||||
mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
mPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
|
||||
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
|
||||
|
||||
res = mPolicy.prepareAddWindowLw(win, attrs);
|
||||
@@ -1846,8 +1849,11 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
MergedConfiguration mergedConfiguration, Surface outSurface) {
|
||||
int result = 0;
|
||||
boolean configChanged;
|
||||
boolean hasStatusBarPermission =
|
||||
mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
|
||||
final boolean hasStatusBarPermission =
|
||||
mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
final boolean hasStatusBarServicePermission =
|
||||
mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
long origId = Binder.clearCallingIdentity();
|
||||
@@ -1867,7 +1873,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
int attrChanges = 0;
|
||||
int flagChanges = 0;
|
||||
if (attrs != null) {
|
||||
mPolicy.adjustWindowParamsLw(attrs);
|
||||
mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
|
||||
// if they don't have the permission, mask out the status bar bits
|
||||
if (seq == win.mSeq) {
|
||||
int systemUiVisibility = attrs.systemUiVisibility
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
<uses-permission android:name="android.permission.DEVICE_POWER" />
|
||||
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
|
||||
|
||||
<!-- Uses API introduced in O (26) -->
|
||||
<uses-sdk android:minSdkVersion="1"
|
||||
@@ -132,6 +134,8 @@
|
||||
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
|
||||
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
|
||||
|
||||
<activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" />
|
||||
|
||||
<activity android:name="com.android.server.pm.ShortcutTestActivity"
|
||||
android:enabled="true" android:exported="true" />
|
||||
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.graphics.Color.BLUE;
|
||||
import static android.graphics.Color.RED;
|
||||
import static android.view.Gravity.BOTTOM;
|
||||
import static android.view.Gravity.LEFT;
|
||||
import static android.view.Gravity.RIGHT;
|
||||
import static android.view.Gravity.TOP;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.os.Handler;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* bit FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
|
||||
*/
|
||||
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
|
||||
// TODO: Test non-Activity windows.
|
||||
// TODO: Test secondary display.
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ScreenDecorWindowTests {
|
||||
|
||||
private final Context mContext = InstrumentationRegistry.getTargetContext();
|
||||
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
|
||||
private WindowManager mWm;
|
||||
private ArrayList<View> mWindows = new ArrayList<>();
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<TestActivity> mTestActivityRule = new ActivityTestRule<>(
|
||||
TestActivity.class, false /* initialTouchMode */, false /* launchActivity */);
|
||||
private Activity mTestActivity;
|
||||
|
||||
private int mDecorThickness;
|
||||
private int mHalfDecorThickness;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mWm = mContext.getSystemService(WindowManager.class);
|
||||
final Point size = new Point();
|
||||
mWm.getDefaultDisplay().getSize(size);
|
||||
mDecorThickness = Math.min(size.x, size.y) / 3;
|
||||
mHalfDecorThickness = mDecorThickness / 2;
|
||||
mTestActivity = launchActivity(mTestActivityRule);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
while (!mWindows.isEmpty()) {
|
||||
removeWindow(mWindows.get(0));
|
||||
}
|
||||
finishActivity(mTestActivityRule);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScreenSides() throws Exception {
|
||||
// Decor on top
|
||||
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
|
||||
WindowInsets insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
|
||||
|
||||
// Decor at the bottom
|
||||
updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), mDecorThickness);
|
||||
|
||||
// Decor to the left
|
||||
updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), mDecorThickness);
|
||||
|
||||
// Decor to the right
|
||||
updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetRight(), mDecorThickness);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleDecors() throws Exception {
|
||||
// Test 2 decor windows on-top.
|
||||
createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness);
|
||||
WindowInsets insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mHalfDecorThickness);
|
||||
createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
|
||||
|
||||
// And one at the bottom.
|
||||
createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), mHalfDecorThickness);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlagChange() throws Exception {
|
||||
WindowInsets initialInsets = getInsets(mTestActivity);
|
||||
|
||||
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
|
||||
WindowInsets insets = getInsets(mTestActivity);
|
||||
assertEquals(mDecorThickness, insets.getSystemWindowInsetTop());
|
||||
|
||||
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
|
||||
0, PRIVATE_FLAG_IS_SCREEN_DECOR);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertEquals(initialInsets.getSystemWindowInsetTop(), insets.getSystemWindowInsetTop());
|
||||
|
||||
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
|
||||
PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertEquals(mDecorThickness, insets.getSystemWindowInsetTop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoval() throws Exception {
|
||||
WindowInsets initialInsets = getInsets(mTestActivity);
|
||||
|
||||
final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
|
||||
WindowInsets insets = getInsets(mTestActivity);
|
||||
assertGreaterOrEqual(insets.getSystemWindowInsetTop(), mDecorThickness);
|
||||
|
||||
removeWindow(decorWindow);
|
||||
insets = getInsets(mTestActivity);
|
||||
assertEquals(initialInsets.getSystemWindowInsetTop(), insets.getSystemWindowInsetTop());
|
||||
}
|
||||
|
||||
private View createAppWindow() {
|
||||
return createWindow("appWindow", TOP, MATCH_PARENT, MATCH_PARENT, BLUE,
|
||||
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR, 0);
|
||||
}
|
||||
|
||||
private View createDecorWindow(int gravity, int width, int height) {
|
||||
return createWindow("decorWindow", gravity, width, height, RED,
|
||||
FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR);
|
||||
}
|
||||
|
||||
private View createWindow(String name, int gravity, int width, int height, int color, int flags,
|
||||
int privateFlags) {
|
||||
|
||||
final View[] viewHolder = new View[1];
|
||||
final int finalFlag = flags
|
||||
| FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE;
|
||||
|
||||
// Needs to run on the UI thread.
|
||||
Handler.getMain().runWithScissors(() -> {
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE);
|
||||
lp.gravity = gravity;
|
||||
lp.privateFlags |= privateFlags;
|
||||
|
||||
final TextView view = new TextView(mContext);
|
||||
view.setText("ScreenDecorWindowTests - " + name);
|
||||
view.setBackgroundColor(color);
|
||||
mWm.addView(view, lp);
|
||||
mWindows.add(view);
|
||||
viewHolder[0] = view;
|
||||
}, 0);
|
||||
|
||||
waitForIdle();
|
||||
return viewHolder[0];
|
||||
}
|
||||
|
||||
private void updateWindow(View v, int gravity, int width, int height,
|
||||
int privateFlags, int privateFlagsMask) {
|
||||
// Needs to run on the UI thread.
|
||||
Handler.getMain().runWithScissors(() -> {
|
||||
final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams();
|
||||
lp.gravity = gravity;
|
||||
lp.width = width;
|
||||
lp.height = height;
|
||||
setPrivateFlags(lp, privateFlags, privateFlagsMask);
|
||||
|
||||
mWm.updateViewLayout(v, lp);
|
||||
}, 0);
|
||||
|
||||
waitForIdle();
|
||||
}
|
||||
|
||||
private void removeWindow(View v) {
|
||||
Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0);
|
||||
mWindows.remove(v);
|
||||
waitForIdle();
|
||||
}
|
||||
|
||||
private WindowInsets getInsets(View v) {
|
||||
return new WindowInsets(v.getRootWindowInsets());
|
||||
}
|
||||
|
||||
private WindowInsets getInsets(Activity a) {
|
||||
return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flags of the window, as per the
|
||||
* {@link WindowManager.LayoutParams WindowManager.LayoutParams}
|
||||
* flags.
|
||||
*
|
||||
* @param flags The new window flags (see WindowManager.LayoutParams).
|
||||
* @param mask Which of the window flag bits to modify.
|
||||
*/
|
||||
public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) {
|
||||
lp.flags = (lp.flags & ~mask) | (flags & mask);
|
||||
}
|
||||
|
||||
/** Asserts that the first entry is greater than or equal to the second entry. */
|
||||
private void assertGreaterOrEqual(int first, int second) throws Exception {
|
||||
Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
|
||||
}
|
||||
|
||||
private Activity launchActivity(ActivityTestRule activityRule) {
|
||||
final Activity activity = activityRule.launchActivity(null);
|
||||
waitForIdle();
|
||||
return activity;
|
||||
}
|
||||
|
||||
private void finishActivity(ActivityTestRule activityRule) {
|
||||
final Activity activity = activityRule.getActivity();
|
||||
if (activity != null) {
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForIdle() {
|
||||
mInstrumentation.waitForIdleSync();
|
||||
}
|
||||
|
||||
public static class TestActivity extends Activity {
|
||||
}
|
||||
}
|
||||
@@ -148,8 +148,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
|
||||
|
||||
public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
|
||||
boolean hasStatusBarServicePermission) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -290,7 +290,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
|
||||
public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
|
||||
int displayRotation, int uiMode) {
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user