diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7c0657795f515..9418aaf0c826a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,7 +16,8 @@ package android.view; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; +import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -96,8 +97,8 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; import java.util.HashSet; +import java.util.concurrent.CountDownLatch; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -146,9 +147,6 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; - private static final int RESIZE_MODE_FREEFORM = 0; - private static final int RESIZE_MODE_DOCKED_DIVIDER = 1; - static final ThreadLocal sRunQueues = new ThreadLocal(); static final ArrayList sFirstDrawHandlers = new ArrayList(); @@ -233,6 +231,7 @@ public final class ViewRootImpl implements ViewParent, boolean mIsAnimating; private boolean mDragResizing; + private boolean mInvalidateRootRequested; private int mResizeMode; private int mCanvasOffsetX; private int mCanvasOffsetY; @@ -1828,12 +1827,12 @@ public final class ViewRootImpl implements ViewParent, final boolean dragResizing = freeformResizing || dockedResizing; if (mDragResizing != dragResizing) { if (dragResizing) { - startDragResizing(mPendingBackDropFrame, - mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets, - mPendingStableInsets); mResizeMode = freeformResizing ? RESIZE_MODE_FREEFORM : RESIZE_MODE_DOCKED_DIVIDER; + startDragResizing(mPendingBackDropFrame, + mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets, + mPendingStableInsets, mResizeMode); } else { // We shouldn't come here, but if we come we should end the resize. endDragResizing(); @@ -2438,6 +2437,11 @@ public final class ViewRootImpl implements ViewParent, @Override public void onHardwarePostDraw(DisplayListCanvas canvas) { drawAccessibilityFocusedDrawableIfNeeded(canvas); + synchronized (mWindowCallbacks) { + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onPostDraw(canvas); + } + } } /** @@ -2675,7 +2679,8 @@ public final class ViewRootImpl implements ViewParent, if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { // If accessibility focus moved, always invalidate the root. - boolean invalidateRoot = accessibilityFocusDirty; + boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested; + mInvalidateRootRequested = false; // Draw with hardware renderer. mIsAnimating = false; @@ -2908,6 +2913,14 @@ public final class ViewRootImpl implements ViewParent, return mAttachInfo.mAccessibilityFocusDrawable; } + /** + * Requests that the root render node is invalidated next time we perform a draw, such that + * {@link WindowCallbacks#onPostDraw} gets called. + */ + public void requestInvalidateRootRenderNode() { + mInvalidateRootRequested = true; + } + boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) { final Rect ci = mAttachInfo.mContentInsets; final Rect vi = mAttachInfo.mVisibleInsets; @@ -7090,13 +7103,13 @@ public final class ViewRootImpl implements ViewParent, * Start a drag resizing which will inform all listeners that a window resize is taking place. */ private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets, - Rect stableInsets) { + Rect stableInsets, int resizeMode) { if (!mDragResizing) { mDragResizing = true; synchronized (mWindowCallbacks) { for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen, - systemInsets, stableInsets); + systemInsets, stableInsets, resizeMode); } } mFullRedrawNeeded = true; diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java index d2bfca7e1c30c..b2dc1e91bdf7d 100644 --- a/core/java/android/view/WindowCallbacks.java +++ b/core/java/android/view/WindowCallbacks.java @@ -26,6 +26,11 @@ import android.graphics.Rect; * @hide */ public interface WindowCallbacks { + + public static final int RESIZE_MODE_INVALID = -1; + public static final int RESIZE_MODE_FREEFORM = 0; + public static final int RESIZE_MODE_DOCKED_DIVIDER = 1; + /** * Called by the system when the window got changed by the user, before the layouter got called. * It also gets called when the insets changed, or when the window switched between a fullscreen @@ -51,7 +56,7 @@ public interface WindowCallbacks { * @param stableInsets The stable insets for the window. */ void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, - Rect stableInsets); + Rect stableInsets, int resizeMode); /** * Called when a drag resize ends. @@ -69,4 +74,13 @@ public interface WindowCallbacks { * @param reportNextDraw Whether it should report when the requested draw finishes. */ void onRequestDraw(boolean reportNextDraw); + + /** + * Called after all the content has drawn and the callback now has the ability to draw something + * on top of everything. Call {@link ViewRootImpl#requestInvalidateRootRenderNode} when this + * content needs to be redrawn. + * + * @param canvas The canvas to draw on. + */ + void onPostDraw(DisplayListCanvas canvas); } diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 69311938c5ade..738aaca5d47d3 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -16,6 +16,8 @@ package com.android.internal.policy; +import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; + import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -69,6 +71,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame private ColorDrawable mNavigationBarColor; private boolean mOldFullscreen; private boolean mFullscreen; + private final int mResizeMode; private final Rect mOldSystemInsets = new Rect(); private final Rect mOldStableInsets = new Rect(); private final Rect mSystemInsets = new Rect(); @@ -77,7 +80,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds, Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable, Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor, - boolean fullscreen, Rect systemInsets, Rect stableInsets) { + boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode) { setName("ResizeFrame"); mRenderer = renderer; @@ -98,6 +101,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mStableInsets.set(stableInsets); mOldSystemInsets.set(systemInsets); mOldStableInsets.set(stableInsets); + mResizeMode = resizeMode; synchronized (this) { redrawLocked(initialBounds, fullscreen, mSystemInsets, mStableInsets); } @@ -266,11 +270,16 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mLastXOffset = xOffset; mLastYOffset = yOffset; - mRenderer.setContentDrawBounds( - mLastXOffset, - mLastYOffset, - mLastXOffset + mLastContentWidth, - mLastYOffset + mLastCaptionHeight + mLastContentHeight); + // Only clip the content to the bounds if we are not fullscreen. In the other case, we + // actually need to draw outside these. + if (mResizeMode == RESIZE_MODE_FREEFORM) { + mRenderer.setContentDrawBounds( + mLastXOffset, + mLastYOffset, + mLastXOffset + mLastContentWidth, + mLastYOffset + mLastCaptionHeight + mLastContentHeight); + } + // If this was the first call and redrawLocked got already called prior // to us, we should re-issue a redrawLocked now. return firstCall diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index bdf2a214d8495..9904893f3e5c2 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -39,8 +39,11 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.RemoteException; @@ -49,6 +52,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; import android.view.ContextThemeWrapper; +import android.view.DisplayListCanvas; import android.view.Gravity; import android.view.InputQueue; import android.view.KeyEvent; @@ -88,6 +92,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -211,6 +216,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private boolean mApplyFloatingVerticalInsets = false; private boolean mApplyFloatingHorizontalInsets = false; + private int mResizeMode = RESIZE_MODE_INVALID; + private final int mResizeShadowSize; + private final Paint mVerticalResizeShadowPaint = new Paint(); + private final Paint mHorizontalResizeShadowPaint = new Paint(); + DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { super(context); @@ -233,6 +243,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind setWindow(window); updateLogTag(params); + + mResizeShadowSize = context.getResources().getDimensionPixelSize( + R.dimen.resize_shadow_size); + initResizingPaints(); } void setBackgroundFallback(int resId) { @@ -699,6 +713,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // our shadow elevation. updateElevation(); mAllowUpdateElevation = true; + + if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) { + getViewRootImpl().requestInvalidateRootRenderNode(); + } } @Override @@ -1907,7 +1925,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind @Override public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, - Rect stableInsets) { + Rect stableInsets, int resizeMode) { if (mWindow.isDestroyed()) { // If the owner's window is gone, we should not be able to come here anymore. releaseThreadedRenderer(); @@ -1923,7 +1941,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets, - stableInsets); + stableInsets, resizeMode); // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time. // If we want to get the shadow shown while resizing, we would need to elevate a new @@ -1932,12 +1950,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind updateColorViews(null /* insets */, false); } + mResizeMode = resizeMode; + getViewRootImpl().requestInvalidateRootRenderNode(); } @Override public void onWindowDragResizeEnd() { releaseThreadedRenderer(); updateColorViews(null /* insets */, false); + mResizeMode = RESIZE_MODE_INVALID; + getViewRootImpl().requestInvalidateRootRenderNode(); } @Override @@ -1960,6 +1982,41 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } + @Override + public void onPostDraw(DisplayListCanvas canvas) { + drawResizingShadowIfNeeded(canvas); + } + + private void initResizingPaints() { + final int startColor = mContext.getResources().getColor( + R.color.resize_shadow_start_color, null); + final int endColor = mContext.getResources().getColor( + R.color.resize_shadow_end_color, null); + final int middleColor = (startColor + endColor) / 2; + mHorizontalResizeShadowPaint.setShader(new LinearGradient( + 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor }, + new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP)); + mVerticalResizeShadowPaint.setShader(new LinearGradient( + 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor }, + new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP)); + } + + private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) { + if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating + || mWindow.isTranslucent() + || (mWindow.getAttributes().flags & FLAG_SHOW_WALLPAPER) != 0) { + return; + } + canvas.save(); + canvas.translate(0, getHeight() - mFrameOffsets.bottom); + canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint); + canvas.restore(); + canvas.save(); + canvas.translate(getWidth() - mFrameOffsets.right, 0); + canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint); + canvas.restore(); + } + /** Release the renderer thread which is usually done when the user stops resizing. */ private void releaseThreadedRenderer() { if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) { diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 7637eec4b29fe..794a6d6789366 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -157,6 +157,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { InputQueue.Callback mTakeInputQueueCallback; boolean mIsFloating; + private boolean mIsTranslucent; private LayoutInflater mLayoutInflater; @@ -490,6 +491,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mIsFloating; } + public boolean isTranslucent() { + return mIsTranslucent; + } + /** * Return a LayoutInflater instance that can be used to inflate XML view layout * resources for use in this Window. @@ -2400,6 +2405,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { requestFeature(FEATURE_ACTIVITY_TRANSITIONS); } + mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false); + final Context context = getContext(); final int targetSdk = context.getApplicationInfo().targetSdkVersion; final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 48aa44059b82d..bddd2255aa815 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -180,4 +180,7 @@ #66000000 + + #2a000000 + #00000000 diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 9b7b1d46068c6..71d9a1f6c6ef6 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -452,4 +452,6 @@ 15dp 34.15% + + 5dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7426ddf3b33c0..3f4676839c1a2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1512,6 +1512,9 @@ + + +