blob: b8cc206bfba8486520db5a7120d5293f46a0e0a0 [file] [log] [blame]
Winson Chungb3347bb2010-08-19 14:51:28 -07001/*
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;
Winson Chung241c3b42010-08-25 16:53:03 -070020import android.content.pm.PackageManager;
21import android.content.pm.ResolveInfo;
Winson Chungb3347bb2010-08-19 14:51:28 -070022import android.content.res.Resources;
23import android.graphics.Bitmap;
24import android.graphics.BlurMaskFilter;
25import android.graphics.Canvas;
Winson Chungb3347bb2010-08-19 14:51:28 -070026import 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;
Winson Chung03c568e2010-08-19 17:16:59 -070033import android.graphics.drawable.Drawable;
Winson Chungb3347bb2010-08-19 14:51:28 -070034import android.util.AttributeSet;
35import android.util.DisplayMetrics;
Winson Chung241c3b42010-08-25 16:53:03 -070036import android.util.Log;
Winson Chung03c568e2010-08-19 17:16:59 -070037import android.widget.Checkable;
Winson Chungb3347bb2010-08-19 14:51:28 -070038import android.widget.TextView;
39
Winson Chung241c3b42010-08-25 16:53:03 -070040import com.android.launcher2.PagedView.PagedViewIconCache;
41
Winson Chungb3347bb2010-08-19 14:51:28 -070042class HolographicOutlineHelper {
Winson Chung5f2aa4e2010-08-20 14:49:25 -070043 private float mDensity;
Winson Chungb3347bb2010-08-19 14:51:28 -070044 private final Paint mHolographicPaint = new Paint();
45 private final Paint mBlurPaint = new Paint();
46 private final Paint mErasePaint = new Paint();
Winson Chung5f2aa4e2010-08-20 14:49:25 -070047 private static final Matrix mIdentity = new Matrix();;
Winson Chungb3347bb2010-08-19 14:51:28 -070048 private static final float BLUR_FACTOR = 3.5f;
49
Winson Chung5f2aa4e2010-08-20 14:49:25 -070050 public static final float DEFAULT_STROKE_WIDTH = 6.0f;
Winson Chung03c568e2010-08-19 17:16:59 -070051 public static final int HOLOGRAPHIC_BLUE = 0xFF6699FF;
Winson Chung5f2aa4e2010-08-20 14:49:25 -070052 public static final int HOLOGRAPHIC_GREEN = 0xFF51E633;
Winson Chung03c568e2010-08-19 17:16:59 -070053
Winson Chungb3347bb2010-08-19 14:51:28 -070054 HolographicOutlineHelper(float density) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -070055 mDensity = density;
Winson Chung03c568e2010-08-19 17:16:59 -070056 mHolographicPaint.setColor(HOLOGRAPHIC_BLUE);
Winson Chungb3347bb2010-08-19 14:51:28 -070057 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 /**
Winson Chung03c568e2010-08-19 17:16:59 -070097 * 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 /**
Winson Chungb3347bb2010-08-19 14:51:28 -0700104 * Applies an outline to whatever is currently drawn in the specified bitmap.
105 */
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700106 void applyOutline(Bitmap srcDst, Canvas srcDstCanvas, float strokeWidth, PointF offset) {
107 strokeWidth *= mDensity;
Winson Chungb3347bb2010-08-19 14:51:28 -0700108 Bitmap mask = srcDst.extractAlpha();
109 Matrix m = new Matrix();
110 final int width = srcDst.getWidth();
111 final int height = srcDst.getHeight();
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700112 float xScale = strokeWidth*2.0f/width;
113 float yScale = strokeWidth*2.0f/height;
Winson Chungb3347bb2010-08-19 14:51:28 -0700114 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 */
Winson Chung03c568e2010-08-19 17:16:59 -0700138public class PagedViewIcon extends TextView implements Checkable {
Winson Chungb3347bb2010-08-19 14:51:28 -0700139 private static final String TAG = "PagedViewIcon";
140
141 // holographic outline
142 private final Paint mPaint = new Paint();
143 private static HolographicOutlineHelper sHolographicOutlineHelper;
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700144 private Bitmap mCheckedOutline;
Winson Chungb3347bb2010-08-19 14:51:28 -0700145 private Bitmap mHolographicOutline;
146 private Canvas mHolographicOutlineCanvas;
147 private boolean mIsHolographicUpdatePass;
148 private Rect mDrawableClipRect;
149
Winson Chung241c3b42010-08-25 16:53:03 -0700150 private Object mIconCacheKey;
151 private PagedViewIconCache mIconCache;
152
Winson Chungb3347bb2010-08-19 14:51:28 -0700153 private int mAlpha;
154 private int mHolographicAlpha;
155
Winson Chung03c568e2010-08-19 17:16:59 -0700156 private boolean mIsChecked;
157
Winson Chungb3347bb2010-08-19 14:51:28 -0700158 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
Winson Chung241c3b42010-08-25 16:53:03 -0700181 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
Winson Chungb3347bb2010-08-19 14:51:28 -0700205 @Override
206 public void setAlpha(float alpha) {
207 final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha);
Winson Chungb3347bb2010-08-19 14:51:28 -0700208 final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
Winson Chungaffd7b42010-08-20 15:11:56 -0700209 mAlpha = (int) (viewAlpha * 255);
Winson Chungb3347bb2010-08-19 14:51:28 -0700210 mHolographicAlpha = (int) (holographicAlpha * 255);
Winson Chungb3347bb2010-08-19 14:51:28 -0700211 super.setAlpha(viewAlpha);
212 }
213
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700214 public void invalidateCheckedImage() {
215 if (mCheckedOutline != null) {
216 mCheckedOutline.recycle();
217 mCheckedOutline = null;
218 }
219 }
220
Winson Chungb3347bb2010-08-19 14:51:28 -0700221 @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);
Winson Chung03c568e2010-08-19 17:16:59 -0700241 sHolographicOutlineHelper.setColor(HolographicOutlineHelper.HOLOGRAPHIC_BLUE);
242 sHolographicOutlineHelper.applyOutline(mHolographicOutline, mHolographicOutlineCanvas,
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700243 HolographicOutlineHelper.DEFAULT_STROKE_WIDTH, offset);
Winson Chungb3347bb2010-08-19 14:51:28 -0700244 sHolographicOutlineHelper.applyBlur(mHolographicOutline, mHolographicOutlineCanvas);
245 mIsHolographicUpdatePass = false;
Winson Chung241c3b42010-08-25 16:53:03 -0700246 mIconCache.addOutline(mIconCacheKey, mHolographicOutline);
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700247 mHolographicOutlineCanvas = null;
Winson Chungb3347bb2010-08-19 14:51:28 -0700248 }
249 }
250
Winson Chungb3347bb2010-08-19 14:51:28 -0700251 @Override
252 protected void onDraw(Canvas canvas) {
Winson Chungaffd7b42010-08-20 15:11:56 -0700253 // draw the view itself
Winson Chungb3347bb2010-08-19 14:51:28 -0700254 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);
Winson Chungaffd7b42010-08-20 15:11:56 -0700258 final float alpha = getAlpha();
259 super.setAlpha(1.0f);
Winson Chungb3347bb2010-08-19 14:51:28 -0700260 super.onDraw(canvas);
Winson Chungaffd7b42010-08-20 15:11:56 -0700261 super.setAlpha(alpha);
Winson Chungb3347bb2010-08-19 14:51:28 -0700262 canvas.restore();
263 } else {
264 if (mAlpha > 0) {
265 super.onDraw(canvas);
266 }
267 }
268
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700269 // 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 }
Winson Chungb3347bb2010-08-19 14:51:28 -0700280 }
281 }
Winson Chung03c568e2010-08-19 17:16:59 -0700282
283 @Override
284 public boolean isChecked() {
285 return mIsChecked;
286 }
287
288 @Override
289 public void setChecked(boolean checked) {
Winson Chung5f2aa4e2010-08-20 14:49:25 -0700290 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 }
Winson Chung03c568e2010-08-19 17:16:59 -0700316 }
317
318 @Override
319 public void toggle() {
320 setChecked(!mIsChecked);
321 }
Winson Chungb3347bb2010-08-19 14:51:28 -0700322}