blob: 2dcceaea012b9ac922ea81ae0c9c934ff81ec0c3 [file] [log] [blame]
Winson Chung29d6fea2010-12-01 15:47:31 -08001/*
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
Winson Chung59e1f9a2010-12-21 11:31:54 -080019import android.animation.ObjectAnimator;
Winson Chung29d6fea2010-12-01 15:47:31 -080020import android.appwidget.AppWidgetProviderInfo;
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.content.pm.ResolveInfo;
Winson Chung59e1f9a2010-12-21 11:31:54 -080024import android.content.res.Resources;
Winson Chung29d6fea2010-12-01 15:47:31 -080025import android.content.res.TypedArray;
26import android.graphics.Bitmap;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Paint;
Winson Chung1908d072011-02-24 18:09:44 -080030import android.graphics.PorterDuff;
Winson Chungc3f9f4f2010-12-14 17:25:59 -080031import android.graphics.PorterDuff.Mode;
Winson Chung1908d072011-02-24 18:09:44 -080032import android.graphics.PorterDuffXfermode;
33import android.graphics.Rect;
34import android.graphics.RectF;
Winson Chung29d6fea2010-12-01 15:47:31 -080035import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.Message;
38import android.util.AttributeSet;
Winson Chung97d85d22011-04-13 11:27:36 -070039import android.view.KeyEvent;
Winson Chung29d6fea2010-12-01 15:47:31 -080040import android.view.MotionEvent;
Winson Chung3e6882c2011-01-17 16:37:14 -080041import android.view.View;
Winson Chung59e1f9a2010-12-21 11:31:54 -080042import android.widget.Checkable;
Winson Chung29d6fea2010-12-01 15:47:31 -080043import android.widget.ImageView;
44import android.widget.LinearLayout;
45import android.widget.TextView;
46
47import com.android.launcher.R;
Winson Chung29d6fea2010-12-01 15:47:31 -080048
49/**
50 * The linear layout used strictly for the widget/wallpaper tab of the customization tray
51 */
Winson Chung59e1f9a2010-12-21 11:31:54 -080052public class PagedViewWidget extends LinearLayout implements Checkable {
Winson Chung29d6fea2010-12-01 15:47:31 -080053 static final String TAG = "PagedViewWidgetLayout";
54
55 private final Paint mPaint = new Paint();
56 private static HolographicOutlineHelper sHolographicOutlineHelper;
57 private Bitmap mHolographicOutline;
58 private final Canvas mHolographicOutlineCanvas = new Canvas();
59 private FastBitmapDrawable mPreview;
Winson Chung1908d072011-02-24 18:09:44 -080060 private ImageView mPreviewImageView;
61 private final RectF mTmpScaleRect = new RectF();
62 private final Rect mEraseStrokeRect = new Rect();
63 private final Paint mEraseStrokeRectPaint = new Paint();
Winson Chung29d6fea2010-12-01 15:47:31 -080064
Winson Chung04998342011-01-05 13:54:43 -080065 private PagedViewIconCache.Key mIconCacheKey;
66 private PagedViewIconCache mIconCache;
67
Winson Chung29d6fea2010-12-01 15:47:31 -080068 private int mAlpha = 255;
69 private int mHolographicAlpha;
70
71 // Highlight colors
72 private int mHoloBlurColor;
73 private int mHoloOutlineColor;
74
Winson Chung59e1f9a2010-12-21 11:31:54 -080075 private boolean mIsChecked;
76 private ObjectAnimator mCheckedAlphaAnimator;
77 private float mCheckedAlpha = 1.0f;
78 private int mCheckedFadeInDuration;
79 private int mCheckedFadeOutDuration;
80
Winson Chung29d6fea2010-12-01 15:47:31 -080081 private static final HandlerThread sWorkerThread = new HandlerThread("pagedviewwidget-helper");
82 static {
83 sWorkerThread.start();
84 }
85
86 private static final int MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE = 1;
87
88 private static final Handler sWorker = new Handler(sWorkerThread.getLooper()) {
89 private DeferredHandler mHandler = new DeferredHandler();
90 public void handleMessage(Message msg) {
91 final PagedViewWidget widget = (PagedViewWidget) msg.obj;
92 final int prevAlpha = widget.mPreview.getAlpha();
Winson Chung1908d072011-02-24 18:09:44 -080093 final int width = Math.max(widget.mPreview.getIntrinsicWidth(),
94 widget.getMeasuredWidth());
95 final int height = Math.max(widget.mPreview.getIntrinsicHeight(),
96 widget.getMeasuredHeight());
97 final Bitmap outline = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Winson Chung29d6fea2010-12-01 15:47:31 -080098
99 widget.mHolographicOutlineCanvas.setBitmap(outline);
100 widget.mHolographicOutlineCanvas.save();
101 widget.mHolographicOutlineCanvas.translate(widget.mPaddingLeft, widget.mPaddingTop);
102 widget.mPreview.setAlpha(255);
103 widget.mPreview.draw(widget.mHolographicOutlineCanvas);
104 widget.mPreview.setAlpha(prevAlpha);
Winson Chungc3f9f4f2010-12-14 17:25:59 -0800105 // Temporary workaround to make the default widget outlines visible
106 widget.mHolographicOutlineCanvas.drawColor(Color.argb(156, 0, 0, 0), Mode.SRC_OVER);
Winson Chung29d6fea2010-12-01 15:47:31 -0800107 widget.mHolographicOutlineCanvas.restore();
108
Winson Chung1908d072011-02-24 18:09:44 -0800109 // To account for the fact that some previews run up straight to the edge (we subtract
110 // the edge from the holographic preview (before we apply the holograph)
111 widget.mEraseStrokeRect.set(0, 0, width, height);
112 widget.mHolographicOutlineCanvas.drawRect(widget.mEraseStrokeRect,
113 widget.mEraseStrokeRectPaint);
114
Adam Cohen5bb50bd2010-12-03 11:39:55 -0800115 sHolographicOutlineHelper.applyThickExpensiveOutlineWithBlur(outline,
Winson Chung29d6fea2010-12-01 15:47:31 -0800116 widget.mHolographicOutlineCanvas, widget.mHoloBlurColor,
117 widget.mHoloOutlineColor);
118
119 mHandler.post(new Runnable() {
120 public void run() {
121 widget.mHolographicOutline = outline;
Winson Chung04998342011-01-05 13:54:43 -0800122 widget.mIconCache.addOutline(widget.mIconCacheKey, outline);
Winson Chung29d6fea2010-12-01 15:47:31 -0800123 widget.invalidate();
124 }
125 });
126 }
127 };
128
129 public PagedViewWidget(Context context) {
130 this(context, null);
131 }
132
133 public PagedViewWidget(Context context, AttributeSet attrs) {
134 this(context, attrs, 0);
135 }
136
137 public PagedViewWidget(Context context, AttributeSet attrs, int defStyle) {
138 super(context, attrs, defStyle);
139
140 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedViewWidget,
141 defStyle, 0);
142 mHoloBlurColor = a.getColor(R.styleable.PagedViewWidget_blurColor, 0);
143 mHoloOutlineColor = a.getColor(R.styleable.PagedViewWidget_outlineColor, 0);
Winson Chung1908d072011-02-24 18:09:44 -0800144 mEraseStrokeRectPaint.setStyle(Paint.Style.STROKE);
145 mEraseStrokeRectPaint.setStrokeWidth(HolographicOutlineHelper.MIN_OUTER_BLUR_RADIUS);
146 mEraseStrokeRectPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
147 mEraseStrokeRectPaint.setFilterBitmap(true);
148 mEraseStrokeRectPaint.setAntiAlias(true);
Winson Chung29d6fea2010-12-01 15:47:31 -0800149 a.recycle();
150
151 if (sHolographicOutlineHelper == null) {
152 sHolographicOutlineHelper = new HolographicOutlineHelper();
153 }
154
Winson Chung59e1f9a2010-12-21 11:31:54 -0800155 // Set up fade in/out constants
156 final Resources r = context.getResources();
Winson Chung785d2eb2011-04-14 16:08:02 -0700157 final int alpha = r.getInteger(R.integer.config_dragAppsCustomizeIconFadeAlpha);
Winson Chung59e1f9a2010-12-21 11:31:54 -0800158 if (alpha > 0) {
Winson Chung785d2eb2011-04-14 16:08:02 -0700159 mCheckedAlpha = r.getInteger(R.integer.config_dragAppsCustomizeIconFadeAlpha) / 256.0f;
160 mCheckedFadeInDuration =
161 r.getInteger(R.integer.config_dragAppsCustomizeIconFadeInDuration);
162 mCheckedFadeOutDuration =
163 r.getInteger(R.integer.config_dragAppsCustomizeIconFadeOutDuration);
Winson Chung59e1f9a2010-12-21 11:31:54 -0800164 }
165
Winson Chung29d6fea2010-12-01 15:47:31 -0800166 setWillNotDraw(false);
167 setClipToPadding(false);
168 }
169
170 private void queueHolographicOutlineCreation() {
171 // Generate the outline in the background
Winson Chung04998342011-01-05 13:54:43 -0800172 if (mHolographicOutline == null && mPreview != null) {
Winson Chung29d6fea2010-12-01 15:47:31 -0800173 Message m = sWorker.obtainMessage(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE);
174 m.obj = this;
175 sWorker.sendMessage(m);
176 }
Winson Chung29d6fea2010-12-01 15:47:31 -0800177 }
178
179 public void applyFromAppWidgetProviderInfo(AppWidgetProviderInfo info,
Winson Chung04998342011-01-05 13:54:43 -0800180 FastBitmapDrawable preview, int maxWidth, int[] cellSpan,
181 PagedViewIconCache cache, boolean createHolographicOutline) {
Winson Chung29d6fea2010-12-01 15:47:31 -0800182 final ImageView image = (ImageView) findViewById(R.id.widget_preview);
183 image.setMaxWidth(maxWidth);
184 image.setImageDrawable(preview);
Winson Chung1908d072011-02-24 18:09:44 -0800185 mPreviewImageView = image;
Winson Chung29d6fea2010-12-01 15:47:31 -0800186 final TextView name = (TextView) findViewById(R.id.widget_name);
187 name.setText(info.label);
Winson Chung3e6882c2011-01-17 16:37:14 -0800188 name.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Winson Chung29d6fea2010-12-01 15:47:31 -0800189 final TextView dims = (TextView) findViewById(R.id.widget_dims);
190 dims.setText(mContext.getString(R.string.widget_dims_format, cellSpan[0], cellSpan[1]));
Winson Chung3e6882c2011-01-17 16:37:14 -0800191 dims.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Winson Chung04998342011-01-05 13:54:43 -0800192
193 if (createHolographicOutline) {
194 mIconCache = cache;
195 mIconCacheKey = new PagedViewIconCache.Key(info);
196 mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
197 mPreview = preview;
198 }
Winson Chung29d6fea2010-12-01 15:47:31 -0800199 }
200
201 public void applyFromWallpaperInfo(ResolveInfo info, PackageManager packageManager,
Winson Chung04998342011-01-05 13:54:43 -0800202 FastBitmapDrawable preview, int maxWidth, PagedViewIconCache cache,
203 boolean createHolographicOutline) {
Winson Chung29d6fea2010-12-01 15:47:31 -0800204 ImageView image = (ImageView) findViewById(R.id.wallpaper_preview);
205 image.setMaxWidth(maxWidth);
206 image.setImageDrawable(preview);
Winson Chung1908d072011-02-24 18:09:44 -0800207 mPreviewImageView = image;
Winson Chung29d6fea2010-12-01 15:47:31 -0800208 TextView name = (TextView) findViewById(R.id.wallpaper_name);
209 name.setText(info.loadLabel(packageManager));
Winson Chung3e6882c2011-01-17 16:37:14 -0800210 name.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Winson Chung04998342011-01-05 13:54:43 -0800211
212 if (createHolographicOutline) {
213 mIconCache = cache;
214 mIconCacheKey = new PagedViewIconCache.Key(info);
215 mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
216 mPreview = preview;
217 }
Winson Chung29d6fea2010-12-01 15:47:31 -0800218 }
219
220 @Override
221 public boolean onTouchEvent(MotionEvent event) {
222 // We eat up the touch events here, since the PagedView (which uses the same swiping
223 // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
224 // the user is scrolling between pages. This means that if the pages themselves don't
225 // handle touch events, it gets forwarded up to PagedView itself, and it's own
226 // onTouchEvent() handling will prevent further intercept touch events from being called
227 // (it's the same view in that case). This is not ideal, but to prevent more changes,
228 // we just always mark the touch event as handled.
229 return super.onTouchEvent(event) || true;
230 }
231
232 @Override
Winson Chung97d85d22011-04-13 11:27:36 -0700233 public boolean onKeyDown(int keyCode, KeyEvent event) {
234 return FocusHelper.handlePagedViewWidgetKeyEvent(this, keyCode, event)
235 || super.onKeyDown(keyCode, event);
236 }
237
238 @Override
239 public boolean onKeyUp(int keyCode, KeyEvent event) {
240 return FocusHelper.handlePagedViewWidgetKeyEvent(this, keyCode, event)
241 || super.onKeyUp(keyCode, event);
242 }
243
244 @Override
Winson Chung29d6fea2010-12-01 15:47:31 -0800245 protected void onDraw(Canvas canvas) {
246 if (mAlpha > 0) {
247 super.onDraw(canvas);
248 }
249
250 // draw any blended overlays
251 if (mHolographicOutline != null && mHolographicAlpha > 0) {
Winson Chung1908d072011-02-24 18:09:44 -0800252 // Calculate how much to scale the holographic preview
253 mTmpScaleRect.set(0,0,1,1);
254 mPreviewImageView.getImageMatrix().mapRect(mTmpScaleRect);
255
Winson Chung29d6fea2010-12-01 15:47:31 -0800256 mPaint.setAlpha(mHolographicAlpha);
Winson Chung1908d072011-02-24 18:09:44 -0800257 canvas.save();
258 canvas.scale(mTmpScaleRect.right, mTmpScaleRect.bottom);
Winson Chung29d6fea2010-12-01 15:47:31 -0800259 canvas.drawBitmap(mHolographicOutline, 0, 0, mPaint);
Winson Chung1908d072011-02-24 18:09:44 -0800260 canvas.restore();
Winson Chung29d6fea2010-12-01 15:47:31 -0800261 }
262 }
263
264 @Override
265 protected boolean onSetAlpha(int alpha) {
266 return true;
267 }
268
269 @Override
270 public void setAlpha(float alpha) {
271 final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha);
272 final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
273 int newViewAlpha = (int) (viewAlpha * 255);
274 int newHolographicAlpha = (int) (holographicAlpha * 255);
275 if ((mAlpha != newViewAlpha) || (mHolographicAlpha != newHolographicAlpha)) {
276 mAlpha = newViewAlpha;
277 mHolographicAlpha = newHolographicAlpha;
278 setChildrenAlpha(viewAlpha);
279 super.setAlpha(viewAlpha);
280 }
281 }
282
283 private void setChildrenAlpha(float alpha) {
284 final int childCount = getChildCount();
285 for (int i = 0; i < childCount; i++) {
286 getChildAt(i).setAlpha(alpha);
287 }
288 }
289
290 @Override
291 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
292 if (w > 0 && h > 0) {
293 queueHolographicOutlineCreation();
294 }
295
296 super.onSizeChanged(w, h, oldw, oldh);
297 }
298
299 @Override
300 public void onDetachedFromWindow() {
301 super.onDetachedFromWindow();
302 sWorker.removeMessages(MESSAGE_CREATE_HOLOGRAPHIC_OUTLINE, this);
303 }
Winson Chung59e1f9a2010-12-21 11:31:54 -0800304
Patrick Dubroy5f445422011-02-18 14:35:21 -0800305 void setChecked(boolean checked, boolean animate) {
Winson Chung59e1f9a2010-12-21 11:31:54 -0800306 if (mIsChecked != checked) {
307 mIsChecked = checked;
308
309 float alpha;
310 int duration;
311 if (mIsChecked) {
312 alpha = mCheckedAlpha;
313 duration = mCheckedFadeInDuration;
314 } else {
315 alpha = 1.0f;
316 duration = mCheckedFadeOutDuration;
317 }
318
319 // Initialize the animator
320 if (mCheckedAlphaAnimator != null) {
321 mCheckedAlphaAnimator.cancel();
322 }
Patrick Dubroy5f445422011-02-18 14:35:21 -0800323 if (animate) {
324 mCheckedAlphaAnimator = ObjectAnimator.ofFloat(this, "alpha", getAlpha(), alpha);
325 mCheckedAlphaAnimator.setDuration(duration);
326 mCheckedAlphaAnimator.start();
327 } else {
328 setAlpha(alpha);
329 }
Winson Chung59e1f9a2010-12-21 11:31:54 -0800330
331 invalidate();
332 }
333 }
334
335 @Override
Patrick Dubroy5f445422011-02-18 14:35:21 -0800336 public void setChecked(boolean checked) {
337 setChecked(checked, true);
338 }
339
340 @Override
Winson Chung59e1f9a2010-12-21 11:31:54 -0800341 public boolean isChecked() {
342 return mIsChecked;
343 }
344
345 @Override
346 public void toggle() {
347 setChecked(!mIsChecked);
348 }
Winson Chung29d6fea2010-12-01 15:47:31 -0800349}