Loading res/values/dimens.xml +7 −0 Original line number Diff line number Diff line Loading @@ -119,4 +119,11 @@ <dimen name="profile_badge_size">24dp</dimen> <dimen name="profile_badge_margin">4dp</dimen> <dimen name="profile_badge_minimum_top">2dp</dimen> <!-- Shadows and outlines --> <dimen name="blur_size_thin_outline">1dp</dimen> <dimen name="blur_size_medium_outline">2dp</dimen> <dimen name="blur_size_click_shadow">4dp</dimen> <dimen name="click_shadow_high_shift">2dp</dimen> </resources> src/com/android/launcher3/BubbleTextView.java +1 −1 Original line number Diff line number Diff line Loading @@ -276,7 +276,7 @@ public class BubbleTextView extends TextView { // Only show the shadow effect when persistent pressed state is set. if (getParent() instanceof ShortcutAndWidgetContainer) { CellLayout layout = (CellLayout) getParent().getParent(); layout.setPressedIcon(this, mPressedBackground, mOutlineHelper.shadowBitmapPadding); layout.setPressedIcon(this, mPressedBackground); } updateIconState(); Loading src/com/android/launcher3/CellLayout.java +27 −35 Original line number Diff line number Diff line Loading @@ -132,7 +132,7 @@ public class CellLayout extends ViewGroup { private int mDragOutlineCurrent = 0; private final Paint mDragOutlinePaint = new Paint(); private final FastBitmapView mTouchFeedbackView; private final ClickShadowView mTouchFeedbackView; @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new HashMap<CellLayout.LayoutParams, Animator>(); Loading Loading @@ -301,9 +301,8 @@ public class CellLayout extends ViewGroup { mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap, mCountX, mCountY); mTouchFeedbackView = new FastBitmapView(context); // Make the feedback view large enough to hold the blur bitmap. addView(mTouchFeedbackView, (int) (grid.cellWidthPx * 1.5), (int) (grid.cellHeightPx * 1.5)); mTouchFeedbackView = new ClickShadowView(context); addView(mTouchFeedbackView); addView(mShortcutsAndWidgets); } Loading Loading @@ -410,22 +409,14 @@ public class CellLayout extends ViewGroup { invalidate(); } void setPressedIcon(BubbleTextView icon, Bitmap background, int padding) { void setPressedIcon(BubbleTextView icon, Bitmap background) { if (icon == null || background == null) { mTouchFeedbackView.setBitmap(null); mTouchFeedbackView.animate().cancel(); } else { int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); mTouchFeedbackView.setTranslationX(icon.getLeft() + (int) Math.ceil(offset / 2f) - padding); mTouchFeedbackView.setTranslationY(icon.getTop() - padding); if (mTouchFeedbackView.setBitmap(background)) { mTouchFeedbackView.setAlpha(0); mTouchFeedbackView.animate().alpha(1) .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION) .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR) .start(); mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets); mTouchFeedbackView.animateShadow(); } } } Loading Loading @@ -895,19 +886,20 @@ public class CellLayout extends ViewGroup { mWidthGap = mOriginalWidthGap; mHeightGap = mOriginalHeightGap; } int count = getChildCount(); int maxWidth = 0; int maxHeight = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY); int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); child.measure(childWidthMeasureSpec, childheightMeasureSpec); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); } // Make the feedback view large enough to hold the blur bitmap. mTouchFeedbackView.measure( MeasureSpec.makeMeasureSpec(mCellWidth + mTouchFeedbackView.getExtraSize(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mCellHeight + mTouchFeedbackView.getExtraSize(), MeasureSpec.EXACTLY)); mShortcutsAndWidgets.measure( MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY)); int maxWidth = mShortcutsAndWidgets.getMeasuredWidth(); int maxHeight = mShortcutsAndWidgets.getMeasuredHeight(); if (mFixedWidth > 0 && mFixedHeight > 0) { setMeasuredDimension(maxWidth, maxHeight); } else { Loading @@ -921,14 +913,14 @@ public class CellLayout extends ViewGroup { (mCountX * mCellWidth); int left = getPaddingLeft() + (int) Math.ceil(offset / 2f); int top = getPaddingTop(); int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.layout(left, top, mTouchFeedbackView.layout(left, top, left + mTouchFeedbackView.getMeasuredWidth(), top + mTouchFeedbackView.getMeasuredHeight()); mShortcutsAndWidgets.layout(left, top, left + r - l, top + b - t); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { Loading src/com/android/launcher3/FastBitmapView.java→src/com/android/launcher3/ClickShadowView.java +110 −0 Original line number Diff line number Diff line Loading @@ -19,16 +19,38 @@ package com.android.launcher3; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.View; import android.view.ViewGroup; public class FastBitmapView extends View { public class ClickShadowView extends View { private static final int SHADOW_SIZE_FACTOR = 3; private static final int SHADOW_LOW_ALPHA = 30; private static final int SHADOW_HIGH_ALPHA = 60; private final Paint mPaint; private final float mShadowOffset; private final float mShadowPadding; private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private Bitmap mBitmap; public FastBitmapView(Context context) { public ClickShadowView(Context context) { super(context); mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); mPaint.setColor(Color.BLACK); mShadowPadding = getResources().getDimension(R.dimen.blur_size_click_shadow); mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift); } /** * @return extra space required by the view to show the shadow. */ public int getExtraSize() { return (int) (SHADOW_SIZE_FACTOR * mShadowPadding); } /** Loading @@ -37,13 +59,8 @@ public class FastBitmapView extends View { */ public boolean setBitmap(Bitmap b) { if (b != mBitmap){ if (mBitmap != null) { invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); } mBitmap = b; if (mBitmap != null) { invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); } invalidate(); return true; } return false; Loading @@ -52,7 +69,42 @@ public class FastBitmapView extends View { @Override protected void onDraw(Canvas canvas) { if (mBitmap != null) { mPaint.setAlpha(SHADOW_LOW_ALPHA); canvas.drawBitmap(mBitmap, 0, 0, mPaint); mPaint.setAlpha(SHADOW_HIGH_ALPHA); canvas.drawBitmap(mBitmap, 0, mShadowOffset, mPaint); } } public void animateShadow() { setAlpha(0); animate().alpha(1) .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION) .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR) .start(); } /** * Aligns the shadow with {@param view} * @param viewParent immediate parent of {@param view}. It must be a sibling of this view. */ public void alignWithIconView(BubbleTextView view, ViewGroup viewParent) { float leftShift = view.getLeft() + viewParent.getLeft() - getLeft(); float topShift = view.getTop() + viewParent.getTop() - getTop(); int iconWidth = view.getRight() - view.getLeft(); int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft(); float drawableWidth = view.getIcon().getBounds().width(); setTranslationX(leftShift + view.getCompoundPaddingLeft() * view.getScaleX() + (iconHSpace - drawableWidth) * view.getScaleX() / 2 /* drawable gap */ + iconWidth * (1 - view.getScaleX()) / 2 /* gap due to scale */ - mShadowPadding /* extra shadow size */ ); setTranslationY(topShift + view.getPaddingTop() * view.getScaleY() /* drawable gap */ + view.getHeight() * (1 - view.getScaleY()) / 2 /* gap due to scale */ - mShadowPadding /* extra shadow size */ ); } } src/com/android/launcher3/HolographicOutlineHelper.java +45 −62 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.launcher3; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; Loading @@ -25,11 +26,16 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.util.SparseArray; /** * Utility class to generate shadow and outline effect, which are used for click feedback * and drag-n-drop respectively. */ public class HolographicOutlineHelper { private static final Rect sTempRect = new Rect(); private static HolographicOutlineHelper sInstance; private final Canvas mCanvas = new Canvas(); private final Paint mDrawPaint = new Paint(); Loading @@ -40,26 +46,23 @@ public class HolographicOutlineHelper { private final BlurMaskFilter mThinOuterBlurMaskFilter; private final BlurMaskFilter mMediumInnerBlurMaskFilter; private final BlurMaskFilter mShaowBlurMaskFilter; private final int mShadowOffset; /** * Padding used when creating shadow bitmap; */ final int shadowBitmapPadding; private final BlurMaskFilter mShadowBlurMaskFilter; static HolographicOutlineHelper INSTANCE; // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4); private HolographicOutlineHelper(Context context) { final float scale = LauncherAppState.getInstance().getScreenDensity(); Resources res = context.getResources(); float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline); mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER); mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL); mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER); mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER); mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL); mThinOuterBlurMaskFilter = new BlurMaskFilter( res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER); mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL); mShadowOffset = (int) (scale * 2.0f); shadowBitmapPadding = (int) (scale * 4.0f); mShadowBlurMaskFilter = new BlurMaskFilter( res.getDimension(R.dimen.blur_size_click_shadow), BlurMaskFilter.Blur.NORMAL); mDrawPaint.setFilterBitmap(true); mDrawPaint.setAntiAlias(true); Loading @@ -71,10 +74,10 @@ public class HolographicOutlineHelper { } public static HolographicOutlineHelper obtain(Context context) { if (INSTANCE == null) { INSTANCE = new HolographicOutlineHelper(context); if (sInstance == null) { sInstance = new HolographicOutlineHelper(context); } return INSTANCE; return sInstance; } /** Loading Loading @@ -153,51 +156,31 @@ public class HolographicOutlineHelper { } Bitmap createMediumDropShadow(BubbleTextView view) { final Bitmap result = Bitmap.createBitmap( view.getWidth() + shadowBitmapPadding + shadowBitmapPadding, view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset, Bitmap.Config.ARGB_8888); mCanvas.setBitmap(result); final Rect clipRect = sTempRect; view.getDrawingRect(sTempRect); // adjust the clip rect so that we don't include the text label clipRect.bottom = view.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + view.getLayout().getLineTop(0); // Draw the View into the bitmap. // The translate of scrollX and scrollY is necessary when drawing TextViews, because // they set scrollX and scrollY to large values to achieve centered text mCanvas.save(); mCanvas.scale(view.getScaleX(), view.getScaleY(), view.getWidth() / 2 + shadowBitmapPadding, view.getHeight() / 2 + shadowBitmapPadding); mCanvas.translate(-view.getScrollX() + shadowBitmapPadding, -view.getScrollY() + shadowBitmapPadding); mCanvas.clipRect(clipRect, Op.REPLACE); view.draw(mCanvas); mCanvas.restore(); int[] blurOffst = new int[2]; mBlurPaint.setMaskFilter(mShaowBlurMaskFilter); Bitmap blurBitmap = result.extractAlpha(mBlurPaint, blurOffst); mCanvas.save(); Drawable icon = view.getIcon(); Rect rect = icon.getBounds(); int bitmapWidth = (int) (rect.width() * view.getScaleX()); int bitmapHeight = (int) (rect.height() * view.getScaleY()); int key = (bitmapWidth << 16) | bitmapHeight; Bitmap cache = mBitmapCache.get(key); if (cache == null) { cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); mCanvas.setBitmap(cache); mBitmapCache.put(key, cache); } else { mCanvas.setBitmap(cache); mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); mCanvas.translate(blurOffst[0], blurOffst[1]); mDrawPaint.setColor(Color.BLACK); mDrawPaint.setAlpha(30); mCanvas.drawBitmap(blurBitmap, 0, 0, mDrawPaint); } mDrawPaint.setAlpha(60); mCanvas.drawBitmap(blurBitmap, 0, mShadowOffset, mDrawPaint); mCanvas.save(Canvas.MATRIX_SAVE_FLAG); mCanvas.scale(view.getScaleX(), view.getScaleY()); mCanvas.translate(-rect.left, -rect.top); icon.draw(mCanvas); mCanvas.restore(); mCanvas.setBitmap(null); blurBitmap.recycle(); return result; mBlurPaint.setMaskFilter(mShadowBlurMaskFilter); return cache.extractAlpha(mBlurPaint, null); } } Loading
res/values/dimens.xml +7 −0 Original line number Diff line number Diff line Loading @@ -119,4 +119,11 @@ <dimen name="profile_badge_size">24dp</dimen> <dimen name="profile_badge_margin">4dp</dimen> <dimen name="profile_badge_minimum_top">2dp</dimen> <!-- Shadows and outlines --> <dimen name="blur_size_thin_outline">1dp</dimen> <dimen name="blur_size_medium_outline">2dp</dimen> <dimen name="blur_size_click_shadow">4dp</dimen> <dimen name="click_shadow_high_shift">2dp</dimen> </resources>
src/com/android/launcher3/BubbleTextView.java +1 −1 Original line number Diff line number Diff line Loading @@ -276,7 +276,7 @@ public class BubbleTextView extends TextView { // Only show the shadow effect when persistent pressed state is set. if (getParent() instanceof ShortcutAndWidgetContainer) { CellLayout layout = (CellLayout) getParent().getParent(); layout.setPressedIcon(this, mPressedBackground, mOutlineHelper.shadowBitmapPadding); layout.setPressedIcon(this, mPressedBackground); } updateIconState(); Loading
src/com/android/launcher3/CellLayout.java +27 −35 Original line number Diff line number Diff line Loading @@ -132,7 +132,7 @@ public class CellLayout extends ViewGroup { private int mDragOutlineCurrent = 0; private final Paint mDragOutlinePaint = new Paint(); private final FastBitmapView mTouchFeedbackView; private final ClickShadowView mTouchFeedbackView; @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new HashMap<CellLayout.LayoutParams, Animator>(); Loading Loading @@ -301,9 +301,8 @@ public class CellLayout extends ViewGroup { mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap, mCountX, mCountY); mTouchFeedbackView = new FastBitmapView(context); // Make the feedback view large enough to hold the blur bitmap. addView(mTouchFeedbackView, (int) (grid.cellWidthPx * 1.5), (int) (grid.cellHeightPx * 1.5)); mTouchFeedbackView = new ClickShadowView(context); addView(mTouchFeedbackView); addView(mShortcutsAndWidgets); } Loading Loading @@ -410,22 +409,14 @@ public class CellLayout extends ViewGroup { invalidate(); } void setPressedIcon(BubbleTextView icon, Bitmap background, int padding) { void setPressedIcon(BubbleTextView icon, Bitmap background) { if (icon == null || background == null) { mTouchFeedbackView.setBitmap(null); mTouchFeedbackView.animate().cancel(); } else { int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); mTouchFeedbackView.setTranslationX(icon.getLeft() + (int) Math.ceil(offset / 2f) - padding); mTouchFeedbackView.setTranslationY(icon.getTop() - padding); if (mTouchFeedbackView.setBitmap(background)) { mTouchFeedbackView.setAlpha(0); mTouchFeedbackView.animate().alpha(1) .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION) .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR) .start(); mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets); mTouchFeedbackView.animateShadow(); } } } Loading Loading @@ -895,19 +886,20 @@ public class CellLayout extends ViewGroup { mWidthGap = mOriginalWidthGap; mHeightGap = mOriginalHeightGap; } int count = getChildCount(); int maxWidth = 0; int maxHeight = 0; for (int i = 0; i < count; i++) { View child = getChildAt(i); int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY); int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); child.measure(childWidthMeasureSpec, childheightMeasureSpec); maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); } // Make the feedback view large enough to hold the blur bitmap. mTouchFeedbackView.measure( MeasureSpec.makeMeasureSpec(mCellWidth + mTouchFeedbackView.getExtraSize(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mCellHeight + mTouchFeedbackView.getExtraSize(), MeasureSpec.EXACTLY)); mShortcutsAndWidgets.measure( MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY)); int maxWidth = mShortcutsAndWidgets.getMeasuredWidth(); int maxHeight = mShortcutsAndWidgets.getMeasuredHeight(); if (mFixedWidth > 0 && mFixedHeight > 0) { setMeasuredDimension(maxWidth, maxHeight); } else { Loading @@ -921,14 +913,14 @@ public class CellLayout extends ViewGroup { (mCountX * mCellWidth); int left = getPaddingLeft() + (int) Math.ceil(offset / 2f); int top = getPaddingTop(); int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); child.layout(left, top, mTouchFeedbackView.layout(left, top, left + mTouchFeedbackView.getMeasuredWidth(), top + mTouchFeedbackView.getMeasuredHeight()); mShortcutsAndWidgets.layout(left, top, left + r - l, top + b - t); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { Loading
src/com/android/launcher3/FastBitmapView.java→src/com/android/launcher3/ClickShadowView.java +110 −0 Original line number Diff line number Diff line Loading @@ -19,16 +19,38 @@ package com.android.launcher3; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.View; import android.view.ViewGroup; public class FastBitmapView extends View { public class ClickShadowView extends View { private static final int SHADOW_SIZE_FACTOR = 3; private static final int SHADOW_LOW_ALPHA = 30; private static final int SHADOW_HIGH_ALPHA = 60; private final Paint mPaint; private final float mShadowOffset; private final float mShadowPadding; private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private Bitmap mBitmap; public FastBitmapView(Context context) { public ClickShadowView(Context context) { super(context); mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); mPaint.setColor(Color.BLACK); mShadowPadding = getResources().getDimension(R.dimen.blur_size_click_shadow); mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift); } /** * @return extra space required by the view to show the shadow. */ public int getExtraSize() { return (int) (SHADOW_SIZE_FACTOR * mShadowPadding); } /** Loading @@ -37,13 +59,8 @@ public class FastBitmapView extends View { */ public boolean setBitmap(Bitmap b) { if (b != mBitmap){ if (mBitmap != null) { invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); } mBitmap = b; if (mBitmap != null) { invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); } invalidate(); return true; } return false; Loading @@ -52,7 +69,42 @@ public class FastBitmapView extends View { @Override protected void onDraw(Canvas canvas) { if (mBitmap != null) { mPaint.setAlpha(SHADOW_LOW_ALPHA); canvas.drawBitmap(mBitmap, 0, 0, mPaint); mPaint.setAlpha(SHADOW_HIGH_ALPHA); canvas.drawBitmap(mBitmap, 0, mShadowOffset, mPaint); } } public void animateShadow() { setAlpha(0); animate().alpha(1) .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION) .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR) .start(); } /** * Aligns the shadow with {@param view} * @param viewParent immediate parent of {@param view}. It must be a sibling of this view. */ public void alignWithIconView(BubbleTextView view, ViewGroup viewParent) { float leftShift = view.getLeft() + viewParent.getLeft() - getLeft(); float topShift = view.getTop() + viewParent.getTop() - getTop(); int iconWidth = view.getRight() - view.getLeft(); int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft(); float drawableWidth = view.getIcon().getBounds().width(); setTranslationX(leftShift + view.getCompoundPaddingLeft() * view.getScaleX() + (iconHSpace - drawableWidth) * view.getScaleX() / 2 /* drawable gap */ + iconWidth * (1 - view.getScaleX()) / 2 /* gap due to scale */ - mShadowPadding /* extra shadow size */ ); setTranslationY(topShift + view.getPaddingTop() * view.getScaleY() /* drawable gap */ + view.getHeight() * (1 - view.getScaleY()) / 2 /* gap due to scale */ - mShadowPadding /* extra shadow size */ ); } }
src/com/android/launcher3/HolographicOutlineHelper.java +45 −62 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.launcher3; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; Loading @@ -25,11 +26,16 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.util.SparseArray; /** * Utility class to generate shadow and outline effect, which are used for click feedback * and drag-n-drop respectively. */ public class HolographicOutlineHelper { private static final Rect sTempRect = new Rect(); private static HolographicOutlineHelper sInstance; private final Canvas mCanvas = new Canvas(); private final Paint mDrawPaint = new Paint(); Loading @@ -40,26 +46,23 @@ public class HolographicOutlineHelper { private final BlurMaskFilter mThinOuterBlurMaskFilter; private final BlurMaskFilter mMediumInnerBlurMaskFilter; private final BlurMaskFilter mShaowBlurMaskFilter; private final int mShadowOffset; /** * Padding used when creating shadow bitmap; */ final int shadowBitmapPadding; private final BlurMaskFilter mShadowBlurMaskFilter; static HolographicOutlineHelper INSTANCE; // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4); private HolographicOutlineHelper(Context context) { final float scale = LauncherAppState.getInstance().getScreenDensity(); Resources res = context.getResources(); float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline); mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER); mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL); mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER); mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER); mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL); mThinOuterBlurMaskFilter = new BlurMaskFilter( res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER); mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL); mShadowOffset = (int) (scale * 2.0f); shadowBitmapPadding = (int) (scale * 4.0f); mShadowBlurMaskFilter = new BlurMaskFilter( res.getDimension(R.dimen.blur_size_click_shadow), BlurMaskFilter.Blur.NORMAL); mDrawPaint.setFilterBitmap(true); mDrawPaint.setAntiAlias(true); Loading @@ -71,10 +74,10 @@ public class HolographicOutlineHelper { } public static HolographicOutlineHelper obtain(Context context) { if (INSTANCE == null) { INSTANCE = new HolographicOutlineHelper(context); if (sInstance == null) { sInstance = new HolographicOutlineHelper(context); } return INSTANCE; return sInstance; } /** Loading Loading @@ -153,51 +156,31 @@ public class HolographicOutlineHelper { } Bitmap createMediumDropShadow(BubbleTextView view) { final Bitmap result = Bitmap.createBitmap( view.getWidth() + shadowBitmapPadding + shadowBitmapPadding, view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset, Bitmap.Config.ARGB_8888); mCanvas.setBitmap(result); final Rect clipRect = sTempRect; view.getDrawingRect(sTempRect); // adjust the clip rect so that we don't include the text label clipRect.bottom = view.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + view.getLayout().getLineTop(0); // Draw the View into the bitmap. // The translate of scrollX and scrollY is necessary when drawing TextViews, because // they set scrollX and scrollY to large values to achieve centered text mCanvas.save(); mCanvas.scale(view.getScaleX(), view.getScaleY(), view.getWidth() / 2 + shadowBitmapPadding, view.getHeight() / 2 + shadowBitmapPadding); mCanvas.translate(-view.getScrollX() + shadowBitmapPadding, -view.getScrollY() + shadowBitmapPadding); mCanvas.clipRect(clipRect, Op.REPLACE); view.draw(mCanvas); mCanvas.restore(); int[] blurOffst = new int[2]; mBlurPaint.setMaskFilter(mShaowBlurMaskFilter); Bitmap blurBitmap = result.extractAlpha(mBlurPaint, blurOffst); mCanvas.save(); Drawable icon = view.getIcon(); Rect rect = icon.getBounds(); int bitmapWidth = (int) (rect.width() * view.getScaleX()); int bitmapHeight = (int) (rect.height() * view.getScaleY()); int key = (bitmapWidth << 16) | bitmapHeight; Bitmap cache = mBitmapCache.get(key); if (cache == null) { cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); mCanvas.setBitmap(cache); mBitmapCache.put(key, cache); } else { mCanvas.setBitmap(cache); mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); mCanvas.translate(blurOffst[0], blurOffst[1]); mDrawPaint.setColor(Color.BLACK); mDrawPaint.setAlpha(30); mCanvas.drawBitmap(blurBitmap, 0, 0, mDrawPaint); } mDrawPaint.setAlpha(60); mCanvas.drawBitmap(blurBitmap, 0, mShadowOffset, mDrawPaint); mCanvas.save(Canvas.MATRIX_SAVE_FLAG); mCanvas.scale(view.getScaleX(), view.getScaleY()); mCanvas.translate(-rect.left, -rect.top); icon.draw(mCanvas); mCanvas.restore(); mCanvas.setBitmap(null); blurBitmap.recycle(); return result; mBlurPaint.setMaskFilter(mShadowBlurMaskFilter); return cache.extractAlpha(mBlurPaint, null); } }