PagedViewIcon.java revision 5f2aa4efeeb8b0133d891715d71553138d9f9ca7
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.launcher2; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.content.pm.ResolveInfo; 22import android.content.res.Resources; 23import android.graphics.Bitmap; 24import android.graphics.BlurMaskFilter; 25import android.graphics.Canvas; 26import android.graphics.Matrix; 27import android.graphics.Paint; 28import android.graphics.PointF; 29import android.graphics.PorterDuff; 30import android.graphics.PorterDuffXfermode; 31import android.graphics.Rect; 32import android.graphics.Region.Op; 33import android.graphics.drawable.Drawable; 34import android.util.AttributeSet; 35import android.util.DisplayMetrics; 36import android.util.Log; 37import android.widget.Checkable; 38import android.widget.TextView; 39 40import com.android.launcher2.PagedView.PagedViewIconCache; 41 42class HolographicOutlineHelper { 43 private float mDensity; 44 private final Paint mHolographicPaint = new Paint(); 45 private final Paint mBlurPaint = new Paint(); 46 private final Paint mErasePaint = new Paint(); 47 private static final Matrix mIdentity = new Matrix();; 48 private static final float BLUR_FACTOR = 3.5f; 49 50 public static final float DEFAULT_STROKE_WIDTH = 6.0f; 51 public static final int HOLOGRAPHIC_BLUE = 0xFF6699FF; 52 public static final int HOLOGRAPHIC_GREEN = 0xFF51E633; 53 54 HolographicOutlineHelper(float density) { 55 mDensity = density; 56 mHolographicPaint.setColor(HOLOGRAPHIC_BLUE); 57 mHolographicPaint.setFilterBitmap(true); 58 mHolographicPaint.setAntiAlias(true); 59 mBlurPaint.setMaskFilter(new BlurMaskFilter(BLUR_FACTOR * density, 60 BlurMaskFilter.Blur.OUTER)); 61 mBlurPaint.setFilterBitmap(true); 62 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); 63 mErasePaint.setFilterBitmap(true); 64 mErasePaint.setAntiAlias(true); 65 } 66 67 private float cubic(float r) { 68 return (float) (Math.pow(r-1, 3) + 1); 69 } 70 71 /** 72 * Returns the interpolated holographic highlight alpha for the effect we want when scrolling 73 * pages. 74 */ 75 public float highlightAlphaInterpolator(float r) { 76 final float pivot = 0.3f; 77 if (r < pivot) { 78 return Math.max(0.5f, 0.65f*cubic(r/pivot)); 79 } else { 80 return Math.min(1.0f, 0.65f*cubic(1 - (r-pivot)/(1-pivot))); 81 } 82 } 83 84 /** 85 * Returns the interpolated view alpha for the effect we want when scrolling pages. 86 */ 87 public float viewAlphaInterpolator(float r) { 88 final float pivot = 0.6f; 89 if (r < pivot) { 90 return r/pivot; 91 } else { 92 return 1.0f; 93 } 94 } 95 96 /** 97 * Sets the color of the holographic paint to be used when applying the outline/blur. 98 */ 99 void setColor(int color) { 100 mHolographicPaint.setColor(color); 101 } 102 103 /** 104 * Applies an outline to whatever is currently drawn in the specified bitmap. 105 */ 106 void applyOutline(Bitmap srcDst, Canvas srcDstCanvas, float strokeWidth, PointF offset) { 107 strokeWidth *= mDensity; 108 Bitmap mask = srcDst.extractAlpha(); 109 Matrix m = new Matrix(); 110 final int width = srcDst.getWidth(); 111 final int height = srcDst.getHeight(); 112 float xScale = strokeWidth*2.0f/width; 113 float yScale = strokeWidth*2.0f/height; 114 m.preScale(1+xScale, 1+yScale, (width / 2.0f) + offset.x, 115 (height / 2.0f) + offset.y); 116 117 srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR); 118 srcDstCanvas.drawBitmap(mask, m, mHolographicPaint); 119 srcDstCanvas.drawBitmap(mask, mIdentity, mErasePaint); 120 mask.recycle(); 121 } 122 123 /** 124 * Applies an blur to whatever is currently drawn in the specified bitmap. 125 */ 126 void applyBlur(Bitmap srcDst, Canvas srcDstCanvas) { 127 int[] xy = new int[2]; 128 Bitmap mask = srcDst.extractAlpha(mBlurPaint, xy); 129 srcDstCanvas.drawBitmap(mask, xy[0], xy[1], mHolographicPaint); 130 mask.recycle(); 131 } 132} 133 134/** 135 * An icon on a PagedView, specifically for items in the launcher's paged view (with compound 136 * drawables on the top). 137 */ 138public class PagedViewIcon extends TextView implements Checkable { 139 private static final String TAG = "PagedViewIcon"; 140 141 // holographic outline 142 private final Paint mPaint = new Paint(); 143 private static HolographicOutlineHelper sHolographicOutlineHelper; 144 private Bitmap mCheckedOutline; 145 private Bitmap mHolographicOutline; 146 private Canvas mHolographicOutlineCanvas; 147 private boolean mIsHolographicUpdatePass; 148 private Rect mDrawableClipRect; 149 150 private Object mIconCacheKey; 151 private PagedViewIconCache mIconCache; 152 153 private int mAlpha; 154 private int mHolographicAlpha; 155 156 private boolean mIsChecked; 157 158 public PagedViewIcon(Context context) { 159 this(context, null); 160 } 161 162 public PagedViewIcon(Context context, AttributeSet attrs) { 163 this(context, attrs, 0); 164 } 165 166 public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) { 167 super(context, attrs, defStyle); 168 169 if (sHolographicOutlineHelper == null) { 170 final Resources resources = context.getResources(); 171 final DisplayMetrics metrics = resources.getDisplayMetrics(); 172 final float density = metrics.density; 173 sHolographicOutlineHelper = new HolographicOutlineHelper(density); 174 } 175 mDrawableClipRect = new Rect(); 176 177 setFocusable(true); 178 setBackgroundDrawable(null); 179 } 180 181 public void applyFromApplicationInfo(ApplicationInfo info, PagedViewIconCache cache) { 182 mIconCache = cache; 183 mIconCacheKey = info; 184 mHolographicOutline = mIconCache.getOutline(mIconCacheKey); 185 186 setCompoundDrawablesWithIntrinsicBounds(null, 187 new FastBitmapDrawable(info.iconBitmap), null, null); 188 setText(info.title); 189 setTag(info); 190 } 191 192 public void applyFromResolveInfo(ResolveInfo info, PackageManager packageManager, 193 PagedViewIconCache cache) { 194 mIconCache = cache; 195 mIconCacheKey = info; 196 mHolographicOutline = mIconCache.getOutline(mIconCacheKey); 197 198 Drawable image = info.loadIcon(packageManager); 199 image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); 200 setCompoundDrawablesWithIntrinsicBounds(null, image, null, null); 201 setText(info.loadLabel(packageManager)); 202 setTag(info); 203 } 204 205 @Override 206 public void setAlpha(float alpha) { 207 final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha); 208 final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha); 209 mAlpha = (int) (viewAlpha * 255); 210 mHolographicAlpha = (int) (holographicAlpha * 255); 211 super.setAlpha(viewAlpha); 212 } 213 214 public void invalidateCheckedImage() { 215 if (mCheckedOutline != null) { 216 mCheckedOutline.recycle(); 217 mCheckedOutline = null; 218 } 219 } 220 221 @Override 222 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 223 super.onLayout(changed, left, top, right, bottom); 224 225 if (mHolographicOutline == null) { 226 final PointF offset = new PointF(0, 227 -(getCompoundPaddingBottom() + getCompoundPaddingTop())/2.0f); 228 229 // update the clipping rect to be used in the holographic pass below 230 getDrawingRect(mDrawableClipRect); 231 mDrawableClipRect.bottom = getPaddingTop() + getCompoundPaddingTop(); 232 233 // set a flag to indicate that we are going to draw the view at full alpha with the text 234 // clipped for the generation of the holographic icon 235 mIsHolographicUpdatePass = true; 236 mHolographicOutline = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), 237 Bitmap.Config.ARGB_8888); 238 mHolographicOutlineCanvas = new Canvas(mHolographicOutline); 239 mHolographicOutlineCanvas.concat(getMatrix()); 240 draw(mHolographicOutlineCanvas); 241 sHolographicOutlineHelper.setColor(HolographicOutlineHelper.HOLOGRAPHIC_BLUE); 242 sHolographicOutlineHelper.applyOutline(mHolographicOutline, mHolographicOutlineCanvas, 243 HolographicOutlineHelper.DEFAULT_STROKE_WIDTH, offset); 244 sHolographicOutlineHelper.applyBlur(mHolographicOutline, mHolographicOutlineCanvas); 245 mIsHolographicUpdatePass = false; 246 mIconCache.addOutline(mIconCacheKey, mHolographicOutline); 247 mHolographicOutlineCanvas = null; 248 } 249 } 250 251 @Override 252 protected void onDraw(Canvas canvas) { 253 // draw the view itself 254 if (mIsHolographicUpdatePass) { 255 // only clip to the text view (restore its alpha so that we get a proper outline) 256 canvas.save(); 257 canvas.clipRect(mDrawableClipRect, Op.REPLACE); 258 final float alpha = getAlpha(); 259 super.setAlpha(1.0f); 260 super.onDraw(canvas); 261 super.setAlpha(alpha); 262 canvas.restore(); 263 } else { 264 if (mAlpha > 0) { 265 super.onDraw(canvas); 266 } 267 } 268 269 // draw any blended overlays 270 if (!mIsHolographicUpdatePass) { 271 if (mCheckedOutline == null) { 272 if (mHolographicOutline != null && mHolographicAlpha > 0) { 273 mPaint.setAlpha(mHolographicAlpha); 274 canvas.drawBitmap(mHolographicOutline, 0, 0, mPaint); 275 } 276 } else { 277 mPaint.setAlpha(255); 278 canvas.drawBitmap(mCheckedOutline, 0, 0, mPaint); 279 } 280 } 281 } 282 283 @Override 284 public boolean isChecked() { 285 return mIsChecked; 286 } 287 288 @Override 289 public void setChecked(boolean checked) { 290 if (mIsChecked != checked) { 291 mIsChecked = checked; 292 293 if (mIsChecked) { 294 final PointF offset = new PointF(0, 295 -(getCompoundPaddingBottom() + getCompoundPaddingTop())/2.0f); 296 297 // set a flag to indicate that we are going to draw the view at full alpha with the text 298 // clipped for the generation of the holographic icon 299 mIsHolographicUpdatePass = true; 300 mCheckedOutline = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), 301 Bitmap.Config.ARGB_8888); 302 mHolographicOutlineCanvas = new Canvas(mCheckedOutline); 303 mHolographicOutlineCanvas.concat(getMatrix()); 304 draw(mHolographicOutlineCanvas); 305 sHolographicOutlineHelper.setColor(HolographicOutlineHelper.HOLOGRAPHIC_GREEN); 306 sHolographicOutlineHelper.applyOutline(mCheckedOutline, mHolographicOutlineCanvas, 307 HolographicOutlineHelper.DEFAULT_STROKE_WIDTH + 1.0f, offset); 308 mIsHolographicUpdatePass = false; 309 mHolographicOutlineCanvas = null; 310 } else { 311 invalidateCheckedImage(); 312 } 313 314 invalidate(); 315 } 316 } 317 318 @Override 319 public void toggle() { 320 setChecked(!mIsChecked); 321 } 322} 323