diff options
15 files changed, 590 insertions, 135 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index dbf81f95d5c8..d432160ccfbf 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -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); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4004bd6686b1..aede1bb67f80 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -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) { @@ -1253,6 +1259,20 @@ public class ActivityManager { } /** + * @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; } } diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 1abb59b006dd..a70209c705c0 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -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); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index a8e16c96acfa..653796dc39e7 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -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); + } } /** diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 554f50c23daa..ee73b6983888 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -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"> diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 1c71da007fd0..3a43d304b1ca 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -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(); } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 0c2c2043fb15..c7f20b9ff904 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -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); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b507df0b8447..7a315ab972e0 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -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) { diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 4b4be40880ee..2bc3c5f9abba 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -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; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index da524c764eba..4ebf1fcb7206 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -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; diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java index e73d4d2559fb..35f35db5ada3 100644 --- a/services/core/java/com/android/server/wm/SnapshotStartingData.java +++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java @@ -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; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3ffb093ba386..b816d8199aa6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -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; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index b8d0b8c096fe..48b01f40fc65 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -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)); } /** diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 04403e2712c1..1591e480af4f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -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; } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index aab75ee1699b..717ddf26eb2f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -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); + 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); - final Bitmap b = Bitmap.createBitmap(200, 200, Config.ARGB_8888); - mSurface.fillEmptyBackground(mockCanvas, b); + 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()); + } } |