Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84884684 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Some cleanups for window cropping functionality."

parents 7cea0a05 fbbde85b
Loading
Loading
Loading
Loading
+101 −1
Original line number Diff line number Diff line
@@ -3190,7 +3190,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
    }

    boolean isDockedResizing() {
        return mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
        return (mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER)
                || (isChildWindow() && getParentWindow().isDockedResizing());
    }

    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
@@ -4144,6 +4145,105 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        }
    }

    /**
     * Calculate the window crop according to system decor policy. In general this is
     * the system decor rect (see #calculateSystemDecorRect), but we also have some
     * special cases. This rectangle is in screen space.
     */
    void calculatePolicyCrop(Rect policyCrop) {
        final DisplayContent displayContent = getDisplayContent();
        final DisplayInfo displayInfo = displayContent.getDisplayInfo();

        if (!isDefaultDisplay()) {
            // On a different display there is no system decor. Crop the window
            // by the screen boundaries.
            // TODO(multi-display)
            policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
            policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
                    displayInfo.logicalWidth - mCompatFrame.left,
                    displayInfo.logicalHeight - mCompatFrame.top);
        } else if (mLayer >= mService.mSystemDecorLayer) {
            // Above the decor layer is easy, just use the entire window
            policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
        } else if (mDecorFrame.isEmpty()) {
            // Windows without policy decor aren't cropped.
            policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
        } else {
            // Crop to the system decor specified by policy.
            calculateSystemDecorRect(policyCrop);
        }
    }

    /**
     * The system decor rect is the region of the window which is not covered
     * by system decorations.
     */
    private void calculateSystemDecorRect(Rect systemDecorRect) {
        final Rect decorRect = mDecorFrame;
        final int width = mFrame.width();
        final int height = mFrame.height();

        // Compute the offset of the window in relation to the decor rect.
        final int left = mXOffset + mFrame.left;
        final int top = mYOffset + mFrame.top;

        // Initialize the decor rect to the entire frame.
        if (isDockedResizing()) {
            // If we are resizing with the divider, the task bounds might be smaller than the
            // stack bounds. The system decor is used to clip to the task bounds, which we don't
            // want in this case in order to avoid holes.
            //
            // We take care to not shrink the width, for surfaces which are larger than
            // the display region. Of course this area will not eventually be visible
            // but if we truncate the width now, we will calculate incorrectly
            // when adjusting to the stack bounds.
            final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
            systemDecorRect.set(0, 0,
                    Math.max(width, displayInfo.logicalWidth),
                    Math.max(height, displayInfo.logicalHeight));
        } else {
            systemDecorRect.set(0, 0, width, height);
        }

        // If a freeform window is animating from a position where it would be cutoff, it would be
        // cutoff during the animation. We don't want that, so for the duration of the animation
        // we ignore the decor cropping and depend on layering to position windows correctly.
        final boolean cropToDecor = !(inFreeformWorkspace() && isAnimatingLw());
        if (cropToDecor) {
            // Intersect with the decor rect, offsetted by window position.
            systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
                    decorRect.right - left, decorRect.bottom - top);
        }

        // If size compatibility is being applied to the window, the
        // surface is scaled relative to the screen.  Also apply this
        // scaling to the crop rect.  We aren't using the standard rect
        // scale function because we want to round things to make the crop
        // always round to a larger rect to ensure we don't crop too
        // much and hide part of the window that should be seen.
        if (mEnforceSizeCompat && mInvGlobalScale != 1.0f) {
            final float scale = mInvGlobalScale;
            systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
            systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
            systemDecorRect.right = (int) ((systemDecorRect.right + 1) * scale - 0.5f);
            systemDecorRect.bottom = (int) ((systemDecorRect.bottom + 1) * scale - 0.5f);
        }

    }

    /**
     * Expand the given rectangle by this windows surface insets. This
     * takes you from the 'window size' to the 'surface size'.
     * The surface insets are positive in each direction, so we inset by
     * the inverse.
     */
    void expandForSurfaceInsets(Rect r) {
        r.inset(-mAttrs.surfaceInsets.left,
                -mAttrs.surfaceInsets.top,
                -mAttrs.surfaceInsets.right,
                -mAttrs.surfaceInsets.bottom);
    }

    // TODO: Hack to work around the number of states AppWindowToken needs to access without having
    // access to its windows children. Need to investigate re-writing
    // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
