1 /* 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 17 package com.android.launcher3.folder; 18 19 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; 20 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION; 21 22 import android.animation.Animator; 23 import android.animation.AnimatorListenerAdapter; 24 import android.animation.ObjectAnimator; 25 import android.content.Context; 26 import android.graphics.Canvas; 27 import android.graphics.Rect; 28 import android.graphics.drawable.Drawable; 29 import android.util.AttributeSet; 30 import android.util.Property; 31 import android.view.LayoutInflater; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.ViewConfiguration; 35 import android.view.ViewDebug; 36 import android.view.ViewGroup; 37 import android.widget.FrameLayout; 38 39 import androidx.annotation.NonNull; 40 41 import com.android.launcher3.Alarm; 42 import com.android.launcher3.AppInfo; 43 import com.android.launcher3.BubbleTextView; 44 import com.android.launcher3.CellLayout; 45 import com.android.launcher3.CheckLongPressHelper; 46 import com.android.launcher3.DeviceProfile; 47 import com.android.launcher3.DropTarget.DragObject; 48 import com.android.launcher3.FolderInfo; 49 import com.android.launcher3.FolderInfo.FolderListener; 50 import com.android.launcher3.ItemInfo; 51 import com.android.launcher3.Launcher; 52 import com.android.launcher3.LauncherSettings; 53 import com.android.launcher3.OnAlarmListener; 54 import com.android.launcher3.R; 55 import com.android.launcher3.SimpleOnStylusPressListener; 56 import com.android.launcher3.StylusEventHelper; 57 import com.android.launcher3.Utilities; 58 import com.android.launcher3.Workspace; 59 import com.android.launcher3.WorkspaceItemInfo; 60 import com.android.launcher3.anim.Interpolators; 61 import com.android.launcher3.config.FeatureFlags; 62 import com.android.launcher3.dot.FolderDotInfo; 63 import com.android.launcher3.dragndrop.BaseItemDragListener; 64 import com.android.launcher3.dragndrop.DragLayer; 65 import com.android.launcher3.dragndrop.DragView; 66 import com.android.launcher3.icons.DotRenderer; 67 import com.android.launcher3.touch.ItemClickHandler; 68 import com.android.launcher3.util.Executors; 69 import com.android.launcher3.util.Thunk; 70 import com.android.launcher3.views.IconLabelDotView; 71 import com.android.launcher3.widget.PendingAddShortcutInfo; 72 73 import java.util.ArrayList; 74 import java.util.List; 75 import java.util.function.Predicate; 76 77 /** 78 * An icon that can appear on in the workspace representing an {@link Folder}. 79 */ 80 public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView { 81 82 @Thunk Launcher mLauncher; 83 @Thunk Folder mFolder; 84 private FolderInfo mInfo; 85 86 private CheckLongPressHelper mLongPressHelper; 87 private StylusEventHelper mStylusEventHelper; 88 89 static final int DROP_IN_ANIMATION_DURATION = 400; 90 91 // Flag whether the folder should open itself when an item is dragged over is enabled. 92 public static final boolean SPRING_LOADING_ENABLED = true; 93 94 // Delay when drag enters until the folder opens, in miliseconds. 95 private static final int ON_OPEN_DELAY = 800; 96 97 @Thunk BubbleTextView mFolderName; 98 99 PreviewBackground mBackground = new PreviewBackground(); 100 private boolean mBackgroundIsVisible = true; 101 102 FolderGridOrganizer mPreviewVerifier; 103 ClippedFolderIconLayoutRule mPreviewLayoutRule; 104 private PreviewItemManager mPreviewItemManager; 105 private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); 106 private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>(); 107 108 boolean mAnimating = false; 109 110 private float mSlop; 111 112 private Alarm mOpenAlarm = new Alarm(); 113 114 private boolean mForceHideDot; 115 @ViewDebug.ExportedProperty(category = "launcher", deepExport = true) 116 private FolderDotInfo mDotInfo = new FolderDotInfo(); 117 private DotRenderer mDotRenderer; 118 @ViewDebug.ExportedProperty(category = "launcher", deepExport = true) 119 private DotRenderer.DrawParams mDotParams; 120 private float mDotScale; 121 private Animator mDotScaleAnim; 122 123 private static final Property<FolderIcon, Float> DOT_SCALE_PROPERTY 124 = new Property<FolderIcon, Float>(Float.TYPE, "dotScale") { 125 @Override 126 public Float get(FolderIcon folderIcon) { 127 return folderIcon.mDotScale; 128 } 129 130 @Override 131 public void set(FolderIcon folderIcon, Float value) { 132 folderIcon.mDotScale = value; 133 folderIcon.invalidate(); 134 } 135 }; 136 FolderIcon(Context context, AttributeSet attrs)137 public FolderIcon(Context context, AttributeSet attrs) { 138 super(context, attrs); 139 init(); 140 } 141 FolderIcon(Context context)142 public FolderIcon(Context context) { 143 super(context); 144 init(); 145 } 146 init()147 private void init() { 148 mLongPressHelper = new CheckLongPressHelper(this); 149 mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); 150 mPreviewLayoutRule = new ClippedFolderIconLayoutRule(); 151 mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 152 mPreviewItemManager = new PreviewItemManager(this); 153 mDotParams = new DotRenderer.DrawParams(); 154 } 155 fromXml(int resId, Launcher launcher, ViewGroup group, FolderInfo folderInfo)156 public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, 157 FolderInfo folderInfo) { 158 @SuppressWarnings("all") // suppress dead code warning 159 final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION; 160 if (error) { 161 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " + 162 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " + 163 "is dependent on this"); 164 } 165 166 DeviceProfile grid = launcher.getWallpaperDeviceProfile(); 167 FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext()) 168 .inflate(resId, group, false); 169 170 icon.setClipToPadding(false); 171 icon.mFolderName = icon.findViewById(R.id.folder_icon_name); 172 icon.mFolderName.setText(folderInfo.title); 173 icon.mFolderName.setCompoundDrawablePadding(0); 174 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams(); 175 lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx; 176 177 icon.setTag(folderInfo); 178 icon.setOnClickListener(ItemClickHandler.INSTANCE); 179 icon.mInfo = folderInfo; 180 icon.mLauncher = launcher; 181 icon.mDotRenderer = grid.mDotRendererWorkSpace; 182 icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title)); 183 Folder folder = Folder.fromXml(launcher); 184 folder.setDragController(launcher.getDragController()); 185 folder.setFolderIcon(icon); 186 folder.bind(folderInfo); 187 icon.setFolder(folder); 188 icon.setAccessibilityDelegate(launcher.getAccessibilityDelegate()); 189 190 folderInfo.addListener(icon); 191 192 icon.setOnFocusChangeListener(launcher.mFocusHandler); 193 return icon; 194 } 195 animateBgShadowAndStroke()196 public void animateBgShadowAndStroke() { 197 mBackground.fadeInBackgroundShadow(); 198 mBackground.animateBackgroundStroke(); 199 } 200 getFolderName()201 public BubbleTextView getFolderName() { 202 return mFolderName; 203 } 204 getPreviewBounds(Rect outBounds)205 public void getPreviewBounds(Rect outBounds) { 206 mPreviewItemManager.recomputePreviewDrawingParams(); 207 mBackground.getBounds(outBounds); 208 } 209 getBackgroundStrokeWidth()210 public float getBackgroundStrokeWidth() { 211 return mBackground.getStrokeWidth(); 212 } 213 getFolder()214 public Folder getFolder() { 215 return mFolder; 216 } 217 setFolder(Folder folder)218 private void setFolder(Folder folder) { 219 mFolder = folder; 220 mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv); 221 mPreviewVerifier.setFolderInfo(mFolder.getInfo()); 222 updatePreviewItems(false); 223 } 224 willAcceptItem(ItemInfo item)225 private boolean willAcceptItem(ItemInfo item) { 226 final int itemType = item.itemType; 227 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 228 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || 229 itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) && 230 item != mInfo && !mFolder.isOpen()); 231 } 232 acceptDrop(ItemInfo dragInfo)233 public boolean acceptDrop(ItemInfo dragInfo) { 234 return !mFolder.isDestroyed() && willAcceptItem(dragInfo); 235 } 236 addItem(WorkspaceItemInfo item)237 public void addItem(WorkspaceItemInfo item) { 238 mInfo.add(item, true); 239 } 240 removeItem(WorkspaceItemInfo item, boolean animate)241 public void removeItem(WorkspaceItemInfo item, boolean animate) { 242 mInfo.remove(item, animate); 243 } 244 onDragEnter(ItemInfo dragInfo)245 public void onDragEnter(ItemInfo dragInfo) { 246 if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return; 247 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 248 CellLayout cl = (CellLayout) getParent().getParent(); 249 250 mBackground.animateToAccept(cl, lp.cellX, lp.cellY); 251 mOpenAlarm.setOnAlarmListener(mOnOpenListener); 252 if (SPRING_LOADING_ENABLED && 253 ((dragInfo instanceof AppInfo) 254 || (dragInfo instanceof WorkspaceItemInfo) 255 || (dragInfo instanceof PendingAddShortcutInfo))) { 256 mOpenAlarm.setAlarm(ON_OPEN_DELAY); 257 } 258 } 259 260 OnAlarmListener mOnOpenListener = new OnAlarmListener() { 261 public void onAlarm(Alarm alarm) { 262 mFolder.beginExternalDrag(); 263 } 264 }; 265 prepareCreateAnimation(final View destView)266 public Drawable prepareCreateAnimation(final View destView) { 267 return mPreviewItemManager.prepareCreateAnimation(destView); 268 } 269 performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView, final WorkspaceItemInfo srcInfo, final DragView srcView, Rect dstRect, float scaleRelativeToDragLayer)270 public void performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView, 271 final WorkspaceItemInfo srcInfo, final DragView srcView, Rect dstRect, 272 float scaleRelativeToDragLayer) { 273 prepareCreateAnimation(destView); 274 addItem(destInfo); 275 // This will animate the first item from it's position as an icon into its 276 // position as the first item in the preview 277 mPreviewItemManager.createFirstItemAnimation(false /* reverse */, null) 278 .start(); 279 280 // This will animate the dragView (srcView) into the new folder 281 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, 282 false /* itemReturnedOnFailedDrop */); 283 } 284 performDestroyAnimation(Runnable onCompleteRunnable)285 public void performDestroyAnimation(Runnable onCompleteRunnable) { 286 // This will animate the final item in the preview to be full size. 287 mPreviewItemManager.createFirstItemAnimation(true /* reverse */, onCompleteRunnable) 288 .start(); 289 } 290 onDragExit()291 public void onDragExit() { 292 mBackground.animateToRest(); 293 mOpenAlarm.cancelAlarm(); 294 } 295 onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect, float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop)296 private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect, 297 float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) { 298 item.cellX = -1; 299 item.cellY = -1; 300 301 // Typically, the animateView corresponds to the DragView; however, if this is being done 302 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we 303 // will not have a view to animate 304 if (animateView != null) { 305 DragLayer dragLayer = mLauncher.getDragLayer(); 306 Rect from = new Rect(); 307 dragLayer.getViewRectRelativeToSelf(animateView, from); 308 Rect to = finalRect; 309 if (to == null) { 310 to = new Rect(); 311 Workspace workspace = mLauncher.getWorkspace(); 312 // Set cellLayout and this to it's final state to compute final animation locations 313 workspace.setFinalTransitionTransform(); 314 float scaleX = getScaleX(); 315 float scaleY = getScaleY(); 316 setScaleX(1.0f); 317 setScaleY(1.0f); 318 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); 319 // Finished computing final animation locations, restore current state 320 setScaleX(scaleX); 321 setScaleY(scaleY); 322 workspace.resetTransitionTransform(); 323 } 324 325 int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1); 326 boolean itemAdded = false; 327 if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) { 328 List<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); 329 mInfo.add(item, index, false); 330 mCurrentPreviewItems.clear(); 331 mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); 332 333 if (!oldPreviewItems.equals(mCurrentPreviewItems)) { 334 int newIndex = mCurrentPreviewItems.indexOf(item); 335 if (newIndex >= 0) { 336 // If the item dropped is going to be in the preview, we update the 337 // index here to reflect its position in the preview. 338 index = newIndex; 339 } 340 341 mPreviewItemManager.hidePreviewItem(index, true); 342 mPreviewItemManager.onDrop(oldPreviewItems, mCurrentPreviewItems, item); 343 itemAdded = true; 344 } else { 345 removeItem(item, false); 346 } 347 } 348 349 if (!itemAdded) { 350 mInfo.add(item, index, true); 351 } 352 353 int[] center = new int[2]; 354 float scale = getLocalCenterForIndex(index, numItemsInPreview, center); 355 center[0] = Math.round(scaleRelativeToDragLayer * center[0]); 356 center[1] = Math.round(scaleRelativeToDragLayer * center[1]); 357 358 to.offset(center[0] - animateView.getMeasuredWidth() / 2, 359 center[1] - animateView.getMeasuredHeight() / 2); 360 361 float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; 362 363 float finalScale = scale * scaleRelativeToDragLayer; 364 dragLayer.animateView(animateView, from, to, finalAlpha, 365 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, 366 Interpolators.DEACCEL_2, Interpolators.ACCEL_2, 367 null, DragLayer.ANIMATION_END_DISAPPEAR, null); 368 369 mFolder.hideItem(item); 370 371 if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true); 372 final int finalIndex = index; 373 374 String[] suggestedNameOut = new String[1]; 375 if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) { 376 Executors.UI_HELPER_EXECUTOR.post(() -> mLauncher.getFolderNameProvider() 377 .getSuggestedFolderName(getContext(), mInfo.contents, suggestedNameOut)); 378 } 379 postDelayed(() -> { 380 mPreviewItemManager.hidePreviewItem(finalIndex, false); 381 mFolder.showItem(item); 382 invalidate(); 383 mFolder.showSuggestedTitle(suggestedNameOut[0]); 384 }, DROP_IN_ANIMATION_DURATION); 385 } else { 386 addItem(item); 387 } 388 } 389 onDrop(DragObject d, boolean itemReturnedOnFailedDrop)390 public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) { 391 WorkspaceItemInfo item; 392 if (d.dragInfo instanceof AppInfo) { 393 // Came from all apps -- make a copy 394 item = ((AppInfo) d.dragInfo).makeWorkspaceItem(); 395 } else if (d.dragSource instanceof BaseItemDragListener){ 396 // Came from a different window -- make a copy 397 item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo); 398 } else { 399 item = (WorkspaceItemInfo) d.dragInfo; 400 } 401 mFolder.notifyDrop(); 402 onDrop(item, d.dragView, null, 1.0f, 403 itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(), 404 itemReturnedOnFailedDrop); 405 } 406 setDotInfo(FolderDotInfo dotInfo)407 public void setDotInfo(FolderDotInfo dotInfo) { 408 updateDotScale(mDotInfo.hasDot(), dotInfo.hasDot()); 409 mDotInfo = dotInfo; 410 } 411 getLayoutRule()412 public ClippedFolderIconLayoutRule getLayoutRule() { 413 return mPreviewLayoutRule; 414 } 415 416 @Override setForceHideDot(boolean forceHideDot)417 public void setForceHideDot(boolean forceHideDot) { 418 if (mForceHideDot == forceHideDot) { 419 return; 420 } 421 mForceHideDot = forceHideDot; 422 423 if (forceHideDot) { 424 invalidate(); 425 } else if (hasDot()) { 426 animateDotScale(0, 1); 427 } 428 } 429 430 /** 431 * Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false 432 * (the dot is being added or removed). 433 */ updateDotScale(boolean wasDotted, boolean isDotted)434 private void updateDotScale(boolean wasDotted, boolean isDotted) { 435 float newDotScale = isDotted ? 1f : 0f; 436 // Animate when a dot is first added or when it is removed. 437 if ((wasDotted ^ isDotted) && isShown()) { 438 animateDotScale(newDotScale); 439 } else { 440 cancelDotScaleAnim(); 441 mDotScale = newDotScale; 442 invalidate(); 443 } 444 } 445 cancelDotScaleAnim()446 private void cancelDotScaleAnim() { 447 if (mDotScaleAnim != null) { 448 mDotScaleAnim.cancel(); 449 } 450 } 451 animateDotScale(float... dotScales)452 public void animateDotScale(float... dotScales) { 453 cancelDotScaleAnim(); 454 mDotScaleAnim = ObjectAnimator.ofFloat(this, DOT_SCALE_PROPERTY, dotScales); 455 mDotScaleAnim.addListener(new AnimatorListenerAdapter() { 456 @Override 457 public void onAnimationEnd(Animator animation) { 458 mDotScaleAnim = null; 459 } 460 }); 461 mDotScaleAnim.start(); 462 } 463 hasDot()464 public boolean hasDot() { 465 return mDotInfo != null && mDotInfo.hasDot(); 466 } 467 getLocalCenterForIndex(int index, int curNumItems, int[] center)468 private float getLocalCenterForIndex(int index, int curNumItems, int[] center) { 469 mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams( 470 Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index), curNumItems, mTmpParams); 471 472 mTmpParams.transX += mBackground.basePreviewOffsetX; 473 mTmpParams.transY += mBackground.basePreviewOffsetY; 474 475 float intrinsicIconSize = mPreviewItemManager.getIntrinsicIconSize(); 476 float offsetX = mTmpParams.transX + (mTmpParams.scale * intrinsicIconSize) / 2; 477 float offsetY = mTmpParams.transY + (mTmpParams.scale * intrinsicIconSize) / 2; 478 479 center[0] = Math.round(offsetX); 480 center[1] = Math.round(offsetY); 481 return mTmpParams.scale; 482 } 483 setFolderBackground(PreviewBackground bg)484 public void setFolderBackground(PreviewBackground bg) { 485 mBackground = bg; 486 mBackground.setInvalidateDelegate(this); 487 } 488 489 @Override setIconVisible(boolean visible)490 public void setIconVisible(boolean visible) { 491 mBackgroundIsVisible = visible; 492 invalidate(); 493 } 494 getFolderBackground()495 public PreviewBackground getFolderBackground() { 496 return mBackground; 497 } 498 getPreviewItemManager()499 public PreviewItemManager getPreviewItemManager() { 500 return mPreviewItemManager; 501 } 502 503 @Override dispatchDraw(Canvas canvas)504 protected void dispatchDraw(Canvas canvas) { 505 super.dispatchDraw(canvas); 506 507 if (!mBackgroundIsVisible) return; 508 509 mPreviewItemManager.recomputePreviewDrawingParams(); 510 511 if (!mBackground.drawingDelegated()) { 512 mBackground.drawBackground(canvas); 513 } 514 515 if (mCurrentPreviewItems.isEmpty() && !mAnimating) return; 516 517 final int saveCount = canvas.save(); 518 canvas.clipPath(mBackground.getClipPath()); 519 mPreviewItemManager.draw(canvas); 520 canvas.restoreToCount(saveCount); 521 522 if (!mBackground.drawingDelegated()) { 523 mBackground.drawBackgroundStroke(canvas); 524 } 525 526 drawDot(canvas); 527 } 528 drawDot(Canvas canvas)529 public void drawDot(Canvas canvas) { 530 if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) { 531 Rect iconBounds = mDotParams.iconBounds; 532 BubbleTextView.getIconBounds(this, iconBounds, 533 mLauncher.getWallpaperDeviceProfile().iconSizePx); 534 float iconScale = (float) mBackground.previewSize / iconBounds.width(); 535 Utilities.scaleRectAboutCenter(iconBounds, iconScale); 536 537 // If we are animating to the accepting state, animate the dot out. 538 mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress()); 539 mDotParams.color = mBackground.getDotColor(); 540 mDotRenderer.draw(canvas, mDotParams); 541 } 542 } 543 setTextVisible(boolean visible)544 public void setTextVisible(boolean visible) { 545 if (visible) { 546 mFolderName.setVisibility(VISIBLE); 547 } else { 548 mFolderName.setVisibility(INVISIBLE); 549 } 550 } 551 getTextVisible()552 public boolean getTextVisible() { 553 return mFolderName.getVisibility() == VISIBLE; 554 } 555 556 /** 557 * Returns the list of items which should be visible in the preview 558 */ getPreviewItemsOnPage(int page)559 public List<WorkspaceItemInfo> getPreviewItemsOnPage(int page) { 560 return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.contents); 561 } 562 563 @Override verifyDrawable(@onNull Drawable who)564 protected boolean verifyDrawable(@NonNull Drawable who) { 565 return mPreviewItemManager.verifyDrawable(who) || super.verifyDrawable(who); 566 } 567 568 @Override onItemsChanged(boolean animate)569 public void onItemsChanged(boolean animate) { 570 updatePreviewItems(animate); 571 invalidate(); 572 requestLayout(); 573 } 574 updatePreviewItems(boolean animate)575 private void updatePreviewItems(boolean animate) { 576 mPreviewItemManager.updatePreviewItems(animate); 577 mCurrentPreviewItems.clear(); 578 mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); 579 } 580 581 /** 582 * Updates the preview items which match the provided condition 583 */ updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck)584 public void updatePreviewItems(Predicate<WorkspaceItemInfo> itemCheck) { 585 mPreviewItemManager.updatePreviewItems(itemCheck); 586 } 587 588 @Override onAdd(WorkspaceItemInfo item, int rank)589 public void onAdd(WorkspaceItemInfo item, int rank) { 590 boolean wasDotted = mDotInfo.hasDot(); 591 mDotInfo.addDotInfo(mLauncher.getDotInfoForItem(item)); 592 boolean isDotted = mDotInfo.hasDot(); 593 updateDotScale(wasDotted, isDotted); 594 invalidate(); 595 requestLayout(); 596 } 597 598 @Override onRemove(WorkspaceItemInfo item)599 public void onRemove(WorkspaceItemInfo item) { 600 boolean wasDotted = mDotInfo.hasDot(); 601 mDotInfo.subtractDotInfo(mLauncher.getDotInfoForItem(item)); 602 boolean isDotted = mDotInfo.hasDot(); 603 updateDotScale(wasDotted, isDotted); 604 invalidate(); 605 requestLayout(); 606 } 607 onTitleChanged(CharSequence title)608 public void onTitleChanged(CharSequence title) { 609 mFolderName.setText(title); 610 setContentDescription(getContext().getString(R.string.folder_name_format, title)); 611 } 612 613 @Override onTouchEvent(MotionEvent event)614 public boolean onTouchEvent(MotionEvent event) { 615 // Call the superclass onTouchEvent first, because sometimes it changes the state to 616 // isPressed() on an ACTION_UP 617 boolean result = super.onTouchEvent(event); 618 619 // Check for a stylus button press, if it occurs cancel any long press checks. 620 if (mStylusEventHelper.onMotionEvent(event)) { 621 mLongPressHelper.cancelLongPress(); 622 return true; 623 } 624 625 switch (event.getAction()) { 626 case MotionEvent.ACTION_DOWN: 627 mLongPressHelper.postCheckForLongPress(); 628 break; 629 case MotionEvent.ACTION_CANCEL: 630 case MotionEvent.ACTION_UP: 631 mLongPressHelper.cancelLongPress(); 632 break; 633 case MotionEvent.ACTION_MOVE: 634 if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) { 635 mLongPressHelper.cancelLongPress(); 636 } 637 break; 638 } 639 return result; 640 } 641 642 @Override cancelLongPress()643 public void cancelLongPress() { 644 super.cancelLongPress(); 645 mLongPressHelper.cancelLongPress(); 646 } 647 removeListeners()648 public void removeListeners() { 649 mInfo.removeListener(this); 650 mInfo.removeListener(mFolder); 651 } 652 clearLeaveBehindIfExists()653 public void clearLeaveBehindIfExists() { 654 ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; 655 if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 656 CellLayout cl = (CellLayout) getParent().getParent(); 657 cl.clearFolderLeaveBehind(); 658 } 659 } 660 drawLeaveBehindIfExists()661 public void drawLeaveBehindIfExists() { 662 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 663 // While the folder is open, the position of the icon cannot change. 664 lp.canReorder = false; 665 if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 666 CellLayout cl = (CellLayout) getParent().getParent(); 667 cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY); 668 } 669 } 670 onFolderClose(int currentPage)671 public void onFolderClose(int currentPage) { 672 mPreviewItemManager.onFolderClose(currentPage); 673 } 674 } 675