diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8f4a836b68614..ae8e43c4f6105 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -77,6 +77,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; 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_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -502,6 +503,13 @@ public final class ViewRootImpl implements ViewParent, Region mTouchableRegion; Region mPreviousTouchableRegion; + private int mMeasuredWidth; + private int mMeasuredHeight; + + // This indicates that we've already known the window size but without measuring the views. + // If this is true, we must measure the views before laying out them. + private boolean mViewMeasureDeferred; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) int mWidth; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -2557,7 +2565,8 @@ public final class ViewRootImpl implements ViewParent, } private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, - final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { + final Resources res, final int desiredWindowWidth, final int desiredWindowHeight, + boolean forRootSizeOnly) { int childWidthMeasureSpec; int childHeightMeasureSpec; boolean windowSizeMayChange = false; @@ -2613,7 +2622,15 @@ public final class ViewRootImpl implements ViewParent, lp.privateFlags); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height, lp.privateFlags); - performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); + if (!forRootSizeOnly || !setMeasuredRootSizeFromSpec( + childWidthMeasureSpec, childHeightMeasureSpec)) { + performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); + } else { + // We already know how big the window should be before measuring the views. + // We can measure the views before laying out them. This is to avoid unnecessary + // measure. + mViewMeasureDeferred = true; + } if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { windowSizeMayChange = true; } @@ -2628,6 +2645,25 @@ public final class ViewRootImpl implements ViewParent, return windowSizeMayChange; } + /** + * Sets the measured root size for requesting the window frame. + * + * @param widthMeasureSpec contains the size and the mode of the width. + * @param heightMeasureSpec contains the size and the mode of the height. + * @return {@code true} if we actually set the measured size; {@code false} otherwise. + */ + private boolean setMeasuredRootSizeFromSpec(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) { + // We don't know the exact size. We need to measure the hierarchy to know that. + return false; + } + mMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec); + mMeasuredHeight = MeasureSpec.getSize(heightMeasureSpec); + return true; + } + /** * Modifies the input matrix such that it maps view-local coordinates to * on-screen coordinates. @@ -2715,6 +2751,14 @@ public final class ViewRootImpl implements ViewParent, || lp.type == TYPE_VOLUME_OVERLAY; } + /** + * @return {@code true} if we should reduce unnecessary measure for the window. + * TODO(b/260382739): Apply this to all windows. + */ + private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) { + return lp.type == TYPE_NOTIFICATION_SHADE; + } + private Rect getWindowBoundsInsetSystemBars() { final Rect bounds = new Rect( mContext.getResources().getConfiguration().windowConfiguration.getBounds()); @@ -2765,6 +2809,7 @@ public final class ViewRootImpl implements ViewParent, mAppVisibilityChanged = false; final boolean viewUserVisibilityChanged = !mFirst && ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE)); + final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp); WindowManager.LayoutParams params = null; CompatibilityInfo compatibilityInfo = @@ -2886,7 +2931,7 @@ public final class ViewRootImpl implements ViewParent, // Ask host how big it wants to be windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(), - desiredWindowWidth, desiredWindowHeight); + desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure); } if (collectViewAttributes()) { @@ -2926,8 +2971,8 @@ public final class ViewRootImpl implements ViewParent, // we don't need to go through two layout passes when things // change due to fitting system windows, which can happen a lot. windowSizeMayChange |= measureHierarchy(host, lp, - mView.getContext().getResources(), - desiredWindowWidth, desiredWindowHeight); + mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight, + shouldOptimizeMeasure); } } @@ -3342,6 +3387,13 @@ public final class ViewRootImpl implements ViewParent, maybeHandleWindowMove(frame); } + if (mViewMeasureDeferred) { + // It's time to measure the views since we are going to layout them. + performMeasure( + MeasureSpec.makeMeasureSpec(frame.width(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(frame.height(), MeasureSpec.EXACTLY)); + } + if (!mRelayoutRequested && mCheckIfCanDraw) { // We had a sync previously, but we didn't call IWindowSession#relayout in this // traversal. So we don't know if the sync is complete that we can continue to draw. @@ -3931,6 +3983,9 @@ public final class ViewRootImpl implements ViewParent, } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + mMeasuredWidth = mView.getMeasuredWidth(); + mMeasuredHeight = mView.getMeasuredHeight(); + mViewMeasureDeferred = false; } /** @@ -4026,7 +4081,7 @@ public final class ViewRootImpl implements ViewParent, view.requestLayout(); } measureHierarchy(host, lp, mView.getContext().getResources(), - desiredWindowWidth, desiredWindowHeight); + desiredWindowWidth, desiredWindowHeight, false /* forRootSizeOnly */); mInLayout = true; host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); @@ -8092,8 +8147,8 @@ public final class ViewRootImpl implements ViewParent, final WindowConfiguration winConfigFromWm = mLastReportedMergedConfiguration.getGlobalConfiguration().windowConfiguration; final WindowConfiguration winConfig = getCompatWindowConfiguration(); - final int measuredWidth = mView.getMeasuredWidth(); - final int measuredHeight = mView.getMeasuredHeight(); + final int measuredWidth = mMeasuredWidth; + final int measuredHeight = mMeasuredHeight; final boolean relayoutAsync; if (LOCAL_LAYOUT && (mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0