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