Merge "Handle case when snapshot dimensions don't match" into oc-dev

This commit is contained in:
Jorim Jaggi
2017-04-04 09:14:35 +00:00
committed by Android (Google) Code Review
15 changed files with 590 additions and 135 deletions

View File

@@ -4159,14 +4159,25 @@ public class Activity extends ContextThemeWrapper
mTaskDescription.setPrimaryColor(colorPrimary);
}
}
// For dev-preview only.
if (mTaskDescription.getBackgroundColor() == 0) {
int colorBackground = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0);
if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) {
mTaskDescription.setBackgroundColor(colorBackground);
}
int colorBackground = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_colorBackground, 0);
if (colorBackground != 0 && Color.alpha(colorBackground) == 0xFF) {
mTaskDescription.setBackgroundColor(colorBackground);
}
final int statusBarColor = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_statusBarColor, 0);
if (statusBarColor != 0) {
mTaskDescription.setStatusBarColor(statusBarColor);
}
final int navigationBarColor = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_navigationBarColor, 0);
if (navigationBarColor != 0) {
mTaskDescription.setNavigationBarColor(navigationBarColor);
}
a.recycle();
setTaskDescription(mTaskDescription);
}

View File

@@ -1145,6 +1145,8 @@ public class ActivityManager {
private String mIconFilename;
private int mColorPrimary;
private int mColorBackground;
private int mStatusBarColor;
private int mNavigationBarColor;
/**
* Creates the TaskDescription to the specified values.
@@ -1155,7 +1157,7 @@ public class ActivityManager {
* opaque.
*/
public TaskDescription(String label, Bitmap icon, int colorPrimary) {
this(label, icon, null, colorPrimary, 0);
this(label, icon, null, colorPrimary, 0, 0, 0);
if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
throw new RuntimeException("A TaskDescription's primary color should be opaque");
}
@@ -1168,7 +1170,7 @@ public class ActivityManager {
* @param icon An icon that represents the current state of this activity.
*/
public TaskDescription(String label, Bitmap icon) {
this(label, icon, null, 0, 0);
this(label, icon, null, 0, 0, 0, 0);
}
/**
@@ -1177,24 +1179,26 @@ public class ActivityManager {
* @param label A label and description of the current state of this activity.
*/
public TaskDescription(String label) {
this(label, null, null, 0, 0);
this(label, null, null, 0, 0, 0, 0);
}
/**
* Creates an empty TaskDescription.
*/
public TaskDescription() {
this(null, null, null, 0, 0);
this(null, null, null, 0, 0, 0, 0);
}
/** @hide */
public TaskDescription(String label, Bitmap icon, String iconFilename, int colorPrimary,
int colorBackground) {
int colorBackground, int statusBarColor, int navigationBarColor) {
mLabel = label;
mIcon = icon;
mIconFilename = iconFilename;
mColorPrimary = colorPrimary;
mColorBackground = colorBackground;
mStatusBarColor = statusBarColor;
mNavigationBarColor = navigationBarColor;
}
/**
@@ -1214,6 +1218,8 @@ public class ActivityManager {
mIconFilename = other.mIconFilename;
mColorPrimary = other.mColorPrimary;
mColorBackground = other.mColorBackground;
mStatusBarColor = other.mStatusBarColor;
mNavigationBarColor = other.mNavigationBarColor;
}
private TaskDescription(Parcel source) {
@@ -1252,6 +1258,20 @@ public class ActivityManager {
mColorBackground = backgroundColor;
}
/**
* @hide
*/
public void setStatusBarColor(int statusBarColor) {
mStatusBarColor = statusBarColor;
}
/**
* @hide
*/
public void setNavigationBarColor(int navigationBarColor) {
mNavigationBarColor = navigationBarColor;
}
/**
* Sets the icon for this task description.
* @hide
@@ -1325,6 +1345,20 @@ public class ActivityManager {
return mColorBackground;
}
/**
* @hide
*/
public int getStatusBarColor() {
return mStatusBarColor;
}
/**
* @hide
*/
public int getNavigationBarColor() {
return mNavigationBarColor;
}
/** @hide */
public void saveToXml(XmlSerializer out) throws IOException {
if (mLabel != null) {
@@ -1377,6 +1411,8 @@ public class ActivityManager {
}
dest.writeInt(mColorPrimary);
dest.writeInt(mColorBackground);
dest.writeInt(mStatusBarColor);
dest.writeInt(mNavigationBarColor);
if (mIconFilename == null) {
dest.writeInt(0);
} else {
@@ -1390,6 +1426,8 @@ public class ActivityManager {
mIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
mColorPrimary = source.readInt();
mColorBackground = source.readInt();
mStatusBarColor = source.readInt();
mNavigationBarColor = source.readInt();
mIconFilename = source.readInt() > 0 ? source.readString() : null;
}
@@ -1407,7 +1445,9 @@ public class ActivityManager {
public String toString() {
return "TaskDescription Label: " + mLabel + " Icon: " + mIcon +
" IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary +
" colorBackground: " + mColorBackground;
" colorBackground: " + mColorBackground +
" statusBarColor: " + mColorBackground +
" navigationBarColor: " + mNavigationBarColor;
}
}

View File

@@ -74,6 +74,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
private final Rect mOldStableInsets = new Rect();
private final Rect mSystemInsets = new Rect();
private final Rect mStableInsets = new Rect();
private final Rect mTmpRect = new Rect();
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
@@ -370,12 +371,6 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height);
mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
final int bottomInset = DecorView.getColorViewBottomInset(stableInsets.bottom,
systemInsets.bottom);
final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
systemInsets.right);
final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left,
systemInsets.left);
if (mStatusBarColor != null) {
mStatusBarColor.setBounds(0, 0, left + width, topInset);
mStatusBarColor.draw(canvas);
@@ -385,14 +380,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
// don't want the navigation bar background be moving around when resizing in docked mode.
// However, we need it for the transitions into/out of docked mode.
if (mNavigationBarColor != null && fullscreen) {
final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
mNavigationBarColor.setBounds(width - size, 0, width, height);
} else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) {
mNavigationBarColor.setBounds(0, 0, size, height);
} else {
mNavigationBarColor.setBounds(0, height - size, width, height);
}
DecorView.getNavigationBarRect(width, height, stableInsets, systemInsets, mTmpRect);
mNavigationBarColor.setBounds(mTmpRect);
mNavigationBarColor.draw(canvas);
}
mSystemBarBackgroundNode.end(canvas);

View File

@@ -119,6 +119,21 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// The height of a window which has not in DIP.
private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.statusBarBackground,
FLAG_FULLSCREEN);
public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
new ColorViewAttributes(
SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.navigationBarBackground,
0 /* hideWindowFlag */);
// Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
// size calculation takes the shadow size into account. We set the elevation currently
// to max until the first layout command has been executed.
@@ -162,18 +177,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// View added at runtime to draw under the navigation bar area
private View mNavigationGuard;
private final ColorViewState mStatusColorViewState = new ColorViewState(
SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.statusBarBackground,
FLAG_FULLSCREEN);
private final ColorViewState mNavigationColorViewState = new ColorViewState(
SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
com.android.internal.R.id.navigationBarBackground,
0 /* hideWindowFlag */);
private final ColorViewState mStatusColorViewState =
new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
private final ColorViewState mNavigationColorViewState =
new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
private final Interpolator mShowInterpolator;
private final Interpolator mHideInterpolator;
@@ -983,35 +990,50 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return false;
}
static int getColorViewTopInset(int stableTop, int systemTop) {
public static int getColorViewTopInset(int stableTop, int systemTop) {
return Math.min(stableTop, systemTop);
}
static int getColorViewBottomInset(int stableBottom, int systemBottom) {
public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
return Math.min(stableBottom, systemBottom);
}
static int getColorViewRightInset(int stableRight, int systemRight) {
public static int getColorViewRightInset(int stableRight, int systemRight) {
return Math.min(stableRight, systemRight);
}
static int getColorViewLeftInset(int stableLeft, int systemLeft) {
public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
return Math.min(stableLeft, systemLeft);
}
static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
return bottomInset == 0 && rightInset > 0;
}
static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
return bottomInset == 0 && leftInset > 0;
}
static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
: isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
}
public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
Rect contentInsets, Rect outRect) {
final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
final int size = getNavBarSize(bottomInset, rightInset, leftInset);
if (isNavBarToRightEdge(bottomInset, rightInset)) {
outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
} else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
outRect.set(0, 0, size, canvasHeight);
} else {
outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
}
}
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
WindowManager.LayoutParams attrs = mWindow.getAttributes();
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
@@ -1131,9 +1153,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
private int calculateStatusBarColor() {
int flags = mWindow.getAttributes().flags;
return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? mSemiTransparentStatusBarColor
: (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? mWindow.mStatusBarColor
return calculateStatusBarColor(mWindow.getAttributes().flags,
mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
}
public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
int statusBarColor) {
return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
: (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
: Color.BLACK;
}
@@ -1160,13 +1187,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int size, boolean verticalBar, boolean seascape, int sideMargin,
boolean animate, boolean force) {
state.present = (sysUiVis & state.systemUiHideFlag) == 0
&& (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
&& ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
|| force);
boolean show = state.present
&& (color & Color.BLACK) != 0
&& ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force);
state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
boolean showView = show && !isResizing() && size > 0;
boolean visibilityChanged = false;
@@ -1175,15 +1198,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
int resolvedGravity = verticalBar
? (seascape ? state.seascapeGravity : state.horizontalGravity)
: state.verticalGravity;
? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
: state.attributes.verticalGravity;
if (view == null) {
if (showView) {
state.view = view = new View(mContext);
view.setBackgroundColor(color);
view.setTransitionName(state.transitionName);
view.setId(state.id);
view.setTransitionName(state.attributes.transitionName);
view.setId(state.attributes.id);
visibilityChanged = true;
view.setVisibility(INVISIBLE);
state.targetVisibility = VISIBLE;
@@ -2269,6 +2292,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
boolean visible;
int color;
final ColorViewAttributes attributes;
ColorViewState(ColorViewAttributes attributes) {
this.attributes = attributes;
}
}
public static class ColorViewAttributes {
final int id;
final int systemUiHideFlag;
final int translucentFlag;
@@ -2278,9 +2310,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
final String transitionName;
final int hideWindowFlag;
ColorViewState(int systemUiHideFlag,
int translucentFlag, int verticalGravity, int horizontalGravity,
int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
int horizontalGravity, int seascapeGravity, String transitionName, int id,
int hideWindowFlag) {
this.id = id;
this.systemUiHideFlag = systemUiHideFlag;
this.translucentFlag = translucentFlag;
@@ -2290,6 +2322,24 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
this.transitionName = transitionName;
this.hideWindowFlag = hideWindowFlag;
}
public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
return (sysUiVis & systemUiHideFlag) == 0
&& (windowFlags & hideWindowFlag) == 0
&& ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
|| force);
}
public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
return present
&& (color & Color.BLACK) != 0
&& ((windowFlags & translucentFlag) == 0 || force);
}
public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
final boolean present = isPresent(sysUiVis, windowFlags, force);
return isVisible(present, color, windowFlags, force);
}
}
/**

View File

@@ -8566,6 +8566,11 @@
<!-- @hide From Theme.colorBackground, used for the TaskDescription background
color. -->
<attr name="colorBackground" />
<!-- @hide From Theme.statusBarColor, used for the TaskDescription status bar color. -->
<attr name="statusBarColor"/>
<!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar
color. -->
<attr name="navigationBarColor"/>
</declare-styleable>
<declare-styleable name="Shortcut">

View File

@@ -355,9 +355,10 @@ public class SystemServicesProxy {
rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
Bitmap.createBitmap(mDummyIcon), null,
0xFF000000 | (0xFFFFFF & new Random().nextInt()),
0xFF000000 | (0xFFFFFF & new Random().nextInt()));
Bitmap.createBitmap(mDummyIcon), null,
0xFF000000 | (0xFFFFFF & new Random().nextInt()),
0xFF000000 | (0xFFFFFF & new Random().nextInt()),
0, 0);
} else {
rti.taskDescription = new ActivityManager.TaskDescription();
}

View File

@@ -1613,6 +1613,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
String iconFilename = null;
int colorPrimary = 0;
int colorBackground = 0;
int statusBarColor = 0;
int navigationBarColor = 0;
boolean topActivity = true;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.taskDescription != null) {
@@ -1625,13 +1628,16 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
if (colorPrimary == 0) {
colorPrimary = r.taskDescription.getPrimaryColor();
}
if (colorBackground == 0) {
if (topActivity) {
colorBackground = r.taskDescription.getBackgroundColor();
statusBarColor = r.taskDescription.getStatusBarColor();
navigationBarColor = r.taskDescription.getNavigationBarColor();
}
}
topActivity = false;
}
lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
colorBackground);
colorBackground, statusBarColor, navigationBarColor);
if (mWindowContainerController != null) {
mWindowContainerController.setTaskDescription(lastTaskDescription);
}

View File

@@ -5254,11 +5254,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
// Don't allow snapshots to influence SystemUI visibility flags.
// TODO: Revisit this once SystemUI flags for snapshots are handled correctly
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW
&& (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0;
&& attrs.type < FIRST_SYSTEM_WINDOW;
final int stackId = win.getStackId();
if (mTopFullscreenOpaqueWindowState == null && visible) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {

View File

@@ -566,7 +566,7 @@ public class AppWindowContainerController
return false;
}
mContainer.startingData = new SnapshotStartingData(mService, snapshot.getSnapshot());
mContainer.startingData = new SnapshotStartingData(mService, snapshot);
scheduleAddStartingWindow();
return true;
}

View File

@@ -2920,7 +2920,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (stack != null) {
stack.getBounds(frame);
}
} else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
// We want to screenshot with the exact bounds of the surface of the app. Thus,
// intersect it with the frame.
frame.intersect(w.mFrame);
}else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
final Rect wf = w.mFrame;
final Rect cr = w.mContentInsets;
int left = wf.left + cr.left;

View File

@@ -16,6 +16,7 @@
package com.android.server.wm;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.GraphicBuffer;
import android.view.WindowManagerPolicy.StartingSurface;
@@ -25,9 +26,9 @@ import android.view.WindowManagerPolicy.StartingSurface;
class SnapshotStartingData extends StartingData {
private final WindowManagerService mService;
private final GraphicBuffer mSnapshot;
private final TaskSnapshot mSnapshot;
SnapshotStartingData(WindowManagerService service, GraphicBuffer snapshot) {
SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) {
super(service);
mService = service;
mSnapshot = snapshot;

View File

@@ -17,15 +17,14 @@
package com.android.server.wm;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -40,7 +39,6 @@ import android.view.DisplayInfo;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.EventLogTags;
import java.io.PrintWriter;

View File

@@ -28,6 +28,7 @@ import android.app.ActivityManager.StackId;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.os.Environment;
import android.util.ArraySet;
import android.view.WindowManagerPolicy.StartingSurface;
@@ -152,7 +153,7 @@ class TaskSnapshotController {
* MANAGER LOCK WHEN CALLING THIS METHOD!
*/
StartingSurface createStartingSurface(AppWindowToken token,
GraphicBuffer snapshot) {
TaskSnapshot snapshot) {
return TaskSnapshotSurface.create(mService, token, snapshot);
}
@@ -166,8 +167,17 @@ class TaskSnapshotController {
if (buffer == null) {
return null;
}
final WindowState mainWindow = top.findMainWindow();
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
top.findMainWindow().mStableInsets, false /* reduced */, 1f /* scale */);
minRect(mainWindow.mContentInsets, mainWindow.mStableInsets), false /* reduced */,
1f /* scale */);
}
private Rect minRect(Rect rect1, Rect rect2) {
return new Rect(Math.min(rect1.left, rect2.left),
Math.min(rect1.top, rect2.top),
Math.min(rect1.right, rect2.right),
Math.min(rect1.bottom, rect2.bottom));
}
/**

View File

@@ -16,20 +16,35 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.view.SurfaceControl.HIDDEN;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
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_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
import static com.android.internal.policy.DecorView.getColorViewTopInset;
import static com.android.internal.policy.DecorView.getNavigationBarRect;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager.TaskDescription;
import android.graphics.Bitmap;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -37,17 +52,22 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.IWindowSession;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy.StartingSurface;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DecorView;
import com.android.internal.view.BaseIWindow;
/**
@@ -57,19 +77,57 @@ import com.android.internal.view.BaseIWindow;
*/
class TaskSnapshotSurface implements StartingSurface {
private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
/**
* When creating the starting window, we use the exact same layout flags such that we end up
* with a window with the exact same dimensions etc. However, these flags are not used in layout
* and might cause other side effects so we exclude them.
*/
private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
| FLAG_NOT_TOUCHABLE
| FLAG_NOT_TOUCH_MODAL
| FLAG_ALT_FOCUSABLE_IM
| FLAG_NOT_FOCUSABLE
| FLAG_HARDWARE_ACCELERATED
| FLAG_IGNORE_CHEEK_PRESSES
| FLAG_LOCAL_FOCUS_MODE
| FLAG_SLIPPERY
| FLAG_WATCH_OUTSIDE_TOUCH
| FLAG_SPLIT_TOUCH
| FLAG_SCALED
| FLAG_SECURE;
private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
private static final int MSG_REPORT_DRAW = 0;
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
private final Window mWindow;
private final Surface mSurface;
private SurfaceControl mChildSurfaceControl;
private final IWindowSession mSession;
private final WindowManagerService mService;
private final Rect mTaskBounds;
private final Rect mStableInsets = new Rect();
private final Rect mContentInsets = new Rect();
private final Rect mFrame = new Rect();
private final TaskSnapshot mSnapshot;
private final CharSequence mTitle;
private boolean mHasDrawn;
private boolean mReportNextDraw;
private Paint mFillBackgroundPaint = new Paint();
private long mShownTime;
private final Handler mHandler;
private final boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
private final Paint mStatusBarPaint = new Paint();
private final Paint mNavigationBarPaint = new Paint();
private final int mStatusBarColor;
private final int mNavigationBarColor;
private final int mSysUiVis;
private final int mWindowFlags;
private final int mWindowPrivateFlags;
static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
GraphicBuffer snapshot) {
TaskSnapshot snapshot) {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
final Window window = new Window();
@@ -78,32 +136,51 @@ class TaskSnapshotSurface implements StartingSurface {
final Surface surface = new Surface();
final Rect tmpRect = new Rect();
final Rect tmpFrame = new Rect();
final Rect taskBounds;
final Rect tmpContentInsets = new Rect();
final Rect tmpStableInsets = new Rect();
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
int fillBackgroundColor = Color.WHITE;
int backgroundColor = WHITE;
int statusBarColor = 0;
int navigationBarColor = 0;
final int sysUiVis;
final int windowFlags;
final int windowPrivateFlags;
synchronized (service.mWindowMap) {
final WindowState mainWindow = token.findMainWindow();
if (mainWindow == null) {
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
+ token);
return null;
}
sysUiVis = mainWindow.getSystemUiVisibility();
windowFlags = mainWindow.getAttrs().flags;
windowPrivateFlags = mainWindow.getAttrs().privateFlags;
layoutParams.type = TYPE_APPLICATION_STARTING;
layoutParams.format = snapshot.getFormat();
layoutParams.flags = FLAG_LAYOUT_INSET_DECOR
| FLAG_LAYOUT_IN_SCREEN
layoutParams.format = snapshot.getSnapshot().getFormat();
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
| FLAG_NOT_FOCUSABLE
| FLAG_NOT_TOUCHABLE
| FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
| FLAG_NOT_TOUCHABLE;
layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT;
layoutParams.token = token.token;
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
// TODO: Inherit behavior whether to draw behind status bar/nav bar.
layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
layoutParams.systemUiVisibility = sysUiVis;
final Task task = token.getTask();
if (task != null) {
layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId));
layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
final TaskDescription taskDescription = task.getTaskDescription();
if (taskDescription != null) {
fillBackgroundColor = taskDescription.getBackgroundColor();
backgroundColor = taskDescription.getBackgroundColor();
statusBarColor = taskDescription.getStatusBarColor();
navigationBarColor = taskDescription.getNavigationBarColor();
}
taskBounds = new Rect();
task.getBounds(taskBounds);
} else {
taskBounds = null;
}
}
try {
@@ -118,31 +195,57 @@ class TaskSnapshotSurface implements StartingSurface {
// Local call.
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
surface, fillBackgroundColor);
surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds);
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration,
surface);
tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
tmpMergedConfiguration, surface);
} catch (RemoteException e) {
// Local call.
}
snapshotSurface.drawSnapshot(snapshot);
snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
snapshotSurface.drawSnapshot();
return snapshotSurface;
}
@VisibleForTesting
TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
int fillBackgroundColor) {
TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
Rect taskBounds) {
mService = service;
mHandler = new Handler(mService.mH.getLooper());
mSession = WindowManagerGlobal.getWindowSession();
mWindow = window;
mSurface = surface;
mFillBackgroundPaint.setColor(fillBackgroundColor);
mSnapshot = snapshot;
mTitle = title;
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mTaskBounds = taskBounds;
mSysUiVis = sysUiVis;
mWindowFlags = windowFlags;
mWindowPrivateFlags = windowPrivateFlags;
mSizeMismatch = (mFrame.width() != snapshot.getSnapshot().getWidth()
|| mFrame.height() != snapshot.getSnapshot().getHeight());
mStatusBarColor = DecorView.calculateStatusBarColor(windowFlags,
service.mContext.getColor(R.color.system_bar_background_semi_transparent),
statusBarColor);
mNavigationBarColor = navigationBarColor;
mStatusBarPaint.setColor(mStatusBarColor);
mNavigationBarPaint.setColor(navigationBarColor);
}
@Override
public void remove() {
synchronized (mService.mWindowMap) {
final long now = SystemClock.uptimeMillis();
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
return;
}
}
try {
mSession.remove(mWindow);
} catch (RemoteException e) {
@@ -150,31 +253,149 @@ class TaskSnapshotSurface implements StartingSurface {
}
}
private void drawSnapshot(GraphicBuffer snapshot) {
mSurface.attachAndQueueBuffer(snapshot);
@VisibleForTesting
void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
mFrame.set(frame);
mContentInsets.set(contentInsets);
mStableInsets.set(stableInsets);
}
private void drawSnapshot() {
final GraphicBuffer buffer = mSnapshot.getSnapshot();
if (mSizeMismatch) {
// The dimensions of the buffer and the window don't match, so attaching the buffer
// will fail. Better create a child window with the exact dimensions and fill the parent
// window with the background color!
drawSizeMismatchSnapshot(buffer);
} else {
drawSizeMatchSnapshot(buffer);
}
final boolean reportNextDraw;
synchronized (mService.mWindowMap) {
mShownTime = SystemClock.uptimeMillis();
mHasDrawn = true;
reportNextDraw = mReportNextDraw;
}
if (reportNextDraw) {
reportDrawn();
}
}
private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
mSurface.attachAndQueueBuffer(buffer);
mSurface.release();
}
private void drawSizeMismatchSnapshot(GraphicBuffer buffer) {
final SurfaceSession session = new SurfaceSession(mSurface);
// Keep a reference to it such that it doesn't get destroyed when finalized.
mChildSurfaceControl = new SurfaceControl(session,
mTitle + " - task-snapshot-surface",
buffer.getWidth(), buffer.getHeight(), buffer.getFormat(), HIDDEN);
Surface surface = new Surface();
surface.copyFrom(mChildSurfaceControl);
// Clip off ugly navigation bar.
final Rect crop = calculateSnapshotCrop();
final Rect frame = calculateSnapshotFrame(crop);
SurfaceControl.openTransaction();
try {
// We can just show the surface here as it will still be hidden as the parent is
// still hidden.
mChildSurfaceControl.show();
mChildSurfaceControl.setWindowCrop(crop);
mChildSurfaceControl.setPosition(frame.left, frame.top);
} finally {
SurfaceControl.closeTransaction();
}
surface.attachAndQueueBuffer(buffer);
surface.release();
final Canvas c = mSurface.lockCanvas(null);
drawBackgroundAndBars(c, frame);
mSurface.unlockCanvasAndPost(c);
mSurface.release();
}
@VisibleForTesting
void fillEmptyBackground(Canvas c, Bitmap b) {
final boolean fillHorizontally = c.getWidth() > b.getWidth();
final boolean fillVertically = c.getHeight() > b.getHeight();
Rect calculateSnapshotCrop() {
final Rect rect = new Rect();
rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
final Rect insets = mSnapshot.getContentInsets();
// Let's remove all system decorations except the status bar, but only if the task is at the
// very top of the screen.
rect.inset(insets.left, mTaskBounds.top != 0 ? insets.top : 0, insets.right, insets.bottom);
return rect;
}
@VisibleForTesting
Rect calculateSnapshotFrame(Rect crop) {
final Rect frame = new Rect(crop);
// By default, offset it to to top/left corner
frame.offsetTo(-crop.left, -crop.top);
// However, we also need to make space for the navigation bar on the left side.
final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
mContentInsets.left);
frame.offset(colorViewLeftInset, 0);
return frame;
}
@VisibleForTesting
void drawBackgroundAndBars(Canvas c, Rect frame) {
final int statusBarHeight = getStatusBarColorViewHeight();
final boolean fillHorizontally = c.getWidth() > frame.right;
final boolean fillVertically = c.getHeight() > frame.bottom;
if (fillHorizontally) {
c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically
? b.getHeight()
: c.getHeight(),
mFillBackgroundPaint);
c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
c.getWidth(), fillVertically
? frame.bottom
: c.getHeight(),
mBackgroundPaint);
}
if (fillVertically) {
c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint);
c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
}
drawStatusBarBackground(c, frame, statusBarHeight);
drawNavigationBarBackground(c);
}
private int getStatusBarColorViewHeight() {
final boolean forceStatusBarBackground =
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
mSysUiVis, mStatusBarColor, mWindowFlags, forceStatusBarBackground)) {
return getColorViewTopInset(mStableInsets.top, mContentInsets.top);
} else {
return 0;
}
}
private boolean isNavigationBarColorViewVisible() {
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
mSysUiVis, mNavigationBarColor, mWindowFlags, false /* force */);
}
@VisibleForTesting
void drawStatusBarBackground(Canvas c, Rect frame, int statusBarHeight) {
if (statusBarHeight > 0 && c.getWidth() > frame.right) {
final int rightInset = DecorView.getColorViewRightInset(mStableInsets.right,
mContentInsets.right);
c.drawRect(frame.right, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
}
}
@VisibleForTesting
void drawNavigationBarBackground(Canvas c) {
final Rect navigationBarRect = new Rect();
getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
navigationBarRect);
final boolean visible = isNavigationBarColorViewVisible();
if (visible && !navigationBarRect.isEmpty()) {
c.drawRect(navigationBarRect, mNavigationBarPaint);
}
}
@@ -211,10 +432,10 @@ class TaskSnapshotSurface implements StartingSurface {
}
};
private static class Window extends BaseIWindow {
@VisibleForTesting
static class Window extends BaseIWindow {
private TaskSnapshotSurface mOuter;
public void setOuter(TaskSnapshotSurface outer) {
mOuter = outer;
}

View File

@@ -16,6 +16,9 @@
package com.android.server.wm;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -24,15 +27,19 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.Surface;
import com.android.server.wm.TaskSnapshotSurface.Window;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,59 +55,174 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
private TaskSnapshotSurface mSurface;
@Before
public void setUp() {
mSurface = new TaskSnapshotSurface(null, null, null, Color.WHITE);
private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
int windowFlags, Rect taskBounds) {
final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER);
final TaskSnapshot snapshot = new TaskSnapshot(buffer,
ORIENTATION_PORTRAIT, contentInsets, false, 1.0f);
mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test",
Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds);
}
private void setupSurface(int width, int height) {
setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, width, height));
}
@Test
public void fillEmptyBackground_fillHorizontally() throws Exception {
setupSurface(200, 100);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(200);
when(mockCanvas.getHeight()).thenReturn(100);
final Bitmap b = Bitmap.createBitmap(100, 200, Config.ARGB_8888);
mSurface.fillEmptyBackground(mockCanvas, b);
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
}
@Test
public void fillEmptyBackground_fillVertically() throws Exception {
setupSurface(100, 200);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(200);
final Bitmap b = Bitmap.createBitmap(200, 100, Config.ARGB_8888);
mSurface.fillEmptyBackground(mockCanvas, b);
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
}
@Test
public void fillEmptyBackground_fillBoth() throws Exception {
setupSurface(200, 200);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(200);
when(mockCanvas.getHeight()).thenReturn(200);
final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
mSurface.fillEmptyBackground(mockCanvas, b);
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
}
@Test
public void fillEmptyBackground_dontFill_sameSize() throws Exception {
setupSurface(100, 100);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
final Bitmap b = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
mSurface.fillEmptyBackground(mockCanvas, b);
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
@Test
public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception {
setupSurface(100, 100);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888);
mSurface.fillEmptyBackground(mockCanvas, b);
mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
@Test
public void testCalculateSnapshotCrop() {
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop());
}
@Test
public void testCalculateSnapshotCrop_taskNotOnTop() {
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100));
assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
}
@Test
public void testCalculateSnapshotCrop_navBarLeft() {
setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop());
}
@Test
public void testCalculateSnapshotCrop_navBarRight() {
setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop());
}
@Test
public void testCalculateSnapshotFrame() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 0, 10);
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
assertEquals(new Rect(0, -10, 100, 70),
mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
}
@Test
public void testCalculateSnapshotFrame_navBarLeft() {
setupSurface(100, 100);
final Rect insets = new Rect(10, 10, 0, 0);
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
assertEquals(new Rect(0, -10, 90, 80),
mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
}
@Test
public void testDrawStatusBarBackground() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100), 10);
verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
}
@Test
public void testDrawStatusBarBackground_nope() {
setupSurface(100, 100);
final Rect insets = new Rect(0, 10, 10, 0);
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
mSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100), 10);
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
}
@Test
public void testDrawNavigationBarBackground() {
final Rect insets = new Rect(0, 10, 0, 10);
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
mSurface.drawNavigationBarBackground(mockCanvas);
verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
}
@Test
public void testDrawNavigationBarBackground_left() {
final Rect insets = new Rect(10, 10, 0, 0);
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
mSurface.drawNavigationBarBackground(mockCanvas);
verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
}
@Test
public void testDrawNavigationBarBackground_right() {
final Rect insets = new Rect(0, 10, 10, 0);
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
new Rect(0, 0, 100, 100));
mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets);
final Canvas mockCanvas = mock(Canvas.class);
when(mockCanvas.getWidth()).thenReturn(100);
when(mockCanvas.getHeight()).thenReturn(100);
mSurface.drawNavigationBarBackground(mockCanvas);
verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
}
}