blob: ff7e10f5fa89a28978f210d6a4c6d4869effeff1 [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;
Adam Cohen3e8f8112011-07-02 18:03:00 -070028import android.graphics.Rect;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080029import android.graphics.drawable.Drawable;
Adam Cohen099f60d2011-08-23 21:07:26 -070030import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080031import 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;
Adam Cohen099f60d2011-08-23 21:07:26 -070054 private static boolean sStaticValuesDirty = true;
Adam Cohena9cf38f2011-05-02 15:36:58 -070055
Adam Cohenbadf71e2011-05-26 19:08:29 -070056 // The number of icons to display in the
Adam Cohen76fc0852011-06-17 13:26:23 -070057 private static final int NUM_ITEMS_IN_PREVIEW = 3;
Adam Cohenf4b08912011-05-17 18:45:47 -070058 private static final int CONSUMPTION_ANIMATION_DURATION = 100;
Adam Cohend0445262011-07-04 23:53:22 -070059 private static final int DROP_IN_ANIMATION_DURATION = 400;
Adam Cohen8dfcba42011-07-07 16:38:18 -070060 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
Adam Cohenbadf71e2011-05-26 19:08:29 -070061
62 // The degree to which the inner ring grows when accepting drop
Adam Cohen69ce2e52011-07-03 19:25:21 -070063 private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070064
Adam Cohenbadf71e2011-05-26 19:08:29 -070065 // The degree to which the outer ring is scaled in its natural state
Adam Cohen69ce2e52011-07-03 19:25:21 -070066 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070067
68 // The amount of vertical spread between items in the stack [0...1]
Adam Cohen76fc0852011-06-17 13:26:23 -070069 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070070
71 // The degree to which the item in the back of the stack is scaled [0...1]
72 // (0 means it's not scaled at all, 1 means it's scaled to nothing)
Adam Cohen76fc0852011-06-17 13:26:23 -070073 private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070074
Adam Cohenc51934b2011-07-26 21:07:43 -070075 public static Drawable sSharedFolderLeaveBehind = null;
76
Adam Cohen76fc0852011-06-17 13:26:23 -070077 private ImageView mPreviewBackground;
78 private BubbleTextView mFolderName;
Adam Cohen073a46f2011-05-17 16:28:09 -070079
Adam Cohen19072da2011-05-31 14:30:45 -070080 FolderRingAnimator mFolderRingAnimator = null;
Adam Cohen2801caf2011-05-13 20:57:39 -070081
Adam Cohend0445262011-07-04 23:53:22 -070082 // These variables are all associated with the drawing of the preview; they are stored
83 // as member variables for shared usage and to avoid computation on each frame
84 private int mIntrinsicIconSize;
85 private float mBaselineIconScale;
86 private int mBaselineIconSize;
87 private int mAvailableSpaceInPreview;
88 private int mTotalWidth = -1;
89 private int mPreviewOffsetX;
90 private int mPreviewOffsetY;
91 private float mMaxPerspectiveShift;
92 boolean mAnimating = false;
93 private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
94 private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
95
The Android Open Source Project31dd5032009-03-03 19:32:27 -080096 public FolderIcon(Context context, AttributeSet attrs) {
97 super(context, attrs);
98 }
99
100 public FolderIcon(Context context) {
101 super(context);
102 }
103
Michael Jurka0280c3b2010-09-17 15:00:07 -0700104 public boolean isDropEnabled() {
Winson Chung7a1d1652011-03-18 15:56:01 -0700105 final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
106 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
107 final Workspace workspace = (Workspace) cellLayout.getParent();
108 return !workspace.isSmall();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700109 }
110
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800111 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Adam Cohendf2cc412011-04-27 16:56:57 -0700112 FolderInfo folderInfo, IconCache iconCache) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113
Adam Cohend0445262011-07-04 23:53:22 -0700114 if (INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION) {
115 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
116 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
117 "is dependent on this");
118 }
119
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
121
Adam Cohend2eca6b2011-07-26 22:49:13 -0700122 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
Adam Cohen76fc0852011-06-17 13:26:23 -0700123 icon.mFolderName.setText(folderInfo.title);
124 icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
125
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 Cohen3371da02011-10-25 21:38:29 -0700130 icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
131 folderInfo.title));
Adam Cohena9cf38f2011-05-02 15:36:58 -0700132 Folder folder = Folder.fromXml(launcher);
133 folder.setDragController(launcher.getDragController());
Adam Cohen2801caf2011-05-13 20:57:39 -0700134 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700135 folder.bind(folderInfo);
136 icon.mFolder = folder;
Adam Cohen099f60d2011-08-23 21:07:26 -0700137
Adam Cohen099f60d2011-08-23 21:07:26 -0700138 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
139 folderInfo.addListener(icon);
140
Adam Cohen19072da2011-05-31 14:30:45 -0700141 return icon;
142 }
143
Adam Cohen099f60d2011-08-23 21:07:26 -0700144 @Override
145 protected Parcelable onSaveInstanceState() {
146 sStaticValuesDirty = true;
147 return super.onSaveInstanceState();
148 }
149
Adam Cohen19072da2011-05-31 14:30:45 -0700150 public static class FolderRingAnimator {
Adam Cohen69ce2e52011-07-03 19:25:21 -0700151 public int mCellX;
152 public int mCellY;
153 private CellLayout mCellLayout;
Adam Cohen76fc0852011-06-17 13:26:23 -0700154 public float mOuterRingSize;
155 public float mInnerRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700156 public FolderIcon mFolderIcon = null;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700157 public Drawable mOuterRingDrawable = null;
158 public Drawable mInnerRingDrawable = null;
159 public static Drawable sSharedOuterRingDrawable = null;
160 public static Drawable sSharedInnerRingDrawable = null;
Adam Cohen76fc0852011-06-17 13:26:23 -0700161 public static int sPreviewSize = -1;
162 public static int sPreviewPadding = -1;
163
Adam Cohenc0dcf592011-06-01 15:30:43 -0700164 private ValueAnimator mAcceptAnimator;
165 private ValueAnimator mNeutralAnimator;
Adam Cohen19072da2011-05-31 14:30:45 -0700166
167 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
Adam Cohen19072da2011-05-31 14:30:45 -0700168 mFolderIcon = folderIcon;
Adam Cohen76fc0852011-06-17 13:26:23 -0700169 Resources res = launcher.getResources();
170 mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
171 mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
172
Adam Cohen099f60d2011-08-23 21:07:26 -0700173 // We need to reload the static values when configuration changes in case they are
174 // different in another configuration
175 if (sStaticValuesDirty) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700176 sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
177 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
Adam Cohen76fc0852011-06-17 13:26:23 -0700178 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
Adam Cohen76fc0852011-06-17 13:26:23 -0700179 sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
Adam Cohendf6af572011-10-19 14:38:16 -0700180 sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest);
Adam Cohen099f60d2011-08-23 21:07:26 -0700181 sStaticValuesDirty = false;
Adam Cohen19072da2011-05-31 14:30:45 -0700182 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700183 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700184
Adam Cohen19072da2011-05-31 14:30:45 -0700185 public void animateToAcceptState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700186 if (mNeutralAnimator != null) {
187 mNeutralAnimator.cancel();
188 }
189 mAcceptAnimator = ValueAnimator.ofFloat(0f, 1f);
190 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
191 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700192 public void onAnimationUpdate(ValueAnimator animation) {
193 final float percent = (Float) animation.getAnimatedValue();
Adam Cohen76fc0852011-06-17 13:26:23 -0700194 mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
195 mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700196 if (mCellLayout != null) {
197 mCellLayout.invalidate();
Adam Cohen19072da2011-05-31 14:30:45 -0700198 }
199 }
200 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700201 mAcceptAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700202 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700203 public void onAnimationStart(Animator animation) {
Adam Cohen19072da2011-05-31 14:30:45 -0700204 if (mFolderIcon != null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700205 mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE);
Adam Cohen19072da2011-05-31 14:30:45 -0700206 }
207 }
208 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700209 mAcceptAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700210 }
211
212 public void animateToNaturalState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700213 if (mAcceptAnimator != null) {
214 mAcceptAnimator.cancel();
215 }
216 mNeutralAnimator = ValueAnimator.ofFloat(0f, 1f);
217 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
218 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700219 public void onAnimationUpdate(ValueAnimator animation) {
220 final float percent = (Float) animation.getAnimatedValue();
Adam Cohen76fc0852011-06-17 13:26:23 -0700221 mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
222 mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700223 if (mCellLayout != null) {
224 mCellLayout.invalidate();
Adam Cohen19072da2011-05-31 14:30:45 -0700225 }
226 }
227 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700228 mNeutralAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700229 @Override
230 public void onAnimationEnd(Animator animation) {
Adam Cohen69ce2e52011-07-03 19:25:21 -0700231 if (mCellLayout != null) {
232 mCellLayout.hideFolderAccept(FolderRingAnimator.this);
233 }
Adam Cohen19072da2011-05-31 14:30:45 -0700234 if (mFolderIcon != null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700235 mFolderIcon.mPreviewBackground.setVisibility(VISIBLE);
Adam Cohen19072da2011-05-31 14:30:45 -0700236 }
Adam Cohen19072da2011-05-31 14:30:45 -0700237 }
238 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700239 mNeutralAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700240 }
241
Adam Cohenc0dcf592011-06-01 15:30:43 -0700242 // Location is expressed in window coordinates
Adam Cohen69ce2e52011-07-03 19:25:21 -0700243 public void getCell(int[] loc) {
244 loc[0] = mCellX;
245 loc[1] = mCellY;
246 }
247
248 // Location is expressed in window coordinates
249 public void setCell(int x, int y) {
250 mCellX = x;
251 mCellY = y;
252 }
253
254 public void setCellLayout(CellLayout layout) {
255 mCellLayout = layout;
Adam Cohen19072da2011-05-31 14:30:45 -0700256 }
257
Adam Cohen76fc0852011-06-17 13:26:23 -0700258 public float getOuterRingSize() {
259 return mOuterRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700260 }
261
Adam Cohen76fc0852011-06-17 13:26:23 -0700262 public float getInnerRingSize() {
263 return mInnerRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700264 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800265 }
266
Adam Cohen073a46f2011-05-17 16:28:09 -0700267 private boolean willAcceptItem(ItemInfo item) {
268 final int itemType = item.itemType;
269 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
270 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
Adam Cohenc51934b2011-07-26 21:07:43 -0700271 !mFolder.isFull() && item != mInfo && !mInfo.opened);
Adam Cohen073a46f2011-05-17 16:28:09 -0700272 }
273
Adam Cohenc0dcf592011-06-01 15:30:43 -0700274 public boolean acceptDrop(Object dragInfo) {
275 final ItemInfo item = (ItemInfo) dragInfo;
Adam Cohen073a46f2011-05-17 16:28:09 -0700276 return willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277 }
278
Adam Cohendf035382011-04-11 17:22:04 -0700279 public void addItem(ShortcutInfo item) {
280 mInfo.add(item);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700281 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
Adam Cohendf035382011-04-11 17:22:04 -0700282 }
283
Adam Cohenc0dcf592011-06-01 15:30:43 -0700284 public void onDragEnter(Object dragInfo) {
285 if (!willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700286 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
287 CellLayout layout = (CellLayout) getParent().getParent();
288 mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
289 mFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -0700290 mFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -0700291 layout.showFolderAccept(mFolderRingAnimator);
Adam Cohen073a46f2011-05-17 16:28:09 -0700292 }
293
Adam Cohenc0dcf592011-06-01 15:30:43 -0700294 public void onDragOver(Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800295 }
296
Adam Cohend0445262011-07-04 23:53:22 -0700297 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800298 final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
Adam Cohenac8c8762011-07-13 11:15:27 -0700299 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700300
301 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
302 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
Adam Cohenac8c8762011-07-13 11:15:27 -0700303
Adam Cohend0445262011-07-04 23:53:22 -0700304 // This will animate the dragView (srcView) into the new folder
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800305 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
Adam Cohend0445262011-07-04 23:53:22 -0700306
307 // This will animate the first item from it's position as an icon into its
308 // position as the first item in the preview
Adam Cohen8dfcba42011-07-07 16:38:18 -0700309 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION);
Adam Cohend0445262011-07-04 23:53:22 -0700310
311 postDelayed(new Runnable() {
312 public void run() {
313 addItem(destInfo);
314 }
315 }, INITIAL_ITEM_ANIMATION_DURATION);
316 }
317
Adam Cohenc0dcf592011-06-01 15:30:43 -0700318 public void onDragExit(Object dragInfo) {
319 if (!willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen19072da2011-05-31 14:30:45 -0700320 mFolderRingAnimator.animateToNaturalState();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800321 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700322
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800323 private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
324 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
325 DragObject d) {
Adam Cohend0445262011-07-04 23:53:22 -0700326 item.cellX = -1;
327 item.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -0700328
Adam Cohen558baaf2011-08-15 15:22:57 -0700329 // Typically, the animateView corresponds to the DragView; however, if this is being done
330 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
331 // will not have a view to animate
332 if (animateView != null) {
333 DragLayer dragLayer = mLauncher.getDragLayer();
334 Rect from = new Rect();
335 dragLayer.getViewRectRelativeToSelf(animateView, from);
336 Rect to = finalRect;
337 if (to == null) {
338 to = new Rect();
339 Workspace workspace = mLauncher.getWorkspace();
340 // Set cellLayout and this to it's final state to compute final animation locations
341 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
342 float scaleX = getScaleX();
343 float scaleY = getScaleY();
344 setScaleX(1.0f);
345 setScaleY(1.0f);
346 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
347 // Finished computing final animation locations, restore current state
348 setScaleX(scaleX);
349 setScaleY(scaleY);
350 workspace.resetTransitionTransform((CellLayout) getParent().getParent());
Adam Cohend0445262011-07-04 23:53:22 -0700351 }
Adam Cohen558baaf2011-08-15 15:22:57 -0700352
353 int[] center = new int[2];
354 float scale = getLocalCenterForIndex(index, center);
355 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
356 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
357
358 to.offset(center[0] - animateView.getMeasuredWidth() / 2,
359 center[1] - animateView.getMeasuredHeight() / 2);
360
361 float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
362
Adam Cohened66b2b2012-01-23 17:28:51 -0800363 float finalScale = scale * scaleRelativeToDragLayer;
Adam Cohen558baaf2011-08-15 15:22:57 -0700364 dragLayer.animateView(animateView, from, to, finalAlpha,
Adam Cohened66b2b2012-01-23 17:28:51 -0800365 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
Adam Cohen558baaf2011-08-15 15:22:57 -0700366 new DecelerateInterpolator(2), new AccelerateInterpolator(2),
Adam Cohened66b2b2012-01-23 17:28:51 -0800367 postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
Adam Cohen558baaf2011-08-15 15:22:57 -0700368 postDelayed(new Runnable() {
369 public void run() {
370 addItem(item);
371 }
372 }, DROP_IN_ANIMATION_DURATION);
373 } else {
374 addItem(item);
375 }
Adam Cohend0445262011-07-04 23:53:22 -0700376 }
377
Adam Cohen3e8f8112011-07-02 18:03:00 -0700378 public void onDrop(DragObject d) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700379 ShortcutInfo item;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700380 if (d.dragInfo instanceof ApplicationInfo) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700381 // Came from all apps -- make a copy
Adam Cohen3e8f8112011-07-02 18:03:00 -0700382 item = ((ApplicationInfo) d.dragInfo).makeShortcut();
Adam Cohenc0dcf592011-06-01 15:30:43 -0700383 } else {
Adam Cohen3e8f8112011-07-02 18:03:00 -0700384 item = (ShortcutInfo) d.dragInfo;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700385 }
Adam Cohen67bd9cc2011-07-29 14:07:04 -0700386 mFolder.notifyDrop();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800387 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
Adam Cohenc0dcf592011-06-01 15:30:43 -0700388 }
389
Adam Cohencb3382b2011-05-24 14:07:08 -0700390 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -0700391 return null;
392 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700393
Adam Cohend0445262011-07-04 23:53:22 -0700394 private void computePreviewDrawingParams(int drawableSize, int totalSize) {
395 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
396 mIntrinsicIconSize = drawableSize;
397 mTotalWidth = totalSize;
398
399 final int previewSize = FolderRingAnimator.sPreviewSize;
400 final int previewPadding = FolderRingAnimator.sPreviewPadding;
401
402 mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
403 // cos(45) = 0.707 + ~= 0.1) = 0.8f
404 int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
405
406 int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
407 mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
408
409 mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
410 mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
411
412 mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
413 mPreviewOffsetY = previewPadding;
414 }
415 }
416
417 private void computePreviewDrawingParams(Drawable d) {
418 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
419 }
420
421 class PreviewItemDrawingParams {
422 PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) {
423 this.transX = transX;
424 this.transY = transY;
425 this.scale = scale;
426 this.overlayAlpha = overlayAlpha;
427 }
428 float transX;
429 float transY;
430 float scale;
431 int overlayAlpha;
432 Drawable drawable;
433 }
434
Adam Cohenac8c8762011-07-13 11:15:27 -0700435 private float getLocalCenterForIndex(int index, int[] center) {
Adam Cohend0445262011-07-04 23:53:22 -0700436 mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams);
437
438 mParams.transX += mPreviewOffsetX;
439 mParams.transY += mPreviewOffsetY;
Adam Cohenac8c8762011-07-13 11:15:27 -0700440 float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
441 float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
Adam Cohend0445262011-07-04 23:53:22 -0700442
Adam Cohenac8c8762011-07-13 11:15:27 -0700443 center[0] = (int) Math.round(offsetX);
444 center[1] = (int) Math.round(offsetY);
Adam Cohend0445262011-07-04 23:53:22 -0700445 return mParams.scale;
446 }
447
448 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
449 PreviewItemDrawingParams params) {
450 index = NUM_ITEMS_IN_PREVIEW - index - 1;
451 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
452 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
453
454 float offset = (1 - r) * mMaxPerspectiveShift;
455 float scaledSize = scale * mBaselineIconSize;
456 float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
457
458 // We want to imagine our coordinates from the bottom left, growing up and to the
459 // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
460 float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
461 float transX = offset + scaleOffsetCorrection;
462 float totalScale = mBaselineIconScale * scale;
463 final int overlayAlpha = (int) (80 * (1 - r));
464
465 if (params == null) {
466 params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
467 } else {
468 params.transX = transX;
469 params.transY = transY;
470 params.scale = totalScale;
471 params.overlayAlpha = overlayAlpha;
472 }
473 return params;
474 }
475
476 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
477 canvas.save();
478 canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY);
479 canvas.scale(params.scale, params.scale);
480 Drawable d = params.drawable;
481
482 if (d != null) {
483 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
484 d.setFilterBitmap(true);
485 d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
486 d.draw(canvas);
487 d.clearColorFilter();
488 d.setFilterBitmap(false);
489 }
490 canvas.restore();
491 }
492
Adam Cohena9cf38f2011-05-02 15:36:58 -0700493 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700494 protected void dispatchDraw(Canvas canvas) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700495 super.dispatchDraw(canvas);
496
Adam Cohena9cf38f2011-05-02 15:36:58 -0700497 if (mFolder == null) return;
Adam Cohend0445262011-07-04 23:53:22 -0700498 if (mFolder.getItemCount() == 0 && !mAnimating) return;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700499
Adam Cohen76078c42011-06-09 15:06:52 -0700500 ArrayList<View> items = mFolder.getItemsInReadingOrder(false);
Adam Cohend0445262011-07-04 23:53:22 -0700501 Drawable d;
502 TextView v;
Adam Cohenbadf71e2011-05-26 19:08:29 -0700503
Adam Cohend0445262011-07-04 23:53:22 -0700504 // Update our drawing parameters if necessary
505 if (mAnimating) {
506 computePreviewDrawingParams(mAnimParams.drawable);
507 } else {
508 v = (TextView) items.get(0);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700509 d = v.getCompoundDrawables()[1];
Adam Cohend0445262011-07-04 23:53:22 -0700510 computePreviewDrawingParams(d);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700511 }
Adam Cohend0445262011-07-04 23:53:22 -0700512
513 int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
514 if (!mAnimating) {
515 for (int i = nItemsInPreview - 1; i >= 0; i--) {
516 v = (TextView) items.get(i);
517 d = v.getCompoundDrawables()[1];
518
519 mParams = computePreviewItemDrawingParams(i, mParams);
520 mParams.drawable = d;
521 drawPreviewItem(canvas, mParams);
522 }
523 } else {
524 drawPreviewItem(canvas, mAnimParams);
525 }
526 }
527
528 private void animateFirstItem(final Drawable d, int duration) {
529 computePreviewDrawingParams(d);
530 final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
531
532 final float scale0 = 1.0f;
Adam Cohen1d4ee4e2011-08-12 15:51:59 -0700533 final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2;
534 final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2;
Adam Cohend0445262011-07-04 23:53:22 -0700535 mAnimParams.drawable = d;
536
537 ValueAnimator va = ValueAnimator.ofFloat(0f, 1.0f);
538 va.addUpdateListener(new AnimatorUpdateListener(){
539 public void onAnimationUpdate(ValueAnimator animation) {
540 float progress = (Float) animation.getAnimatedValue();
541
542 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0);
543 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0);
544 mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0);
545 invalidate();
546 }
547 });
548 va.addListener(new AnimatorListenerAdapter() {
549 @Override
550 public void onAnimationStart(Animator animation) {
551 mAnimating = true;
552 }
553 @Override
554 public void onAnimationEnd(Animator animation) {
555 mAnimating = false;
556 }
557 });
558 va.setDuration(duration);
559 va.start();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700560 }
561
Adam Cohen099f60d2011-08-23 21:07:26 -0700562 public void setTextVisible(boolean visible) {
563 if (visible) {
564 mFolderName.setVisibility(VISIBLE);
565 } else {
566 mFolderName.setVisibility(INVISIBLE);
567 }
568 }
569
570 public boolean getTextVisible() {
571 return mFolderName.getVisibility() == VISIBLE;
572 }
573
Adam Cohen76078c42011-06-09 15:06:52 -0700574 public void onItemsChanged() {
575 invalidate();
576 requestLayout();
577 }
578
Adam Cohena9cf38f2011-05-02 15:36:58 -0700579 public void onAdd(ShortcutInfo item) {
580 invalidate();
581 requestLayout();
582 }
583
584 public void onRemove(ShortcutInfo item) {
585 invalidate();
586 requestLayout();
587 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700588
589 public void onTitleChanged(CharSequence title) {
Adam Cohend63cfa92011-06-24 14:17:17 -0700590 mFolderName.setText(title.toString());
Adam Cohen3371da02011-10-25 21:38:29 -0700591 setContentDescription(String.format(mContext.getString(R.string.folder_name_format),
592 title));
Adam Cohen76fc0852011-06-17 13:26:23 -0700593 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800594}