blob: 18b242bfa1c7aa06494b0b669d87b923d85813b0 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 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
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Adam Cohen2801caf2011-05-13 20:57:39 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Adam Cohen2801caf2011-05-13 20:57:39 -070021import android.animation.ValueAnimator;
22import android.animation.ValueAnimator.AnimatorUpdateListener;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080023import android.content.Context;
24import android.content.res.Resources;
Adam Cohena9cf38f2011-05-02 15:36:58 -070025import android.graphics.Canvas;
Adam Cohenf4b08912011-05-17 18:45:47 -070026import android.graphics.Color;
Adam Cohenbadf71e2011-05-26 19:08:29 -070027import android.graphics.PorterDuff;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080028import android.graphics.drawable.Drawable;
29import android.util.AttributeSet;
30import android.view.LayoutInflater;
Adam Cohen7c693212011-05-18 15:26:57 -070031import android.view.View;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.view.ViewGroup;
Adam Cohena9cf38f2011-05-02 15:36:58 -070033import android.widget.FrameLayout;
34import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035
Romain Guyedcce092010-03-04 13:03:17 -080036import com.android.launcher.R;
Adam Cohenc0dcf592011-06-01 15:30:43 -070037import com.android.launcher2.DropTarget.DragObject;
Adam Cohena9cf38f2011-05-02 15:36:58 -070038import com.android.launcher2.FolderInfo.FolderListener;
Romain Guyedcce092010-03-04 13:03:17 -080039
Adam Cohenc0dcf592011-06-01 15:30:43 -070040import java.util.ArrayList;
41
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042/**
43 * An icon that can appear on in the workspace representing an {@link UserFolder}.
44 */
Adam Cohenc0dcf592011-06-01 15:30:43 -070045public class FolderIcon extends FrameLayout implements FolderListener {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046 private Launcher mLauncher;
Adam Cohena9cf38f2011-05-02 15:36:58 -070047 Folder mFolder;
48 FolderInfo mInfo;
49
Adam Cohenbadf71e2011-05-26 19:08:29 -070050 // The number of icons to display in the
Adam Cohena9cf38f2011-05-02 15:36:58 -070051 private static final int NUM_ITEMS_IN_PREVIEW = 4;
Adam Cohenf4b08912011-05-17 18:45:47 -070052 private static final int CONSUMPTION_ANIMATION_DURATION = 100;
Adam Cohenbadf71e2011-05-26 19:08:29 -070053
54 // The degree to which the inner ring grows when accepting drop
Adam Cohen073a46f2011-05-17 16:28:09 -070055 private static final float INNER_RING_GROWTH_FACTOR = 0.1f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070056
57 // The degree to which the inner ring is scaled in its natural state
Adam Cohenf4b08912011-05-17 18:45:47 -070058 private static final float INNER_RING_BASELINE_SCALE = 1.0f;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059
Adam Cohenbadf71e2011-05-26 19:08:29 -070060 // The degree to which the outer ring grows when accepting drop
61 private static final float OUTER_RING_BASELINE_SCALE = 0.7f;
62
63 // The degree to which the outer ring is scaled in its natural state
64 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
65
66 // The amount of vertical spread between items in the stack [0...1]
Adam Cohenc0dcf592011-06-01 15:30:43 -070067 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.3f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070068
69 // The degree to which the item in the back of the stack is scaled [0...1]
70 // (0 means it's not scaled at all, 1 means it's scaled to nothing)
71 private static final float PERSPECTIVE_SCALE_FACTOR = 0.3f;
72
73 // The percentage of the FolderIcons view that will be dedicated to the items preview
74 private static final float SPACE_PERCENTAGE_FOR_ICONS = 0.8f;
75
Adam Cohen073a46f2011-05-17 16:28:09 -070076 private int mOriginalWidth = -1;
77 private int mOriginalHeight = -1;
Adam Cohen073a46f2011-05-17 16:28:09 -070078
Adam Cohen19072da2011-05-31 14:30:45 -070079 FolderRingAnimator mFolderRingAnimator = null;
Adam Cohen2801caf2011-05-13 20:57:39 -070080
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081 public FolderIcon(Context context, AttributeSet attrs) {
82 super(context, attrs);
83 }
84
85 public FolderIcon(Context context) {
86 super(context);
87 }
88
Michael Jurka0280c3b2010-09-17 15:00:07 -070089 public boolean isDropEnabled() {
Winson Chung7a1d1652011-03-18 15:56:01 -070090 final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
91 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
92 final Workspace workspace = (Workspace) cellLayout.getParent();
93 return !workspace.isSmall();
Michael Jurka0280c3b2010-09-17 15:00:07 -070094 }
95
The Android Open Source Project31dd5032009-03-03 19:32:27 -080096 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Adam Cohendf2cc412011-04-27 16:56:57 -070097 FolderInfo folderInfo, IconCache iconCache) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098
99 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
100
101 final Resources resources = launcher.getResources();
Adam Cohen2801caf2011-05-13 20:57:39 -0700102 Drawable d = iconCache.getFullResIcon(resources, R.drawable.portal_ring_inner_holo);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700103 icon.setBackgroundDrawable(d);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800104 icon.setTag(folderInfo);
105 icon.setOnClickListener(launcher);
106 icon.mInfo = folderInfo;
107 icon.mLauncher = launcher;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700108
109 Folder folder = Folder.fromXml(launcher);
110 folder.setDragController(launcher.getDragController());
111 folder.setLauncher(launcher);
Adam Cohen2801caf2011-05-13 20:57:39 -0700112 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700113 folder.bind(folderInfo);
114 icon.mFolder = folder;
Adam Cohen19072da2011-05-31 14:30:45 -0700115 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700116 folderInfo.addListener(icon);
Adam Cohen19072da2011-05-31 14:30:45 -0700117
118 return icon;
119 }
120
121 public static class FolderRingAnimator {
122 public int mFolderLocX;
123 public int mFolderLocY;
124 public float mOuterRingScale;
125 public float mInnerRingScale;
126 public FolderIcon mFolderIcon = null;
127 private Launcher mLauncher;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700128 public Drawable mOuterRingDrawable = null;
129 public Drawable mInnerRingDrawable = null;
130 public static Drawable sSharedOuterRingDrawable = null;
131 public static Drawable sSharedInnerRingDrawable = null;
132 private ValueAnimator mAcceptAnimator;
133 private ValueAnimator mNeutralAnimator;
Adam Cohen19072da2011-05-31 14:30:45 -0700134
135 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
136 mLauncher = launcher;
137 mFolderIcon = folderIcon;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700138 mOuterRingDrawable =
139 launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo);
140 mInnerRingDrawable =
141 launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo);
142 if (sSharedOuterRingDrawable == null) {
143 sSharedOuterRingDrawable =
Adam Cohen19072da2011-05-31 14:30:45 -0700144 launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo);
145 }
Adam Cohenc0dcf592011-06-01 15:30:43 -0700146 if (sSharedInnerRingDrawable == null) {
147 sSharedInnerRingDrawable =
Adam Cohen19072da2011-05-31 14:30:45 -0700148 launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo);
149 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700150 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700151
Adam Cohenc0dcf592011-06-01 15:30:43 -0700152 // Location is expressed in window coordinates
Adam Cohen19072da2011-05-31 14:30:45 -0700153 public void setLocation(int x, int y) {
154 mFolderLocX = x;
155 mFolderLocY = y;
Adam Cohenf4b08912011-05-17 18:45:47 -0700156 }
Adam Cohen19072da2011-05-31 14:30:45 -0700157
158 public void animateToAcceptState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700159 if (mNeutralAnimator != null) {
160 mNeutralAnimator.cancel();
161 }
162 mAcceptAnimator = ValueAnimator.ofFloat(0f, 1f);
163 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
164 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700165 public void onAnimationUpdate(ValueAnimator animation) {
166 final float percent = (Float) animation.getAnimatedValue();
167 mOuterRingScale = OUTER_RING_BASELINE_SCALE + percent * OUTER_RING_GROWTH_FACTOR;
168 mInnerRingScale = INNER_RING_BASELINE_SCALE + percent * INNER_RING_GROWTH_FACTOR;
169 mLauncher.getWorkspace().invalidate();
170 if (mFolderIcon != null) {
171 mFolderIcon.invalidate();
172 }
173 }
174 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700175 mAcceptAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700176 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700177 public void onAnimationStart(Animator animation) {
Adam Cohen19072da2011-05-31 14:30:45 -0700178 if (mFolderIcon != null) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700179 mFolderIcon.setBackgroundDrawable(null);
Adam Cohen19072da2011-05-31 14:30:45 -0700180 }
181 }
182 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700183 mAcceptAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700184 }
185
186 public void animateToNaturalState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700187 if (mAcceptAnimator != null) {
188 mAcceptAnimator.cancel();
189 }
190 mNeutralAnimator = ValueAnimator.ofFloat(0f, 1f);
191 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
192 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700193 public void onAnimationUpdate(ValueAnimator animation) {
194 final float percent = (Float) animation.getAnimatedValue();
195 mOuterRingScale = OUTER_RING_BASELINE_SCALE + OUTER_RING_GROWTH_FACTOR
196 - percent * OUTER_RING_GROWTH_FACTOR;
197 mInnerRingScale = INNER_RING_BASELINE_SCALE + INNER_RING_GROWTH_FACTOR
198 - percent * INNER_RING_GROWTH_FACTOR;
199 mLauncher.getWorkspace().invalidate();
200 if (mFolderIcon != null) {
201 mFolderIcon.invalidate();
202 }
203 }
204 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700205 mNeutralAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700206 @Override
207 public void onAnimationEnd(Animator animation) {
208 if (mFolderIcon != null) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700209 mFolderIcon.setBackgroundDrawable(mInnerRingDrawable);
Adam Cohen19072da2011-05-31 14:30:45 -0700210 }
211 mLauncher.getWorkspace().hideFolderAccept(FolderRingAnimator.this);
212 }
213 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700214 mNeutralAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700215 }
216
Adam Cohenc0dcf592011-06-01 15:30:43 -0700217 // Location is expressed in window coordinates
Adam Cohen19072da2011-05-31 14:30:45 -0700218 public void getLocation(int[] loc) {
219 loc[0] = mFolderLocX;
220 loc[1] = mFolderLocY;
221 }
222
223 public float getOuterRingScale() {
224 return mOuterRingScale;
225 }
226
227 public float getInnerRingScale() {
228 return mInnerRingScale;
229 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800230 }
231
Adam Cohen073a46f2011-05-17 16:28:09 -0700232 private boolean willAcceptItem(ItemInfo item) {
233 final int itemType = item.itemType;
234 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
235 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
236 !mFolder.isFull() && item != mInfo);
237 }
238
Adam Cohenc0dcf592011-06-01 15:30:43 -0700239 public boolean acceptDrop(Object dragInfo) {
240 final ItemInfo item = (ItemInfo) dragInfo;
Adam Cohen073a46f2011-05-17 16:28:09 -0700241 return willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800242 }
243
Adam Cohendf035382011-04-11 17:22:04 -0700244 public void addItem(ShortcutInfo item) {
245 mInfo.add(item);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700246 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
Adam Cohendf035382011-04-11 17:22:04 -0700247 }
248
Adam Cohen2801caf2011-05-13 20:57:39 -0700249 void saveState(CellLayout.LayoutParams lp) {
250 mOriginalWidth = lp.width;
251 mOriginalHeight = lp.height;
Adam Cohen2801caf2011-05-13 20:57:39 -0700252 }
253
Adam Cohen073a46f2011-05-17 16:28:09 -0700254 private void determineFolderLocationInWorkspace() {
255 int tvLocation[] = new int[2];
256 int wsLocation[] = new int[2];
Adam Cohenc0dcf592011-06-01 15:30:43 -0700257 getLocationInWindow(tvLocation);
258 mLauncher.getWorkspace().getLocationInWindow(wsLocation);
Adam Cohen19072da2011-05-31 14:30:45 -0700259
260 int x = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2;
261 int y = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2;
262 mFolderRingAnimator.setLocation(x, y);
Adam Cohen073a46f2011-05-17 16:28:09 -0700263 }
264
Adam Cohenc0dcf592011-06-01 15:30:43 -0700265 public void onDragEnter(Object dragInfo) {
266 if (!willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen073a46f2011-05-17 16:28:09 -0700267 determineFolderLocationInWorkspace();
Adam Cohen19072da2011-05-31 14:30:45 -0700268 mLauncher.getWorkspace().showFolderAccept(mFolderRingAnimator);
269 mFolderRingAnimator.animateToAcceptState();
Adam Cohen073a46f2011-05-17 16:28:09 -0700270 }
271
Adam Cohenc0dcf592011-06-01 15:30:43 -0700272 public void onDragOver(Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800273 }
274
Adam Cohenc0dcf592011-06-01 15:30:43 -0700275 public void onDragExit(Object dragInfo) {
276 if (!willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen19072da2011-05-31 14:30:45 -0700277 mFolderRingAnimator.animateToNaturalState();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800278 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700279
Adam Cohenc0dcf592011-06-01 15:30:43 -0700280 public void onDrop(Object dragInfo) {
281 ShortcutInfo item;
282 if (dragInfo instanceof ApplicationInfo) {
283 // Came from all apps -- make a copy
284 item = ((ApplicationInfo) dragInfo).makeShortcut();
285 } else {
286 item = (ShortcutInfo) dragInfo;
287 }
288 item.cellX = -1;
289 item.cellY = -1;
290 addItem(item);
291 }
292
Adam Cohencb3382b2011-05-24 14:07:08 -0700293 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -0700294 return null;
295 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700296
297 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700298 protected void dispatchDraw(Canvas canvas) {
Adam Cohena9cf38f2011-05-02 15:36:58 -0700299 if (mFolder == null) return;
300 if (mFolder.getItemCount() == 0) return;
301
302 canvas.save();
303 TextView v = (TextView) mFolder.getItemAt(0);
304 Drawable d = v.getCompoundDrawables()[1];
305
Adam Cohen073a46f2011-05-17 16:28:09 -0700306 if (mOriginalWidth < 0 || mOriginalHeight < 0) {
307 mOriginalWidth = getMeasuredWidth();
308 mOriginalHeight = getMeasuredHeight();
309 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700310
Adam Cohenbadf71e2011-05-26 19:08:29 -0700311 int unscaledHeight = (int) (d.getIntrinsicHeight() * (1 + PERSPECTIVE_SHIFT_FACTOR));
312 float baselineIconScale = SPACE_PERCENTAGE_FOR_ICONS / (unscaledHeight / (mOriginalHeight * 1.0f));
313
314 int baselineHeight = (int) (d.getIntrinsicHeight() * baselineIconScale);
315 int totalStackHeight = (int) (baselineHeight * (1 + PERSPECTIVE_SHIFT_FACTOR));
316 int baselineWidth = (int) (d.getIntrinsicWidth() * baselineIconScale);
317 float maxPerpectiveShift = baselineHeight * PERSPECTIVE_SHIFT_FACTOR;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700318
Adam Cohen76078c42011-06-09 15:06:52 -0700319 ArrayList<View> items = mFolder.getItemsInReadingOrder(false);
Adam Cohen7c693212011-05-18 15:26:57 -0700320 int firstItemIndex = Math.max(0, items.size() - NUM_ITEMS_IN_PREVIEW);
Adam Cohenbadf71e2011-05-26 19:08:29 -0700321
322 int xShift = (int) (mOriginalWidth - baselineWidth) / 2;
323 int yShift = (int) (mOriginalHeight - totalStackHeight) / 2;
324 canvas.translate(xShift, yShift);
Adam Cohen4dbe6d92011-05-18 17:14:15 -0700325 for (int i = firstItemIndex; i < items.size(); i++) {
Adam Cohenbadf71e2011-05-26 19:08:29 -0700326 int index = i - firstItemIndex;
327 index += Math.max(0, NUM_ITEMS_IN_PREVIEW - items.size());
328
329 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
330 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
331 r = (float) Math.pow(r, 2);
332
333 float transY = r * maxPerpectiveShift;
334 float transX = (1 - scale) * baselineWidth / 2.0f;
335
Adam Cohen7c693212011-05-18 15:26:57 -0700336 v = (TextView) items.get(i);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700337 d = v.getCompoundDrawables()[1];
338
Adam Cohenbadf71e2011-05-26 19:08:29 -0700339 canvas.save();
340 canvas.translate(transX, transY);
341 canvas.scale(baselineIconScale * scale, baselineIconScale * scale);
Adam Cohenf4b08912011-05-17 18:45:47 -0700342
Adam Cohenbadf71e2011-05-26 19:08:29 -0700343 int overlayAlpha = (int) (80 * (1 - r));
Adam Cohena9cf38f2011-05-02 15:36:58 -0700344 if (d != null) {
345 d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
Adam Cohenbadf71e2011-05-26 19:08:29 -0700346 d.setColorFilter(Color.argb(overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700347 d.draw(canvas);
Adam Cohenbadf71e2011-05-26 19:08:29 -0700348 d.clearColorFilter();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700349 }
Adam Cohenbadf71e2011-05-26 19:08:29 -0700350 canvas.restore();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700351 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700352 canvas.restore();
353 }
354
Adam Cohen76078c42011-06-09 15:06:52 -0700355 public void onItemsChanged() {
356 invalidate();
357 requestLayout();
358 }
359
Adam Cohena9cf38f2011-05-02 15:36:58 -0700360 public void onAdd(ShortcutInfo item) {
361 invalidate();
362 requestLayout();
363 }
364
365 public void onRemove(ShortcutInfo item) {
366 invalidate();
367 requestLayout();
368 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800369}