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 android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ObjectAnimator; 22 import android.content.Context; 23 import android.graphics.Canvas; 24 import android.graphics.Point; 25 import android.graphics.Rect; 26 import android.graphics.Region; 27 import android.graphics.drawable.Drawable; 28 import android.os.Parcelable; 29 import android.support.annotation.NonNull; 30 import android.util.AttributeSet; 31 import android.util.Property; 32 import android.view.LayoutInflater; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.ViewConfiguration; 36 import android.view.ViewGroup; 37 import android.view.animation.AccelerateInterpolator; 38 import android.view.animation.DecelerateInterpolator; 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.LauncherAnimUtils; 53 import com.android.launcher3.LauncherSettings; 54 import com.android.launcher3.OnAlarmListener; 55 import com.android.launcher3.R; 56 import com.android.launcher3.ShortcutInfo; 57 import com.android.launcher3.SimpleOnStylusPressListener; 58 import com.android.launcher3.StylusEventHelper; 59 import com.android.launcher3.Utilities; 60 import com.android.launcher3.Workspace; 61 import com.android.launcher3.badge.BadgeRenderer; 62 import com.android.launcher3.badge.FolderBadgeInfo; 63 import com.android.launcher3.config.FeatureFlags; 64 import com.android.launcher3.dragndrop.BaseItemDragListener; 65 import com.android.launcher3.dragndrop.DragLayer; 66 import com.android.launcher3.dragndrop.DragView; 67 import com.android.launcher3.graphics.IconPalette; 68 import com.android.launcher3.util.Thunk; 69 import com.android.launcher3.widget.PendingAddShortcutInfo; 70 71 import java.util.ArrayList; 72 import java.util.List; 73 74 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION; 75 76 /** 77 * An icon that can appear on in the workspace representing an {@link Folder}. 78 */ 79 public class FolderIcon extends FrameLayout implements FolderListener { 80 @Thunk Launcher mLauncher; 81 @Thunk Folder mFolder; 82 private FolderInfo mInfo; 83 @Thunk static boolean sStaticValuesDirty = true; 84 85 public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ? 86 StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW : 87 ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; 88 89 private CheckLongPressHelper mLongPressHelper; 90 private StylusEventHelper mStylusEventHelper; 91 92 static final int DROP_IN_ANIMATION_DURATION = 400; 93 94 // Flag whether the folder should open itself when an item is dragged over is enabled. 95 public static final boolean SPRING_LOADING_ENABLED = true; 96 97 // Delay when drag enters until the folder opens, in miliseconds. 98 private static final int ON_OPEN_DELAY = 800; 99 100 @Thunk BubbleTextView mFolderName; 101 102 PreviewBackground mBackground = new PreviewBackground(); 103 private boolean mBackgroundIsVisible = true; 104 105 FolderIconPreviewVerifier mPreviewVerifier; 106 PreviewLayoutRule mPreviewLayoutRule; 107 private PreviewItemManager mPreviewItemManager; 108 private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); 109 110 boolean mAnimating = false; 111 private Rect mTempBounds = new Rect(); 112 113 private float mSlop; 114 115 private Alarm mOpenAlarm = new Alarm(); 116 117 private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo(); 118 private BadgeRenderer mBadgeRenderer; 119 private float mBadgeScale; 120 private Point mTempSpaceForBadgeOffset = new Point(); 121 122 private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY 123 = new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") { 124 @Override 125 public Float get(FolderIcon folderIcon) { 126 return folderIcon.mBadgeScale; 127 } 128 129 @Override 130 public void set(FolderIcon folderIcon, Float value) { 131 folderIcon.mBadgeScale = value; 132 folderIcon.invalidate(); 133 } 134 }; 135 136 public FolderIcon(Context context, AttributeSet attrs) { 137 super(context, attrs); 138 init(); 139 } 140 141 public FolderIcon(Context context) { 142 super(context); 143 init(); 144 } 145 146 private void init() { 147 mLongPressHelper = new CheckLongPressHelper(this); 148 mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); 149 mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ? 150 new StackFolderIconLayoutRule() : 151 new ClippedFolderIconLayoutRule(); 152 mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 153 mPreviewItemManager = new PreviewItemManager(this); 154 } 155 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.getDeviceProfile(); 167 FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext()) 168 .inflate(resId, group, false); 169 170 icon.setClipToPadding(false); 171 icon.mFolderName = (BubbleTextView) 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(launcher); 179 icon.mInfo = folderInfo; 180 icon.mLauncher = launcher; 181 icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer; 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 196 @Override 197 protected Parcelable onSaveInstanceState() { 198 sStaticValuesDirty = true; 199 return super.onSaveInstanceState(); 200 } 201 202 public Folder getFolder() { 203 return mFolder; 204 } 205 206 private void setFolder(Folder folder) { 207 mFolder = folder; 208 mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv); 209 mPreviewItemManager.updateItemDrawingParams(false); 210 } 211 212 private boolean willAcceptItem(ItemInfo item) { 213 final int itemType = item.itemType; 214 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 215 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT || 216 itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) && 217 !mFolder.isFull() && item != mInfo && !mFolder.isOpen()); 218 } 219 220 public boolean acceptDrop(ItemInfo dragInfo) { 221 return !mFolder.isDestroyed() && willAcceptItem(dragInfo); 222 } 223 224 public void addItem(ShortcutInfo item) { 225 addItem(item, true); 226 } 227 228 public void addItem(ShortcutInfo item, boolean animate) { 229 mInfo.add(item, animate); 230 } 231 232 public void removeItem(ShortcutInfo item, boolean animate) { 233 mInfo.remove(item, animate); 234 } 235 236 public void onDragEnter(ItemInfo dragInfo) { 237 if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return; 238 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 239 CellLayout cl = (CellLayout) getParent().getParent(); 240 241 mBackground.animateToAccept(cl, lp.cellX, lp.cellY); 242 mOpenAlarm.setOnAlarmListener(mOnOpenListener); 243 if (SPRING_LOADING_ENABLED && 244 ((dragInfo instanceof AppInfo) 245 || (dragInfo instanceof ShortcutInfo) 246 || (dragInfo instanceof PendingAddShortcutInfo))) { 247 mOpenAlarm.setAlarm(ON_OPEN_DELAY); 248 } 249 } 250 251 OnAlarmListener mOnOpenListener = new OnAlarmListener() { 252 public void onAlarm(Alarm alarm) { 253 mFolder.beginExternalDrag(); 254 mFolder.animateOpen(); 255 } 256 }; 257 258 public Drawable prepareCreateAnimation(final View destView) { 259 return mPreviewItemManager.prepareCreateAnimation(destView); 260 } 261 262 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView, 263 final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect, 264 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) { 265 prepareCreateAnimation(destView); 266 addItem(destInfo); 267 // This will animate the first item from it's position as an icon into its 268 // position as the first item in the preview 269 mPreviewItemManager.createFirstItemAnimation(false /* reverse */, null) 270 .start(); 271 272 // This will animate the dragView (srcView) into the new folder 273 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable); 274 } 275 276 public void performDestroyAnimation(Runnable onCompleteRunnable) { 277 // This will animate the final item in the preview to be full size. 278 mPreviewItemManager.createFirstItemAnimation(true /* reverse */, onCompleteRunnable) 279 .start(); 280 } 281 282 public void onDragExit() { 283 mBackground.animateToRest(); 284 mOpenAlarm.cancelAlarm(); 285 } 286 287 private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect, 288 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) { 289 item.cellX = -1; 290 item.cellY = -1; 291 292 // Typically, the animateView corresponds to the DragView; however, if this is being done 293 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we 294 // will not have a view to animate 295 if (animateView != null) { 296 DragLayer dragLayer = mLauncher.getDragLayer(); 297 Rect from = new Rect(); 298 dragLayer.getViewRectRelativeToSelf(animateView, from); 299 Rect to = finalRect; 300 if (to == null) { 301 to = new Rect(); 302 Workspace workspace = mLauncher.getWorkspace(); 303 // Set cellLayout and this to it's final state to compute final animation locations 304 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); 305 float scaleX = getScaleX(); 306 float scaleY = getScaleY(); 307 setScaleX(1.0f); 308 setScaleY(1.0f); 309 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); 310 // Finished computing final animation locations, restore current state 311 setScaleX(scaleX); 312 setScaleY(scaleY); 313 workspace.resetTransitionTransform((CellLayout) getParent().getParent()); 314 } 315 316 boolean itemAdded = false; 317 if (index >= mPreviewLayoutRule.maxNumItems() 318 && mPreviewLayoutRule.hasEnterExitIndices()) { 319 List<BubbleTextView> oldPreviewItems = getPreviewItemsOnPage(0); 320 addItem(item, false); 321 List<BubbleTextView> newPreviewItems = getPreviewItemsOnPage(0); 322 323 if (!oldPreviewItems.containsAll(newPreviewItems)) { 324 for (int i = 0; i < newPreviewItems.size(); ++i) { 325 if (newPreviewItems.get(i).getTag().equals(item)) { 326 // If the item dropped is going to be in the preview, we update the 327 // index here to reflect its position in the preview. 328 index = i; 329 } 330 } 331 mPreviewItemManager.onDrop(oldPreviewItems, newPreviewItems, item); 332 itemAdded = true; 333 } else { 334 removeItem(item, false); 335 } 336 } 337 338 if (!itemAdded) { 339 addItem(item); 340 } 341 342 int[] center = new int[2]; 343 float scale = getLocalCenterForIndex(index, index + 1, center); 344 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); 345 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); 346 347 to.offset(center[0] - animateView.getMeasuredWidth() / 2, 348 center[1] - animateView.getMeasuredHeight() / 2); 349 350 float finalAlpha = index < mPreviewLayoutRule.maxNumItems() ? 0.5f : 0f; 351 352 float finalScale = scale * scaleRelativeToDragLayer; 353 dragLayer.animateView(animateView, from, to, finalAlpha, 354 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, 355 new DecelerateInterpolator(2), new AccelerateInterpolator(2), 356 postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); 357 358 mFolder.hideItem(item); 359 360 if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true); 361 final int finalIndex = index; 362 postDelayed(new Runnable() { 363 public void run() { 364 mPreviewItemManager.hidePreviewItem(finalIndex, false); 365 mFolder.showItem(item); 366 invalidate(); 367 } 368 }, DROP_IN_ANIMATION_DURATION); 369 } else { 370 addItem(item); 371 } 372 } 373 374 public void onDrop(DragObject d) { 375 ShortcutInfo item; 376 if (d.dragInfo instanceof AppInfo) { 377 // Came from all apps -- make a copy 378 item = ((AppInfo) d.dragInfo).makeShortcut(); 379 } else if (d.dragSource instanceof BaseItemDragListener){ 380 // Came from a different window -- make a copy 381 item = new ShortcutInfo((ShortcutInfo) d.dragInfo); 382 } else { 383 item = (ShortcutInfo) d.dragInfo; 384 } 385 mFolder.notifyDrop(); 386 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable); 387 } 388 389 public void setBadgeInfo(FolderBadgeInfo badgeInfo) { 390 updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge()); 391 mBadgeInfo = badgeInfo; 392 } 393 394 public PreviewLayoutRule getLayoutRule() { 395 return mPreviewLayoutRule; 396 } 397 398 /** 399 * Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false 400 * (the badge is being added or removed). 401 */ 402 private void updateBadgeScale(boolean wasBadged, boolean isBadged) { 403 float newBadgeScale = isBadged ? 1f : 0f; 404 // Animate when a badge is first added or when it is removed. 405 if ((wasBadged ^ isBadged) && isShown()) { 406 createBadgeScaleAnimator(newBadgeScale).start(); 407 } else { 408 mBadgeScale = newBadgeScale; 409 invalidate(); 410 } 411 } 412 413 public Animator createBadgeScaleAnimator(float... badgeScales) { 414 return ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, badgeScales); 415 } 416 417 public boolean hasBadge() { 418 return mBadgeInfo != null && mBadgeInfo.hasBadge(); 419 } 420 421 private float getLocalCenterForIndex(int index, int curNumItems, int[] center) { 422 mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams( 423 Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams); 424 425 mTmpParams.transX += mBackground.basePreviewOffsetX; 426 mTmpParams.transY += mBackground.basePreviewOffsetY; 427 428 float intrinsicIconSize = mPreviewItemManager.getIntrinsicIconSize(); 429 float offsetX = mTmpParams.transX + (mTmpParams.scale * intrinsicIconSize) / 2; 430 float offsetY = mTmpParams.transY + (mTmpParams.scale * intrinsicIconSize) / 2; 431 432 center[0] = Math.round(offsetX); 433 center[1] = Math.round(offsetY); 434 return mTmpParams.scale; 435 } 436 437 public void setFolderBackground(PreviewBackground bg) { 438 mBackground = bg; 439 mBackground.setInvalidateDelegate(this); 440 } 441 442 public void setBackgroundVisible(boolean visible) { 443 mBackgroundIsVisible = visible; 444 invalidate(); 445 } 446 447 public PreviewBackground getFolderBackground() { 448 return mBackground; 449 } 450 451 public PreviewItemManager getPreviewItemManager() { 452 return mPreviewItemManager; 453 } 454 455 @Override 456 protected void dispatchDraw(Canvas canvas) { 457 super.dispatchDraw(canvas); 458 459 if (!mBackgroundIsVisible) return; 460 461 mPreviewItemManager.recomputePreviewDrawingParams(); 462 463 if (!mBackground.drawingDelegated()) { 464 mBackground.drawBackground(canvas); 465 } 466 467 if (mFolder == null) return; 468 if (mFolder.getItemCount() == 0 && !mAnimating) return; 469 470 final int saveCount; 471 472 if (canvas.isHardwareAccelerated()) { 473 saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, 474 Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); 475 } else { 476 saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); 477 if (mPreviewLayoutRule.clipToBackground()) { 478 canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT); 479 } 480 } 481 482 mPreviewItemManager.draw(canvas); 483 484 if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) { 485 mBackground.clipCanvasHardware(canvas); 486 } 487 canvas.restoreToCount(saveCount); 488 489 if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) { 490 mBackground.drawBackgroundStroke(canvas); 491 } 492 493 drawBadge(canvas); 494 } 495 496 public void drawBadge(Canvas canvas) { 497 if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) { 498 int offsetX = mBackground.getOffsetX(); 499 int offsetY = mBackground.getOffsetY(); 500 int previewSize = (int) (mBackground.previewSize * mBackground.mScale); 501 mTempBounds.set(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize); 502 503 // If we are animating to the accepting state, animate the badge out. 504 float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress()); 505 mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top); 506 IconPalette badgePalette = IconPalette.getFolderBadgePalette(getResources()); 507 mBadgeRenderer.draw(canvas, badgePalette, mBadgeInfo, mTempBounds, 508 badgeScale, mTempSpaceForBadgeOffset); 509 } 510 } 511 512 public void setTextVisible(boolean visible) { 513 if (visible) { 514 mFolderName.setVisibility(VISIBLE); 515 } else { 516 mFolderName.setVisibility(INVISIBLE); 517 } 518 } 519 520 public boolean getTextVisible() { 521 return mFolderName.getVisibility() == VISIBLE; 522 } 523 524 /** 525 * Returns the list of preview items displayed in the icon. 526 */ 527 public List<BubbleTextView> getPreviewItems() { 528 return getPreviewItemsOnPage(0); 529 } 530 531 /** 532 * Returns the list of "preview items" on {@param page}. 533 */ 534 public List<BubbleTextView> getPreviewItemsOnPage(int page) { 535 mPreviewVerifier.setFolderInfo(mFolder.getInfo()); 536 537 List<BubbleTextView> itemsToDisplay = new ArrayList<>(); 538 List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page); 539 int numItems = itemsOnPage.size(); 540 for (int rank = 0; rank < numItems; ++rank) { 541 if (mPreviewVerifier.isItemInPreview(page, rank)) { 542 itemsToDisplay.add(itemsOnPage.get(rank)); 543 } 544 545 if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) { 546 break; 547 } 548 } 549 return itemsToDisplay; 550 } 551 552 @Override 553 protected boolean verifyDrawable(@NonNull Drawable who) { 554 return mPreviewItemManager.verifyDrawable(who) || super.verifyDrawable(who); 555 } 556 557 @Override 558 public void onItemsChanged(boolean animate) { 559 mPreviewItemManager.updateItemDrawingParams(animate); 560 invalidate(); 561 requestLayout(); 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.getPopupDataProvider().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.getPopupDataProvider().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 shrinkAndFadeIn(boolean animate) { 635 // We remove and re-draw the FolderIcon in-case it has changed 636 final PreviewImageView previewImage = PreviewImageView.get(getContext()); 637 previewImage.removeFromParent(); 638 copyToPreview(previewImage); 639 640 clearLeaveBehindIfExists(); 641 642 ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1); 643 oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); 644 oa.addListener(new AnimatorListenerAdapter() { 645 @Override 646 public void onAnimationEnd(Animator animation) { 647 // Remove the ImageView copy of the FolderIcon and make the original visible. 648 previewImage.removeFromParent(); 649 setVisibility(View.VISIBLE); 650 } 651 }); 652 oa.start(); 653 if (!animate) { 654 oa.end(); 655 } 656 } 657 658 public void clearLeaveBehindIfExists() { 659 ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; 660 if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 661 CellLayout cl = (CellLayout) getParent().getParent(); 662 cl.clearFolderLeaveBehind(); 663 } 664 } 665 666 public void drawLeaveBehindIfExists() { 667 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 668 // While the folder is open, the position of the icon cannot change. 669 lp.canReorder = false; 670 if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 671 CellLayout cl = (CellLayout) getParent().getParent(); 672 cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY); 673 } 674 } 675 676 public void growAndFadeOut() { 677 drawLeaveBehindIfExists(); 678 679 // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original 680 PreviewImageView previewImage = PreviewImageView.get(getContext()); 681 copyToPreview(previewImage); 682 setVisibility(View.INVISIBLE); 683 684 ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 0, 1.5f, 1.5f); 685 oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); 686 oa.start(); 687 } 688 689 /** 690 * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView 691 * in the DragLayer in the exact absolute location of the original FolderIcon. 692 */ 693 private void copyToPreview(PreviewImageView previewImageView) { 694 previewImageView.copy(this); 695 if (mFolder != null) { 696 previewImageView.setPivotX(mFolder.getPivotXForIconAnimation()); 697 previewImageView.setPivotY(mFolder.getPivotYForIconAnimation()); 698 mFolder.bringToFront(); 699 } 700 } 701 702 public void onFolderClose(int currentPage) { 703 mPreviewItemManager.onFolderClose(currentPage); 704 } 705 706 interface PreviewLayoutRule { 707 PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems, 708 PreviewItemDrawingParams params); 709 void init(int availableSpace, float intrinsicIconSize, boolean rtl); 710 float scaleForItem(int index, int totalNumItems); 711 float getIconSize(); 712 int maxNumItems(); 713 boolean clipToBackground(); 714 715 boolean hasEnterExitIndices(); 716 int getExitIndex(); 717 int getEnterIndex(); 718 719 } 720 } 721