Merge "Add shadow during resizing in docked mode" into nyc-dev
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7c06577..9418aaf 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.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 @@
*/
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<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
@@ -233,6 +231,7 @@
boolean mIsAnimating;
private boolean mDragResizing;
+ private boolean mInvalidateRootRequested;
private int mResizeMode;
private int mCanvasOffsetX;
private int mCanvasOffsetY;
@@ -1828,12 +1827,12 @@
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 @@
@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 @@
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 @@
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 @@
* 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 d2bfca7..b2dc1e9 100644
--- a/core/java/android/view/WindowCallbacks.java
+++ b/core/java/android/view/WindowCallbacks.java
@@ -26,6 +26,11 @@
* @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 @@
* @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 @@
* @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 6931193..738aaca 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 @@
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 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 @@
mStableInsets.set(stableInsets);
mOldSystemInsets.set(systemInsets);
mOldStableInsets.set(stableInsets);
+ mResizeMode = resizeMode;
synchronized (this) {
redrawLocked(initialBounds, fullscreen, mSystemInsets, mStableInsets);
}
@@ -266,11 +270,16 @@
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 bdf2a21..9904893 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.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.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_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 @@
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 @@
setWindow(window);
updateLogTag(params);
+
+ mResizeShadowSize = context.getResources().getDimensionPixelSize(
+ R.dimen.resize_shadow_size);
+ initResizingPaints();
}
void setBackgroundFallback(int resId) {
@@ -699,6 +713,10 @@
// our shadow elevation.
updateElevation();
mAllowUpdateElevation = true;
+
+ if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
+ getViewRootImpl().requestInvalidateRootRenderNode();
+ }
}
@Override
@@ -1907,7 +1925,7 @@
@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 @@
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 @@
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 @@
}
}
+ @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 7637eec..794a6d6 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -157,6 +157,7 @@
InputQueue.Callback mTakeInputQueueCallback;
boolean mIsFloating;
+ private boolean mIsTranslucent;
private LayoutInflater mLayoutInflater;
@@ -490,6 +491,10 @@
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 @@
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 48aa440..bddd225 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -180,4 +180,7 @@
<!-- Status bar color for semi transparent mode. -->
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
+
+ <color name="resize_shadow_start_color">#2a000000</color>
+ <color name="resize_shadow_end_color">#00000000</color>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9b7b1d4..71d9a1f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -452,4 +452,6 @@
<item type="dimen" name="aerr_padding_list_top">15dp</item>
<item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item>
+
+ <dimen name="resize_shadow_size">5dp</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7426ddf..3f46768 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1512,6 +1512,9 @@
<java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
<java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
<java-symbol type="fraction" name="thumbnail_fullscreen_scale" />
+ <java-symbol type="dimen" name="resize_shadow_size" />
+ <java-symbol type="color" name="resize_shadow_start_color" />
+ <java-symbol type="color" name="resize_shadow_end_color" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />