diff options
Diffstat (limited to 'src/com/android/launcher3/graphics/ShadowDrawable.java')
-rw-r--r-- | src/com/android/launcher3/graphics/ShadowDrawable.java | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java new file mode 100644 index 000000000..5e76649ca --- /dev/null +++ b/src/com/android/launcher3/graphics/ShadowDrawable.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.graphics; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; + +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * A drawable which adds shadow around a child drawable. + */ +public class ShadowDrawable extends Drawable { + + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + private final ShadowDrawableState mState; + + public ShadowDrawable() { + this(new ShadowDrawableState()); + } + + private ShadowDrawable(ShadowDrawableState state) { + mState = state; + } + + @Override + public void draw(Canvas canvas) { + Rect bounds = getBounds(); + if (bounds.isEmpty()) { + return; + } + if (mState.mLastDrawnBitmap == null) { + regenerateBitmapCache(); + } + canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + invalidateSelf(); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + invalidateSelf(); + } + + @Override + public ConstantState getConstantState() { + return mState; + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public int getIntrinsicHeight() { + return mState.mIntrinsicHeight; + } + + @Override + public int getIntrinsicWidth() { + return mState.mIntrinsicWidth; + } + + /** + * Sets the color for the generated shadow + */ + public void setShadowColor(int color) { + if (mState.mShadowColor != color) { + mState.mShadowColor = color; + mState.mLastDrawnBitmap = null; + invalidateSelf(); + } + } + + private void regenerateBitmapCache() { + Bitmap bitmap = Bitmap.createBitmap(mState.mIntrinsicWidth, mState.mIntrinsicHeight, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + + // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared. + Drawable d = mState.mChildState.newDrawable().mutate(); + d.setBounds(mState.mShadowSize, mState.mShadowSize, + mState.mIntrinsicWidth - mState.mShadowSize, + mState.mIntrinsicHeight - mState.mShadowSize); + d.draw(canvas); + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, BlurMaskFilter.Blur.NORMAL)); + int[] offset = new int[2]; + Bitmap shadow = bitmap.extractAlpha(paint, offset); + + paint.setMaskFilter(null); + paint.setColor(mState.mShadowColor); + bitmap.eraseColor(Color.TRANSPARENT); + canvas.drawBitmap(shadow, offset[0], offset[1], paint); + d.draw(canvas); + + if (Utilities.isAtLeastO()) { + bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false); + } + mState.mLastDrawnBitmap = bitmap; + } + + @Override + public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, + Resources.Theme theme) throws XmlPullParserException, IOException { + super.inflate(r, parser, attrs, theme); + + final TypedArray a = theme == null + ? r.obtainAttributes(attrs, R.styleable.ShadowDrawable) + : theme.obtainStyledAttributes(attrs, R.styleable.ShadowDrawable, 0, 0); + + try { + Drawable d = a.getDrawable(R.styleable.ShadowDrawable_android_src); + if (d == null) { + throw new XmlPullParserException("missing src attribute"); + } + mState.mShadowColor = a.getColor( + R.styleable.ShadowDrawable_android_shadowColor, Color.BLACK); + mState.mShadowSize = r.getDimensionPixelSize(R.dimen.drawable_shadow_size); + + mState.mIntrinsicHeight = d.getIntrinsicHeight() + 2 * mState.mShadowSize; + mState.mIntrinsicWidth = d.getIntrinsicWidth() + 2 * mState.mShadowSize; + mState.mChangingConfigurations = d.getChangingConfigurations(); + + mState.mChildState = d.getConstantState(); + } finally { + a.recycle(); + } + } + + private static class ShadowDrawableState extends ConstantState { + + int mChangingConfigurations; + int mIntrinsicWidth; + int mIntrinsicHeight; + + int mShadowColor; + int mShadowSize; + + Bitmap mLastDrawnBitmap; + ConstantState mChildState; + + @Override + public Drawable newDrawable() { + return new ShadowDrawable(this); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } +} |