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