blob: 963702ae2708b39d24f40f23ef153d07c5ed847c [file] [log] [blame]
Winson Chung82f55532011-08-09 14:14:23 -07001/*
2 * Copyright (C) 2011 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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
Winson Chung82f55532011-08-09 14:14:23 -070018
Winson Chungaf40f202013-09-18 18:26:31 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
Winson Chung82f55532011-08-09 14:14:23 -070022import android.content.Context;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
Winson Chung7d7541e2011-09-16 20:14:36 -070025import android.graphics.Bitmap;
Winson Chung82f55532011-08-09 14:14:23 -070026import android.graphics.Canvas;
27import android.graphics.Paint;
Winson Chung7d7541e2011-09-16 20:14:36 -070028import android.graphics.PorterDuff;
29import android.graphics.PorterDuffXfermode;
30import android.graphics.Rect;
Winson Chung82f55532011-08-09 14:14:23 -070031import android.graphics.drawable.Drawable;
32import android.util.AttributeSet;
33import android.util.DisplayMetrics;
Svetoslav Ganov55d225d2012-05-22 15:19:14 -070034import android.view.FocusFinder;
35import android.view.MotionEvent;
36import android.view.View;
Winson Chungaf40f202013-09-18 18:26:31 -070037import android.view.animation.AccelerateInterpolator;
Winson Chung82f55532011-08-09 14:14:23 -070038import android.widget.FrameLayout;
39
Winson Chungaf40f202013-09-18 18:26:31 -070040public class Cling extends FrameLayout implements Insettable, View.OnLongClickListener {
Winson Chung82f55532011-08-09 14:14:23 -070041
Winson Chungaf40f202013-09-18 18:26:31 -070042 static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
43 static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
44 static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
45
46 private static String FIRST_RUN_PORTRAIT = "first_run_portrait";
47 private static String FIRST_RUN_LANDSCAPE = "first_run_landscape";
Winson Chung7d7541e2011-09-16 20:14:36 -070048
Winson Chung82f55532011-08-09 14:14:23 -070049 private static String WORKSPACE_PORTRAIT = "workspace_portrait";
50 private static String WORKSPACE_LANDSCAPE = "workspace_landscape";
Andrew Flynn2f5f9452012-05-12 17:00:35 -070051 private static String WORKSPACE_LARGE = "workspace_large";
52 private static String WORKSPACE_CUSTOM = "workspace_custom";
53
Winson Chung7d7541e2011-09-16 20:14:36 -070054 private static String FOLDER_PORTRAIT = "folder_portrait";
55 private static String FOLDER_LANDSCAPE = "folder_landscape";
Winson Chungd0160152011-11-15 15:04:42 -080056 private static String FOLDER_LARGE = "folder_large";
Winson Chung82f55532011-08-09 14:14:23 -070057
Winson Chungaf40f202013-09-18 18:26:31 -070058 private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 40;
59 private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50;
60 private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60;
61 private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30;
62
Winson Chung82f55532011-08-09 14:14:23 -070063 private Launcher mLauncher;
64 private boolean mIsInitialized;
65 private String mDrawIdentifier;
Winson Chung7d7541e2011-09-16 20:14:36 -070066 private Drawable mBackground;
Winson Chung82f55532011-08-09 14:14:23 -070067
Winson Chung7d7541e2011-09-16 20:14:36 -070068 private Paint mErasePaint;
Winson Chungaf40f202013-09-18 18:26:31 -070069 private Paint mBubblePaint;
70 private Paint mDotPaint;
71
72 private final Rect mInsets = new Rect();
Winson Chung7d7541e2011-09-16 20:14:36 -070073
Winson Chung82f55532011-08-09 14:14:23 -070074 public Cling(Context context) {
75 this(context, null, 0);
76 }
77
78 public Cling(Context context, AttributeSet attrs) {
79 this(context, attrs, 0);
80 }
81
82 public Cling(Context context, AttributeSet attrs, int defStyle) {
83 super(context, attrs, defStyle);
84
85 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Cling, defStyle, 0);
86 mDrawIdentifier = a.getString(R.styleable.Cling_drawIdentifier);
87 a.recycle();
Michael Jurka9bc8eba2012-05-21 20:36:44 -070088
89 setClickable(true);
Winson Chungaf40f202013-09-18 18:26:31 -070090
Winson Chung82f55532011-08-09 14:14:23 -070091 }
92
Winson Chung7d7541e2011-09-16 20:14:36 -070093 void init(Launcher l, int[] positionData) {
Winson Chung82f55532011-08-09 14:14:23 -070094 if (!mIsInitialized) {
95 mLauncher = l;
Winson Chungaf40f202013-09-18 18:26:31 -070096 setOnLongClickListener(this);
Winson Chung82f55532011-08-09 14:14:23 -070097
Winson Chung7d7541e2011-09-16 20:14:36 -070098 mErasePaint = new Paint();
99 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
100 mErasePaint.setColor(0xFFFFFF);
101 mErasePaint.setAlpha(0);
Winson Chungaf40f202013-09-18 18:26:31 -0700102 mErasePaint.setAntiAlias(true);
103
104 mBubblePaint = new Paint();
105 mBubblePaint.setColor(0xFFFFFF);
106 mBubblePaint.setAntiAlias(true);
107
108 mDotPaint = new Paint();
109 mDotPaint.setColor(0x72BBED);
110 mDotPaint.setAntiAlias(true);
Winson Chung7d7541e2011-09-16 20:14:36 -0700111
Winson Chung82f55532011-08-09 14:14:23 -0700112 mIsInitialized = true;
113 }
114 }
115
Winson Chungaf40f202013-09-18 18:26:31 -0700116 void show(boolean animate, int duration) {
117 setVisibility(View.VISIBLE);
118 setLayerType(View.LAYER_TYPE_HARDWARE, null);
119 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
120 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
121 mDrawIdentifier.equals(WORKSPACE_LARGE) ||
122 mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
123 View content = getContent();
124 content.setAlpha(0f);
125 content.animate()
126 .alpha(1f)
127 .setDuration(duration)
128 .setListener(new AnimatorListenerAdapter() {
129 public void onAnimationEnd(Animator animation) {
130 };
131 })
132 .start();
133 setAlpha(1f);
134 } else {
135 if (animate) {
136 buildLayer();
137 setAlpha(0f);
138 animate()
139 .alpha(1f)
140 .setInterpolator(new AccelerateInterpolator())
141 .setDuration(duration)
142 .start();
143 } else {
144 setAlpha(1f);
145 }
146 }
147 setFocusableInTouchMode(true);
148 post(new Runnable() {
149 public void run() {
150 setFocusable(true);
151 requestFocus();
152 }
153 });
154 }
155
156 void hide(final int duration, final Runnable postCb) {
157 if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
158 mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
159 View content = getContent();
160 ObjectAnimator anim = LauncherAnimUtils.ofFloat(content, "alpha", 0f);
161 anim.setDuration(duration);
162 anim.addListener(new AnimatorListenerAdapter() {
163 public void onAnimationEnd(Animator animation) {
164 // We are about to trigger the workspace cling, so don't do anything else
165 setVisibility(View.GONE);
166 postCb.run();
167 };
168 });
169 anim.start();
170 } else {
171 ObjectAnimator anim = LauncherAnimUtils.ofFloat(this, "alpha", 0f);
172 anim.setDuration(duration);
173 anim.addListener(new AnimatorListenerAdapter() {
174 public void onAnimationEnd(Animator animation) {
175 setVisibility(View.GONE);
176 postCb.run();
177 };
178 });
179 anim.start();
180 }
181 }
182
Winson Chung82f55532011-08-09 14:14:23 -0700183 void cleanup() {
Winson Chung7d7541e2011-09-16 20:14:36 -0700184 mBackground = null;
Winson Chung7d7541e2011-09-16 20:14:36 -0700185 mIsInitialized = false;
186 }
187
Winson Chungaf40f202013-09-18 18:26:31 -0700188 @Override
189 public void setInsets(Rect insets) {
190 mInsets.set(insets);
191 setPadding(insets.left, insets.top, insets.right, insets.bottom);
Michael Jurka974c3862012-05-22 22:00:31 -0700192 }
193
Winson Chungaf40f202013-09-18 18:26:31 -0700194 View getContent() {
195 return findViewById(R.id.content);
196 }
197
198 String getDrawIdentifier() {
199 return mDrawIdentifier;
Winson Chung82f55532011-08-09 14:14:23 -0700200 }
201
202 @Override
Svetoslav Ganov55d225d2012-05-22 15:19:14 -0700203 public View focusSearch(int direction) {
Michael Jurka9bc8eba2012-05-21 20:36:44 -0700204 return this.focusSearch(this, direction);
Svetoslav Ganov55d225d2012-05-22 15:19:14 -0700205 }
206
207 @Override
208 public View focusSearch(View focused, int direction) {
209 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
210 }
211
212 @Override
213 public boolean onHoverEvent(MotionEvent event) {
214 return (mDrawIdentifier.equals(WORKSPACE_PORTRAIT)
215 || mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)
216 || mDrawIdentifier.equals(WORKSPACE_LARGE)
Svetoslav Ganov55d225d2012-05-22 15:19:14 -0700217 || mDrawIdentifier.equals(WORKSPACE_CUSTOM));
218 }
219
220 @Override
Winson Chung82f55532011-08-09 14:14:23 -0700221 public boolean onTouchEvent(android.view.MotionEvent event) {
Winson Chungaf40f202013-09-18 18:26:31 -0700222 if (mDrawIdentifier.equals(FOLDER_PORTRAIT) ||
Winson Chungd0160152011-11-15 15:04:42 -0800223 mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
224 mDrawIdentifier.equals(FOLDER_LARGE)) {
Winson Chung7d7541e2011-09-16 20:14:36 -0700225 Folder f = mLauncher.getWorkspace().getOpenFolder();
226 if (f != null) {
227 Rect r = new Rect();
228 f.getHitRect(r);
229 if (r.contains((int) event.getX(), (int) event.getY())) {
230 return false;
231 }
232 }
233 }
Winson Chungaf40f202013-09-18 18:26:31 -0700234 return super.onTouchEvent(event);
Winson Chung82f55532011-08-09 14:14:23 -0700235 };
236
237 @Override
Winson Chungaf40f202013-09-18 18:26:31 -0700238 public boolean onLongClick(View v) {
239 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
240 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
241 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
242 mLauncher.dismissWorkspaceCling(null);
243 return true;
244 }
245 return false;
246 }
247
248 @Override
Winson Chung82f55532011-08-09 14:14:23 -0700249 protected void dispatchDraw(Canvas canvas) {
Winson Chung82f55532011-08-09 14:14:23 -0700250 if (mIsInitialized) {
Winson Chungaf40f202013-09-18 18:26:31 -0700251 canvas.save();
Winson Chung82f55532011-08-09 14:14:23 -0700252
Winson Chungaf40f202013-09-18 18:26:31 -0700253 // Get the background override if there is one
Winson Chung7d7541e2011-09-16 20:14:36 -0700254 if (mBackground == null) {
Winson Chungaf40f202013-09-18 18:26:31 -0700255 if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
Andrew Flynna743d352012-05-18 13:38:52 -0700256 mBackground = getResources().getDrawable(R.drawable.bg_cling5);
Winson Chung7d7541e2011-09-16 20:14:36 -0700257 }
258 }
Winson Chungaf40f202013-09-18 18:26:31 -0700259 // Draw the background
260 Bitmap eraseBg = null;
261 Canvas eraseCanvas = null;
Winson Chung7d7541e2011-09-16 20:14:36 -0700262 if (mBackground != null) {
263 mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
Winson Chungaf40f202013-09-18 18:26:31 -0700264 mBackground.draw(canvas);
265 } else if (mDrawIdentifier.equals(FOLDER_PORTRAIT) ||
266 mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
267 mDrawIdentifier.equals(FOLDER_LARGE)) {
268 canvas.drawColor(0xcc000000);
269 } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
270 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
271 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
272 // Initialize the draw buffer (to allow punching through)
273 eraseBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
274 Bitmap.Config.ARGB_8888);
275 eraseCanvas = new Canvas(eraseBg);
276 eraseCanvas.drawColor(0xdd000000);
Winson Chung7d7541e2011-09-16 20:14:36 -0700277 } else {
Winson Chungaf40f202013-09-18 18:26:31 -0700278 canvas.drawColor(0xdd000000);
Winson Chung7d7541e2011-09-16 20:14:36 -0700279 }
Winson Chung82f55532011-08-09 14:14:23 -0700280
Winson Chungaf40f202013-09-18 18:26:31 -0700281 // Draw everything else
282 DisplayMetrics metrics = new DisplayMetrics();
283 mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
284 float alpha = getAlpha();
285 View content = getContent();
286 if (content != null) {
287 alpha *= content.getAlpha();
288 }
289 if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
290 mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
291 // Draw the white circle
292 View bubbleContent = findViewById(R.id.bubble_content);
293 Rect bubbleRect = new Rect();
294 bubbleContent.getGlobalVisibleRect(bubbleRect);
295 mBubblePaint.setAlpha((int) (255 * alpha));
296 float buffer = DynamicGrid.pxFromDp(FIRST_RUN_CIRCLE_BUFFER_DPS, metrics);
297 canvas.drawCircle(metrics.widthPixels / 2,
298 bubbleRect.centerY(),
299 (bubbleContent.getMeasuredWidth() + buffer) / 2,
300 mBubblePaint);
301 } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
302 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
303 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
304 int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
305 mErasePaint.setAlpha((int) (128));
306 eraseCanvas.drawCircle(metrics.widthPixels / 2,
307 metrics.heightPixels / 2 - offset,
308 DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
309 mErasePaint);
310 mErasePaint.setAlpha(0);
311 eraseCanvas.drawCircle(metrics.widthPixels / 2,
312 metrics.heightPixels / 2 - offset,
313 DynamicGrid.pxFromDp(WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
314 mErasePaint);
315 canvas.drawBitmap(eraseBg, 0, 0, null);
316 eraseCanvas.setBitmap(null);
317 eraseBg = null;
Winson Chung82f55532011-08-09 14:14:23 -0700318 }
Winson Chung7d7541e2011-09-16 20:14:36 -0700319
Winson Chung7d7541e2011-09-16 20:14:36 -0700320
Winson Chungaf40f202013-09-18 18:26:31 -0700321 canvas.restore();
Winson Chung82f55532011-08-09 14:14:23 -0700322 }
Winson Chung7d7541e2011-09-16 20:14:36 -0700323
324 // Draw the rest of the cling
325 super.dispatchDraw(canvas);
Winson Chung82f55532011-08-09 14:14:23 -0700326 };
327}