Add API for specifying popup window shadows and shadow insets
BUG: 14569120
BUG: 13211941
Change-Id: Ia21596b25a0471344d42d59377074f67fce00042
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d69d01d..be677ea 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Surface.OutOfResourcesException;
@@ -247,21 +248,24 @@
abstract void detachSurfaceTexture(long hardwareLayer);
/**
- * Setup the hardware renderer for drawing. This is called whenever the
- * size of the target surface changes or when the surface is first created.
+ * Setup the hardware renderer for drawing. This is called whenever the size
+ * of the target surface changes or when the surface is first created.
*
* @param width Width of the drawing surface.
* @param height Height of the drawing surface.
+ * @param surfaceInsets Insets between the drawing surface and actual
+ * surface bounds.
* @param lightX X position of the shadow casting light
* @param lightY Y position of the shadow casting light
* @param lightZ Z position of the shadow casting light
* @param lightRadius radius of the shadow casting light
*/
- abstract void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius);
+ abstract void setup(int width, int height, Rect surfaceInsets, float lightX, float lightY,
+ float lightZ, float lightRadius);
/**
* Gets the current width of the surface. This is the width that the surface
- * was last set to in a call to {@link #setup(int, int, float, float, float, float)}.
+ * was last set to in a call to {@link #setup(int, int, Rect, float, float, float, float)}.
*
* @return the current width of the surface
*/
@@ -269,7 +273,7 @@
/**
* Gets the current height of the surface. This is the height that the surface
- * was last set to in a call to {@link #setup(int, int, float, float, float, float)}.
+ * was last set to in a call to {@link #setup(int, int, Rect, float, float, float, float)}.
*
* @return the current width of the surface
*/
@@ -344,7 +348,6 @@
* @param view The view to draw.
* @param attachInfo AttachInfo tied to the specified view.
* @param callbacks Callbacks invoked when drawing happens.
- * @param dirty The dirty rectangle to update, can be null.
*/
abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks);
@@ -369,17 +372,18 @@
* @param height The height of the drawing surface.
* @param surface The surface to hardware accelerate
* @param metrics The display metrics used to draw the output.
+ * @param surfaceInsets The drawing surface insets to apply
*
* @return true if the surface was initialized, false otherwise. Returning
* false might mean that the surface was already initialized.
*/
- boolean initializeIfNeeded(int width, int height, Surface surface, DisplayMetrics metrics)
+ boolean initializeIfNeeded(int width, int height, Surface surface, Rect surfaceInsets, DisplayMetrics metrics)
throws OutOfResourcesException {
if (isRequested()) {
// We lost the gl context, so recreate it.
if (!isEnabled()) {
if (initialize(surface)) {
- setup(width, height, metrics);
+ setup(width, height, surfaceInsets, metrics);
return true;
}
}
@@ -387,12 +391,12 @@
return false;
}
- void setup(int width, int height, DisplayMetrics metrics) {
+ void setup(int width, int height, Rect surfaceInsets, DisplayMetrics metrics) {
float lightX = width / 2.0f;
float lightY = -400 * metrics.density;
float lightZ = 800 * metrics.density;
float lightRadius = 800 * metrics.density;
- setup(width, height, lightX, lightY, lightZ, lightRadius);
+ setup(width, height, surfaceInsets, lightX, lightY, lightZ, lightRadius);
}
/**
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 57d1beb..acb2fe4 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
import android.os.RemoteException;
@@ -67,7 +68,16 @@
PROFILE_PROPERTY_VISUALIZE_BARS,
};
+ // Size of the rendered content.
private int mWidth, mHeight;
+
+ // Actual size of the drawing surface.
+ private int mSurfaceWidth, mSurfaceHeight;
+
+ // Insets between the drawing surface and rendered content. These are
+ // applied as translation when updating the root render node.
+ private int mInsetTop, mInsetLeft;
+
private long mNativeProxy;
private boolean mInitialized = false;
private RenderNode mRootNode;
@@ -154,11 +164,23 @@
}
@Override
- void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius) {
+ void setup(int width, int height, Rect surfaceInsets, float lightX, float lightY, float lightZ,
+ float lightRadius) {
mWidth = width;
mHeight = height;
- mRootNode.setLeftTopRightBottom(0, 0, mWidth, mHeight);
- nSetup(mNativeProxy, width, height, lightX, lightY, lightZ, lightRadius);
+ if (surfaceInsets != null) {
+ mInsetLeft = surfaceInsets.left;
+ mInsetTop = surfaceInsets.top;
+ mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
+ mSurfaceHeight = height + mInsetTop + surfaceInsets.bottom;
+ } else {
+ mInsetLeft = 0;
+ mInsetTop = 0;
+ mSurfaceWidth = width;
+ mSurfaceHeight = height;
+ }
+ mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
+ nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, lightX, lightY, lightZ, lightRadius);
}
@Override
@@ -214,9 +236,10 @@
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
- HardwareCanvas canvas = mRootNode.start(mWidth, mHeight);
+ HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
try {
canvas.save();
+ canvas.translate(mInsetLeft, mInsetTop);
callbacks.onHardwarePreDraw(canvas);
canvas.drawRenderNode(view.getDisplayList());
callbacks.onHardwarePostDraw(canvas);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5def940..9405299 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1713,7 +1713,8 @@
if (hwInitialized ||
mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
- mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight,
+ final Rect shadowInsets = params != null ? params.shadowInsets : null;
+ mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, shadowInsets,
mAttachInfo.mRootView.getResources().getDisplayMetrics());
if (!hwInitialized) {
mAttachInfo.mHardwareRenderer.invalidate(mSurface);
@@ -2211,20 +2212,22 @@
return measureSpec;
}
+ int mHardwareXOffset;
int mHardwareYOffset;
int mResizeAlpha;
final Paint mResizePaint = new Paint();
@Override
public void onHardwarePreDraw(HardwareCanvas canvas) {
- canvas.translate(0, -mHardwareYOffset);
+ canvas.translate(-mHardwareXOffset, -mHardwareYOffset);
}
@Override
public void onHardwarePostDraw(HardwareCanvas canvas) {
if (mResizeBuffer != null) {
mResizePaint.setAlpha(mResizeAlpha);
- canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
+ canvas.drawHardwareLayer(mResizeBuffer, mHardwareXOffset, mHardwareYOffset,
+ mResizePaint);
}
drawAccessibilityFocusedDrawableIfNeeded(canvas);
}
@@ -2368,15 +2371,17 @@
attachInfo.mTreeObserver.dispatchOnScrollChanged();
}
- int yoff;
+ final WindowManager.LayoutParams params = mWindowAttributes;
+ final Rect surfaceInsets = params != null ? params.shadowInsets : null;
boolean animating = mScroller != null && mScroller.computeScrollOffset();
+ final int curScrollY;
if (animating) {
- yoff = mScroller.getCurrY();
+ curScrollY = mScroller.getCurrY();
} else {
- yoff = mScrollY;
+ curScrollY = mScrollY;
}
- if (mCurScrollY != yoff) {
- mCurScrollY = yoff;
+ if (mCurScrollY != curScrollY) {
+ mCurScrollY = curScrollY;
fullRedrawNeeded = true;
}
@@ -2425,11 +2430,14 @@
attachInfo.mTreeObserver.dispatchOnDraw();
+ final int xOffset = surfaceInsets != null ? -surfaceInsets.left : 0;
+ final int yOffset = curScrollY + (surfaceInsets != null ? -surfaceInsets.top : 0);
if (!dirty.isEmpty() || mIsAnimating) {
if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
// Draw with hardware renderer.
mIsAnimating = false;
- mHardwareYOffset = yoff;
+ mHardwareYOffset = yOffset;
+ mHardwareXOffset = xOffset;
mResizeAlpha = resizeAlpha;
dirty.setEmpty();
@@ -2450,8 +2458,9 @@
attachInfo.mHardwareRenderer.isRequested()) {
try {
- attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
- mSurface, attachInfo.mRootView.getResources().getDisplayMetrics());
+ attachInfo.mHardwareRenderer.initializeIfNeeded(
+ mWidth, mHeight, mSurface, surfaceInsets,
+ attachInfo.mRootView.getResources().getDisplayMetrics());
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
@@ -2462,7 +2471,7 @@
return;
}
- if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
+ if (!drawSoftware(surface, attachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
@@ -2475,9 +2484,9 @@
}
/**
- * @return true if drawing was succesfull, false if an error occurred
+ * @return true if drawing was successful, false if an error occurred
*/
- private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
+ private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
@@ -2526,7 +2535,7 @@
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
- if (!canvas.isOpaque() || yoff != 0) {
+ if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
@@ -2542,7 +2551,7 @@
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
try {
- canvas.translate(0, -yoff);
+ canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
@@ -3147,8 +3156,10 @@
if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){
mFullRedrawNeeded = true;
try {
+ final WindowManager.LayoutParams lp = mWindowAttributes;
+ final Rect surfaceInsets = lp != null ? lp.shadowInsets : null;
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
- mWidth, mHeight, mSurface,
+ mWidth, mHeight, mSurface, surfaceInsets,
mAttachInfo.mRootView.getResources().getDisplayMetrics());
} catch (OutOfResourcesException e) {
Log.e(TAG, "OutOfResourcesException locking surface", e);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4eecc6a..c06b5d8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -19,7 +19,9 @@
import android.app.Presentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1290,6 +1292,13 @@
* field is added with {@link #y} to supply the <var>yAdj</var> parameter.
*/
public float verticalMargin;
+
+ /**
+ * Positive insets between the drawing surface and window content.
+ *
+ * @hide
+ */
+ public Rect shadowInsets = new Rect();
/**
* The desired bitmap format. May be one of the constants in
@@ -1571,6 +1580,10 @@
out.writeInt(hasSystemUiListeners ? 1 : 0);
out.writeInt(inputFeatures);
out.writeLong(userActivityTimeout);
+ out.writeInt(shadowInsets.left);
+ out.writeInt(shadowInsets.top);
+ out.writeInt(shadowInsets.right);
+ out.writeInt(shadowInsets.bottom);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1613,6 +1626,7 @@
hasSystemUiListeners = in.readInt() != 0;
inputFeatures = in.readInt();
userActivityTimeout = in.readLong();
+ shadowInsets.set(in.readInt(), in.readInt(), in.readInt(), in.readInt());
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1644,6 +1658,8 @@
/** {@hide} */
public static final int TRANSLUCENT_FLAGS_CHANGED = 1<<19;
/** {@hide} */
+ public static final int SHADOW_INSETS_CHANGED = 1<<20;
+ /** {@hide} */
public static final int EVERYTHING_CHANGED = 0xffffffff;
// internal buffer to backup/restore parameters under compatibility mode.
@@ -1778,6 +1794,11 @@
changes |= USER_ACTIVITY_TIMEOUT_CHANGED;
}
+ if (!shadowInsets.equals(o.shadowInsets)) {
+ shadowInsets.set(o.shadowInsets);
+ changes |= SHADOW_INSETS_CHANGED;
+ }
+
return changes;
}
@@ -1877,6 +1898,9 @@
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
}
+ if (!shadowInsets.equals(Insets.NONE)) {
+ sb.append(" shadowInsets=").append(shadowInsets);
+ }
sb.append('}');
return sb.toString();
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 01632ae..a35d447 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -109,6 +110,8 @@
private int mPopupWidth;
private int mPopupHeight;
+ private float mElevation;
+
private int[] mDrawingLocation = new int[2];
private int[] mScreenLocation = new int[2];
private Rect mTempRect = new Rect();
@@ -196,6 +199,7 @@
attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
+ mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
@@ -319,25 +323,49 @@
}
/**
- * <p>Return the drawable used as the popup window's background.</p>
+ * Return the drawable used as the popup window's background.
*
- * @return the background drawable or null
+ * @return the background drawable or {@code null} if not set
+ * @see #setBackgroundDrawable(Drawable)
+ * @attr ref android.R.styleable#PopupWindow_popupBackground
*/
public Drawable getBackground() {
return mBackground;
}
/**
- * <p>Change the background drawable for this popup window. The background
- * can be set to null.</p>
+ * Specifies the background drawable for this popup window. The background
+ * can be set to {@code null}.
*
* @param background the popup's background
+ * @see #getBackground()
+ * @attr ref android.R.styleable#PopupWindow_popupBackground
*/
public void setBackgroundDrawable(Drawable background) {
mBackground = background;
}
/**
+ * @return the elevation for this popup window in pixels
+ * @see #setElevation(float)
+ * @attr ref android.R.styleable#PopupWindow_popupElevation
+ */
+ public float getElevation() {
+ return mElevation;
+ }
+
+ /**
+ * Specifies the elevation for this popup window.
+ *
+ * @param elevation the popup's elevation in pixels
+ * @see #getElevation()
+ * @attr ref android.R.styleable#PopupWindow_popupElevation
+ */
+ public void setElevation(float elevation) {
+ mElevation = elevation;
+ }
+
+ /**
* <p>Return the animation style to use the popup appears and disappears</p>
*
* @return the animation style to use the popup appears and disappears
@@ -973,7 +1001,7 @@
/**
* <p>Prepare the popup by embedding in into a new ViewGroup if the
* background drawable is not null. If embedding is required, the layout
- * parameters' height is mnodified to take into account the background's
+ * parameters' height is modified to take into account the background's
* padding.</p>
*
* @param p the layout parameters of the popup's content view
@@ -998,13 +1026,15 @@
PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, height
);
- popupViewContainer.setBackgroundDrawable(mBackground);
+ popupViewContainer.setBackground(mBackground);
popupViewContainer.addView(mContentView, listParams);
mPopupView = popupViewContainer;
} else {
mPopupView = mContentView;
}
+
+ mPopupView.setElevation(mElevation);
mPopupViewInitialLayoutDirectionInherited =
(mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
@@ -1066,6 +1096,10 @@
p.softInputMode = mSoftInputMode;
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
+ // TODO: Use real shadow insets once that algorithm is finalized.
+ final int shadowInset = (int) Math.ceil(mElevation * 2);
+ p.shadowInsets.set(shadowInset, shadowInset, shadowInset, shadowInset);
+
return p;
}
diff --git a/core/res/res/drawable/popup_background_material.xml b/core/res/res/drawable/popup_background_material.xml
index 9e50790..b1f0cf5 100644
--- a/core/res/res/drawable/popup_background_material.xml
+++ b/core/res/res/drawable/popup_background_material.xml
@@ -14,7 +14,12 @@
limitations under the License.
-->
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/popup_background_mtrl_mult"
- android:tint="?attr/colorBackground"
- android:tintMode="multiply" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <corners
+ android:radius="2dp" />
+ <solid
+ android:color="?attr/colorBackground" />
+
+</shape>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4b708a7..e95239c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4057,6 +4057,8 @@
<declare-styleable name="PopupWindow">
<!-- The background to use for the popup window. -->
<attr name="popupBackground" format="reference|color" />
+ <!-- Window elevation to use for the popup window. -->
+ <attr name="popupElevation" format="dimension" />
<!-- The animation style to use for the popup window. -->
<attr name="popupAnimationStyle" format="reference" />
<!-- Whether the popup window should overlap its anchor view. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1eb8946..2dd67ba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2231,6 +2231,7 @@
<public type="attr" name="buttonBarPositiveButtonStyle" />
<public type="attr" name="buttonBarNeutralButtonStyle" />
<public type="attr" name="buttonBarNegativeButtonStyle" />
+ <public type="attr" name="popupElevation" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index c1eb999..3880fb3 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -733,6 +733,7 @@
<style name="Widget.Material.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
<item name="popupAnimationStyle">@style/Animation.Material.Popup</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>