blob: 8cf3fa7d1031bb2a792d7c8f4832e3b9c22f8fe4 [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 Cohenac8c8762011-07-13 11:15:27 -070027import android.graphics.Paint;
Adam Cohenbadf71e2011-05-26 19:08:29 -070028import android.graphics.PorterDuff;
Adam Cohen3e8f8112011-07-02 18:03:00 -070029import android.graphics.Rect;
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 Cohen3e8f8112011-07-02 18:03:00 -070035import android.view.animation.AccelerateInterpolator;
36import android.view.animation.DecelerateInterpolator;
Adam Cohen76fc0852011-06-17 13:26:23 -070037import android.widget.ImageView;
38import android.widget.LinearLayout;
Adam Cohena9cf38f2011-05-02 15:36:58 -070039import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040
Romain Guyedcce092010-03-04 13:03:17 -080041import com.android.launcher.R;
Adam Cohenc0dcf592011-06-01 15:30:43 -070042import com.android.launcher2.DropTarget.DragObject;
Adam Cohena9cf38f2011-05-02 15:36:58 -070043import com.android.launcher2.FolderInfo.FolderListener;
Romain Guyedcce092010-03-04 13:03:17 -080044
Adam Cohenc0dcf592011-06-01 15:30:43 -070045import java.util.ArrayList;
46
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047/**
48 * An icon that can appear on in the workspace representing an {@link UserFolder}.
49 */
Adam Cohen76fc0852011-06-17 13:26:23 -070050public class FolderIcon extends LinearLayout implements FolderListener {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051 private Launcher mLauncher;
Adam Cohena9cf38f2011-05-02 15:36:58 -070052 Folder mFolder;
53 FolderInfo mInfo;
54
Adam Cohenbadf71e2011-05-26 19:08:29 -070055 // The number of icons to display in the
Adam Cohen76fc0852011-06-17 13:26:23 -070056 private static final int NUM_ITEMS_IN_PREVIEW = 3;
Adam Cohenf4b08912011-05-17 18:45:47 -070057 private static final int CONSUMPTION_ANIMATION_DURATION = 100;
Adam Cohend0445262011-07-04 23:53:22 -070058 private static final int DROP_IN_ANIMATION_DURATION = 400;
Adam Cohen8dfcba42011-07-07 16:38:18 -070059 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
Adam Cohenbadf71e2011-05-26 19:08:29 -070060
61 // The degree to which the inner ring grows when accepting drop
Adam Cohen69ce2e52011-07-03 19:25:21 -070062 private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070063
Adam Cohenbadf71e2011-05-26 19:08:29 -070064 // The degree to which the outer ring is scaled in its natural state
Adam Cohen69ce2e52011-07-03 19:25:21 -070065 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070066
67 // The amount of vertical spread between items in the stack [0...1]
Adam Cohen76fc0852011-06-17 13:26:23 -070068 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070069
70 // The degree to which the item in the back of the stack is scaled [0...1]
71 // (0 means it's not scaled at all, 1 means it's scaled to nothing)
Adam Cohen76fc0852011-06-17 13:26:23 -070072 private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070073
Adam Cohenc51934b2011-07-26 21:07:43 -070074 public static Drawable sSharedFolderLeaveBehind = null;
75
Adam Cohen76fc0852011-06-17 13:26:23 -070076 private ImageView mPreviewBackground;
77 private BubbleTextView mFolderName;
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
Adam Cohend0445262011-07-04 23:53:22 -070081 // These variables are all associated with the drawing of the preview; they are stored
82 // as member variables for shared usage and to avoid computation on each frame
83 private int mIntrinsicIconSize;
84 private float mBaselineIconScale;
85 private int mBaselineIconSize;
86 private int mAvailableSpaceInPreview;
87 private int mTotalWidth = -1;
88 private int mPreviewOffsetX;
89 private int mPreviewOffsetY;
90 private float mMaxPerspectiveShift;
91 boolean mAnimating = false;
92 private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
93 private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
94
The Android Open Source Project31dd5032009-03-03 19:32:27 -080095 public FolderIcon(Context context, AttributeSet attrs) {
96 super(context, attrs);
97 }
98
99 public FolderIcon(Context context) {
100 super(context);
101 }
102
Michael Jurka0280c3b2010-09-17 15:00:07 -0700103 public boolean isDropEnabled() {
Winson Chung7a1d1652011-03-18 15:56:01 -0700104 final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
105 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
106 final Workspace workspace = (Workspace) cellLayout.getParent();
107 return !workspace.isSmall();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700108 }
109
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800110 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Adam Cohendf2cc412011-04-27 16:56:57 -0700111 FolderInfo folderInfo, IconCache iconCache) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800112
Adam Cohend0445262011-07-04 23:53:22 -0700113 if (INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION) {
114 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
115 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
116 "is dependent on this");
117 }
118
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800119 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
120
Adam Cohend2eca6b2011-07-26 22:49:13 -0700121 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
Adam Cohen76fc0852011-06-17 13:26:23 -0700122 icon.mFolderName.setText(folderInfo.title);
123 icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
Winson Chung6a0f57d2011-06-29 20:10:49 -0700124 icon.mPreviewBackground.setContentDescription(folderInfo.title);
Adam Cohen76fc0852011-06-17 13:26:23 -0700125
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126 icon.setTag(folderInfo);
127 icon.setOnClickListener(launcher);
128 icon.mInfo = folderInfo;
129 icon.mLauncher = launcher;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700130
131 Folder folder = Folder.fromXml(launcher);
132 folder.setDragController(launcher.getDragController());
Adam Cohen2801caf2011-05-13 20:57:39 -0700133 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700134 folder.bind(folderInfo);
135 icon.mFolder = folder;
Adam Cohen19072da2011-05-31 14:30:45 -0700136 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
Adam Cohen69ce2e52011-07-03 19:25:21 -0700137
Adam Cohena9cf38f2011-05-02 15:36:58 -0700138 folderInfo.addListener(icon);
Adam Cohen19072da2011-05-31 14:30:45 -0700139
Adam Cohenc51934b2011-07-26 21:07:43 -0700140 Resources res = launcher.getResources();
141 if (sSharedFolderLeaveBehind == null) {
142 sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest);
143 }
144
Adam Cohen19072da2011-05-31 14:30:45 -0700145 return icon;
146 }
147
148 public static class FolderRingAnimator {
Adam Cohen69ce2e52011-07-03 19:25:21 -0700149 public int mCellX;
150 public int mCellY;
151 private CellLayout mCellLayout;
Adam Cohen76fc0852011-06-17 13:26:23 -0700152 public float mOuterRingSize;
153 public float mInnerRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700154 public FolderIcon mFolderIcon = null;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700155 public Drawable mOuterRingDrawable = null;
156 public Drawable mInnerRingDrawable = null;
157 public static Drawable sSharedOuterRingDrawable = null;
158 public static Drawable sSharedInnerRingDrawable = null;
Adam Cohen76fc0852011-06-17 13:26:23 -0700159 public static int sPreviewSize = -1;
160 public static int sPreviewPadding = -1;
161
Adam Cohenc0dcf592011-06-01 15:30:43 -0700162 private ValueAnimator mAcceptAnimator;
163 private ValueAnimator mNeutralAnimator;
Adam Cohen19072da2011-05-31 14:30:45 -0700164
165 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
Adam Cohen19072da2011-05-31 14:30:45 -0700166 mFolderIcon = folderIcon;
Adam Cohen76fc0852011-06-17 13:26:23 -0700167 Resources res = launcher.getResources();
168 mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
169 mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
170
171 if (sPreviewSize < 0 || sPreviewPadding < 0) {
172 sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
173 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
174 }
Adam Cohenc0dcf592011-06-01 15:30:43 -0700175 if (sSharedOuterRingDrawable == null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700176 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
Adam Cohen19072da2011-05-31 14:30:45 -0700177 }
Adam Cohenc0dcf592011-06-01 15:30:43 -0700178 if (sSharedInnerRingDrawable == null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700179 sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
Adam Cohen19072da2011-05-31 14:30:45 -0700180 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700181 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700182
Adam Cohen19072da2011-05-31 14:30:45 -0700183 public void animateToAcceptState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700184 if (mNeutralAnimator != null) {
185 mNeutralAnimator.cancel();
186 }
187 mAcceptAnimator = ValueAnimator.ofFloat(0f, 1f);
188 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
189 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700190 public void onAnimationUpdate(ValueAnimator animation) {
191 final float percent = (Float) animation.getAnimatedValue();
Adam Cohen76fc0852011-06-17 13:26:23 -0700192 mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
193 mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700194 if (mCellLayout != null) {
195 mCellLayout.invalidate();
Adam Cohen19072da2011-05-31 14:30:45 -0700196 }
197 }
198 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700199 mAcceptAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700200 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700201 public void onAnimationStart(Animator animation) {
Adam Cohen19072da2011-05-31 14:30:45 -0700202 if (mFolderIcon != null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700203 mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE);
Adam Cohen19072da2011-05-31 14:30:45 -0700204 }
205 }
206 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700207 mAcceptAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700208 }
209
210 public void animateToNaturalState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700211 if (mAcceptAnimator != null) {
212 mAcceptAnimator.cancel();
213 }
214 mNeutralAnimator = ValueAnimator.ofFloat(0f, 1f);
215 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
216 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700217 public void onAnimationUpdate(ValueAnimator animation) {
218 final float percent = (Float) animation.getAnimatedValue();
Adam Cohen76fc0852011-06-17 13:26:23 -0700219 mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
220 mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700221 if (mCellLayout != null) {
222 mCellLayout.invalidate();
Adam Cohen19072da2011-05-31 14:30:45 -0700223 }
224 }
225 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700226 mNeutralAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700227 @Override
228 public void onAnimationEnd(Animator animation) {
Adam Cohen69ce2e52011-07-03 19:25:21 -0700229 if (mCellLayout != null) {
230 mCellLayout.hideFolderAccept(FolderRingAnimator.this);
231 }
Adam Cohen19072da2011-05-31 14:30:45 -0700232 if (mFolderIcon != null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700233 mFolderIcon.mPreviewBackground.setVisibility(VISIBLE);
Adam Cohen19072da2011-05-31 14:30:45 -0700234 }
Adam Cohen19072da2011-05-31 14:30:45 -0700235 }
236 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700237 mNeutralAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700238 }
239
Adam Cohenc0dcf592011-06-01 15:30:43 -0700240 // Location is expressed in window coordinates
Adam Cohen69ce2e52011-07-03 19:25:21 -0700241 public void getCell(int[] loc) {
242 loc[0] = mCellX;
243 loc[1] = mCellY;
244 }
245
246 // Location is expressed in window coordinates
247 public void setCell(int x, int y) {
248 mCellX = x;
249 mCellY = y;
250 }
251
252 public void setCellLayout(CellLayout layout) {
253 mCellLayout = layout;
Adam Cohen19072da2011-05-31 14:30:45 -0700254 }
255
Adam Cohen76fc0852011-06-17 13:26:23 -0700256 public float getOuterRingSize() {
257 return mOuterRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700258 }
259
Adam Cohen76fc0852011-06-17 13:26:23 -0700260 public float getInnerRingSize() {
261 return mInnerRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700262 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800263 }
264
Adam Cohen073a46f2011-05-17 16:28:09 -0700265 private boolean willAcceptItem(ItemInfo item) {
266 final int itemType = item.itemType;
267 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
268 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
Adam Cohenc51934b2011-07-26 21:07:43 -0700269 !mFolder.isFull() && item != mInfo && !mInfo.opened);
Adam Cohen073a46f2011-05-17 16:28:09 -0700270 }
271
Adam Cohenc0dcf592011-06-01 15:30:43 -0700272 public boolean acceptDrop(Object dragInfo) {
273 final ItemInfo item = (ItemInfo) dragInfo;
Adam Cohen073a46f2011-05-17 16:28:09 -0700274 return willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800275 }
276
Adam Cohendf035382011-04-11 17:22:04 -0700277 public void addItem(ShortcutInfo item) {
278 mInfo.add(item);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700279 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
Adam Cohendf035382011-04-11 17:22:04 -0700280 }
281
Adam Cohenc0dcf592011-06-01 15:30:43 -0700282 public void onDragEnter(Object dragInfo) {
283 if (!willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700284 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
285 CellLayout layout = (CellLayout) getParent().getParent();
286 mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
287 mFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -0700288 mFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -0700289 layout.showFolderAccept(mFolderRingAnimator);
Adam Cohen073a46f2011-05-17 16:28:09 -0700290 }
291
Adam Cohenc0dcf592011-06-01 15:30:43 -0700292 public void onDragOver(Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800293 }
294
Adam Cohend0445262011-07-04 23:53:22 -0700295 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
Winson Chung557d6ed2011-07-08 15:34:52 -0700296 final ShortcutInfo srcInfo, final View srcView, Rect dstRect,
Adam Cohenac8c8762011-07-13 11:15:27 -0700297 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700298
299 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
300 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
Adam Cohenac8c8762011-07-13 11:15:27 -0700301
Adam Cohend0445262011-07-04 23:53:22 -0700302 // This will animate the dragView (srcView) into the new folder
Adam Cohenac8c8762011-07-13 11:15:27 -0700303 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
Adam Cohend0445262011-07-04 23:53:22 -0700304
305 // This will animate the first item from it's position as an icon into its
306 // position as the first item in the preview
Adam Cohen8dfcba42011-07-07 16:38:18 -0700307 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);
Adam Cohend0445262011-07-04 23:53:22 -0700308
309 postDelayed(new Runnable() {
310 public void run() {
311 addItem(destInfo);
312 }
313 }, INITIAL_ITEM_ANIMATION_DURATION);
314 }
315
Adam Cohenc0dcf592011-06-01 15:30:43 -0700316 public void onDragExit(Object dragInfo) {
317 if (!willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen19072da2011-05-31 14:30:45 -0700318 mFolderRingAnimator.animateToNaturalState();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800319 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700320
Adam Cohenac8c8762011-07-13 11:15:27 -0700321 private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect,
322 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700323 item.cellX = -1;
324 item.cellY = -1;
325 DragLayer dragLayer = mLauncher.getDragLayer();
326 Rect from = new Rect();
327 dragLayer.getViewRectRelativeToSelf(animateView, from);
328 Rect to = finalRect;
329 if (to == null) {
330 to = new Rect();
Adam Cohen4b285c52011-07-21 14:24:06 -0700331 Workspace workspace = mLauncher.getWorkspace();
332 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
Adam Cohenac8c8762011-07-13 11:15:27 -0700333 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
Adam Cohen4b285c52011-07-21 14:24:06 -0700334 workspace.resetTransitionTransform((CellLayout) getParent().getParent());
Adam Cohend0445262011-07-04 23:53:22 -0700335 }
336
Adam Cohenac8c8762011-07-13 11:15:27 -0700337 int[] center = new int[2];
338 float scale = getLocalCenterForIndex(index, center);
339 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
340 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
341
342 to.offset(center[0] - animateView.getMeasuredWidth() / 2,
343 center[1] - animateView.getMeasuredHeight() / 2);
Adam Cohend0445262011-07-04 23:53:22 -0700344
345 float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
346
Adam Cohenac8c8762011-07-13 11:15:27 -0700347 dragLayer.animateView(animateView, from, to, finalAlpha, scale * scaleRelativeToDragLayer,
348 DROP_IN_ANIMATION_DURATION, new DecelerateInterpolator(2),
349 new AccelerateInterpolator(2), postAnimationRunnable, false);
Adam Cohend0445262011-07-04 23:53:22 -0700350 postDelayed(new Runnable() {
351 public void run() {
352 addItem(item);
353 }
354 }, DROP_IN_ANIMATION_DURATION);
355 }
356
Adam Cohen3e8f8112011-07-02 18:03:00 -0700357 public void onDrop(DragObject d) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700358 ShortcutInfo item;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700359 if (d.dragInfo instanceof ApplicationInfo) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700360 // Came from all apps -- make a copy
Adam Cohen3e8f8112011-07-02 18:03:00 -0700361 item = ((ApplicationInfo) d.dragInfo).makeShortcut();
Adam Cohenc0dcf592011-06-01 15:30:43 -0700362 } else {
Adam Cohen3e8f8112011-07-02 18:03:00 -0700363 item = (ShortcutInfo) d.dragInfo;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700364 }
Adam Cohenac8c8762011-07-13 11:15:27 -0700365 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
Adam Cohenc0dcf592011-06-01 15:30:43 -0700366 }
367
Adam Cohencb3382b2011-05-24 14:07:08 -0700368 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -0700369 return null;
370 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700371
Adam Cohend0445262011-07-04 23:53:22 -0700372 private void computePreviewDrawingParams(int drawableSize, int totalSize) {
373 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
374 mIntrinsicIconSize = drawableSize;
375 mTotalWidth = totalSize;
376
377 final int previewSize = FolderRingAnimator.sPreviewSize;
378 final int previewPadding = FolderRingAnimator.sPreviewPadding;
379
380 mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
381 // cos(45) = 0.707 + ~= 0.1) = 0.8f
382 int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
383
384 int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
385 mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
386
387 mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
388 mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
389
390 mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
391 mPreviewOffsetY = previewPadding;
392 }
393 }
394
395 private void computePreviewDrawingParams(Drawable d) {
396 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
397 }
398
399 class PreviewItemDrawingParams {
400 PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) {
401 this.transX = transX;
402 this.transY = transY;
403 this.scale = scale;
404 this.overlayAlpha = overlayAlpha;
405 }
406 float transX;
407 float transY;
408 float scale;
409 int overlayAlpha;
410 Drawable drawable;
411 }
412
Adam Cohenac8c8762011-07-13 11:15:27 -0700413 private float getLocalCenterForIndex(int index, int[] center) {
Adam Cohend0445262011-07-04 23:53:22 -0700414 mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams);
415
416 mParams.transX += mPreviewOffsetX;
417 mParams.transY += mPreviewOffsetY;
Adam Cohenac8c8762011-07-13 11:15:27 -0700418 float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
419 float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
Adam Cohend0445262011-07-04 23:53:22 -0700420
Adam Cohenac8c8762011-07-13 11:15:27 -0700421 center[0] = (int) Math.round(offsetX);
422 center[1] = (int) Math.round(offsetY);
Adam Cohend0445262011-07-04 23:53:22 -0700423 return mParams.scale;
424 }
425
426 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
427 PreviewItemDrawingParams params) {
428 index = NUM_ITEMS_IN_PREVIEW - index - 1;
429 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
430 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
431
432 float offset = (1 - r) * mMaxPerspectiveShift;
433 float scaledSize = scale * mBaselineIconSize;
434 float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
435
436 // We want to imagine our coordinates from the bottom left, growing up and to the
437 // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
438 float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
439 float transX = offset + scaleOffsetCorrection;
440 float totalScale = mBaselineIconScale * scale;
441 final int overlayAlpha = (int) (80 * (1 - r));
442
443 if (params == null) {
444 params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
445 } else {
446 params.transX = transX;
447 params.transY = transY;
448 params.scale = totalScale;
449 params.overlayAlpha = overlayAlpha;
450 }
451 return params;
452 }
453
454 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
455 canvas.save();
456 canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY);
457 canvas.scale(params.scale, params.scale);
458 Drawable d = params.drawable;
459
460 if (d != null) {
461 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
462 d.setFilterBitmap(true);
463 d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
464 d.draw(canvas);
465 d.clearColorFilter();
466 d.setFilterBitmap(false);
467 }
468 canvas.restore();
469 }
470
Adam Cohena9cf38f2011-05-02 15:36:58 -0700471 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700472 protected void dispatchDraw(Canvas canvas) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700473 super.dispatchDraw(canvas);
474
Adam Cohena9cf38f2011-05-02 15:36:58 -0700475 if (mFolder == null) return;
Adam Cohend0445262011-07-04 23:53:22 -0700476 if (mFolder.getItemCount() == 0 && !mAnimating) return;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700477
Adam Cohen76078c42011-06-09 15:06:52 -0700478 ArrayList<View> items = mFolder.getItemsInReadingOrder(false);
Adam Cohend0445262011-07-04 23:53:22 -0700479 Drawable d;
480 TextView v;
Adam Cohenbadf71e2011-05-26 19:08:29 -0700481
Adam Cohend0445262011-07-04 23:53:22 -0700482 // Update our drawing parameters if necessary
483 if (mAnimating) {
484 computePreviewDrawingParams(mAnimParams.drawable);
485 } else {
486 v = (TextView) items.get(0);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700487 d = v.getCompoundDrawables()[1];
Adam Cohend0445262011-07-04 23:53:22 -0700488 computePreviewDrawingParams(d);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700489 }
Adam Cohend0445262011-07-04 23:53:22 -0700490
491 int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
492 if (!mAnimating) {
493 for (int i = nItemsInPreview - 1; i >= 0; i--) {
494 v = (TextView) items.get(i);
495 d = v.getCompoundDrawables()[1];
496
497 mParams = computePreviewItemDrawingParams(i, mParams);
498 mParams.drawable = d;
499 drawPreviewItem(canvas, mParams);
500 }
501 } else {
502 drawPreviewItem(canvas, mAnimParams);
503 }
504 }
505
506 private void animateFirstItem(final Drawable d, int duration) {
507 computePreviewDrawingParams(d);
508 final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
509
510 final float scale0 = 1.0f;
511 final float transX0 = 0;
512 final float transY0 = 0;
513 mAnimParams.drawable = d;
514
515 ValueAnimator va = ValueAnimator.ofFloat(0f, 1.0f);
516 va.addUpdateListener(new AnimatorUpdateListener(){
517 public void onAnimationUpdate(ValueAnimator animation) {
518 float progress = (Float) animation.getAnimatedValue();
519
520 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0);
521 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0);
522 mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0);
523 invalidate();
524 }
525 });
526 va.addListener(new AnimatorListenerAdapter() {
527 @Override
528 public void onAnimationStart(Animator animation) {
529 mAnimating = true;
530 }
531 @Override
532 public void onAnimationEnd(Animator animation) {
533 mAnimating = false;
534 }
535 });
536 va.setDuration(duration);
537 va.start();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700538 }
539
Adam Cohen76078c42011-06-09 15:06:52 -0700540 public void onItemsChanged() {
541 invalidate();
542 requestLayout();
543 }
544
Adam Cohena9cf38f2011-05-02 15:36:58 -0700545 public void onAdd(ShortcutInfo item) {
546 invalidate();
547 requestLayout();
548 }
549
550 public void onRemove(ShortcutInfo item) {
551 invalidate();
552 requestLayout();
553 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700554
555 public void onTitleChanged(CharSequence title) {
Adam Cohend63cfa92011-06-24 14:17:17 -0700556 mFolderName.setText(title.toString());
Winson Chung6a0f57d2011-06-29 20:10:49 -0700557 mPreviewBackground.setContentDescription(title.toString());
Adam Cohen76fc0852011-06-17 13:26:23 -0700558 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800559}