Loading services/core/java/com/android/server/wm/WindowState.java +101 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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. Loading services/core/java/com/android/server/wm/WindowStateAnimator.java +106 −129 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 = Loading @@ -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 Loading @@ -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); Loading @@ -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)) { Loading @@ -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); Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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, Loading Loading @@ -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); Loading services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +68 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -62,6 +64,11 @@ public class WindowFrameTests { Task getTask() { return mTask; } @Override boolean isDockedResizing() { return mDockedResizingForTest; } }; class TaskWithBounds extends Task { Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading
services/core/java/com/android/server/wm/WindowState.java +101 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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. Loading
services/core/java/com/android/server/wm/WindowStateAnimator.java +106 −129 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 = Loading @@ -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 Loading @@ -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); Loading @@ -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)) { Loading @@ -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); Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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, Loading Loading @@ -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); Loading
services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +68 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -62,6 +64,11 @@ public class WindowFrameTests { Task getTask() { return mTask; } @Override boolean isDockedResizing() { return mDockedResizingForTest; } }; class TaskWithBounds extends Task { Loading Loading @@ -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); Loading Loading @@ -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; Loading