blob: 952d704beee7d0efefb3ba4fff1018d072af44ca [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 Cohen7c693212011-05-18 15:26:57 -070019import java.util.ArrayList;
20
Adam Cohen2801caf2011-05-13 20:57:39 -070021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
Adam Cohen2801caf2011-05-13 20:57:39 -070023import android.animation.ValueAnimator;
24import android.animation.ValueAnimator.AnimatorUpdateListener;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.Context;
26import android.content.res.Resources;
Adam Cohena9cf38f2011-05-02 15:36:58 -070027import android.graphics.Canvas;
Adam Cohenf4b08912011-05-17 18:45:47 -070028import android.graphics.Color;
Adam Cohenbadf71e2011-05-26 19:08:29 -070029import android.graphics.PorterDuff;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.graphics.drawable.Drawable;
31import android.util.AttributeSet;
32import android.view.LayoutInflater;
Adam Cohen7c693212011-05-18 15:26:57 -070033import android.view.View;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.view.ViewGroup;
Adam Cohena9cf38f2011-05-02 15:36:58 -070035import android.widget.FrameLayout;
36import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037
Romain Guyedcce092010-03-04 13:03:17 -080038import com.android.launcher.R;
Adam Cohena9cf38f2011-05-02 15:36:58 -070039import com.android.launcher2.FolderInfo.FolderListener;
Romain Guyedcce092010-03-04 13:03:17 -080040
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041/**
42 * An icon that can appear on in the workspace representing an {@link UserFolder}.
43 */
Adam Cohena9cf38f2011-05-02 15:36:58 -070044public class FolderIcon extends FrameLayout implements DropTarget, FolderListener {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080045 private Launcher mLauncher;
Adam Cohena9cf38f2011-05-02 15:36:58 -070046 Folder mFolder;
47 FolderInfo mInfo;
48
Adam Cohenbadf71e2011-05-26 19:08:29 -070049 // The number of icons to display in the
Adam Cohena9cf38f2011-05-02 15:36:58 -070050 private static final int NUM_ITEMS_IN_PREVIEW = 4;
Adam Cohenf4b08912011-05-17 18:45:47 -070051 private static final int CONSUMPTION_ANIMATION_DURATION = 100;
Adam Cohenbadf71e2011-05-26 19:08:29 -070052
53 // The degree to which the inner ring grows when accepting drop
Adam Cohen073a46f2011-05-17 16:28:09 -070054 private static final float INNER_RING_GROWTH_FACTOR = 0.1f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070055
56 // The degree to which the inner ring is scaled in its natural state
Adam Cohenf4b08912011-05-17 18:45:47 -070057 private static final float INNER_RING_BASELINE_SCALE = 1.0f;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080058
Adam Cohenbadf71e2011-05-26 19:08:29 -070059 // The degree to which the outer ring grows when accepting drop
60 private static final float OUTER_RING_BASELINE_SCALE = 0.7f;
61
62 // The degree to which the outer ring is scaled in its natural state
63 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
64
65 // The amount of vertical spread between items in the stack [0...1]
66 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
67
68 // The degree to which the item in the back of the stack is scaled [0...1]
69 // (0 means it's not scaled at all, 1 means it's scaled to nothing)
70 private static final float PERSPECTIVE_SCALE_FACTOR = 0.3f;
71
72 // The percentage of the FolderIcons view that will be dedicated to the items preview
73 private static final float SPACE_PERCENTAGE_FOR_ICONS = 0.8f;
74
Adam Cohen073a46f2011-05-17 16:28:09 -070075 public static Drawable sFolderOuterRingDrawable = null;
Adam Cohenf4b08912011-05-17 18:45:47 -070076 public static Drawable sFolderInnerRingDrawable = null;
Adam Cohen073a46f2011-05-17 16:28:09 -070077
78 private int mOriginalWidth = -1;
79 private int mOriginalHeight = -1;
Adam Cohen073a46f2011-05-17 16:28:09 -070080
Adam Cohen19072da2011-05-31 14:30:45 -070081 FolderRingAnimator mFolderRingAnimator = null;
Adam Cohen2801caf2011-05-13 20:57:39 -070082
The Android Open Source Project31dd5032009-03-03 19:32:27 -080083 public FolderIcon(Context context, AttributeSet attrs) {
84 super(context, attrs);
85 }
86
87 public FolderIcon(Context context) {
88 super(context);
89 }
90
Michael Jurka0280c3b2010-09-17 15:00:07 -070091 public boolean isDropEnabled() {
Winson Chung7a1d1652011-03-18 15:56:01 -070092 final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
93 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
94 final Workspace workspace = (Workspace) cellLayout.getParent();
95 return !workspace.isSmall();
Michael Jurka0280c3b2010-09-17 15:00:07 -070096 }
97
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Adam Cohendf2cc412011-04-27 16:56:57 -070099 FolderInfo folderInfo, IconCache iconCache) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100
101 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
102
103 final Resources resources = launcher.getResources();
Adam Cohen2801caf2011-05-13 20:57:39 -0700104 Drawable d = iconCache.getFullResIcon(resources, R.drawable.portal_ring_inner_holo);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700105 icon.setBackgroundDrawable(d);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800106 icon.setTag(folderInfo);
107 icon.setOnClickListener(launcher);
108 icon.mInfo = folderInfo;
109 icon.mLauncher = launcher;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700110
111 Folder folder = Folder.fromXml(launcher);
112 folder.setDragController(launcher.getDragController());
113 folder.setLauncher(launcher);
Adam Cohen2801caf2011-05-13 20:57:39 -0700114 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700115 folder.bind(folderInfo);
116 icon.mFolder = folder;
Adam Cohen19072da2011-05-31 14:30:45 -0700117 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700118 folderInfo.addListener(icon);
Adam Cohen19072da2011-05-31 14:30:45 -0700119
120 return icon;
121 }
122
123 public static class FolderRingAnimator {
124 public int mFolderLocX;
125 public int mFolderLocY;
126 public float mOuterRingScale;
127 public float mInnerRingScale;
128 public FolderIcon mFolderIcon = null;
129 private Launcher mLauncher;
130
131 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
132 mLauncher = launcher;
133 mFolderIcon = folderIcon;
134 if (sFolderOuterRingDrawable == null) {
135 sFolderOuterRingDrawable =
136 launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo);
137 }
138 if (sFolderInnerRingDrawable == null) {
139 sFolderInnerRingDrawable =
140 launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo);
141 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700142 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700143
Adam Cohen19072da2011-05-31 14:30:45 -0700144 public void setLocation(int x, int y) {
145 mFolderLocX = x;
146 mFolderLocY = y;
Adam Cohenf4b08912011-05-17 18:45:47 -0700147 }
Adam Cohen19072da2011-05-31 14:30:45 -0700148
149 public void animateToAcceptState() {
150 ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
151 va.setDuration(CONSUMPTION_ANIMATION_DURATION);
152 va.addUpdateListener(new AnimatorUpdateListener() {
153 public void onAnimationUpdate(ValueAnimator animation) {
154 final float percent = (Float) animation.getAnimatedValue();
155 mOuterRingScale = OUTER_RING_BASELINE_SCALE + percent * OUTER_RING_GROWTH_FACTOR;
156 mInnerRingScale = INNER_RING_BASELINE_SCALE + percent * INNER_RING_GROWTH_FACTOR;
157 mLauncher.getWorkspace().invalidate();
158 if (mFolderIcon != null) {
159 mFolderIcon.invalidate();
160 }
161 }
162 });
163 va.addListener(new AnimatorListenerAdapter() {
164 @Override
165 public void onAnimationEnd(Animator animation) {
166 // Instead of setting the background drawable to null, we set the color to
167 // transparent. Setting the background drawable to null results in onDraw
168 // not getting called.
169 if (mFolderIcon != null) {
170 mFolderIcon.setBackgroundColor(Color.TRANSPARENT);
171 mFolderIcon.requestLayout();
172 }
173 }
174 });
175 va.start();
176 }
177
178 public void animateToNaturalState() {
179 ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
180 va.setDuration(CONSUMPTION_ANIMATION_DURATION);
181 va.addUpdateListener(new AnimatorUpdateListener() {
182 public void onAnimationUpdate(ValueAnimator animation) {
183 final float percent = (Float) animation.getAnimatedValue();
184 mOuterRingScale = OUTER_RING_BASELINE_SCALE + OUTER_RING_GROWTH_FACTOR
185 - percent * OUTER_RING_GROWTH_FACTOR;
186 mInnerRingScale = INNER_RING_BASELINE_SCALE + INNER_RING_GROWTH_FACTOR
187 - percent * INNER_RING_GROWTH_FACTOR;
188 mLauncher.getWorkspace().invalidate();
189 if (mFolderIcon != null) {
190 mFolderIcon.invalidate();
191 }
192 }
193 });
194 va.addListener(new AnimatorListenerAdapter() {
195 @Override
196 public void onAnimationEnd(Animator animation) {
197 if (mFolderIcon != null) {
198 mFolderIcon.setBackgroundDrawable(sFolderInnerRingDrawable);
199 }
200 mLauncher.getWorkspace().hideFolderAccept(FolderRingAnimator.this);
201 }
202 });
203 va.start();
204 }
205
206 public void getLocation(int[] loc) {
207 loc[0] = mFolderLocX;
208 loc[1] = mFolderLocY;
209 }
210
211 public float getOuterRingScale() {
212 return mOuterRingScale;
213 }
214
215 public float getInnerRingScale() {
216 return mInnerRingScale;
217 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800218 }
219
Adam Cohen073a46f2011-05-17 16:28:09 -0700220 private boolean willAcceptItem(ItemInfo item) {
221 final int itemType = item.itemType;
222 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
223 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
224 !mFolder.isFull() && item != mInfo);
225 }
226
Adam Cohencb3382b2011-05-24 14:07:08 -0700227 public boolean acceptDrop(DragObject d) {
228 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohen073a46f2011-05-17 16:28:09 -0700229 return willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800230 }
231
Adam Cohendf035382011-04-11 17:22:04 -0700232 public void addItem(ShortcutInfo item) {
233 mInfo.add(item);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700234 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
Adam Cohendf035382011-04-11 17:22:04 -0700235 }
236
Adam Cohencb3382b2011-05-24 14:07:08 -0700237 public void onDrop(DragObject d) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800238 ShortcutInfo item;
Adam Cohencb3382b2011-05-24 14:07:08 -0700239 if (d.dragInfo instanceof ApplicationInfo) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800240 // Came from all apps -- make a copy
Adam Cohencb3382b2011-05-24 14:07:08 -0700241 item = ((ApplicationInfo) d.dragInfo).makeShortcut();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800242 } else {
Adam Cohencb3382b2011-05-24 14:07:08 -0700243 item = (ShortcutInfo) d.dragInfo;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800244 }
Michael Jurka66d72172011-04-12 16:29:25 -0700245 item.cellX = -1;
246 item.cellY = -1;
Adam Cohendf035382011-04-11 17:22:04 -0700247 addItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800248 }
249
Adam Cohen2801caf2011-05-13 20:57:39 -0700250 void saveState(CellLayout.LayoutParams lp) {
251 mOriginalWidth = lp.width;
252 mOriginalHeight = lp.height;
Adam Cohen2801caf2011-05-13 20:57:39 -0700253 }
254
Adam Cohen073a46f2011-05-17 16:28:09 -0700255 private void determineFolderLocationInWorkspace() {
256 int tvLocation[] = new int[2];
257 int wsLocation[] = new int[2];
258 getLocationOnScreen(tvLocation);
259 mLauncher.getWorkspace().getLocationOnScreen(wsLocation);
Adam Cohen19072da2011-05-31 14:30:45 -0700260
261 int x = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2;
262 int y = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2;
263 mFolderRingAnimator.setLocation(x, y);
Adam Cohen073a46f2011-05-17 16:28:09 -0700264 }
265
Adam Cohencb3382b2011-05-24 14:07:08 -0700266 public void onDragEnter(DragObject d) {
267 if (!willAcceptItem((ItemInfo) d.dragInfo)) return;
Adam Cohen073a46f2011-05-17 16:28:09 -0700268 determineFolderLocationInWorkspace();
Adam Cohen19072da2011-05-31 14:30:45 -0700269 mLauncher.getWorkspace().showFolderAccept(mFolderRingAnimator);
270 mFolderRingAnimator.animateToAcceptState();
Adam Cohen073a46f2011-05-17 16:28:09 -0700271 }
272
Adam Cohencb3382b2011-05-24 14:07:08 -0700273 public void onDragOver(DragObject d) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800274 }
275
Adam Cohencb3382b2011-05-24 14:07:08 -0700276 public void onDragExit(DragObject d) {
277 if (!willAcceptItem((ItemInfo) d.dragInfo)) return;
Adam Cohen19072da2011-05-31 14:30:45 -0700278 mFolderRingAnimator.animateToNaturalState();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800279 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700280
Adam Cohencb3382b2011-05-24 14:07:08 -0700281 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -0700282 return null;
283 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700284
285 @Override
286 protected void onDraw(Canvas canvas) {
287 if (mFolder == null) return;
288 if (mFolder.getItemCount() == 0) return;
289
290 canvas.save();
291 TextView v = (TextView) mFolder.getItemAt(0);
292 Drawable d = v.getCompoundDrawables()[1];
293
Adam Cohen073a46f2011-05-17 16:28:09 -0700294 if (mOriginalWidth < 0 || mOriginalHeight < 0) {
295 mOriginalWidth = getMeasuredWidth();
296 mOriginalHeight = getMeasuredHeight();
297 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700298
Adam Cohenbadf71e2011-05-26 19:08:29 -0700299 int unscaledHeight = (int) (d.getIntrinsicHeight() * (1 + PERSPECTIVE_SHIFT_FACTOR));
300 float baselineIconScale = SPACE_PERCENTAGE_FOR_ICONS / (unscaledHeight / (mOriginalHeight * 1.0f));
301
302 int baselineHeight = (int) (d.getIntrinsicHeight() * baselineIconScale);
303 int totalStackHeight = (int) (baselineHeight * (1 + PERSPECTIVE_SHIFT_FACTOR));
304 int baselineWidth = (int) (d.getIntrinsicWidth() * baselineIconScale);
305 float maxPerpectiveShift = baselineHeight * PERSPECTIVE_SHIFT_FACTOR;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700306
Adam Cohen7c693212011-05-18 15:26:57 -0700307 ArrayList<View> items = mFolder.getItemsInReadingOrder();
308 int firstItemIndex = Math.max(0, items.size() - NUM_ITEMS_IN_PREVIEW);
Adam Cohenbadf71e2011-05-26 19:08:29 -0700309
310 int xShift = (int) (mOriginalWidth - baselineWidth) / 2;
311 int yShift = (int) (mOriginalHeight - totalStackHeight) / 2;
312 canvas.translate(xShift, yShift);
Adam Cohen4dbe6d92011-05-18 17:14:15 -0700313 for (int i = firstItemIndex; i < items.size(); i++) {
Adam Cohenbadf71e2011-05-26 19:08:29 -0700314 int index = i - firstItemIndex;
315 index += Math.max(0, NUM_ITEMS_IN_PREVIEW - items.size());
316
317 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
318 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
319 r = (float) Math.pow(r, 2);
320
321 float transY = r * maxPerpectiveShift;
322 float transX = (1 - scale) * baselineWidth / 2.0f;
323
Adam Cohen7c693212011-05-18 15:26:57 -0700324 v = (TextView) items.get(i);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700325 d = v.getCompoundDrawables()[1];
326
Adam Cohenbadf71e2011-05-26 19:08:29 -0700327 canvas.save();
328 canvas.translate(transX, transY);
329 canvas.scale(baselineIconScale * scale, baselineIconScale * scale);
Adam Cohenf4b08912011-05-17 18:45:47 -0700330
Adam Cohenbadf71e2011-05-26 19:08:29 -0700331 int overlayAlpha = (int) (80 * (1 - r));
Adam Cohena9cf38f2011-05-02 15:36:58 -0700332 if (d != null) {
333 d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
Adam Cohenbadf71e2011-05-26 19:08:29 -0700334 d.setColorFilter(Color.argb(overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700335 d.draw(canvas);
Adam Cohenbadf71e2011-05-26 19:08:29 -0700336 d.clearColorFilter();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700337 }
Adam Cohenbadf71e2011-05-26 19:08:29 -0700338 canvas.restore();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700339 }
340
341 canvas.restore();
342 }
343
344 public void onAdd(ShortcutInfo item) {
345 invalidate();
346 requestLayout();
347 }
348
349 public void onRemove(ShortcutInfo item) {
350 invalidate();
351 requestLayout();
352 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800353}