blob: 338b722ab8e8e1f1529f1132040faaf268fc84ba [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;
Winson Chung3a6e7f32013-10-09 15:50:52 -070021import android.app.ActivityOptions;
Winson Chung82f55532011-08-09 14:14:23 -070022import android.content.Context;
Winson Chung3a6e7f32013-10-09 15:50:52 -070023import android.content.ComponentName;
24import android.content.Intent;
Winson Chung82f55532011-08-09 14:14:23 -070025import android.content.res.Resources;
26import android.content.res.TypedArray;
Winson Chung3a6e7f32013-10-09 15:50:52 -070027import android.graphics.*;
Winson Chung82f55532011-08-09 14:14:23 -070028import android.graphics.drawable.Drawable;
29import android.util.AttributeSet;
30import android.util.DisplayMetrics;
Svetoslav Ganov55d225d2012-05-22 15:19:14 -070031import android.view.FocusFinder;
32import android.view.MotionEvent;
33import android.view.View;
Winson Chungaf40f202013-09-18 18:26:31 -070034import android.view.animation.AccelerateInterpolator;
Winson Chung82f55532011-08-09 14:14:23 -070035import android.widget.FrameLayout;
Winson Chung3a6e7f32013-10-09 15:50:52 -070036import android.widget.TextView;
Winson Chung82f55532011-08-09 14:14:23 -070037
Winson Chung3a6e7f32013-10-09 15:50:52 -070038public class Cling extends FrameLayout implements Insettable, View.OnClickListener,
39 View.OnLongClickListener, View.OnTouchListener {
Winson Chung82f55532011-08-09 14:14:23 -070040
Winson Chungaf40f202013-09-18 18:26:31 -070041 static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
42 static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
43 static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
44
45 private static String FIRST_RUN_PORTRAIT = "first_run_portrait";
46 private static String FIRST_RUN_LANDSCAPE = "first_run_landscape";
Winson Chung7d7541e2011-09-16 20:14:36 -070047
Winson Chung82f55532011-08-09 14:14:23 -070048 private static String WORKSPACE_PORTRAIT = "workspace_portrait";
49 private static String WORKSPACE_LANDSCAPE = "workspace_landscape";
Andrew Flynn2f5f9452012-05-12 17:00:35 -070050 private static String WORKSPACE_LARGE = "workspace_large";
51 private static String WORKSPACE_CUSTOM = "workspace_custom";
52
Winson Chung7d7541e2011-09-16 20:14:36 -070053 private static String FOLDER_PORTRAIT = "folder_portrait";
54 private static String FOLDER_LANDSCAPE = "folder_landscape";
Winson Chungd0160152011-11-15 15:04:42 -080055 private static String FOLDER_LARGE = "folder_large";
Winson Chung82f55532011-08-09 14:14:23 -070056
Winson Chunge6eabff2013-09-24 11:01:23 -070057 private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 60;
Winson Chungaf40f202013-09-18 18:26:31 -070058 private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50;
59 private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60;
60 private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30;
61
Winson Chung82f55532011-08-09 14:14:23 -070062 private Launcher mLauncher;
63 private boolean mIsInitialized;
64 private String mDrawIdentifier;
Winson Chung7d7541e2011-09-16 20:14:36 -070065 private Drawable mBackground;
Winson Chung82f55532011-08-09 14:14:23 -070066
Winson Chung3a6e7f32013-10-09 15:50:52 -070067 private int[] mTouchDownPt = new int[2];
68
69 private Drawable mFocusedHotseatApp;
70 private ComponentName mFocusedHotseatAppComponent;
71 private Rect mFocusedHotseatAppBounds;
72
Winson Chung7d7541e2011-09-16 20:14:36 -070073 private Paint mErasePaint;
Winson Chungaf40f202013-09-18 18:26:31 -070074 private Paint mBubblePaint;
75 private Paint mDotPaint;
76
Winson Chungfa545132013-09-26 17:45:32 -070077 private View mScrimView;
78 private int mBackgroundColor;
79
Winson Chungaf40f202013-09-18 18:26:31 -070080 private final Rect mInsets = new Rect();
Winson Chung7d7541e2011-09-16 20:14:36 -070081
Winson Chung82f55532011-08-09 14:14:23 -070082 public Cling(Context context) {
83 this(context, null, 0);
84 }
85
86 public Cling(Context context, AttributeSet attrs) {
87 this(context, attrs, 0);
88 }
89
90 public Cling(Context context, AttributeSet attrs, int defStyle) {
91 super(context, attrs, defStyle);
92
93 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Cling, defStyle, 0);
94 mDrawIdentifier = a.getString(R.styleable.Cling_drawIdentifier);
95 a.recycle();
Michael Jurka9bc8eba2012-05-21 20:36:44 -070096
97 setClickable(true);
Winson Chungaf40f202013-09-18 18:26:31 -070098
Winson Chung82f55532011-08-09 14:14:23 -070099 }
100
Winson Chungfa545132013-09-26 17:45:32 -0700101 void init(Launcher l, View scrim) {
Winson Chung82f55532011-08-09 14:14:23 -0700102 if (!mIsInitialized) {
103 mLauncher = l;
Winson Chungfa545132013-09-26 17:45:32 -0700104 mScrimView = scrim;
105 mBackgroundColor = 0xdd000000;
Winson Chungaf40f202013-09-18 18:26:31 -0700106 setOnLongClickListener(this);
Winson Chung3a6e7f32013-10-09 15:50:52 -0700107 setOnClickListener(this);
108 setOnTouchListener(this);
Winson Chung82f55532011-08-09 14:14:23 -0700109
Winson Chung7d7541e2011-09-16 20:14:36 -0700110 mErasePaint = new Paint();
111 mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
112 mErasePaint.setColor(0xFFFFFF);
113 mErasePaint.setAlpha(0);
Winson Chungaf40f202013-09-18 18:26:31 -0700114 mErasePaint.setAntiAlias(true);
115
Winson Chunge6eabff2013-09-24 11:01:23 -0700116 int circleColor = getResources().getColor(
117 R.color.first_run_cling_circle_background_color);
Winson Chungaf40f202013-09-18 18:26:31 -0700118 mBubblePaint = new Paint();
Winson Chunge6eabff2013-09-24 11:01:23 -0700119 mBubblePaint.setColor(circleColor);
Winson Chungaf40f202013-09-18 18:26:31 -0700120 mBubblePaint.setAntiAlias(true);
121
122 mDotPaint = new Paint();
123 mDotPaint.setColor(0x72BBED);
124 mDotPaint.setAntiAlias(true);
Winson Chung7d7541e2011-09-16 20:14:36 -0700125
Winson Chung82f55532011-08-09 14:14:23 -0700126 mIsInitialized = true;
127 }
128 }
129
Winson Chung3a6e7f32013-10-09 15:50:52 -0700130 void setFocusedHotseatApp(int drawableId, int appRank, ComponentName cn, String title,
131 String description) {
132 // Get the app to draw
133 Resources r = getResources();
134 int appIconId = drawableId;
135 Hotseat hotseat = mLauncher.getHotseat();
136 if (hotseat != null && appIconId > -1 && appRank > -1 && !title.isEmpty() &&
137 !description.isEmpty()) {
138 // Set the app bounds
139 int x = hotseat.getCellXFromOrder(appRank);
140 int y = hotseat.getCellYFromOrder(appRank);
141 Rect pos = hotseat.getCellCoordinates(x, y);
142 LauncherAppState app = LauncherAppState.getInstance();
143 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
144 mFocusedHotseatApp = getResources().getDrawable(appIconId);
145 mFocusedHotseatAppComponent = cn;
146 mFocusedHotseatAppBounds = new Rect(pos.left, pos.top,
147 pos.left + Utilities.sIconTextureWidth,
148 pos.top + Utilities.sIconTextureHeight);
149 Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds,
150 (grid.hotseatIconSize / grid.iconSize));
151
152 // Set the title
153 TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title);
154 if (v != null) {
155 v.setText(title);
156 }
157
158 // Set the description
159 v = (TextView) findViewById(R.id.focused_hotseat_app_description);
160 if (v != null) {
161 v.setText(description);
162 }
163
164 // Show the bubble
165 View bubble = findViewById(R.id.focused_hotseat_app_bubble);
166 bubble.setVisibility(View.VISIBLE);
167 }
168 }
169
Winson Chungaf40f202013-09-18 18:26:31 -0700170 void show(boolean animate, int duration) {
171 setVisibility(View.VISIBLE);
172 setLayerType(View.LAYER_TYPE_HARDWARE, null);
173 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
174 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
175 mDrawIdentifier.equals(WORKSPACE_LARGE) ||
176 mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
177 View content = getContent();
178 content.setAlpha(0f);
179 content.animate()
180 .alpha(1f)
181 .setDuration(duration)
Winson Chungfa545132013-09-26 17:45:32 -0700182 .setListener(null)
Winson Chungaf40f202013-09-18 18:26:31 -0700183 .start();
184 setAlpha(1f);
185 } else {
186 if (animate) {
187 buildLayer();
188 setAlpha(0f);
189 animate()
190 .alpha(1f)
191 .setInterpolator(new AccelerateInterpolator())
192 .setDuration(duration)
Winson Chungfa545132013-09-26 17:45:32 -0700193 .setListener(null)
Winson Chungaf40f202013-09-18 18:26:31 -0700194 .start();
195 } else {
196 setAlpha(1f);
197 }
198 }
Winson Chungfa545132013-09-26 17:45:32 -0700199
200 // Show the scrim if necessary
201 if (mScrimView != null) {
202 mScrimView.setVisibility(View.VISIBLE);
203 mScrimView.setAlpha(0f);
204 mScrimView.animate()
205 .alpha(1f)
206 .setDuration(duration)
207 .setListener(null)
208 .start();
209 }
210
Winson Chungaf40f202013-09-18 18:26:31 -0700211 setFocusableInTouchMode(true);
212 post(new Runnable() {
213 public void run() {
214 setFocusable(true);
215 requestFocus();
216 }
217 });
218 }
219
220 void hide(final int duration, final Runnable postCb) {
221 if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
222 mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
223 View content = getContent();
Winson Chungfa545132013-09-26 17:45:32 -0700224 content.animate()
225 .alpha(0f)
226 .setDuration(duration)
227 .setListener(new AnimatorListenerAdapter() {
228 public void onAnimationEnd(Animator animation) {
229 // We are about to trigger the workspace cling, so don't do anything else
230 setVisibility(View.GONE);
231 postCb.run();
232 };
233 })
234 .start();
Winson Chungaf40f202013-09-18 18:26:31 -0700235 } else {
Winson Chungfa545132013-09-26 17:45:32 -0700236 animate()
237 .alpha(0f)
238 .setDuration(duration)
239 .setListener(new AnimatorListenerAdapter() {
240 public void onAnimationEnd(Animator animation) {
241 // We are about to trigger the workspace cling, so don't do anything else
242 setVisibility(View.GONE);
243 postCb.run();
244 };
245 })
246 .start();
247 }
248
249 // Show the scrim if necessary
250 if (mScrimView != null) {
251 mScrimView.animate()
252 .alpha(0f)
253 .setDuration(duration)
254 .setListener(new AnimatorListenerAdapter() {
255 public void onAnimationEnd(Animator animation) {
256 mScrimView.setVisibility(View.GONE);
257 };
258 })
259 .start();
Winson Chungaf40f202013-09-18 18:26:31 -0700260 }
261 }
262
Winson Chung82f55532011-08-09 14:14:23 -0700263 void cleanup() {
Winson Chung7d7541e2011-09-16 20:14:36 -0700264 mBackground = null;
Winson Chung7d7541e2011-09-16 20:14:36 -0700265 mIsInitialized = false;
266 }
267
Winson Chungfa545132013-09-26 17:45:32 -0700268 void bringScrimToFront() {
269 if (mScrimView != null) {
270 mScrimView.bringToFront();
271 }
272 }
273
Winson Chungaf40f202013-09-18 18:26:31 -0700274 @Override
275 public void setInsets(Rect insets) {
276 mInsets.set(insets);
277 setPadding(insets.left, insets.top, insets.right, insets.bottom);
Michael Jurka974c3862012-05-22 22:00:31 -0700278 }
279
Winson Chungaf40f202013-09-18 18:26:31 -0700280 View getContent() {
281 return findViewById(R.id.content);
282 }
283
284 String getDrawIdentifier() {
285 return mDrawIdentifier;
Winson Chung82f55532011-08-09 14:14:23 -0700286 }
287
288 @Override
Svetoslav Ganov55d225d2012-05-22 15:19:14 -0700289 public View focusSearch(int direction) {
Michael Jurka9bc8eba2012-05-21 20:36:44 -0700290 return this.focusSearch(this, direction);
Svetoslav Ganov55d225d2012-05-22 15:19:14 -0700291 }
292
293 @Override
294 public View focusSearch(View focused, int direction) {
295 return FocusFinder.getInstance().findNextFocus(this, focused, direction);
296 }
297
298 @Override
299 public boolean onHoverEvent(MotionEvent event) {
300 return (mDrawIdentifier.equals(WORKSPACE_PORTRAIT)
301 || mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)
302 || mDrawIdentifier.equals(WORKSPACE_LARGE)
Svetoslav Ganov55d225d2012-05-22 15:19:14 -0700303 || mDrawIdentifier.equals(WORKSPACE_CUSTOM));
304 }
305
306 @Override
Winson Chung82f55532011-08-09 14:14:23 -0700307 public boolean onTouchEvent(android.view.MotionEvent event) {
Winson Chungaf40f202013-09-18 18:26:31 -0700308 if (mDrawIdentifier.equals(FOLDER_PORTRAIT) ||
Winson Chungd0160152011-11-15 15:04:42 -0800309 mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
310 mDrawIdentifier.equals(FOLDER_LARGE)) {
Winson Chung7d7541e2011-09-16 20:14:36 -0700311 Folder f = mLauncher.getWorkspace().getOpenFolder();
312 if (f != null) {
313 Rect r = new Rect();
314 f.getHitRect(r);
315 if (r.contains((int) event.getX(), (int) event.getY())) {
316 return false;
317 }
318 }
319 }
Winson Chungaf40f202013-09-18 18:26:31 -0700320 return super.onTouchEvent(event);
Winson Chung82f55532011-08-09 14:14:23 -0700321 };
322
323 @Override
Winson Chung3a6e7f32013-10-09 15:50:52 -0700324 public boolean onTouch(View v, MotionEvent ev) {
325 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
326 mTouchDownPt[0] = (int) ev.getX();
327 mTouchDownPt[1] = (int) ev.getY();
328 }
329 return false;
330 }
331
332 @Override
333 public void onClick(View v) {
334 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
335 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
336 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
337 if (mFocusedHotseatAppBounds != null &&
338 mFocusedHotseatAppBounds.contains(mTouchDownPt[0], mTouchDownPt[1])) {
339 // Launch the activity that is being highlighted
340 Intent intent = new Intent(Intent.ACTION_MAIN);
341 intent.setComponent(mFocusedHotseatAppComponent);
342 intent.addCategory(Intent.CATEGORY_LAUNCHER);
343 mLauncher.startActivity(intent, null);
344 mLauncher.dismissWorkspaceCling(this);
345 }
346 }
347 }
348
349 @Override
Winson Chungaf40f202013-09-18 18:26:31 -0700350 public boolean onLongClick(View v) {
351 if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
352 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
353 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
354 mLauncher.dismissWorkspaceCling(null);
355 return true;
356 }
357 return false;
358 }
359
360 @Override
Winson Chung82f55532011-08-09 14:14:23 -0700361 protected void dispatchDraw(Canvas canvas) {
Winson Chung82f55532011-08-09 14:14:23 -0700362 if (mIsInitialized) {
Winson Chungaf40f202013-09-18 18:26:31 -0700363 canvas.save();
Winson Chung82f55532011-08-09 14:14:23 -0700364
Winson Chungaf40f202013-09-18 18:26:31 -0700365 // Get the background override if there is one
Winson Chung7d7541e2011-09-16 20:14:36 -0700366 if (mBackground == null) {
Winson Chungaf40f202013-09-18 18:26:31 -0700367 if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
Andrew Flynna743d352012-05-18 13:38:52 -0700368 mBackground = getResources().getDrawable(R.drawable.bg_cling5);
Winson Chung7d7541e2011-09-16 20:14:36 -0700369 }
370 }
Winson Chungaf40f202013-09-18 18:26:31 -0700371 // Draw the background
372 Bitmap eraseBg = null;
373 Canvas eraseCanvas = null;
Winson Chungfa545132013-09-26 17:45:32 -0700374 if (mScrimView != null) {
375 // Skip drawing the background
376 mScrimView.setBackgroundColor(mBackgroundColor);
377 } else if (mBackground != null) {
Winson Chung7d7541e2011-09-16 20:14:36 -0700378 mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
Winson Chungaf40f202013-09-18 18:26:31 -0700379 mBackground.draw(canvas);
Winson Chungaf40f202013-09-18 18:26:31 -0700380 } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
381 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
382 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
383 // Initialize the draw buffer (to allow punching through)
384 eraseBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
385 Bitmap.Config.ARGB_8888);
386 eraseCanvas = new Canvas(eraseBg);
Winson Chungfa545132013-09-26 17:45:32 -0700387 eraseCanvas.drawColor(mBackgroundColor);
Winson Chung7d7541e2011-09-16 20:14:36 -0700388 } else {
Winson Chungfa545132013-09-26 17:45:32 -0700389 canvas.drawColor(mBackgroundColor);
Winson Chung7d7541e2011-09-16 20:14:36 -0700390 }
Winson Chung82f55532011-08-09 14:14:23 -0700391
Winson Chungaf40f202013-09-18 18:26:31 -0700392 // Draw everything else
393 DisplayMetrics metrics = new DisplayMetrics();
394 mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
395 float alpha = getAlpha();
396 View content = getContent();
397 if (content != null) {
398 alpha *= content.getAlpha();
399 }
400 if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
401 mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
Winson Chung45cab392013-10-02 17:45:32 -0700402 // Draw the circle
Winson Chungaf40f202013-09-18 18:26:31 -0700403 View bubbleContent = findViewById(R.id.bubble_content);
404 Rect bubbleRect = new Rect();
405 bubbleContent.getGlobalVisibleRect(bubbleRect);
406 mBubblePaint.setAlpha((int) (255 * alpha));
407 float buffer = DynamicGrid.pxFromDp(FIRST_RUN_CIRCLE_BUFFER_DPS, metrics);
408 canvas.drawCircle(metrics.widthPixels / 2,
409 bubbleRect.centerY(),
410 (bubbleContent.getMeasuredWidth() + buffer) / 2,
411 mBubblePaint);
412 } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
413 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
414 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
415 int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
416 mErasePaint.setAlpha((int) (128));
417 eraseCanvas.drawCircle(metrics.widthPixels / 2,
418 metrics.heightPixels / 2 - offset,
419 DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
420 mErasePaint);
421 mErasePaint.setAlpha(0);
422 eraseCanvas.drawCircle(metrics.widthPixels / 2,
423 metrics.heightPixels / 2 - offset,
424 DynamicGrid.pxFromDp(WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
425 mErasePaint);
426 canvas.drawBitmap(eraseBg, 0, 0, null);
427 eraseCanvas.setBitmap(null);
428 eraseBg = null;
Winson Chung7d7541e2011-09-16 20:14:36 -0700429
Winson Chung3a6e7f32013-10-09 15:50:52 -0700430 // Draw the focused hotseat app icon
431 if (mFocusedHotseatAppBounds != null && mFocusedHotseatApp != null) {
432 mFocusedHotseatApp.setBounds(mFocusedHotseatAppBounds.left,
433 mFocusedHotseatAppBounds.top, mFocusedHotseatAppBounds.right,
434 mFocusedHotseatAppBounds.bottom);
435 mFocusedHotseatApp.setAlpha((int) (255 * alpha));
436 mFocusedHotseatApp.draw(canvas);
437 }
438 }
Winson Chung7d7541e2011-09-16 20:14:36 -0700439
Winson Chungaf40f202013-09-18 18:26:31 -0700440 canvas.restore();
Winson Chung82f55532011-08-09 14:14:23 -0700441 }
Winson Chung7d7541e2011-09-16 20:14:36 -0700442
443 // Draw the rest of the cling
444 super.dispatchDraw(canvas);
Winson Chung82f55532011-08-09 14:14:23 -0700445 };
446}