+106 −129
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -1068,99 +1069,75 @@ class WindowStateAnimator {
        }
    }

    private void calculateSystemDecorRect() {
        final WindowState w = mWin;
        final Rect decorRect = w.mDecorFrame;
        final int width = w.mFrame.width();
        final int height = w.mFrame.height();

        // Compute the offset of the window in relation to the decor rect.
        final int left = w.mXOffset + w.mFrame.left;
        final int top = w.mYOffset + w.mFrame.top;

        // Initialize the decor rect to the entire frame.
        if (w.isDockedResizing() ||
                (w.isChildWindow() && w.getParentWindow().isDockedResizing())) {

            // If we are resizing with the divider, the task bounds might be smaller than the
            // stack bounds. The system decor is used to clip to the task bounds, which we don't
            // want in this case in order to avoid holes.
            //
            // We take care to not shrink the width, for surfaces which are larger than
            // the display region. Of course this area will not eventually be visible
            // but if we truncate the width now, we will calculate incorrectly
            // when adjusting to the stack bounds.
            final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
            mSystemDecorRect.set(0, 0,
                    Math.max(width, displayInfo.logicalWidth),
                    Math.max(height, displayInfo.logicalHeight));
        } else {
            mSystemDecorRect.set(0, 0, width, height);
    /**
     * In some scenarios we use a screen space clip rect (so called, final clip rect)
     * to crop to stack bounds. Generally because it's easier to deal with while
     * animating.
     *
     * @return True in scenarios where we use the final clip rect for stack clipping.
     */
    private boolean useFinalClipRect() {
        return (isAnimationSet() && resolveStackClip() == STACK_CLIP_AFTER_ANIM)
                || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWorkspace();
    }

        // If a freeform window is animating from a position where it would be cutoff, it would be
        // cutoff during the animation. We don't want that, so for the duration of the animation
        // we ignore the decor cropping and depend on layering to position windows correctly.
        final boolean cropToDecor = !(w.inFreeformWorkspace() && w.isAnimatingLw());
        if (cropToDecor) {
            // Intersect with the decor rect, offsetted by window position.
            mSystemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
                    decorRect.right - left, decorRect.bottom - top);
    /**
     * Calculate the screen-space crop rect and fill finalClipRect.
     * @return true if finalClipRect has been filled, otherwise,
     * no screen space crop should be applied.
     */
    private boolean calculateFinalCrop(Rect finalClipRect) {
        final WindowState w = mWin;
        final DisplayContent displayContent = w.getDisplayContent();
        finalClipRect.setEmpty();

        if (displayContent == null) {
            return false;
        }

        // If size compatibility is being applied to the window, the
        // surface is scaled relative to the screen.  Also apply this
        // scaling to the crop rect.  We aren't using the standard rect
        // scale function because we want to round things to make the crop
        // always round to a larger rect to ensure we don't crop too
        // much and hide part of the window that should be seen.
        if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) {
            final float scale = w.mInvGlobalScale;
            mSystemDecorRect.left = (int) (mSystemDecorRect.left * scale - 0.5f);
            mSystemDecorRect.top = (int) (mSystemDecorRect.top * scale - 0.5f);
            mSystemDecorRect.right = (int) ((mSystemDecorRect.right + 1) * scale - 0.5f);
            mSystemDecorRect.bottom = (int) ((mSystemDecorRect.bottom + 1) * scale - 0.5f);
        if (!shouldCropToStackBounds() || !useFinalClipRect()) {
            return false;
        }

        // Task is non-null per shouldCropToStackBounds
        final TaskStack stack = w.getTask().mStack;
        stack.getDimBounds(finalClipRect);
        w.expandForSurfaceInsets(finalClipRect);
        return true;
    }

    void calculateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect) {
    /**
     * Calculate the window-space crop rect and fill clipRect.
     * @return true if clipRect has been filled otherwise, no window space
     * crop should be applied.
     */
    boolean calculateCrop(Rect clipRect) {
        final WindowState w = mWin;
        final DisplayContent displayContent = w.getDisplayContent();
        if (displayContent == null) {
        clipRect.setEmpty();
            finalClipRect.setEmpty();
            return;

        if (displayContent == null) {
            return false;
        }

        if (w.inPinnedWorkspace()) {
            return false;
        }

        // If we're animating, the wallpaper should only
        // be updated at the end of the animation.
        if (w.mAttrs.type == TYPE_WALLPAPER) {
            return false;
        }

        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
        if (DEBUG_WINDOW_CROP) Slog.d(TAG,
                "Updating crop win=" + w + " mLastCrop=" + mLastClipRect);

        // Need to recompute a new system decor rect each time.
        if (!w.isDefaultDisplay()) {
            // On a different display there is no system decor.  Crop the window
            // by the screen boundaries.
            mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
            mSystemDecorRect.intersect(-w.mCompatFrame.left, -w.mCompatFrame.top,
                    displayInfo.logicalWidth - w.mCompatFrame.left,
                    displayInfo.logicalHeight - w.mCompatFrame.top);
        } else if (w.mLayer >= mService.mSystemDecorLayer) {
            // Above the decor layer is easy, just use the entire window.
            mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
        } else if (w.mDecorFrame.isEmpty()) {
            // Windows without policy decor aren't cropped.
            mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
        } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.isAnimating()) {
            // If we're animating, the wallpaper crop should only be updated at the end of the
            // animation.
            mTmpClipRect.set(mSystemDecorRect);
            calculateSystemDecorRect();
            mSystemDecorRect.union(mTmpClipRect);
        } else {
            // Crop to the system decor specified by policy.
            calculateSystemDecorRect();
        w.calculatePolicyCrop(mSystemDecorRect);

        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
                + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
        }

        final boolean fullscreen = w.isFrameFullscreen(displayInfo);
        final boolean isFreeformResizing =
@@ -1178,12 +1155,7 @@ class WindowStateAnimator {
            clipRect.offset(w.mShownPosition.x, w.mShownPosition.y);
        }

        // Expand the clip rect for surface insets.
        final WindowManager.LayoutParams attrs = w.mAttrs;
        clipRect.left -= attrs.surfaceInsets.left;
        clipRect.top -= attrs.surfaceInsets.top;
        clipRect.right += attrs.surfaceInsets.right;
        clipRect.bottom += attrs.surfaceInsets.bottom;
        w.expandForSurfaceInsets(clipRect);

        if (mHasClipRect && fullscreen) {
            // We intersect the clip rect specified by the transformation with the expanded system
@@ -1193,10 +1165,11 @@ class WindowStateAnimator {
        }
        // The clip rect was generated assuming (0,0) as the window origin,
        // so we need to translate to match the actual surface coordinates.
        clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
        clipRect.offset(w.mAttrs.surfaceInsets.left, w.mAttrs.surfaceInsets.top);

        finalClipRect.setEmpty();
        adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing);
        if (!useFinalClipRect()) {
            adjustCropToStackBounds(clipRect, isFreeformResizing);
        }
        if (DEBUG_WINDOW_CROP) Slog.d(TAG,
                "win=" + w + " Clip rect after stack adjustment=" + clipRect);

@@ -1206,10 +1179,11 @@ class WindowStateAnimator {
        if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) {
            clipRect.setEmpty();
        }
        return true;
    }

    void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "updateSurfaceWindowCrop: win=" + mWin
    private void applyCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "applyCrop: win=" + mWin
                + " clipRect=" + clipRect + " finalClipRect=" + finalClipRect);
        if (clipRect != null) {
            if (!clipRect.equals(mLastClipRect)) {
@@ -1219,6 +1193,11 @@ class WindowStateAnimator {
        } else {
            mSurfaceController.clearCropInTransaction(recoveringMemory);
        }

        if (finalClipRect == null) {
            finalClipRect = mService.mTmpRect;
            finalClipRect.setEmpty();
        }
        if (!finalClipRect.equals(mLastFinalClipRect)) {
            mLastFinalClipRect.set(finalClipRect);
            mSurfaceController.setFinalCropInTransaction(finalClipRect);
@@ -1236,9 +1215,9 @@ class WindowStateAnimator {
            return mStackClip;
        }
    }
    private void adjustCropToStackBounds(WindowState w, Rect clipRect, Rect finalClipRect,
            boolean isFreeformResizing) {

    private boolean shouldCropToStackBounds() {
        final WindowState w = mWin;
        final DisplayContent displayContent = w.getDisplayContent();
        if (displayContent != null && !displayContent.isDefaultDisplay) {
            // There are some windows that live on other displays while their app and main window
@@ -1246,22 +1225,32 @@ class WindowStateAnimator {
            // to the stack bounds which is only currently supported on the default display.
            // TODO(multi-display): Need to support cropping to stack bounds on other displays
            // when we have stacks on other displays.
            return;
            return false;
        }

        final Task task = w.getTask();
        if (task == null || !task.cropWindowsToStackBounds()) {
            return;
            return false;
        }

        final int stackClip = resolveStackClip();

        // It's animating and we don't want to clip it to stack bounds during animation - abort.
        if (isAnimationSet() && stackClip == STACK_CLIP_NONE) {
            return false;
        }
        return true;
    }

    private void adjustCropToStackBounds(Rect clipRect,
            boolean isFreeformResizing) {
        final WindowState w = mWin;

        if (!shouldCropToStackBounds()) {
            return;
        }

        final TaskStack stack = task.mStack;
        final TaskStack stack = w.getTask().mStack;
        stack.getDimBounds(mTmpStackBounds);
        final Rect surfaceInsets = w.getAttrs().surfaceInsets;
        // When we resize we use the big surface approach, which means we can't trust the
@@ -1272,16 +1261,8 @@ class WindowStateAnimator {
        final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
                w.mFrame.top + mWin.mYOffset - surfaceInsets.top;

        // If we are animating, we either apply the clip before applying all the animation
        // transformation or after all the transformation.
        final boolean useFinalClipRect = isAnimationSet() && stackClip == STACK_CLIP_AFTER_ANIM
                || mDestroyPreservedSurfaceUponRedraw;

        // We need to do some acrobatics with surface position, because their clip region is
        // relative to the inside of the surface, but the stack bounds aren't.
        if (useFinalClipRect) {
            finalClipRect.set(mTmpStackBounds);
        } else {
        if (StackId.hasWindowShadow(stack.mStackId)
                && !StackId.isTaskResizeAllowed(stack.mStackId)) {
                // The windows in this stack display drop shadows and the fill the entire stack
@@ -1300,7 +1281,6 @@ class WindowStateAnimator {
        clipRect.bottom = Math.max(0,
                Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
    }
    }

    void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
        final WindowState w = mWin;
@@ -1339,7 +1319,13 @@ class WindowStateAnimator {
        // updates until a resize occurs.
        mService.markForSeamlessRotation(w, w.mSeamlesslyRotated && !mSurfaceResized);

        calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
        Rect clipRect = null, finalClipRect = null;
        if (calculateCrop(mTmpClipRect)) {
            clipRect = mTmpClipRect;
        }
        if (calculateFinalCrop(mTmpFinalClipRect)) {
            finalClipRect = mTmpFinalClipRect;
        }

        float surfaceWidth = mSurfaceController.getWidth();
        float surfaceHeight = mSurfaceController.getHeight();
@@ -1404,16 +1390,8 @@ class WindowStateAnimator {
            mSurfaceController.forceScaleableInTransaction(false);
        }

        Rect clipRect = mTmpClipRect;
        if (w.inPinnedWorkspace()) {
            clipRect = null;
            task.mStack.getDimBounds(mTmpFinalClipRect);
            mTmpFinalClipRect.inset(-w.mAttrs.surfaceInsets.left, -w.mAttrs.surfaceInsets.top,
                    -w.mAttrs.surfaceInsets.right, -w.mAttrs.surfaceInsets.bottom);
        }

        if (!w.mSeamlesslyRotated) {
            updateSurfaceWindowCrop(clipRect, mTmpFinalClipRect, recoveringMemory);
            applyCrop(clipRect, finalClipRect, recoveringMemory);
            mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
                    mDtDx * w.mVScale * mExtraVScale,
                    mDsDy * w.mHScale * mExtraHScale,
@@ -1571,8 +1549,7 @@ class WindowStateAnimator {
            mService.openSurfaceTransaction();
            mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
                    mWin.mFrame.top + top, false);
            calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, false);
            applyCrop(null, null, false);
        } catch (RuntimeException e) {
            Slog.w(TAG, "Error positioning surface of " + mWin
                    + " pos=(" + left + "," + top + ")", e);
+68 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IWindow;
import android.view.WindowManager;
@@ -53,6 +54,7 @@ public class WindowFrameTests {

    class WindowStateWithTask extends WindowState {
        final Task mTask;
        boolean mDockedResizingForTest = false;
        WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
            super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0);
            mTask = t;
@@ -62,6 +64,11 @@ public class WindowFrameTests {
        Task getTask() {
            return mTask;
        }

        @Override
        boolean isDockedResizing() {
            return mDockedResizingForTest;
        }
    };

    class TaskWithBounds extends Task {
@@ -92,6 +99,10 @@ public class WindowFrameTests {
    public void setUp() throws Exception {
        final Context context = InstrumentationRegistry.getTargetContext();
        sWm = TestWindowManagerPolicy.getWindowManagerService(context);

        // Just any non zero value.
        sWm.mSystemDecorLayer = 10000;

        mWindowToken = new WindowToken(sWm, new Binder(), 0, false,
                sWm.getDefaultDisplayContentLocked());
        mStubStack = new TaskStack(sWm, 0);
@@ -274,7 +285,63 @@ public class WindowFrameTests {
        assertRect(w.mContentInsets, 0, 0, 100, 100);
    }

    private WindowState createWindow(Task task, int width, int height) {
    @Test
    public void testCalculatePolicyCrop() {
        final WindowStateWithTask w = createWindow(
                new TaskWithBounds(null), FILL_PARENT, FILL_PARENT);
        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;

        final Rect pf = new Rect(0, 0, 1000, 1000);
        final Rect df = pf;
        final Rect of = df;
        final Rect cf = new Rect(pf);
        // Produce some insets
        cf.top += 50;
        cf.bottom -= 100;
        final Rect vf = cf;
        final Rect sf = vf;
        // We use a decor content frame with insets to produce cropping.
        Rect dcf = cf;

        final Rect policyCrop = new Rect();

        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
        w.calculatePolicyCrop(policyCrop);
        // If we were above system decor we wouldnt' get any cropping though
        w.mLayer = sWm.mSystemDecorLayer + 1;
        w.calculatePolicyCrop(policyCrop);
        assertRect(policyCrop, 0, 0, 1000, 1000);
        w.mLayer = 1;
        dcf.setEmpty();
        // Likewise with no decor frame we would get no crop
        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
        w.calculatePolicyCrop(policyCrop);
        assertRect(policyCrop, 0, 0, 1000, 1000);

        // Now we set up a window which doesn't fill the entire decor frame.
        // Normally it would be cropped to it's frame but in the case of docked resizing
        // we need to account for the fact the windows surface will be made
        // fullscreen and thus also make the crop fullscreen.
        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
        w.mAttrs.width = 500;
        w.mAttrs.height = 500;
        w.mRequestedWidth = 500;
        w.mRequestedHeight = 500;
        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);

        w.calculatePolicyCrop(policyCrop);
        // Normally the crop is shrunk from the decor frame
        // to the computed window frame.
        assertRect(policyCrop, 0, 0, 500, 500);

        w.mDockedResizingForTest = true;
        w.calculatePolicyCrop(policyCrop);
        // But if we are docked resizing it won't be.
        final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
        assertRect(policyCrop, 0, 0, 1000, 1000);
    }

    private WindowStateWithTask createWindow(Task task, int width, int height) {
        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
        attrs.width = width;
        attrs.height = height;