blob: e97676d483f8557fde96b7cdb196c71774091ce8 [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
81 private int mFolderLocX;
82 private int mFolderLocY;
83 private float mOuterRingScale;
Adam Cohenf4b08912011-05-17 18:45:47 -070084 private float mInnerRingScale;
Adam Cohen2801caf2011-05-13 20:57:39 -070085
The Android Open Source Project31dd5032009-03-03 19:32:27 -080086 public FolderIcon(Context context, AttributeSet attrs) {
87 super(context, attrs);
88 }
89
90 public FolderIcon(Context context) {
91 super(context);
92 }
93
Michael Jurka0280c3b2010-09-17 15:00:07 -070094 public boolean isDropEnabled() {
Winson Chung7a1d1652011-03-18 15:56:01 -070095 final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
96 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
97 final Workspace workspace = (Workspace) cellLayout.getParent();
98 return !workspace.isSmall();
Michael Jurka0280c3b2010-09-17 15:00:07 -070099 }
100
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800101 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Adam Cohendf2cc412011-04-27 16:56:57 -0700102 FolderInfo folderInfo, IconCache iconCache) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800103
104 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
105
106 final Resources resources = launcher.getResources();
Adam Cohen2801caf2011-05-13 20:57:39 -0700107 Drawable d = iconCache.getFullResIcon(resources, R.drawable.portal_ring_inner_holo);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700108 icon.setBackgroundDrawable(d);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800109 icon.setTag(folderInfo);
110 icon.setOnClickListener(launcher);
111 icon.mInfo = folderInfo;
112 icon.mLauncher = launcher;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700113
114 Folder folder = Folder.fromXml(launcher);
115 folder.setDragController(launcher.getDragController());
116 folder.setLauncher(launcher);
Adam Cohen2801caf2011-05-13 20:57:39 -0700117 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700118 folder.bind(folderInfo);
119 icon.mFolder = folder;
120
121 folderInfo.addListener(icon);
Adam Cohen073a46f2011-05-17 16:28:09 -0700122 if (sFolderOuterRingDrawable == null) {
123 sFolderOuterRingDrawable =
124 launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo);
125 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700126
Adam Cohenf4b08912011-05-17 18:45:47 -0700127 if (sFolderInnerRingDrawable == null) {
128 sFolderInnerRingDrawable =
129 launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo);
130 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800131 return icon;
132 }
133
Adam Cohen073a46f2011-05-17 16:28:09 -0700134 private boolean willAcceptItem(ItemInfo item) {
135 final int itemType = item.itemType;
136 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
137 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
138 !mFolder.isFull() && item != mInfo);
139 }
140
Adam Cohencb3382b2011-05-24 14:07:08 -0700141 public boolean acceptDrop(DragObject d) {
142 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohen073a46f2011-05-17 16:28:09 -0700143 return willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800144 }
145
Adam Cohendf035382011-04-11 17:22:04 -0700146 public void addItem(ShortcutInfo item) {
147 mInfo.add(item);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700148 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
Adam Cohendf035382011-04-11 17:22:04 -0700149 }
150
Adam Cohencb3382b2011-05-24 14:07:08 -0700151 public void onDrop(DragObject d) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800152 ShortcutInfo item;
Adam Cohencb3382b2011-05-24 14:07:08 -0700153 if (d.dragInfo instanceof ApplicationInfo) {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800154 // Came from all apps -- make a copy
Adam Cohencb3382b2011-05-24 14:07:08 -0700155 item = ((ApplicationInfo) d.dragInfo).makeShortcut();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800156 } else {
Adam Cohencb3382b2011-05-24 14:07:08 -0700157 item = (ShortcutInfo) d.dragInfo;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800158 }
Michael Jurka66d72172011-04-12 16:29:25 -0700159 item.cellX = -1;
160 item.cellY = -1;
Adam Cohendf035382011-04-11 17:22:04 -0700161 addItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800162 }
163
Adam Cohen2801caf2011-05-13 20:57:39 -0700164 void saveState(CellLayout.LayoutParams lp) {
165 mOriginalWidth = lp.width;
166 mOriginalHeight = lp.height;
Adam Cohen2801caf2011-05-13 20:57:39 -0700167 }
168
Adam Cohen073a46f2011-05-17 16:28:09 -0700169 private void animateToAcceptState() {
Adam Cohenf4b08912011-05-17 18:45:47 -0700170 ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
171 va.setDuration(CONSUMPTION_ANIMATION_DURATION);
172 va.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen073a46f2011-05-17 16:28:09 -0700173 public void onAnimationUpdate(ValueAnimator animation) {
174 final float percent = (Float) animation.getAnimatedValue();
175 mOuterRingScale = OUTER_RING_BASELINE_SCALE + percent * OUTER_RING_GROWTH_FACTOR;
Adam Cohenf4b08912011-05-17 18:45:47 -0700176 mInnerRingScale = INNER_RING_BASELINE_SCALE + percent * INNER_RING_GROWTH_FACTOR;
Adam Cohen073a46f2011-05-17 16:28:09 -0700177 mLauncher.getWorkspace().invalidate();
Adam Cohen073a46f2011-05-17 16:28:09 -0700178 invalidate();
179 }
180 });
Adam Cohenf4b08912011-05-17 18:45:47 -0700181 va.addListener(new AnimatorListenerAdapter() {
Adam Cohen073a46f2011-05-17 16:28:09 -0700182 @Override
183 public void onAnimationEnd(Animator animation) {
Adam Cohenf4b08912011-05-17 18:45:47 -0700184 // Instead of setting the background drawable to null, we set the color to
185 // transparent. Setting the background drawable to null results in onDraw
186 // not getting called.
187 setBackgroundColor(Color.TRANSPARENT);
188 requestLayout();
Adam Cohen073a46f2011-05-17 16:28:09 -0700189 }
190 });
Adam Cohenf4b08912011-05-17 18:45:47 -0700191 va.start();
192 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700193
Adam Cohenf4b08912011-05-17 18:45:47 -0700194 private void animateToNaturalState() {
195 ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
196 va.setDuration(CONSUMPTION_ANIMATION_DURATION);
197 va.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen073a46f2011-05-17 16:28:09 -0700198 public void onAnimationUpdate(ValueAnimator animation) {
199 final float percent = (Float) animation.getAnimatedValue();
200 mOuterRingScale = OUTER_RING_BASELINE_SCALE + OUTER_RING_GROWTH_FACTOR
201 - percent * OUTER_RING_GROWTH_FACTOR;
Adam Cohenf4b08912011-05-17 18:45:47 -0700202 mInnerRingScale = INNER_RING_BASELINE_SCALE + INNER_RING_GROWTH_FACTOR
203 - percent * INNER_RING_GROWTH_FACTOR;
Adam Cohen073a46f2011-05-17 16:28:09 -0700204 mLauncher.getWorkspace().invalidate();
Adam Cohenf4b08912011-05-17 18:45:47 -0700205 invalidate();
Adam Cohen073a46f2011-05-17 16:28:09 -0700206 }
207 });
Adam Cohenf4b08912011-05-17 18:45:47 -0700208 va.addListener(new AnimatorListenerAdapter() {
209 @Override
210 public void onAnimationEnd(Animator animation) {
211 setBackgroundDrawable(sFolderInnerRingDrawable);
212 mLauncher.getWorkspace().hideFolderAccept(FolderIcon.this);
213 }
214 });
215 va.start();
Adam Cohen073a46f2011-05-17 16:28:09 -0700216 }
217
218 private void determineFolderLocationInWorkspace() {
219 int tvLocation[] = new int[2];
220 int wsLocation[] = new int[2];
221 getLocationOnScreen(tvLocation);
222 mLauncher.getWorkspace().getLocationOnScreen(wsLocation);
223 mFolderLocX = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2;
224 mFolderLocY = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2;
225 }
226
Adam Cohencb3382b2011-05-24 14:07:08 -0700227 public void onDragEnter(DragObject d) {
228 if (!willAcceptItem((ItemInfo) d.dragInfo)) return;
Adam Cohen073a46f2011-05-17 16:28:09 -0700229 determineFolderLocationInWorkspace();
230 mLauncher.getWorkspace().showFolderAccept(this);
231 animateToAcceptState();
232 }
233
Adam Cohencb3382b2011-05-24 14:07:08 -0700234 public void onDragOver(DragObject d) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800235 }
236
Adam Cohencb3382b2011-05-24 14:07:08 -0700237 public void onDragExit(DragObject d) {
238 if (!willAcceptItem((ItemInfo) d.dragInfo)) return;
Adam Cohen073a46f2011-05-17 16:28:09 -0700239 animateToNaturalState();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800240 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700241
Adam Cohencb3382b2011-05-24 14:07:08 -0700242 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -0700243 return null;
244 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700245
Adam Cohen073a46f2011-05-17 16:28:09 -0700246 public void getFolderLocation(int[] loc) {
247 loc[0] = mFolderLocX;
248 loc[1] = mFolderLocY;
249 }
250
251 public float getOuterRingScale() {
252 return mOuterRingScale;
253 }
254
Adam Cohenf4b08912011-05-17 18:45:47 -0700255 public float getInnerRingScale() {
256 return mInnerRingScale;
257 }
258
Adam Cohena9cf38f2011-05-02 15:36:58 -0700259 @Override
260 protected void onDraw(Canvas canvas) {
261 if (mFolder == null) return;
262 if (mFolder.getItemCount() == 0) return;
263
264 canvas.save();
265 TextView v = (TextView) mFolder.getItemAt(0);
266 Drawable d = v.getCompoundDrawables()[1];
267
Adam Cohen073a46f2011-05-17 16:28:09 -0700268 if (mOriginalWidth < 0 || mOriginalHeight < 0) {
269 mOriginalWidth = getMeasuredWidth();
270 mOriginalHeight = getMeasuredHeight();
271 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700272
Adam Cohenbadf71e2011-05-26 19:08:29 -0700273 int unscaledHeight = (int) (d.getIntrinsicHeight() * (1 + PERSPECTIVE_SHIFT_FACTOR));
274 float baselineIconScale = SPACE_PERCENTAGE_FOR_ICONS / (unscaledHeight / (mOriginalHeight * 1.0f));
275
276 int baselineHeight = (int) (d.getIntrinsicHeight() * baselineIconScale);
277 int totalStackHeight = (int) (baselineHeight * (1 + PERSPECTIVE_SHIFT_FACTOR));
278 int baselineWidth = (int) (d.getIntrinsicWidth() * baselineIconScale);
279 float maxPerpectiveShift = baselineHeight * PERSPECTIVE_SHIFT_FACTOR;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700280
Adam Cohen7c693212011-05-18 15:26:57 -0700281 ArrayList<View> items = mFolder.getItemsInReadingOrder();
282 int firstItemIndex = Math.max(0, items.size() - NUM_ITEMS_IN_PREVIEW);
Adam Cohenbadf71e2011-05-26 19:08:29 -0700283
284 int xShift = (int) (mOriginalWidth - baselineWidth) / 2;
285 int yShift = (int) (mOriginalHeight - totalStackHeight) / 2;
286 canvas.translate(xShift, yShift);
Adam Cohen4dbe6d92011-05-18 17:14:15 -0700287 for (int i = firstItemIndex; i < items.size(); i++) {
Adam Cohenbadf71e2011-05-26 19:08:29 -0700288 int index = i - firstItemIndex;
289 index += Math.max(0, NUM_ITEMS_IN_PREVIEW - items.size());
290
291 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
292 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
293 r = (float) Math.pow(r, 2);
294
295 float transY = r * maxPerpectiveShift;
296 float transX = (1 - scale) * baselineWidth / 2.0f;
297
Adam Cohen7c693212011-05-18 15:26:57 -0700298 v = (TextView) items.get(i);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700299 d = v.getCompoundDrawables()[1];
300
Adam Cohenbadf71e2011-05-26 19:08:29 -0700301 canvas.save();
302 canvas.translate(transX, transY);
303 canvas.scale(baselineIconScale * scale, baselineIconScale * scale);
Adam Cohenf4b08912011-05-17 18:45:47 -0700304
Adam Cohenbadf71e2011-05-26 19:08:29 -0700305 int overlayAlpha = (int) (80 * (1 - r));
Adam Cohena9cf38f2011-05-02 15:36:58 -0700306 if (d != null) {
307 d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
Adam Cohenbadf71e2011-05-26 19:08:29 -0700308 d.setColorFilter(Color.argb(overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700309 d.draw(canvas);
Adam Cohenbadf71e2011-05-26 19:08:29 -0700310 d.clearColorFilter();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700311 }
Adam Cohenbadf71e2011-05-26 19:08:29 -0700312 canvas.restore();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700313 }
314
315 canvas.restore();
316 }
317
318 public void onAdd(ShortcutInfo item) {
319 invalidate();
320 requestLayout();
321 }
322
323 public void onRemove(ShortcutInfo item) {
324 invalidate();
325 requestLayout();
326 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800327}