blob: d795c5628e321671ee6e0a1a4e361e376333ca35 [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
Sunny Goyal26119432016-02-18 22:09:23 +000017package com.android.launcher3.folder;
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;
Sunny Goyal740ac7f2016-09-28 16:47:32 -070021import android.animation.ObjectAnimator;
Adam Cohen2801caf2011-05-13 20:57:39 -070022import android.animation.ValueAnimator;
23import android.animation.ValueAnimator.AnimatorUpdateListener;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.content.Context;
Adam Cohena9cf38f2011-05-02 15:36:58 -070025import android.graphics.Canvas;
Adam Cohenf4b08912011-05-17 18:45:47 -070026import android.graphics.Color;
Sunny Goyal19b93b72017-02-19 20:21:37 -080027import android.graphics.Matrix;
Adam Cohenefca0272016-02-24 19:19:06 -080028import android.graphics.Paint;
Adam Cohen119e8982016-02-05 14:47:50 -080029import android.graphics.Path;
Tony Wickham1237df02017-02-24 08:59:36 -080030import android.graphics.Point;
Adam Cohenbadf71e2011-05-26 19:08:29 -070031import android.graphics.PorterDuff;
Sunny Goyal19b93b72017-02-19 20:21:37 -080032import android.graphics.PorterDuffXfermode;
33import android.graphics.RadialGradient;
Adam Cohen3e8f8112011-07-02 18:03:00 -070034import android.graphics.Rect;
Adam Cohenefca0272016-02-24 19:19:06 -080035import android.graphics.Region;
Sunny Goyal19b93b72017-02-19 20:21:37 -080036import android.graphics.Shader;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.graphics.drawable.Drawable;
Adam Cohen099f60d2011-08-23 21:07:26 -070038import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.util.AttributeSet;
Adam Cohenefca0272016-02-24 19:19:06 -080040import android.util.DisplayMetrics;
Tony Wickham1e618492017-02-02 12:57:18 -080041import android.util.Property;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042import android.view.LayoutInflater;
Winson Chung88f33452012-02-23 15:23:44 -080043import android.view.MotionEvent;
Adam Cohen7c693212011-05-18 15:26:57 -070044import android.view.View;
Jason Monk02dd7ae2014-04-15 15:23:31 -040045import android.view.ViewConfiguration;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046import android.view.ViewGroup;
Adam Cohen3e8f8112011-07-02 18:03:00 -070047import android.view.animation.AccelerateInterpolator;
48import android.view.animation.DecelerateInterpolator;
Winson Chung6e1c0d32013-10-25 15:24:24 -070049import android.widget.FrameLayout;
Adam Cohena9cf38f2011-05-02 15:36:58 -070050import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051
Sunny Goyal26119432016-02-18 22:09:23 +000052import com.android.launcher3.Alarm;
53import com.android.launcher3.AppInfo;
54import com.android.launcher3.BubbleTextView;
55import com.android.launcher3.CellLayout;
56import com.android.launcher3.CheckLongPressHelper;
57import com.android.launcher3.DeviceProfile;
Daniel Sandler325dc232013-06-05 22:57:57 -040058import com.android.launcher3.DropTarget.DragObject;
Sunny Goyal26119432016-02-18 22:09:23 +000059import com.android.launcher3.FastBitmapDrawable;
60import com.android.launcher3.FolderInfo;
Daniel Sandler325dc232013-06-05 22:57:57 -040061import com.android.launcher3.FolderInfo.FolderListener;
Sunny Goyal26119432016-02-18 22:09:23 +000062import com.android.launcher3.ItemInfo;
63import com.android.launcher3.Launcher;
64import com.android.launcher3.LauncherAnimUtils;
Sunny Goyal26119432016-02-18 22:09:23 +000065import com.android.launcher3.LauncherSettings;
66import com.android.launcher3.OnAlarmListener;
Sunny Goyal26119432016-02-18 22:09:23 +000067import com.android.launcher3.R;
68import com.android.launcher3.ShortcutInfo;
69import com.android.launcher3.SimpleOnStylusPressListener;
70import com.android.launcher3.StylusEventHelper;
71import com.android.launcher3.Utilities;
72import com.android.launcher3.Workspace;
Tony Wickham11ba5072017-02-02 12:42:50 -080073import com.android.launcher3.badge.BadgeRenderer;
74import com.android.launcher3.badge.FolderBadgeInfo;
Adam Cohen119e8982016-02-05 14:47:50 -080075import com.android.launcher3.config.FeatureFlags;
Vadim Tryshevfedca432015-08-19 17:55:02 -070076import com.android.launcher3.dragndrop.DragLayer;
77import com.android.launcher3.dragndrop.DragView;
Adam Cohen091440a2015-03-18 14:16:05 -070078import com.android.launcher3.util.Thunk;
Jon Mirandac476d6e2017-05-04 16:30:01 -070079import com.android.launcher3.widget.PendingAddShortcutInfo;
Romain Guyedcce092010-03-04 13:03:17 -080080
Adam Cohenc0dcf592011-06-01 15:30:43 -070081import java.util.ArrayList;
Jon Miranda12b616c2017-02-06 15:45:53 -080082import java.util.List;
Adam Cohenc0dcf592011-06-01 15:30:43 -070083
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084/**
Adam Cohenf9c184a2016-01-15 16:47:43 -080085 * An icon that can appear on in the workspace representing an {@link Folder}.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080086 */
Winson Chung6e1c0d32013-10-25 15:24:24 -070087public class FolderIcon extends FrameLayout implements FolderListener {
Adam Cohenefca0272016-02-24 19:19:06 -080088 @Thunk Launcher mLauncher;
Adam Cohen091440a2015-03-18 14:16:05 -070089 @Thunk Folder mFolder;
Adam Cohenfb91f302012-06-11 15:45:18 -070090 private FolderInfo mInfo;
Adam Cohen091440a2015-03-18 14:16:05 -070091 @Thunk static boolean sStaticValuesDirty = true;
Adam Cohena9cf38f2011-05-02 15:36:58 -070092
Adam Cohenefca0272016-02-24 19:19:06 -080093 public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
94 StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW :
95 ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
Adam Cohenf9c184a2016-01-15 16:47:43 -080096
Winson Chung88f33452012-02-23 15:23:44 -080097 private CheckLongPressHelper mLongPressHelper;
Mady Melloref044dd2015-06-02 15:35:07 -070098 private StylusEventHelper mStylusEventHelper;
Winson Chung88f33452012-02-23 15:23:44 -080099
Adam Cohenbadf71e2011-05-26 19:08:29 -0700100 // The number of icons to display in the
Adam Cohenf4b08912011-05-17 18:45:47 -0700101 private static final int CONSUMPTION_ANIMATION_DURATION = 100;
Adam Cohend0445262011-07-04 23:53:22 -0700102 private static final int DROP_IN_ANIMATION_DURATION = 400;
Adam Cohen8dfcba42011-07-07 16:38:18 -0700103 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
Adam Cohenfb91f302012-06-11 15:45:18 -0700104 private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
Adam Cohenbadf71e2011-05-26 19:08:29 -0700105
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800106 // Flag whether the folder should open itself when an item is dragged over is enabled.
107 public static final boolean SPRING_LOADING_ENABLED = true;
108
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800109 // Delay when drag enters until the folder opens, in miliseconds.
110 private static final int ON_OPEN_DELAY = 800;
111
Adam Cohenefca0272016-02-24 19:19:06 -0800112 @Thunk BubbleTextView mFolderName;
Adam Cohen2801caf2011-05-13 20:57:39 -0700113
Adam Cohend0445262011-07-04 23:53:22 -0700114 // These variables are all associated with the drawing of the preview; they are stored
115 // as member variables for shared usage and to avoid computation on each frame
Adam Cohenaa2542a2016-03-21 13:40:02 -0700116 private int mIntrinsicIconSize = -1;
117 private int mTotalWidth = -1;
118 private int mPrevTopPadding = -1;
119
Adam Cohenefca0272016-02-24 19:19:06 -0800120 PreviewBackground mBackground = new PreviewBackground();
Adam Cohenf9c184a2016-01-15 16:47:43 -0800121
122 private PreviewLayoutRule mPreviewLayoutRule;
123
Adam Cohend0445262011-07-04 23:53:22 -0700124 boolean mAnimating = false;
Tony Wickham1237df02017-02-24 08:59:36 -0800125 private Rect mTempBounds = new Rect();
Adam Cohenfb91f302012-06-11 15:45:18 -0700126
Jason Monk02dd7ae2014-04-15 15:23:31 -0400127 private float mSlop;
128
Jon Miranda655ec422017-02-07 11:40:22 -0800129 FolderIconPreviewVerifier mPreviewVerifier;
Adam Cohen4f8071b2016-02-13 16:06:05 -0800130 private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
131 private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
132 private Drawable mReferenceDrawable = null;
Adam Cohend0445262011-07-04 23:53:22 -0700133
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800134 private Alarm mOpenAlarm = new Alarm();
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800135
Tony Wickham11ba5072017-02-02 12:42:50 -0800136 private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
137 private BadgeRenderer mBadgeRenderer;
Tony Wickham1e618492017-02-02 12:57:18 -0800138 private float mBadgeScale;
Tony Wickham1237df02017-02-24 08:59:36 -0800139 private Point mTempSpaceForBadgeOffset = new Point();
Tony Wickham1e618492017-02-02 12:57:18 -0800140
141 private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
142 = new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
143 @Override
144 public Float get(FolderIcon folderIcon) {
145 return folderIcon.mBadgeScale;
146 }
147
148 @Override
149 public void set(FolderIcon folderIcon, Float value) {
150 folderIcon.mBadgeScale = value;
151 folderIcon.invalidate();
152 }
153 };
Tony Wickham11ba5072017-02-02 12:42:50 -0800154
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800155 public FolderIcon(Context context, AttributeSet attrs) {
156 super(context, attrs);
Winson Chung88f33452012-02-23 15:23:44 -0800157 init();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158 }
159
160 public FolderIcon(Context context) {
161 super(context);
Winson Chung88f33452012-02-23 15:23:44 -0800162 init();
163 }
164
165 private void init() {
166 mLongPressHelper = new CheckLongPressHelper(this);
Mady Mellorbb835202015-07-15 16:34:34 -0700167 mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
Adam Cohenefca0272016-02-24 19:19:06 -0800168 mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
169 new StackFolderIconLayoutRule() :
170 new ClippedFolderIconLayoutRule();
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700171 mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
Michael Jurka0280c3b2010-09-17 15:00:07 -0700172 }
173
Adam Cohenf9c184a2016-01-15 16:47:43 -0800174 public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
Sunny Goyal1cd01b02016-11-09 10:43:58 -0800175 FolderInfo folderInfo) {
Michael Jurka3a9fced2012-04-13 14:44:29 -0700176 @SuppressWarnings("all") // suppress dead code warning
177 final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
178 if (error) {
Adam Cohend0445262011-07-04 23:53:22 -0700179 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
180 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
181 "is dependent on this");
182 }
Adam Cohen2e6da152015-05-06 11:42:25 -0700183
184 DeviceProfile grid = launcher.getDeviceProfile();
Sunny Goyal9b29ca52017-02-17 10:39:44 -0800185 FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
186 .inflate(resId, group, false);
Adam Cohenefca0272016-02-24 19:19:06 -0800187
Winson Chung5f8afe62013-08-12 16:19:28 -0700188 icon.setClipToPadding(false);
Adam Cohend2eca6b2011-07-26 22:49:13 -0700189 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
Adam Cohen76fc0852011-06-17 13:26:23 -0700190 icon.mFolderName.setText(folderInfo.title);
Winson Chung6e1c0d32013-10-25 15:24:24 -0700191 icon.mFolderName.setCompoundDrawablePadding(0);
192 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
193 lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
194
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800195 icon.setTag(folderInfo);
196 icon.setOnClickListener(launcher);
197 icon.mInfo = folderInfo;
198 icon.mLauncher = launcher;
Tony Wickham11ba5072017-02-02 12:42:50 -0800199 icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer;
Sunny Goyalf4f89ef2015-09-02 15:06:12 -0700200 icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
Adam Cohena9cf38f2011-05-02 15:36:58 -0700201 Folder folder = Folder.fromXml(launcher);
202 folder.setDragController(launcher.getDragController());
Adam Cohen2801caf2011-05-13 20:57:39 -0700203 folder.setFolderIcon(icon);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700204 folder.bind(folderInfo);
Adam Cohen4f8071b2016-02-13 16:06:05 -0800205 icon.setFolder(folder);
Sunny Goyalae502842016-06-17 08:43:56 -0700206 icon.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
Sunny Goyale48644a2016-05-16 16:16:14 -0700207
Sunny Goyal90cb3e52016-05-17 21:25:48 +0000208 folderInfo.addListener(icon);
209
210 icon.setOnFocusChangeListener(launcher.mFocusHandler);
Adam Cohen19072da2011-05-31 14:30:45 -0700211 return icon;
212 }
213
Adam Cohen099f60d2011-08-23 21:07:26 -0700214 @Override
215 protected Parcelable onSaveInstanceState() {
216 sStaticValuesDirty = true;
217 return super.onSaveInstanceState();
218 }
219
Anjali Koppalf05545f2014-03-10 19:18:43 -0700220 public Folder getFolder() {
Adam Cohenfb91f302012-06-11 15:45:18 -0700221 return mFolder;
222 }
223
Adam Cohen4f8071b2016-02-13 16:06:05 -0800224 private void setFolder(Folder folder) {
225 mFolder = folder;
Jon Miranda655ec422017-02-07 11:40:22 -0800226 mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
Adam Cohen4f8071b2016-02-13 16:06:05 -0800227 updateItemDrawingParams(false);
228 }
229
Adam Cohen073a46f2011-05-17 16:28:09 -0700230 private boolean willAcceptItem(ItemInfo item) {
231 final int itemType = item.itemType;
232 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
Tony Wickhambfbf7f92016-05-19 11:19:39 -0700233 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
234 itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700235 !mFolder.isFull() && item != mInfo && !mFolder.isOpen());
Adam Cohen073a46f2011-05-17 16:28:09 -0700236 }
237
Sunny Goyalaa8ef112015-06-12 20:04:41 -0700238 public boolean acceptDrop(ItemInfo dragInfo) {
239 final ItemInfo item = dragInfo;
Adam Cohenfb91f302012-06-11 15:45:18 -0700240 return !mFolder.isDestroyed() && willAcceptItem(item);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800241 }
242
Adam Cohendf035382011-04-11 17:22:04 -0700243 public void addItem(ShortcutInfo item) {
Sunny Goyalc52ba712016-04-05 15:59:05 -0700244 mInfo.add(item, true);
Adam Cohendf035382011-04-11 17:22:04 -0700245 }
246
Sunny Goyalaa8ef112015-06-12 20:04:41 -0700247 public void onDragEnter(ItemInfo dragInfo) {
248 if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
Adam Cohen69ce2e52011-07-03 19:25:21 -0700249 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
Adam Cohenefca0272016-02-24 19:19:06 -0800250 CellLayout cl = (CellLayout) getParent().getParent();
251
252 mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800253 mOpenAlarm.setOnAlarmListener(mOnOpenListener);
Adam Cohendedfca22014-05-19 15:10:39 -0700254 if (SPRING_LOADING_ENABLED &&
Jon Mirandac476d6e2017-05-04 16:30:01 -0700255 ((dragInfo instanceof AppInfo)
256 || (dragInfo instanceof ShortcutInfo)
257 || (dragInfo instanceof PendingAddShortcutInfo))) {
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800258 mOpenAlarm.setAlarm(ON_OPEN_DELAY);
259 }
Adam Cohen073a46f2011-05-17 16:28:09 -0700260 }
261
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800262 OnAlarmListener mOnOpenListener = new OnAlarmListener() {
263 public void onAlarm(Alarm alarm) {
Sunny Goyale393d3a2016-09-09 12:42:10 -0700264 mFolder.beginExternalDrag();
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700265 mFolder.animateOpen();
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800266 }
267 };
268
Winson8f288aa2016-07-25 16:49:15 -0700269 public Drawable prepareCreate(final View destView) {
Sunny Goyal96ac68a2017-02-02 16:37:21 -0800270 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
Winson8f288aa2016-07-25 16:49:15 -0700271 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
272 destView.getMeasuredWidth());
273 return animateDrawable;
274 }
275
Adam Cohend0445262011-07-04 23:53:22 -0700276 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800277 final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
Adam Cohenac8c8762011-07-13 11:15:27 -0700278 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700279
Adam Cohenfb91f302012-06-11 15:45:18 -0700280 // These correspond two the drawable and view that the icon was dropped _onto_
Winson8f288aa2016-07-25 16:49:15 -0700281 Drawable animateDrawable = prepareCreate(destView);
Adam Cohend0445262011-07-04 23:53:22 -0700282
Adam Cohen4f8071b2016-02-13 16:06:05 -0800283 mReferenceDrawable = animateDrawable;
284
285 addItem(destInfo);
Adam Cohend0445262011-07-04 23:53:22 -0700286 // This will animate the first item from it's position as an icon into its
287 // position as the first item in the preview
Adam Cohenfb91f302012-06-11 15:45:18 -0700288 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
Adam Cohenfb91f302012-06-11 15:45:18 -0700289
290 // This will animate the dragView (srcView) into the new folder
Sunny Goyale393d3a2016-09-09 12:42:10 -0700291 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
Adam Cohenfb91f302012-06-11 15:45:18 -0700292 }
293
294 public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
Sunny Goyal96ac68a2017-02-02 16:37:21 -0800295 Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
Jason Monk02dd7ae2014-04-15 15:23:31 -0400296 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
Adam Cohenfb91f302012-06-11 15:45:18 -0700297 finalView.getMeasuredWidth());
298
299 // This will animate the first item from it's position as an icon into its
300 // position as the first item in the preview
301 animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
302 onCompleteRunnable);
Adam Cohend0445262011-07-04 23:53:22 -0700303 }
304
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700305 public void onDragExit() {
Adam Cohenefca0272016-02-24 19:19:06 -0800306 mBackground.animateToRest();
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800307 mOpenAlarm.cancelAlarm();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800308 }
Patrick Dubroy440c3602010-07-13 17:50:32 -0700309
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800310 private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
Sunny Goyale393d3a2016-09-09 12:42:10 -0700311 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
Adam Cohend0445262011-07-04 23:53:22 -0700312 item.cellX = -1;
313 item.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -0700314
Adam Cohen558baaf2011-08-15 15:22:57 -0700315 // Typically, the animateView corresponds to the DragView; however, if this is being done
316 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
317 // will not have a view to animate
318 if (animateView != null) {
319 DragLayer dragLayer = mLauncher.getDragLayer();
320 Rect from = new Rect();
321 dragLayer.getViewRectRelativeToSelf(animateView, from);
322 Rect to = finalRect;
323 if (to == null) {
324 to = new Rect();
325 Workspace workspace = mLauncher.getWorkspace();
326 // Set cellLayout and this to it's final state to compute final animation locations
327 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
328 float scaleX = getScaleX();
329 float scaleY = getScaleY();
330 setScaleX(1.0f);
331 setScaleY(1.0f);
332 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
333 // Finished computing final animation locations, restore current state
334 setScaleX(scaleX);
335 setScaleY(scaleY);
336 workspace.resetTransitionTransform((CellLayout) getParent().getParent());
Adam Cohend0445262011-07-04 23:53:22 -0700337 }
Adam Cohen558baaf2011-08-15 15:22:57 -0700338
339 int[] center = new int[2];
Adam Cohen119e8982016-02-05 14:47:50 -0800340 float scale = getLocalCenterForIndex(index, index + 1, center);
Adam Cohen558baaf2011-08-15 15:22:57 -0700341 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
342 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
343
344 to.offset(center[0] - animateView.getMeasuredWidth() / 2,
Winson Chung5f8afe62013-08-12 16:19:28 -0700345 center[1] - animateView.getMeasuredHeight() / 2);
Adam Cohen558baaf2011-08-15 15:22:57 -0700346
Jon Miranda12b616c2017-02-06 15:45:53 -0800347 float finalAlpha = index < mPreviewLayoutRule.maxNumItems() ? 0.5f : 0f;
Adam Cohen558baaf2011-08-15 15:22:57 -0700348
Adam Cohened66b2b2012-01-23 17:28:51 -0800349 float finalScale = scale * scaleRelativeToDragLayer;
Adam Cohen558baaf2011-08-15 15:22:57 -0700350 dragLayer.animateView(animateView, from, to, finalAlpha,
Adam Cohened66b2b2012-01-23 17:28:51 -0800351 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
Adam Cohen558baaf2011-08-15 15:22:57 -0700352 new DecelerateInterpolator(2), new AccelerateInterpolator(2),
Adam Cohened66b2b2012-01-23 17:28:51 -0800353 postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
Adam Cohenfb91f302012-06-11 15:45:18 -0700354 addItem(item);
Adam Cohen7a8b82b2013-05-29 18:41:50 -0700355 mFolder.hideItem(item);
Adam Cohen4f8071b2016-02-13 16:06:05 -0800356
357 final PreviewItemDrawingParams params = index < mDrawingParams.size() ?
358 mDrawingParams.get(index) : null;
359 if (params != null) params.hidden = true;
Adam Cohen558baaf2011-08-15 15:22:57 -0700360 postDelayed(new Runnable() {
361 public void run() {
Adam Cohen4f8071b2016-02-13 16:06:05 -0800362 if (params != null) params.hidden = false;
Adam Cohen7a8b82b2013-05-29 18:41:50 -0700363 mFolder.showItem(item);
Adam Cohenfb91f302012-06-11 15:45:18 -0700364 invalidate();
Adam Cohen558baaf2011-08-15 15:22:57 -0700365 }
366 }, DROP_IN_ANIMATION_DURATION);
367 } else {
368 addItem(item);
369 }
Adam Cohend0445262011-07-04 23:53:22 -0700370 }
371
Adam Cohen3e8f8112011-07-02 18:03:00 -0700372 public void onDrop(DragObject d) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700373 ShortcutInfo item;
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200374 if (d.dragInfo instanceof AppInfo) {
Adam Cohenc0dcf592011-06-01 15:30:43 -0700375 // Came from all apps -- make a copy
Michael Jurkaeadbfc52013-09-04 00:45:37 +0200376 item = ((AppInfo) d.dragInfo).makeShortcut();
Adam Cohenc0dcf592011-06-01 15:30:43 -0700377 } else {
Adam Cohen3e8f8112011-07-02 18:03:00 -0700378 item = (ShortcutInfo) d.dragInfo;
Adam Cohenc0dcf592011-06-01 15:30:43 -0700379 }
Adam Cohen67bd9cc2011-07-29 14:07:04 -0700380 mFolder.notifyDrop();
Sunny Goyale393d3a2016-09-09 12:42:10 -0700381 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
Adam Cohenc0dcf592011-06-01 15:30:43 -0700382 }
383
Adam Cohend0445262011-07-04 23:53:22 -0700384 private void computePreviewDrawingParams(int drawableSize, int totalSize) {
Adam Cohenaa2542a2016-03-21 13:40:02 -0700385 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize ||
386 mPrevTopPadding != getPaddingTop()) {
Adam Cohen2e6da152015-05-06 11:42:25 -0700387 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chung5f8afe62013-08-12 16:19:28 -0700388
Adam Cohend0445262011-07-04 23:53:22 -0700389 mIntrinsicIconSize = drawableSize;
390 mTotalWidth = totalSize;
Adam Cohenaa2542a2016-03-21 13:40:02 -0700391 mPrevTopPadding = getPaddingTop();
Adam Cohend0445262011-07-04 23:53:22 -0700392
Adam Cohenefca0272016-02-24 19:19:06 -0800393 mBackground.setup(getResources().getDisplayMetrics(), grid, this, mTotalWidth,
394 getPaddingTop());
395 mPreviewLayoutRule.init(mBackground.previewSize, mIntrinsicIconSize,
Adam Cohen119e8982016-02-05 14:47:50 -0800396 Utilities.isRtl(getResources()));
Adam Cohenefca0272016-02-24 19:19:06 -0800397
Adam Cohen4f8071b2016-02-13 16:06:05 -0800398 updateItemDrawingParams(false);
Adam Cohend0445262011-07-04 23:53:22 -0700399 }
400 }
401
402 private void computePreviewDrawingParams(Drawable d) {
403 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
404 }
405
Tony Wickham11ba5072017-02-02 12:42:50 -0800406 public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
Tony Wickham0530e8c2017-04-26 18:13:56 -0700407 updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge());
Tony Wickham11ba5072017-02-02 12:42:50 -0800408 mBadgeInfo = badgeInfo;
Tony Wickham1e618492017-02-02 12:57:18 -0800409 }
410
Jon Miranda47170112017-03-03 14:57:14 -0800411 public PreviewLayoutRule getLayoutRule() {
412 return mPreviewLayoutRule;
413 }
414
Tony Wickham1e618492017-02-02 12:57:18 -0800415 /**
Tony Wickham0530e8c2017-04-26 18:13:56 -0700416 * Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false
Tony Wickham1e618492017-02-02 12:57:18 -0800417 * (the badge is being added or removed).
418 */
Tony Wickham0530e8c2017-04-26 18:13:56 -0700419 private void updateBadgeScale(boolean wasBadged, boolean isBadged) {
Tony Wickham1e618492017-02-02 12:57:18 -0800420 float newBadgeScale = isBadged ? 1f : 0f;
421 // Animate when a badge is first added or when it is removed.
422 if ((wasBadged ^ isBadged) && isShown()) {
423 ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
424 } else {
425 mBadgeScale = newBadgeScale;
426 invalidate();
427 }
Tony Wickham11ba5072017-02-02 12:42:50 -0800428 }
429
Sunny Goyal26119432016-02-18 22:09:23 +0000430 static class PreviewItemDrawingParams {
431 PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
Adam Cohend0445262011-07-04 23:53:22 -0700432 this.transX = transX;
433 this.transY = transY;
434 this.scale = scale;
435 this.overlayAlpha = overlayAlpha;
436 }
Adam Cohen4f8071b2016-02-13 16:06:05 -0800437
438 public void update(float transX, float transY, float scale) {
439 // We ensure the update will not interfere with an animation on the layout params
440 // If the final values differ, we cancel the animation.
441 if (anim != null) {
442 if (anim.finalTransX == transX || anim.finalTransY == transY
443 || anim.finalScale == scale) {
444 return;
445 }
446 anim.cancel();
447 }
448
449 this.transX = transX;
450 this.transY = transY;
451 this.scale = scale;
452 }
453
Sunny Goyal26119432016-02-18 22:09:23 +0000454 float transX;
455 float transY;
456 float scale;
Adam Cohen4f8071b2016-02-13 16:06:05 -0800457 public float overlayAlpha;
458 boolean hidden;
459 FolderPreviewItemAnim anim;
Sunny Goyal26119432016-02-18 22:09:23 +0000460 Drawable drawable;
Adam Cohend0445262011-07-04 23:53:22 -0700461 }
462
Adam Cohen119e8982016-02-05 14:47:50 -0800463 private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
Jon Miranda12b616c2017-02-06 15:45:53 -0800464 mTmpParams = computePreviewItemDrawingParams(
465 Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams);
Adam Cohend0445262011-07-04 23:53:22 -0700466
Adam Cohenefca0272016-02-24 19:19:06 -0800467 mTmpParams.transX += mBackground.basePreviewOffsetX;
468 mTmpParams.transY += mBackground.basePreviewOffsetY;
Adam Cohen4f8071b2016-02-13 16:06:05 -0800469 float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
470 float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
Adam Cohend0445262011-07-04 23:53:22 -0700471
Adam Cohenac8c8762011-07-13 11:15:27 -0700472 center[0] = (int) Math.round(offsetX);
473 center[1] = (int) Math.round(offsetY);
Adam Cohen4f8071b2016-02-13 16:06:05 -0800474 return mTmpParams.scale;
Adam Cohend0445262011-07-04 23:53:22 -0700475 }
476
Adam Cohen119e8982016-02-05 14:47:50 -0800477 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
Adam Cohend0445262011-07-04 23:53:22 -0700478 PreviewItemDrawingParams params) {
Adam Cohen4f8071b2016-02-13 16:06:05 -0800479 // We use an index of -1 to represent an icon on the workspace for the destroy and
480 // create animations
481 if (index == -1) {
482 return getFinalIconParams(params);
483 }
Adam Cohen119e8982016-02-05 14:47:50 -0800484 return mPreviewLayoutRule.computePreviewItemDrawingParams(index, curNumItems, params);
Adam Cohend0445262011-07-04 23:53:22 -0700485 }
486
Adam Cohen4f8071b2016-02-13 16:06:05 -0800487 private PreviewItemDrawingParams getFinalIconParams(PreviewItemDrawingParams params) {
488 float iconSize = mLauncher.getDeviceProfile().iconSizePx;
489
490 final float scale = iconSize / mReferenceDrawable.getIntrinsicWidth();
Adam Cohenefca0272016-02-24 19:19:06 -0800491 final float trans = (mBackground.previewSize - iconSize) / 2;
Adam Cohen4f8071b2016-02-13 16:06:05 -0800492
493 params.update(trans, trans, scale);
494 return params;
495 }
496
Adam Cohend0445262011-07-04 23:53:22 -0700497 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
Sunny Goyal19b93b72017-02-19 20:21:37 -0800498 canvas.save(Canvas.MATRIX_SAVE_FLAG);
Adam Cohen119e8982016-02-05 14:47:50 -0800499 canvas.translate(params.transX, params.transY);
Adam Cohend0445262011-07-04 23:53:22 -0700500 canvas.scale(params.scale, params.scale);
501 Drawable d = params.drawable;
502
503 if (d != null) {
Tony Wickham1237df02017-02-24 08:59:36 -0800504 mTempBounds.set(d.getBounds());
Adam Cohend0445262011-07-04 23:53:22 -0700505 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
Sunny Goyalc5c60ad2014-07-14 12:02:01 -0700506 if (d instanceof FastBitmapDrawable) {
507 FastBitmapDrawable fd = (FastBitmapDrawable) d;
Sunny Goyal55cb70b2016-11-12 09:58:29 -0800508 fd.drawWithBrightness(canvas, params.overlayAlpha);
Sunny Goyalc5c60ad2014-07-14 12:02:01 -0700509 } else {
Winsonc0880492015-08-21 11:16:27 -0700510 d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
Sunny Goyalc5c60ad2014-07-14 12:02:01 -0700511 PorterDuff.Mode.SRC_ATOP);
512 d.draw(canvas);
513 d.clearColorFilter();
514 }
Tony Wickham1237df02017-02-24 08:59:36 -0800515 d.setBounds(mTempBounds);
Adam Cohend0445262011-07-04 23:53:22 -0700516 }
517 canvas.restore();
518 }
519
Adam Cohenf172b742016-03-30 19:28:34 -0700520 /**
521 * This object represents a FolderIcon preview background. It stores drawing / measurement
522 * information, handles drawing, and animation (accept state <--> rest state).
523 */
Adam Cohenefca0272016-02-24 19:19:06 -0800524 public static class PreviewBackground {
Sunny Goyal19b93b72017-02-19 20:21:37 -0800525
526 private final PorterDuffXfermode mClipPorterDuffXfermode
527 = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
528 // Create a RadialGradient such that it draws a black circle and then extends with
529 // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
530 // just at the edge quickly change it to transparent.
531 private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
532 new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
533 new float[] {0, 0.999f, 1},
534 Shader.TileMode.CLAMP);
535
536 private final PorterDuffXfermode mShadowPorterDuffXfermode
537 = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
538 private RadialGradient mShadowShader = null;
539
540 private final Matrix mShaderMatrix = new Matrix();
541 private final Path mPath = new Path();
542
543 private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
544
Adam Cohenefca0272016-02-24 19:19:06 -0800545 private float mScale = 1f;
546 private float mColorMultiplier = 1f;
Sunny Goyal19b93b72017-02-19 20:21:37 -0800547 private float mStrokeWidth;
Jon Mirandabdb54242017-04-06 14:53:52 -0700548 private int mStrokeAlpha = MAX_BG_OPACITY;
Adam Cohenf172b742016-03-30 19:28:34 -0700549 private View mInvalidateDelegate;
Adam Cohenefca0272016-02-24 19:19:06 -0800550
551 public int previewSize;
552 private int basePreviewOffsetX;
553 private int basePreviewOffsetY;
554
555 private CellLayout mDrawingDelegate;
556 public int delegateCellX;
557 public int delegateCellY;
558
Adam Cohenf172b742016-03-30 19:28:34 -0700559 // When the PreviewBackground is drawn under an icon (for creating a folder) the border
560 // should not occlude the icon
561 public boolean isClipping = true;
562
Adam Cohenefca0272016-02-24 19:19:06 -0800563 // Drawing / animation configurations
564 private static final float ACCEPT_SCALE_FACTOR = 1.25f;
565 private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
566
567 // Expressed on a scale from 0 to 255.
568 private static final int BG_OPACITY = 160;
569 private static final int MAX_BG_OPACITY = 225;
570 private static final int BG_INTENSITY = 245;
Sunny Goyal19b93b72017-02-19 20:21:37 -0800571 private static final int SHADOW_OPACITY = 40;
Adam Cohenefca0272016-02-24 19:19:06 -0800572
573 ValueAnimator mScaleAnimator;
Jon Mirandabdb54242017-04-06 14:53:52 -0700574 ObjectAnimator mStrokeAlphaAnimator;
575
576 private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
577 new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
578 @Override
579 public Integer get(PreviewBackground previewBackground) {
580 return previewBackground.mStrokeAlpha;
581 }
582
583 @Override
584 public void set(PreviewBackground previewBackground, Integer alpha) {
585 previewBackground.mStrokeAlpha = alpha;
586 previewBackground.invalidate();
587 }
588 };
Adam Cohenefca0272016-02-24 19:19:06 -0800589
Adam Cohenf172b742016-03-30 19:28:34 -0700590 public void setup(DisplayMetrics dm, DeviceProfile grid, View invalidateDelegate,
Adam Cohenefca0272016-02-24 19:19:06 -0800591 int availableSpace, int topPadding) {
Adam Cohenf172b742016-03-30 19:28:34 -0700592 mInvalidateDelegate = invalidateDelegate;
Adam Cohenefca0272016-02-24 19:19:06 -0800593
594 final int previewSize = grid.folderIconSizePx;
595 final int previewPadding = grid.folderIconPreviewPadding;
596
597 this.previewSize = (previewSize - 2 * previewPadding);
598
599 basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
600 basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
601
Sunny Goyal19b93b72017-02-19 20:21:37 -0800602 // Stroke width is 1dp
603 mStrokeWidth = dm.density;
604
605 float radius = getScaledRadius();
606 float shadowRadius = radius + mStrokeWidth;
607 int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
608 mShadowShader = new RadialGradient(0, 0, 1,
609 new int[] {shadowColor, Color.TRANSPARENT},
610 new float[] {radius / shadowRadius, 1},
611 Shader.TileMode.CLAMP);
Adam Cohenefca0272016-02-24 19:19:06 -0800612
613 invalidate();
614 }
615
616 int getRadius() {
617 return previewSize / 2;
618 }
619
620 int getScaledRadius() {
621 return (int) (mScale * getRadius());
622 }
623
624 int getOffsetX() {
625 return basePreviewOffsetX - (getScaledRadius() - getRadius());
626 }
627
628 int getOffsetY() {
629 return basePreviewOffsetY - (getScaledRadius() - getRadius());
630 }
631
Tony Wickham1e618492017-02-02 12:57:18 -0800632 /**
633 * Returns the progress of the scale animation, where 0 means the scale is at 1f
634 * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
635 */
636 float getScaleProgress() {
637 return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
638 }
639
Adam Cohenefca0272016-02-24 19:19:06 -0800640 void invalidate() {
Adam Cohenf172b742016-03-30 19:28:34 -0700641 if (mInvalidateDelegate != null) {
642 mInvalidateDelegate.invalidate();
Adam Cohenefca0272016-02-24 19:19:06 -0800643 }
644
645 if (mDrawingDelegate != null) {
646 mDrawingDelegate.invalidate();
647 }
648 }
649
Adam Cohenf172b742016-03-30 19:28:34 -0700650 void setInvalidateDelegate(View invalidateDelegate) {
651 mInvalidateDelegate = invalidateDelegate;
Adam Cohenefca0272016-02-24 19:19:06 -0800652 invalidate();
653 }
654
Sunny Goyal19b93b72017-02-19 20:21:37 -0800655 public void drawBackground(Canvas canvas) {
656 mPaint.setStyle(Paint.Style.FILL);
Adam Cohenefca0272016-02-24 19:19:06 -0800657 int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
Sunny Goyal19b93b72017-02-19 20:21:37 -0800658 mPaint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
Adam Cohenefca0272016-02-24 19:19:06 -0800659
Sunny Goyal19b93b72017-02-19 20:21:37 -0800660 drawCircle(canvas, 0 /* deltaRadius */);
661
662 // Draw shadow.
663 if (mShadowShader == null) {
664 return;
665 }
Adam Cohenefca0272016-02-24 19:19:06 -0800666 float radius = getScaledRadius();
Sunny Goyal19b93b72017-02-19 20:21:37 -0800667 float shadowRadius = radius + mStrokeWidth;
668 mPaint.setColor(Color.BLACK);
669 int offsetX = getOffsetX();
670 int offsetY = getOffsetY();
671 final int saveCount;
672 if (canvas.isHardwareAccelerated()) {
673 saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
674 offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
675 null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
Adam Cohenefca0272016-02-24 19:19:06 -0800676
Sunny Goyal19b93b72017-02-19 20:21:37 -0800677 } else {
678 saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
679 clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
680 }
Adam Cohenefca0272016-02-24 19:19:06 -0800681
Sunny Goyal19b93b72017-02-19 20:21:37 -0800682 mShaderMatrix.setScale(shadowRadius, shadowRadius);
683 mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
684 mShadowShader.setLocalMatrix(mShaderMatrix);
685 mPaint.setShader(mShadowShader);
686 canvas.drawPaint(mPaint);
687 mPaint.setShader(null);
Adam Cohenefca0272016-02-24 19:19:06 -0800688
Sunny Goyal19b93b72017-02-19 20:21:37 -0800689 if (canvas.isHardwareAccelerated()) {
690 mPaint.setXfermode(mShadowPorterDuffXfermode);
691 canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
692 mPaint.setXfermode(null);
693 }
694
695 canvas.restoreToCount(saveCount);
Adam Cohenefca0272016-02-24 19:19:06 -0800696 }
697
Jon Mirandabdb54242017-04-06 14:53:52 -0700698 public void animateBackgroundStroke() {
699 if (mStrokeAlphaAnimator != null) {
700 mStrokeAlphaAnimator.cancel();
701 }
702 mStrokeAlphaAnimator = ObjectAnimator
703 .ofArgb(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
704 .setDuration(100);
705 mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
706 @Override
707 public void onAnimationEnd(Animator animation) {
708 mStrokeAlphaAnimator = null;
709 }
710 });
711 mStrokeAlphaAnimator.start();
712 }
713
Sunny Goyal19b93b72017-02-19 20:21:37 -0800714 public void drawBackgroundStroke(Canvas canvas) {
Jon Mirandabdb54242017-04-06 14:53:52 -0700715 mPaint.setColor(Color.argb(mStrokeAlpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
Sunny Goyal19b93b72017-02-19 20:21:37 -0800716 mPaint.setStyle(Paint.Style.STROKE);
717 mPaint.setStrokeWidth(mStrokeWidth);
718 drawCircle(canvas, 1 /* deltaRadius */);
Adam Cohenefca0272016-02-24 19:19:06 -0800719 }
720
Sunny Goyal19b93b72017-02-19 20:21:37 -0800721 public void drawLeaveBehind(Canvas canvas) {
Adam Cohenefca0272016-02-24 19:19:06 -0800722 float originalScale = mScale;
723 mScale = 0.5f;
724
Sunny Goyal19b93b72017-02-19 20:21:37 -0800725 mPaint.setStyle(Paint.Style.FILL);
726 mPaint.setColor(Color.argb(160, 245, 245, 245));
727 drawCircle(canvas, 0 /* deltaRadius */);
Adam Cohenefca0272016-02-24 19:19:06 -0800728
Adam Cohenefca0272016-02-24 19:19:06 -0800729 mScale = originalScale;
730 }
731
Sunny Goyal19b93b72017-02-19 20:21:37 -0800732 private void drawCircle(Canvas canvas,float deltaRadius) {
733 float radius = getScaledRadius();
734 canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
735 radius - deltaRadius, mPaint);
736 }
737
738 // It is the callers responsibility to save and restore the canvas layers.
739 private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
740 mPath.reset();
741 float r = getScaledRadius();
742 mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
743 canvas.clipPath(mPath, op);
744 }
745
746 // It is the callers responsibility to save and restore the canvas layers.
747 private void clipCanvasHardware(Canvas canvas) {
748 mPaint.setColor(Color.BLACK);
749 mPaint.setXfermode(mClipPorterDuffXfermode);
750
751 float radius = getScaledRadius();
752 mShaderMatrix.setScale(radius, radius);
753 mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
754 mClipShader.setLocalMatrix(mShaderMatrix);
755 mPaint.setShader(mClipShader);
756 canvas.drawPaint(mPaint);
757 mPaint.setXfermode(null);
758 mPaint.setShader(null);
Adam Cohenefca0272016-02-24 19:19:06 -0800759 }
760
761 private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
762 if (mDrawingDelegate != delegate) {
763 delegate.addFolderBackground(this);
764 }
765
766 mDrawingDelegate = delegate;
767 delegateCellX = cellX;
768 delegateCellY = cellY;
769
770 invalidate();
771 }
772
773 private void clearDrawingDelegate() {
774 if (mDrawingDelegate != null) {
775 mDrawingDelegate.removeFolderBackground(this);
776 }
777
778 mDrawingDelegate = null;
779 invalidate();
780 }
781
782 private boolean drawingDelegated() {
783 return mDrawingDelegate != null;
784 }
785
786 private void animateScale(float finalScale, float finalMultiplier,
787 final Runnable onStart, final Runnable onEnd) {
788 final float scale0 = mScale;
789 final float scale1 = finalScale;
790
791 final float bgMultiplier0 = mColorMultiplier;
792 final float bgMultiplier1 = finalMultiplier;
793
794 if (mScaleAnimator != null) {
795 mScaleAnimator.cancel();
796 }
797
Jon Mirandacda3bfb2017-02-06 15:54:41 -0800798 mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
Adam Cohenefca0272016-02-24 19:19:06 -0800799
800 mScaleAnimator.addUpdateListener(new AnimatorUpdateListener() {
801 @Override
802 public void onAnimationUpdate(ValueAnimator animation) {
803 float prog = animation.getAnimatedFraction();
804 mScale = prog * scale1 + (1 - prog) * scale0;
805 mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
806 invalidate();
807 }
808 });
809 mScaleAnimator.addListener(new AnimatorListenerAdapter() {
810 @Override
811 public void onAnimationStart(Animator animation) {
812 if (onStart != null) {
813 onStart.run();
814 }
815 }
816
817 @Override
818 public void onAnimationEnd(Animator animation) {
819 if (onEnd != null) {
820 onEnd.run();
821 }
822 mScaleAnimator = null;
823 }
824 });
825
826 mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
827 mScaleAnimator.start();
828 }
829
830 public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
831 Runnable onStart = new Runnable() {
832 @Override
833 public void run() {
834 delegateDrawing(cl, cellX, cellY);
835 }
836 };
837 animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
838 }
839
840 public void animateToRest() {
841 // This can be called multiple times -- we need to make sure the drawing delegate
842 // is saved and restored at the beginning of the animation, since cancelling the
843 // existing animation can clear the delgate.
844 final CellLayout cl = mDrawingDelegate;
845 final int cellX = delegateCellX;
846 final int cellY = delegateCellY;
847
848 Runnable onStart = new Runnable() {
849 @Override
850 public void run() {
851 delegateDrawing(cl, cellX, cellY);
852 }
853 };
854 Runnable onEnd = new Runnable() {
855 @Override
856 public void run() {
857 clearDrawingDelegate();
858 }
859 };
860 animateScale(1f, 1f, onStart, onEnd);
861 }
Jon Miranda47170112017-03-03 14:57:14 -0800862
863 public int getBackgroundAlpha() {
864 return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
865 }
866
867 public float getStrokeWidth() {
868 return mStrokeWidth;
869 }
Adam Cohenefca0272016-02-24 19:19:06 -0800870 }
871
872 public void setFolderBackground(PreviewBackground bg) {
873 mBackground = bg;
Adam Cohenf172b742016-03-30 19:28:34 -0700874 mBackground.setInvalidateDelegate(this);
Adam Cohenefca0272016-02-24 19:19:06 -0800875 }
876
Adam Cohena9cf38f2011-05-02 15:36:58 -0700877 @Override
Adam Cohenc0dcf592011-06-01 15:30:43 -0700878 protected void dispatchDraw(Canvas canvas) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700879 super.dispatchDraw(canvas);
880
Adam Cohen4f8071b2016-02-13 16:06:05 -0800881 if (mReferenceDrawable != null) {
882 computePreviewDrawingParams(mReferenceDrawable);
Adam Cohena9cf38f2011-05-02 15:36:58 -0700883 }
Adam Cohend0445262011-07-04 23:53:22 -0700884
Adam Cohenefca0272016-02-24 19:19:06 -0800885 if (!mBackground.drawingDelegated()) {
Sunny Goyal19b93b72017-02-19 20:21:37 -0800886 mBackground.drawBackground(canvas);
Adam Cohen119e8982016-02-05 14:47:50 -0800887 }
888
Adam Cohenefca0272016-02-24 19:19:06 -0800889 if (mFolder == null) return;
890 if (mFolder.getItemCount() == 0 && !mAnimating) return;
891
Sunny Goyal19b93b72017-02-19 20:21:37 -0800892 final int saveCount;
Adam Cohenefca0272016-02-24 19:19:06 -0800893
Sunny Goyal19b93b72017-02-19 20:21:37 -0800894 if (canvas.isHardwareAccelerated()) {
895 saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
896 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
897 } else {
898 saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
899 if (mPreviewLayoutRule.clipToBackground()) {
900 mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
901 }
Adam Cohenefca0272016-02-24 19:19:06 -0800902 }
903
904 // The items are drawn in coordinates relative to the preview offset
905 canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
906
Adam Cohen4f8071b2016-02-13 16:06:05 -0800907 // The first item should be drawn last (ie. on top of later items)
908 for (int i = mDrawingParams.size() - 1; i >= 0; i--) {
909 PreviewItemDrawingParams p = mDrawingParams.get(i);
910 if (!p.hidden) {
911 drawPreviewItem(canvas, p);
Adam Cohend0445262011-07-04 23:53:22 -0700912 }
Adam Cohend0445262011-07-04 23:53:22 -0700913 }
Sunny Goyal19b93b72017-02-19 20:21:37 -0800914 canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
915
916 if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
917 mBackground.clipCanvasHardware(canvas);
918 }
919 canvas.restoreToCount(saveCount);
Adam Cohenefca0272016-02-24 19:19:06 -0800920
921 if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
Sunny Goyal19b93b72017-02-19 20:21:37 -0800922 mBackground.drawBackgroundStroke(canvas);
Adam Cohenefca0272016-02-24 19:19:06 -0800923 }
Tony Wickham11ba5072017-02-02 12:42:50 -0800924
Tony Wickham0530e8c2017-04-26 18:13:56 -0700925 if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
Sunny Goyal19b93b72017-02-19 20:21:37 -0800926 int offsetX = mBackground.getOffsetX();
927 int offsetY = mBackground.getOffsetY();
928 int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
Tony Wickham1237df02017-02-24 08:59:36 -0800929 mTempBounds.set(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
Sunny Goyal19b93b72017-02-19 20:21:37 -0800930
Tony Wickham1237df02017-02-24 08:59:36 -0800931 // If we are animating to the accepting state, animate the badge out.
Tony Wickham1e618492017-02-02 12:57:18 -0800932 float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
Tony Wickham1237df02017-02-24 08:59:36 -0800933 mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
Tony Wickhame6a790e2017-05-16 12:07:38 -0700934 mBadgeRenderer.draw(canvas, mBadgeInfo, mTempBounds,
Tony Wickham1237df02017-02-24 08:59:36 -0800935 badgeScale, mTempSpaceForBadgeOffset);
Tony Wickham11ba5072017-02-02 12:42:50 -0800936 }
Adam Cohend0445262011-07-04 23:53:22 -0700937 }
938
Adam Cohen4f8071b2016-02-13 16:06:05 -0800939 class FolderPreviewItemAnim {
940 ValueAnimator mValueAnimator;
941 float finalScale;
942 float finalTransX;
943 float finalTransY;
944
945 /**
946 *
947 * @param params layout params to animate
948 * @param index0 original index of the item to be animated
949 * @param nItems0 original number of items in the preview
950 * @param index1 new index of the item to be animated
951 * @param nItems1 new number of items in the preview
952 * @param duration duration in ms of the animation
953 * @param onCompleteRunnable runnable to execute upon animation completion
954 */
955 public FolderPreviewItemAnim(final PreviewItemDrawingParams params, int index0, int nItems0,
956 int index1, int nItems1, int duration, final Runnable onCompleteRunnable) {
957
958 computePreviewItemDrawingParams(index1, nItems1, mTmpParams);
959
960 finalScale = mTmpParams.scale;
961 finalTransX = mTmpParams.transX;
962 finalTransY = mTmpParams.transY;
963
964 computePreviewItemDrawingParams(index0, nItems0, mTmpParams);
965
966 final float scale0 = mTmpParams.scale;
967 final float transX0 = mTmpParams.transX;
968 final float transY0 = mTmpParams.transY;
969
Jon Mirandacda3bfb2017-02-06 15:54:41 -0800970 mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
Adam Cohen4f8071b2016-02-13 16:06:05 -0800971 mValueAnimator.addUpdateListener(new AnimatorUpdateListener(){
972 public void onAnimationUpdate(ValueAnimator animation) {
973 float progress = animation.getAnimatedFraction();
974
975 params.transX = transX0 + progress * (finalTransX - transX0);
976 params.transY = transY0 + progress * (finalTransY - transY0);
977 params.scale = scale0 + progress * (finalScale - scale0);
978 invalidate();
979 }
980 });
981
982 mValueAnimator.addListener(new AnimatorListenerAdapter() {
983 @Override
984 public void onAnimationStart(Animator animation) {
985 }
986
987 @Override
988 public void onAnimationEnd(Animator animation) {
989 if (onCompleteRunnable != null) {
990 onCompleteRunnable.run();
991 }
992 params.anim = null;
993 }
994 });
995 mValueAnimator.setDuration(duration);
996 }
997
998 public void start() {
999 mValueAnimator.start();
1000 }
1001
1002 public void cancel() {
1003 mValueAnimator.cancel();
1004 }
1005
1006 public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
1007 return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
1008 finalScale == anim.finalScale;
1009
1010 }
1011 }
1012
Adam Cohenfb91f302012-06-11 15:45:18 -07001013 private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
1014 final Runnable onCompleteRunnable) {
Adam Cohen119e8982016-02-05 14:47:50 -08001015
Adam Cohen4f8071b2016-02-13 16:06:05 -08001016 FolderPreviewItemAnim anim;
1017 if (!reverse) {
1018 anim = new FolderPreviewItemAnim(mDrawingParams.get(0), -1, -1, 0, 2, duration,
1019 onCompleteRunnable);
1020 } else {
1021 anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
1022 onCompleteRunnable);
1023 }
1024 anim.start();
Adam Cohena9cf38f2011-05-02 15:36:58 -07001025 }
1026
Adam Cohen099f60d2011-08-23 21:07:26 -07001027 public void setTextVisible(boolean visible) {
1028 if (visible) {
1029 mFolderName.setVisibility(VISIBLE);
1030 } else {
1031 mFolderName.setVisibility(INVISIBLE);
1032 }
1033 }
1034
1035 public boolean getTextVisible() {
1036 return mFolderName.getVisibility() == VISIBLE;
1037 }
1038
Jon Miranda47170112017-03-03 14:57:14 -08001039 public List<BubbleTextView> getItemsToDisplay() {
Jon Miranda655ec422017-02-07 11:40:22 -08001040 mPreviewVerifier.setFolderInfo(mFolder.getInfo());
1041
Jon Miranda47170112017-03-03 14:57:14 -08001042 List<BubbleTextView> itemsToDisplay = new ArrayList<>();
Jon Miranda655ec422017-02-07 11:40:22 -08001043 List<View> allItems = mFolder.getItemsInReadingOrder();
1044 int numItems = allItems.size();
1045 for (int rank = 0; rank < numItems; ++rank) {
1046 if (mPreviewVerifier.isItemInPreview(rank)) {
Jon Miranda47170112017-03-03 14:57:14 -08001047 itemsToDisplay.add((BubbleTextView) allItems.get(rank));
Jon Miranda655ec422017-02-07 11:40:22 -08001048 }
1049
1050 if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
1051 break;
1052 }
1053 }
1054 return itemsToDisplay;
1055 }
1056
Adam Cohen4f8071b2016-02-13 16:06:05 -08001057 private void updateItemDrawingParams(boolean animate) {
Jon Miranda47170112017-03-03 14:57:14 -08001058 List<BubbleTextView> items = getItemsToDisplay();
Jon Miranda12b616c2017-02-06 15:45:53 -08001059 int nItemsInPreview = items.size();
Adam Cohen4f8071b2016-02-13 16:06:05 -08001060
1061 int prevNumItems = mDrawingParams.size();
1062
1063 // We adjust the size of the list to match the number of items in the preview
1064 while (nItemsInPreview < mDrawingParams.size()) {
1065 mDrawingParams.remove(mDrawingParams.size() - 1);
1066 }
1067 while (nItemsInPreview > mDrawingParams.size()) {
1068 mDrawingParams.add(new PreviewItemDrawingParams(0, 0, 0, 0));
1069 }
1070
1071 for (int i = 0; i < mDrawingParams.size(); i++) {
1072 PreviewItemDrawingParams p = mDrawingParams.get(i);
Jon Miranda47170112017-03-03 14:57:14 -08001073 p.drawable = items.get(i).getCompoundDrawables()[1];
Adam Cohen4f8071b2016-02-13 16:06:05 -08001074
Adam Cohenefca0272016-02-24 19:19:06 -08001075 if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
Adam Cohen4f8071b2016-02-13 16:06:05 -08001076 computePreviewItemDrawingParams(i, nItemsInPreview, p);
1077 if (mReferenceDrawable == null) {
1078 mReferenceDrawable = p.drawable;
1079 }
1080 } else {
1081 FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
1082 nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
1083
1084 if (p.anim != null) {
1085 if (p.anim.hasEqualFinalState(anim)) {
1086 // do nothing, let the current animation finish
1087 continue;
1088 }
1089 p.anim.cancel();
1090 }
1091 p.anim = anim;
1092 p.anim.start();
1093 }
1094 }
1095 }
1096
Sunny Goyalc52ba712016-04-05 15:59:05 -07001097 @Override
1098 public void onItemsChanged(boolean animate) {
1099 updateItemDrawingParams(animate);
Adam Cohen76078c42011-06-09 15:06:52 -07001100 invalidate();
1101 requestLayout();
1102 }
1103
Tony Wickham11ba5072017-02-02 12:42:50 -08001104 @Override
Jon Miranda608d0db2017-02-28 13:15:28 -08001105 public void prepareAutoUpdate() {
Jon Miranda44060942017-02-22 10:17:49 -08001106 }
1107
1108 @Override
Jon Mirandac476d6e2017-05-04 16:30:01 -07001109 public void onAdd(ShortcutInfo item, int rank) {
Tony Wickham0530e8c2017-04-26 18:13:56 -07001110 boolean wasBadged = mBadgeInfo.hasBadge();
Tony Wickham11ba5072017-02-02 12:42:50 -08001111 mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
Tony Wickham0530e8c2017-04-26 18:13:56 -07001112 boolean isBadged = mBadgeInfo.hasBadge();
1113 updateBadgeScale(wasBadged, isBadged);
Adam Cohena9cf38f2011-05-02 15:36:58 -07001114 invalidate();
1115 requestLayout();
1116 }
1117
Tony Wickham11ba5072017-02-02 12:42:50 -08001118 @Override
Adam Cohena9cf38f2011-05-02 15:36:58 -07001119 public void onRemove(ShortcutInfo item) {
Tony Wickham0530e8c2017-04-26 18:13:56 -07001120 boolean wasBadged = mBadgeInfo.hasBadge();
Tony Wickham11ba5072017-02-02 12:42:50 -08001121 mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
Tony Wickham0530e8c2017-04-26 18:13:56 -07001122 boolean isBadged = mBadgeInfo.hasBadge();
1123 updateBadgeScale(wasBadged, isBadged);
Adam Cohena9cf38f2011-05-02 15:36:58 -07001124 invalidate();
1125 requestLayout();
1126 }
Adam Cohen76fc0852011-06-17 13:26:23 -07001127
Tony Wickham11ba5072017-02-02 12:42:50 -08001128 @Override
Adam Cohen76fc0852011-06-17 13:26:23 -07001129 public void onTitleChanged(CharSequence title) {
Winson Chung82b016c2015-05-08 17:00:10 -07001130 mFolderName.setText(title);
Sunny Goyalf4f89ef2015-09-02 15:06:12 -07001131 setContentDescription(getContext().getString(R.string.folder_name_format, title));
Adam Cohen76fc0852011-06-17 13:26:23 -07001132 }
Winson Chung88f33452012-02-23 15:23:44 -08001133
1134 @Override
1135 public boolean onTouchEvent(MotionEvent event) {
1136 // Call the superclass onTouchEvent first, because sometimes it changes the state to
1137 // isPressed() on an ACTION_UP
1138 boolean result = super.onTouchEvent(event);
1139
Mady Melloref044dd2015-06-02 15:35:07 -07001140 // Check for a stylus button press, if it occurs cancel any long press checks.
Mady Mellorbb835202015-07-15 16:34:34 -07001141 if (mStylusEventHelper.onMotionEvent(event)) {
Mady Melloref044dd2015-06-02 15:35:07 -07001142 mLongPressHelper.cancelLongPress();
1143 return true;
1144 }
1145
Winson Chung88f33452012-02-23 15:23:44 -08001146 switch (event.getAction()) {
1147 case MotionEvent.ACTION_DOWN:
1148 mLongPressHelper.postCheckForLongPress();
1149 break;
1150 case MotionEvent.ACTION_CANCEL:
1151 case MotionEvent.ACTION_UP:
1152 mLongPressHelper.cancelLongPress();
1153 break;
Jason Monk02dd7ae2014-04-15 15:23:31 -04001154 case MotionEvent.ACTION_MOVE:
1155 if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
1156 mLongPressHelper.cancelLongPress();
1157 }
1158 break;
Winson Chung88f33452012-02-23 15:23:44 -08001159 }
1160 return result;
1161 }
1162
1163 @Override
1164 public void cancelLongPress() {
1165 super.cancelLongPress();
Winson Chung88f33452012-02-23 15:23:44 -08001166 mLongPressHelper.cancelLongPress();
1167 }
Adam Cohenf9c184a2016-01-15 16:47:43 -08001168
Sunny Goyalaaf7d1d2016-05-17 13:38:54 -07001169 public void removeListeners() {
1170 mInfo.removeListener(this);
1171 mInfo.removeListener(mFolder);
1172 }
1173
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001174 public void shrinkAndFadeIn(boolean animate) {
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001175 // We remove and re-draw the FolderIcon in-case it has changed
1176 final PreviewImageView previewImage = PreviewImageView.get(getContext());
1177 previewImage.removeFromParent();
1178 copyToPreview(previewImage);
1179
Jon Mirandaf79e54b2017-03-31 13:48:39 -07001180 clearLeaveBehindIfExists();
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001181
1182 ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1);
1183 oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
1184 oa.addListener(new AnimatorListenerAdapter() {
1185 @Override
1186 public void onAnimationEnd(Animator animation) {
Jon Mirandaf79e54b2017-03-31 13:48:39 -07001187 // Remove the ImageView copy of the FolderIcon and make the original visible.
1188 previewImage.removeFromParent();
1189 setVisibility(View.VISIBLE);
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001190 }
1191 });
1192 oa.start();
1193 if (!animate) {
1194 oa.end();
1195 }
1196 }
1197
Jon Mirandaf79e54b2017-03-31 13:48:39 -07001198 public void clearLeaveBehindIfExists() {
1199 ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
1200 if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1201 CellLayout cl = (CellLayout) getParent().getParent();
1202 cl.clearFolderLeaveBehind();
1203 }
1204 }
1205
1206 public void drawLeaveBehindIfExists() {
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001207 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
1208 // While the folder is open, the position of the icon cannot change.
1209 lp.canReorder = false;
1210 if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1211 CellLayout cl = (CellLayout) getParent().getParent();
1212 cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
1213 }
Jon Mirandaf79e54b2017-03-31 13:48:39 -07001214 }
1215
1216 public void growAndFadeOut() {
1217 drawLeaveBehindIfExists();
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001218
1219 // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
1220 PreviewImageView previewImage = PreviewImageView.get(getContext());
1221 copyToPreview(previewImage);
1222 setVisibility(View.INVISIBLE);
1223
1224 ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 0, 1.5f, 1.5f);
1225 oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
1226 oa.start();
1227 }
1228
1229 /**
1230 * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
1231 * in the DragLayer in the exact absolute location of the original FolderIcon.
1232 */
1233 private void copyToPreview(PreviewImageView previewImageView) {
1234 previewImageView.copy(this);
1235 if (mFolder != null) {
1236 previewImageView.setPivotX(mFolder.getPivotXForIconAnimation());
1237 previewImageView.setPivotY(mFolder.getPivotYForIconAnimation());
1238 mFolder.bringToFront();
1239 }
1240 }
1241
Adam Cohenf9c184a2016-01-15 16:47:43 -08001242 public interface PreviewLayoutRule {
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001243 PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
Adam Cohenf9c184a2016-01-15 16:47:43 -08001244 PreviewItemDrawingParams params);
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001245 void init(int availableSpace, int intrinsicIconSize, boolean rtl);
Jon Miranda12b616c2017-02-06 15:45:53 -08001246 float scaleForItem(int index, int totalNumItems);
Jon Miranda5fcbad02017-03-01 16:06:21 -08001247 float getIconSize();
Jon Miranda12b616c2017-02-06 15:45:53 -08001248 int maxNumItems();
Sunny Goyal740ac7f2016-09-28 16:47:32 -07001249 boolean clipToBackground();
Adam Cohenf9c184a2016-01-15 16:47:43 -08001250 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001251}