blob: 76f31143cf5011e8a7b7526498fcfc05dc89461f [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;
Winson Chung88f33452012-02-23 15:23:44 -080033import android.view.MotionEvent;
Adam Cohen7c693212011-05-18 15:26:57 -070034import android.view.View;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.view.ViewGroup;
Adam Cohen3e8f8112011-07-02 18:03:00 -070036import android.view.animation.AccelerateInterpolator;
37import android.view.animation.DecelerateInterpolator;
Adam Cohen76fc0852011-06-17 13:26:23 -070038import android.widget.ImageView;
39import android.widget.LinearLayout;
Adam Cohena9cf38f2011-05-02 15:36:58 -070040import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041
Romain Guyedcce092010-03-04 13:03:17 -080042import com.android.launcher.R;
Adam Cohenc0dcf592011-06-01 15:30:43 -070043import com.android.launcher2.DropTarget.DragObject;
Adam Cohena9cf38f2011-05-02 15:36:58 -070044import com.android.launcher2.FolderInfo.FolderListener;
Romain Guyedcce092010-03-04 13:03:17 -080045
Adam Cohenc0dcf592011-06-01 15:30:43 -070046import java.util.ArrayList;
47
The Android Open Source Project31dd5032009-03-03 19:32:27 -080048/**
49 * An icon that can appear on in the workspace representing an {@link UserFolder}.
50 */
Adam Cohen76fc0852011-06-17 13:26:23 -070051public class FolderIcon extends LinearLayout implements FolderListener {
The Android Open Source Project31dd5032009-03-03 19:32:27 -080052 private Launcher mLauncher;
Adam Cohenfb91f302012-06-11 15:45:18 -070053 private Folder mFolder;
54 private FolderInfo mInfo;
Adam Cohen099f60d2011-08-23 21:07:26 -070055 private static boolean sStaticValuesDirty = true;
Adam Cohena9cf38f2011-05-02 15:36:58 -070056
Winson Chung88f33452012-02-23 15:23:44 -080057 private CheckLongPressHelper mLongPressHelper;
58
Adam Cohenbadf71e2011-05-26 19:08:29 -070059 // The number of icons to display in the
Adam Cohen76fc0852011-06-17 13:26:23 -070060 private static final int NUM_ITEMS_IN_PREVIEW = 3;
Adam Cohenf4b08912011-05-17 18:45:47 -070061 private static final int CONSUMPTION_ANIMATION_DURATION = 100;
Adam Cohend0445262011-07-04 23:53:22 -070062 private static final int DROP_IN_ANIMATION_DURATION = 400;
Adam Cohen8dfcba42011-07-07 16:38:18 -070063 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
Adam Cohenfb91f302012-06-11 15:45:18 -070064 private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
Adam Cohenbadf71e2011-05-26 19:08:29 -070065
66 // The degree to which the inner ring grows when accepting drop
Adam Cohen69ce2e52011-07-03 19:25:21 -070067 private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070068
Adam Cohenbadf71e2011-05-26 19:08:29 -070069 // The degree to which the outer ring is scaled in its natural state
Adam Cohen69ce2e52011-07-03 19:25:21 -070070 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070071
72 // The amount of vertical spread between items in the stack [0...1]
Adam Cohen76fc0852011-06-17 13:26:23 -070073 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070074
75 // The degree to which the item in the back of the stack is scaled [0...1]
76 // (0 means it's not scaled at all, 1 means it's scaled to nothing)
Adam Cohen76fc0852011-06-17 13:26:23 -070077 private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
Adam Cohenbadf71e2011-05-26 19:08:29 -070078
Adam Cohenc51934b2011-07-26 21:07:43 -070079 public static Drawable sSharedFolderLeaveBehind = null;
80
Adam Cohen76fc0852011-06-17 13:26:23 -070081 private ImageView mPreviewBackground;
82 private BubbleTextView mFolderName;
Adam Cohen073a46f2011-05-17 16:28:09 -070083
Adam Cohen19072da2011-05-31 14:30:45 -070084 FolderRingAnimator mFolderRingAnimator = null;
Adam Cohen2801caf2011-05-13 20:57:39 -070085
Adam Cohend0445262011-07-04 23:53:22 -070086 // These variables are all associated with the drawing of the preview; they are stored
87 // as member variables for shared usage and to avoid computation on each frame
88 private int mIntrinsicIconSize;
89 private float mBaselineIconScale;
90 private int mBaselineIconSize;
91 private int mAvailableSpaceInPreview;
92 private int mTotalWidth = -1;
93 private int mPreviewOffsetX;
94 private int mPreviewOffsetY;
95 private float mMaxPerspectiveShift;
96 boolean mAnimating = false;
Adam Cohenfb91f302012-06-11 15:45:18 -070097
Adam Cohend0445262011-07-04 23:53:22 -070098 private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
99 private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
Adam Cohenfb91f302012-06-11 15:45:18 -0700100 private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
Adam Cohend0445262011-07-04 23:53:22 -0700101
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800102 public FolderIcon(Context context, AttributeSet attrs) {
103 super(context, attrs);
Winson Chung88f33452012-02-23 15:23:44 -0800104 init();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800105 }
106
107 public FolderIcon(Context context) {
108 super(context);
Winson Chung88f33452012-02-23 15:23:44 -0800109 init();
110 }
111
112 private void init() {
113 mLongPressHelper = new CheckLongPressHelper(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800114 }
115
Michael Jurka0280c3b2010-09-17 15:00:07 -0700116 public boolean isDropEnabled() {
Winson Chung7a1d1652011-03-18 15:56:01 -0700117 final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
118 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
119 final Workspace workspace = (Workspace) cellLayout.getParent();
120 return !workspace.isSmall();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700121 }
122
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Adam Cohendf2cc412011-04-27 16:56:57 -0700124 FolderInfo folderInfo, IconCache iconCache) {
Michael Jurka3a9fced2012-04-13 14:44:29 -0700125 @SuppressWarnings("all") // suppress dead code warning
126 final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
127 if (error) {
Adam Cohend0445262011-07-04 23:53:22 -0700128 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
129 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
130 "is dependent on this");
131 }
132
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800133 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
134
Adam Cohend2eca6b2011-07-26 22:49:13 -0700135 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
Adam Cohen76fc0852011-06-17 13:26:23 -0700136 icon.mFolderName.setText(folderInfo.title);
137 icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
138
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800139 icon.setTag(folderInfo);
140 icon.setOnClickListener(launcher);
141 icon.mInfo = folderInfo;
142 icon.mLauncher = launcher;
Adam Cohen3371da02011-10-25 21:38:29 -0700143 icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
144 folderInfo.title));
Adam Cohena9cf38f2011-05-02 15:36:58 -0700145 Folder folder = Folder.fromXml(launcher);
146 folder.setDragController(launcher.getDragController());
Adam Cohen2801caf2011-05-13 20:57:39 -0700147 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700148 folder.bind(folderInfo);
149 icon.mFolder = folder;
Adam Cohen099f60d2011-08-23 21:07:26 -0700150
Adam Cohen099f60d2011-08-23 21:07:26 -0700151 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
152 folderInfo.addListener(icon);
153
Adam Cohen19072da2011-05-31 14:30:45 -0700154 return icon;
155 }
156
Adam Cohen099f60d2011-08-23 21:07:26 -0700157 @Override
158 protected Parcelable onSaveInstanceState() {
159 sStaticValuesDirty = true;
160 return super.onSaveInstanceState();
161 }
162
Adam Cohen19072da2011-05-31 14:30:45 -0700163 public static class FolderRingAnimator {
Adam Cohen69ce2e52011-07-03 19:25:21 -0700164 public int mCellX;
165 public int mCellY;
166 private CellLayout mCellLayout;
Adam Cohen76fc0852011-06-17 13:26:23 -0700167 public float mOuterRingSize;
168 public float mInnerRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700169 public FolderIcon mFolderIcon = null;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700170 public Drawable mOuterRingDrawable = null;
171 public Drawable mInnerRingDrawable = null;
172 public static Drawable sSharedOuterRingDrawable = null;
173 public static Drawable sSharedInnerRingDrawable = null;
Adam Cohen76fc0852011-06-17 13:26:23 -0700174 public static int sPreviewSize = -1;
175 public static int sPreviewPadding = -1;
176
Adam Cohenc0dcf592011-06-01 15:30:43 -0700177 private ValueAnimator mAcceptAnimator;
178 private ValueAnimator mNeutralAnimator;
Adam Cohen19072da2011-05-31 14:30:45 -0700179
180 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
Adam Cohen19072da2011-05-31 14:30:45 -0700181 mFolderIcon = folderIcon;
Adam Cohen76fc0852011-06-17 13:26:23 -0700182 Resources res = launcher.getResources();
183 mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
184 mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
185
Adam Cohen099f60d2011-08-23 21:07:26 -0700186 // We need to reload the static values when configuration changes in case they are
187 // different in another configuration
188 if (sStaticValuesDirty) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700189 sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
190 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
Adam Cohen76fc0852011-06-17 13:26:23 -0700191 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
Adam Cohen76fc0852011-06-17 13:26:23 -0700192 sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
Adam Cohendf6af572011-10-19 14:38:16 -0700193 sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest);
Adam Cohen099f60d2011-08-23 21:07:26 -0700194 sStaticValuesDirty = false;
Adam Cohen19072da2011-05-31 14:30:45 -0700195 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700196 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700197
Adam Cohen19072da2011-05-31 14:30:45 -0700198 public void animateToAcceptState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700199 if (mNeutralAnimator != null) {
200 mNeutralAnimator.cancel();
201 }
202 mAcceptAnimator = ValueAnimator.ofFloat(0f, 1f);
203 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
Andrew Flynn850d2e72012-04-26 16:51:20 -0700204
205 final int previewSize = sPreviewSize;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700206 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700207 public void onAnimationUpdate(ValueAnimator animation) {
208 final float percent = (Float) animation.getAnimatedValue();
Andrew Flynn850d2e72012-04-26 16:51:20 -0700209 mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * previewSize;
210 mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * previewSize;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700211 if (mCellLayout != null) {
212 mCellLayout.invalidate();
Adam Cohen19072da2011-05-31 14:30:45 -0700213 }
214 }
215 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700216 mAcceptAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700217 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700218 public void onAnimationStart(Animator animation) {
Adam Cohen19072da2011-05-31 14:30:45 -0700219 if (mFolderIcon != null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700220 mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE);
Adam Cohen19072da2011-05-31 14:30:45 -0700221 }
222 }
223 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700224 mAcceptAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700225 }
226
227 public void animateToNaturalState() {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700228 if (mAcceptAnimator != null) {
229 mAcceptAnimator.cancel();
230 }
231 mNeutralAnimator = ValueAnimator.ofFloat(0f, 1f);
232 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
Andrew Flynn850d2e72012-04-26 16:51:20 -0700233
234 final int previewSize = sPreviewSize;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700235 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
Adam Cohen19072da2011-05-31 14:30:45 -0700236 public void onAnimationUpdate(ValueAnimator animation) {
237 final float percent = (Float) animation.getAnimatedValue();
Andrew Flynn850d2e72012-04-26 16:51:20 -0700238 mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * previewSize;
239 mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * previewSize;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700240 if (mCellLayout != null) {
241 mCellLayout.invalidate();
Adam Cohen19072da2011-05-31 14:30:45 -0700242 }
243 }
244 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700245 mNeutralAnimator.addListener(new AnimatorListenerAdapter() {
Adam Cohen19072da2011-05-31 14:30:45 -0700246 @Override
247 public void onAnimationEnd(Animator animation) {
Adam Cohen69ce2e52011-07-03 19:25:21 -0700248 if (mCellLayout != null) {
249 mCellLayout.hideFolderAccept(FolderRingAnimator.this);
250 }
Adam Cohen19072da2011-05-31 14:30:45 -0700251 if (mFolderIcon != null) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700252 mFolderIcon.mPreviewBackground.setVisibility(VISIBLE);
Adam Cohen19072da2011-05-31 14:30:45 -0700253 }
Adam Cohen19072da2011-05-31 14:30:45 -0700254 }
255 });
Adam Cohenc0dcf592011-06-01 15:30:43 -0700256 mNeutralAnimator.start();
Adam Cohen19072da2011-05-31 14:30:45 -0700257 }
258
Adam Cohenc0dcf592011-06-01 15:30:43 -0700259 // Location is expressed in window coordinates
Adam Cohen69ce2e52011-07-03 19:25:21 -0700260 public void getCell(int[] loc) {
261 loc[0] = mCellX;
262 loc[1] = mCellY;
263 }
264
265 // Location is expressed in window coordinates
266 public void setCell(int x, int y) {
267 mCellX = x;
268 mCellY = y;
269 }
270
271 public void setCellLayout(CellLayout layout) {
272 mCellLayout = layout;
Adam Cohen19072da2011-05-31 14:30:45 -0700273 }
274
Adam Cohen76fc0852011-06-17 13:26:23 -0700275 public float getOuterRingSize() {
276 return mOuterRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700277 }
278
Adam Cohen76fc0852011-06-17 13:26:23 -0700279 public float getInnerRingSize() {
280 return mInnerRingSize;
Adam Cohen19072da2011-05-31 14:30:45 -0700281 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800282 }
283
Adam Cohenfb91f302012-06-11 15:45:18 -0700284 Folder getFolder() {
285 return mFolder;
286 }
287
288 FolderInfo getFolderInfo() {
289 return mInfo;
290 }
291
Adam Cohen073a46f2011-05-17 16:28:09 -0700292 private boolean willAcceptItem(ItemInfo item) {
293 final int itemType = item.itemType;
294 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
295 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
Adam Cohenc51934b2011-07-26 21:07:43 -0700296 !mFolder.isFull() && item != mInfo && !mInfo.opened);
Adam Cohen073a46f2011-05-17 16:28:09 -0700297 }
298
Adam Cohenc0dcf592011-06-01 15:30:43 -0700299 public boolean acceptDrop(Object dragInfo) {
300 final ItemInfo item = (ItemInfo) dragInfo;
Adam Cohenfb91f302012-06-11 15:45:18 -0700301 return !mFolder.isDestroyed() && willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800302 }
303
Adam Cohendf035382011-04-11 17:22:04 -0700304 public void addItem(ShortcutInfo item) {
305 mInfo.add(item);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700306 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
Adam Cohendf035382011-04-11 17:22:04 -0700307 }
308
Adam Cohenc0dcf592011-06-01 15:30:43 -0700309 public void onDragEnter(Object dragInfo) {
Adam Cohenfb91f302012-06-11 15:45:18 -0700310 if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700311 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
312 CellLayout layout = (CellLayout) getParent().getParent();
313 mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
314 mFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -0700315 mFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -0700316 layout.showFolderAccept(mFolderRingAnimator);
Adam Cohen073a46f2011-05-17 16:28:09 -0700317 }
318
Adam Cohenc0dcf592011-06-01 15:30:43 -0700319 public void onDragOver(Object dragInfo) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800320 }
321
Adam Cohend0445262011-07-04 23:53:22 -0700322 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800323 final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
Adam Cohenac8c8762011-07-13 11:15:27 -0700324 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700325
Adam Cohenfb91f302012-06-11 15:45:18 -0700326 // These correspond two the drawable and view that the icon was dropped _onto_
Adam Cohend0445262011-07-04 23:53:22 -0700327 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
Adam Cohenfb91f302012-06-11 15:45:18 -0700328 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
329 destView.getMeasuredWidth());
Adam Cohend0445262011-07-04 23:53:22 -0700330
331 // This will animate the first item from it's position as an icon into its
332 // position as the first item in the preview
Adam Cohenfb91f302012-06-11 15:45:18 -0700333 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
Adam Cohen268c4752012-06-06 17:47:33 -0700334 addItem(destInfo);
Adam Cohenfb91f302012-06-11 15:45:18 -0700335
336 // This will animate the dragView (srcView) into the new folder
337 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
338 }
339
340 public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
341 Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
342 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
343 finalView.getMeasuredWidth());
344
345 // This will animate the first item from it's position as an icon into its
346 // position as the first item in the preview
347 animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
348 onCompleteRunnable);
Adam Cohend0445262011-07-04 23:53:22 -0700349 }
350
Adam Cohenc0dcf592011-06-01 15:30:43 -0700351 public void onDragExit(Object dragInfo) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700352 onDragExit();
353 }
354
355 public void onDragExit() {
Adam Cohen19072da2011-05-31 14:30:45 -0700356 mFolderRingAnimator.animateToNaturalState();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800357 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700358
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800359 private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
360 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
361 DragObject d) {
Adam Cohend0445262011-07-04 23:53:22 -0700362 item.cellX = -1;
363 item.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -0700364
Adam Cohen558baaf2011-08-15 15:22:57 -0700365 // Typically, the animateView corresponds to the DragView; however, if this is being done
366 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
367 // will not have a view to animate
368 if (animateView != null) {
369 DragLayer dragLayer = mLauncher.getDragLayer();
370 Rect from = new Rect();
371 dragLayer.getViewRectRelativeToSelf(animateView, from);
372 Rect to = finalRect;
373 if (to == null) {
374 to = new Rect();
375 Workspace workspace = mLauncher.getWorkspace();
376 // Set cellLayout and this to it's final state to compute final animation locations
377 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
378 float scaleX = getScaleX();
379 float scaleY = getScaleY();
380 setScaleX(1.0f);
381 setScaleY(1.0f);
382 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
383 // Finished computing final animation locations, restore current state
384 setScaleX(scaleX);
385 setScaleY(scaleY);
386 workspace.resetTransitionTransform((CellLayout) getParent().getParent());
Adam Cohend0445262011-07-04 23:53:22 -0700387 }
Adam Cohen558baaf2011-08-15 15:22:57 -0700388
389 int[] center = new int[2];
390 float scale = getLocalCenterForIndex(index, center);
391 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
392 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
393
394 to.offset(center[0] - animateView.getMeasuredWidth() / 2,
395 center[1] - animateView.getMeasuredHeight() / 2);
396
397 float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
398
Adam Cohened66b2b2012-01-23 17:28:51 -0800399 float finalScale = scale * scaleRelativeToDragLayer;
Adam Cohen558baaf2011-08-15 15:22:57 -0700400 dragLayer.animateView(animateView, from, to, finalAlpha,
Adam Cohened66b2b2012-01-23 17:28:51 -0800401 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
Adam Cohen558baaf2011-08-15 15:22:57 -0700402 new DecelerateInterpolator(2), new AccelerateInterpolator(2),
Adam Cohened66b2b2012-01-23 17:28:51 -0800403 postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
Adam Cohenfb91f302012-06-11 15:45:18 -0700404 addItem(item);
405 mHiddenItems.add(item);
Adam Cohen558baaf2011-08-15 15:22:57 -0700406 postDelayed(new Runnable() {
407 public void run() {
Adam Cohenfb91f302012-06-11 15:45:18 -0700408 mHiddenItems.remove(item);
409 invalidate();
Adam Cohen558baaf2011-08-15 15:22:57 -0700410 }
411 }, DROP_IN_ANIMATION_DURATION);
412 } else {
413 addItem(item);
414 }
Adam Cohend0445262011-07-04 23:53:22 -0700415 }
416
Adam Cohen3e8f8112011-07-02 18:03:00 -0700417 public void onDrop(DragObject d) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700418 ShortcutInfo item;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700419 if (d.dragInfo instanceof ApplicationInfo) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700420 // Came from all apps -- make a copy
Adam Cohen3e8f8112011-07-02 18:03:00 -0700421 item = ((ApplicationInfo) d.dragInfo).makeShortcut();
Adam Cohenc0dcf592011-06-01 15:30:43 -0700422 } else {
Adam Cohen3e8f8112011-07-02 18:03:00 -0700423 item = (ShortcutInfo) d.dragInfo;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700424 }
Adam Cohen67bd9cc2011-07-29 14:07:04 -0700425 mFolder.notifyDrop();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800426 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
Adam Cohenc0dcf592011-06-01 15:30:43 -0700427 }
428
Adam Cohencb3382b2011-05-24 14:07:08 -0700429 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -0700430 return null;
431 }
Adam Cohena9cf38f2011-05-02 15:36:58 -0700432
Adam Cohend0445262011-07-04 23:53:22 -0700433 private void computePreviewDrawingParams(int drawableSize, int totalSize) {
434 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
435 mIntrinsicIconSize = drawableSize;
436 mTotalWidth = totalSize;
437
438 final int previewSize = FolderRingAnimator.sPreviewSize;
439 final int previewPadding = FolderRingAnimator.sPreviewPadding;
440
441 mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
442 // cos(45) = 0.707 + ~= 0.1) = 0.8f
443 int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
444
445 int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
446 mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
447
448 mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
449 mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
450
451 mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
452 mPreviewOffsetY = previewPadding;
453 }
454 }
455
456 private void computePreviewDrawingParams(Drawable d) {
457 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
458 }
459
460 class PreviewItemDrawingParams {
461 PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) {
462 this.transX = transX;
463 this.transY = transY;
464 this.scale = scale;
465 this.overlayAlpha = overlayAlpha;
466 }
467 float transX;
468 float transY;
469 float scale;
470 int overlayAlpha;
471 Drawable drawable;
472 }
473
Adam Cohenac8c8762011-07-13 11:15:27 -0700474 private float getLocalCenterForIndex(int index, int[] center) {
Adam Cohend0445262011-07-04 23:53:22 -0700475 mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams);
476
477 mParams.transX += mPreviewOffsetX;
478 mParams.transY += mPreviewOffsetY;
Adam Cohenac8c8762011-07-13 11:15:27 -0700479 float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
480 float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
Adam Cohend0445262011-07-04 23:53:22 -0700481
Adam Cohenac8c8762011-07-13 11:15:27 -0700482 center[0] = (int) Math.round(offsetX);
483 center[1] = (int) Math.round(offsetY);
Adam Cohend0445262011-07-04 23:53:22 -0700484 return mParams.scale;
485 }
486
487 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
488 PreviewItemDrawingParams params) {
489 index = NUM_ITEMS_IN_PREVIEW - index - 1;
490 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
491 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
492
493 float offset = (1 - r) * mMaxPerspectiveShift;
494 float scaledSize = scale * mBaselineIconSize;
495 float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
496
497 // We want to imagine our coordinates from the bottom left, growing up and to the
498 // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
499 float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
500 float transX = offset + scaleOffsetCorrection;
501 float totalScale = mBaselineIconScale * scale;
502 final int overlayAlpha = (int) (80 * (1 - r));
503
504 if (params == null) {
505 params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
506 } else {
507 params.transX = transX;
508 params.transY = transY;
509 params.scale = totalScale;
510 params.overlayAlpha = overlayAlpha;
511 }
512 return params;
513 }
514
515 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
516 canvas.save();
517 canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY);
518 canvas.scale(params.scale, params.scale);
519 Drawable d = params.drawable;
520
521 if (d != null) {
522 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
523 d.setFilterBitmap(true);
524 d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
525 d.draw(canvas);
526 d.clearColorFilter();
527 d.setFilterBitmap(false);
528 }
529 canvas.restore();
530 }
531
Adam Cohena9cf38f2011-05-02 15:36:58 -0700532 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700533 protected void dispatchDraw(Canvas canvas) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700534 super.dispatchDraw(canvas);
535
Adam Cohena9cf38f2011-05-02 15:36:58 -0700536 if (mFolder == null) return;
Adam Cohend0445262011-07-04 23:53:22 -0700537 if (mFolder.getItemCount() == 0 && !mAnimating) return;
Adam Cohena9cf38f2011-05-02 15:36:58 -0700538
Adam Cohen76078c42011-06-09 15:06:52 -0700539 ArrayList<View> items = mFolder.getItemsInReadingOrder(false);
Adam Cohend0445262011-07-04 23:53:22 -0700540 Drawable d;
541 TextView v;
Adam Cohenbadf71e2011-05-26 19:08:29 -0700542
Adam Cohend0445262011-07-04 23:53:22 -0700543 // Update our drawing parameters if necessary
544 if (mAnimating) {
545 computePreviewDrawingParams(mAnimParams.drawable);
546 } else {
547 v = (TextView) items.get(0);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700548 d = v.getCompoundDrawables()[1];
Adam Cohend0445262011-07-04 23:53:22 -0700549 computePreviewDrawingParams(d);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700550 }
Adam Cohend0445262011-07-04 23:53:22 -0700551
552 int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
553 if (!mAnimating) {
554 for (int i = nItemsInPreview - 1; i >= 0; i--) {
555 v = (TextView) items.get(i);
Adam Cohenfb91f302012-06-11 15:45:18 -0700556 if (!mHiddenItems.contains(v.getTag())) {
557 d = v.getCompoundDrawables()[1];
558 mParams = computePreviewItemDrawingParams(i, mParams);
559 mParams.drawable = d;
560 drawPreviewItem(canvas, mParams);
561 }
Adam Cohend0445262011-07-04 23:53:22 -0700562 }
563 } else {
564 drawPreviewItem(canvas, mAnimParams);
565 }
566 }
567
Adam Cohenfb91f302012-06-11 15:45:18 -0700568 private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
569 final Runnable onCompleteRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700570 final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
571
572 final float scale0 = 1.0f;
Adam Cohen1d4ee4e2011-08-12 15:51:59 -0700573 final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2;
574 final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2;
Adam Cohend0445262011-07-04 23:53:22 -0700575 mAnimParams.drawable = d;
576
577 ValueAnimator va = ValueAnimator.ofFloat(0f, 1.0f);
578 va.addUpdateListener(new AnimatorUpdateListener(){
579 public void onAnimationUpdate(ValueAnimator animation) {
580 float progress = (Float) animation.getAnimatedValue();
Adam Cohenfb91f302012-06-11 15:45:18 -0700581 if (reverse) {
582 progress = 1 - progress;
583 mPreviewBackground.setAlpha(progress);
584 }
Adam Cohend0445262011-07-04 23:53:22 -0700585
586 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0);
587 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0);
588 mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0);
589 invalidate();
590 }
591 });
592 va.addListener(new AnimatorListenerAdapter() {
593 @Override
594 public void onAnimationStart(Animator animation) {
595 mAnimating = true;
596 }
597 @Override
598 public void onAnimationEnd(Animator animation) {
599 mAnimating = false;
Adam Cohenfb91f302012-06-11 15:45:18 -0700600 if (onCompleteRunnable != null) {
601 onCompleteRunnable.run();
602 }
Adam Cohend0445262011-07-04 23:53:22 -0700603 }
604 });
605 va.setDuration(duration);
606 va.start();
Adam Cohena9cf38f2011-05-02 15:36:58 -0700607 }
608
Adam Cohen099f60d2011-08-23 21:07:26 -0700609 public void setTextVisible(boolean visible) {
610 if (visible) {
611 mFolderName.setVisibility(VISIBLE);
612 } else {
613 mFolderName.setVisibility(INVISIBLE);
614 }
615 }
616
617 public boolean getTextVisible() {
618 return mFolderName.getVisibility() == VISIBLE;
619 }
620
Adam Cohen76078c42011-06-09 15:06:52 -0700621 public void onItemsChanged() {
622 invalidate();
623 requestLayout();
624 }
625
Adam Cohena9cf38f2011-05-02 15:36:58 -0700626 public void onAdd(ShortcutInfo item) {
627 invalidate();
628 requestLayout();
629 }
630
631 public void onRemove(ShortcutInfo item) {
632 invalidate();
633 requestLayout();
634 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700635
636 public void onTitleChanged(CharSequence title) {
Adam Cohend63cfa92011-06-24 14:17:17 -0700637 mFolderName.setText(title.toString());
Michael Jurka8b805b12012-04-18 14:23:14 -0700638 setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
Adam Cohen3371da02011-10-25 21:38:29 -0700639 title));
Adam Cohen76fc0852011-06-17 13:26:23 -0700640 }
Winson Chung88f33452012-02-23 15:23:44 -0800641
642 @Override
643 public boolean onTouchEvent(MotionEvent event) {
644 // Call the superclass onTouchEvent first, because sometimes it changes the state to
645 // isPressed() on an ACTION_UP
646 boolean result = super.onTouchEvent(event);
647
648 switch (event.getAction()) {
649 case MotionEvent.ACTION_DOWN:
650 mLongPressHelper.postCheckForLongPress();
651 break;
652 case MotionEvent.ACTION_CANCEL:
653 case MotionEvent.ACTION_UP:
654 mLongPressHelper.cancelLongPress();
655 break;
656 }
657 return result;
658 }
659
660 @Override
661 public void cancelLongPress() {
662 super.cancelLongPress();
663
664 mLongPressHelper.cancelLongPress();
665 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800666}