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.launcher2; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.animation.ValueAnimator.AnimatorUpdateListener; 23 import android.content.Context; 24 import android.content.res.Resources; 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.PorterDuff; 28 import android.graphics.Rect; 29 import android.graphics.drawable.Drawable; 30 import android.os.Parcelable; 31 import android.util.AttributeSet; 32 import android.view.LayoutInflater; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.view.animation.AccelerateInterpolator; 37 import android.view.animation.DecelerateInterpolator; 38 import android.widget.ImageView; 39 import android.widget.LinearLayout; 40 import android.widget.TextView; 41 42 import com.android.launcher.R; 43 import com.android.launcher2.DropTarget.DragObject; 44 import com.android.launcher2.FolderInfo.FolderListener; 45 46 import java.util.ArrayList; 47 48 /** 49 * An icon that can appear on in the workspace representing an {@link UserFolder}. 50 */ 51 public class FolderIcon extends LinearLayout implements FolderListener { 52 private Launcher mLauncher; 53 Folder mFolder; 54 FolderInfo mInfo; 55 private static boolean sStaticValuesDirty = true; 56 57 private CheckLongPressHelper mLongPressHelper; 58 59 // The number of icons to display in the 60 private static final int NUM_ITEMS_IN_PREVIEW = 3; 61 private static final int CONSUMPTION_ANIMATION_DURATION = 100; 62 private static final int DROP_IN_ANIMATION_DURATION = 400; 63 private static final int INITIAL_ITEM_ANIMATION_DURATION = 350; 64 65 // The degree to which the inner ring grows when accepting drop 66 private static final float INNER_RING_GROWTH_FACTOR = 0.15f; 67 68 // The degree to which the outer ring is scaled in its natural state 69 private static final float OUTER_RING_GROWTH_FACTOR = 0.3f; 70 71 // The amount of vertical spread between items in the stack [0...1] 72 private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f; 73 74 // The degree to which the item in the back of the stack is scaled [0...1] 75 // (0 means it's not scaled at all, 1 means it's scaled to nothing) 76 private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f; 77 78 public static Drawable sSharedFolderLeaveBehind = null; 79 80 private ImageView mPreviewBackground; 81 private BubbleTextView mFolderName; 82 83 FolderRingAnimator mFolderRingAnimator = null; 84 85 // These variables are all associated with the drawing of the preview; they are stored 86 // as member variables for shared usage and to avoid computation on each frame 87 private int mIntrinsicIconSize; 88 private float mBaselineIconScale; 89 private int mBaselineIconSize; 90 private int mAvailableSpaceInPreview; 91 private int mTotalWidth = -1; 92 private int mPreviewOffsetX; 93 private int mPreviewOffsetY; 94 private float mMaxPerspectiveShift; 95 boolean mAnimating = false; 96 private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0); 97 private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0); 98 99 public FolderIcon(Context context, AttributeSet attrs) { 100 super(context, attrs); 101 init(); 102 } 103 104 public FolderIcon(Context context) { 105 super(context); 106 init(); 107 } 108 109 private void init() { 110 mLongPressHelper = new CheckLongPressHelper(this); 111 } 112 113 public boolean isDropEnabled() { 114 final ViewGroup cellLayoutChildren = (ViewGroup) getParent(); 115 final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent(); 116 final Workspace workspace = (Workspace) cellLayout.getParent(); 117 return !workspace.isSmall(); 118 } 119 120 static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, 121 FolderInfo folderInfo, IconCache iconCache) { 122 @SuppressWarnings("all") // suppress dead code warning 123 final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION; 124 if (error) { 125 throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " + 126 "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " + 127 "is dependent on this"); 128 } 129 130 FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false); 131 132 icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name); 133 icon.mFolderName.setText(folderInfo.title); 134 icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background); 135 136 icon.setTag(folderInfo); 137 icon.setOnClickListener(launcher); 138 icon.mInfo = folderInfo; 139 icon.mLauncher = launcher; 140 icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format), 141 folderInfo.title)); 142 Folder folder = Folder.fromXml(launcher); 143 folder.setDragController(launcher.getDragController()); 144 folder.setFolderIcon(icon); 145 folder.bind(folderInfo); 146 icon.mFolder = folder; 147 148 icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon); 149 folderInfo.addListener(icon); 150 151 return icon; 152 } 153 154 @Override 155 protected Parcelable onSaveInstanceState() { 156 sStaticValuesDirty = true; 157 return super.onSaveInstanceState(); 158 } 159 160 public static class FolderRingAnimator { 161 public int mCellX; 162 public int mCellY; 163 private CellLayout mCellLayout; 164 public float mOuterRingSize; 165 public float mInnerRingSize; 166 public FolderIcon mFolderIcon = null; 167 public Drawable mOuterRingDrawable = null; 168 public Drawable mInnerRingDrawable = null; 169 public static Drawable sSharedOuterRingDrawable = null; 170 public static Drawable sSharedInnerRingDrawable = null; 171 public static int sPreviewSize = -1; 172 public static int sPreviewPadding = -1; 173 174 private ValueAnimator mAcceptAnimator; 175 private ValueAnimator mNeutralAnimator; 176 177 public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) { 178 mFolderIcon = folderIcon; 179 Resources res = launcher.getResources(); 180 mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); 181 mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); 182 183 // We need to reload the static values when configuration changes in case they are 184 // different in another configuration 185 if (sStaticValuesDirty) { 186 sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size); 187 sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding); 188 sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo); 189 sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo); 190 sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest); 191 sStaticValuesDirty = false; 192 } 193 } 194 195 public void animateToAcceptState() { 196 if (mNeutralAnimator != null) { 197 mNeutralAnimator.cancel(); 198 } 199 mAcceptAnimator = ValueAnimator.ofFloat(0f, 1f); 200 mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); 201 202 final int previewSize = sPreviewSize; 203 mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() { 204 public void onAnimationUpdate(ValueAnimator animation) { 205 final float percent = (Float) animation.getAnimatedValue(); 206 mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * previewSize; 207 mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * previewSize; 208 if (mCellLayout != null) { 209 mCellLayout.invalidate(); 210 } 211 } 212 }); 213 mAcceptAnimator.addListener(new AnimatorListenerAdapter() { 214 @Override 215 public void onAnimationStart(Animator animation) { 216 if (mFolderIcon != null) { 217 mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE); 218 } 219 } 220 }); 221 mAcceptAnimator.start(); 222 } 223 224 public void animateToNaturalState() { 225 if (mAcceptAnimator != null) { 226 mAcceptAnimator.cancel(); 227 } 228 mNeutralAnimator = ValueAnimator.ofFloat(0f, 1f); 229 mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION); 230 231 final int previewSize = sPreviewSize; 232 mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() { 233 public void onAnimationUpdate(ValueAnimator animation) { 234 final float percent = (Float) animation.getAnimatedValue(); 235 mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * previewSize; 236 mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * previewSize; 237 if (mCellLayout != null) { 238 mCellLayout.invalidate(); 239 } 240 } 241 }); 242 mNeutralAnimator.addListener(new AnimatorListenerAdapter() { 243 @Override 244 public void onAnimationEnd(Animator animation) { 245 if (mCellLayout != null) { 246 mCellLayout.hideFolderAccept(FolderRingAnimator.this); 247 } 248 if (mFolderIcon != null) { 249 mFolderIcon.mPreviewBackground.setVisibility(VISIBLE); 250 } 251 } 252 }); 253 mNeutralAnimator.start(); 254 } 255 256 // Location is expressed in window coordinates 257 public void getCell(int[] loc) { 258 loc[0] = mCellX; 259 loc[1] = mCellY; 260 } 261 262 // Location is expressed in window coordinates 263 public void setCell(int x, int y) { 264 mCellX = x; 265 mCellY = y; 266 } 267 268 public void setCellLayout(CellLayout layout) { 269 mCellLayout = layout; 270 } 271 272 public float getOuterRingSize() { 273 return mOuterRingSize; 274 } 275 276 public float getInnerRingSize() { 277 return mInnerRingSize; 278 } 279 } 280 281 private boolean willAcceptItem(ItemInfo item) { 282 final int itemType = item.itemType; 283 return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || 284 itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) && 285 !mFolder.isFull() && item != mInfo && !mInfo.opened); 286 } 287 288 public boolean acceptDrop(Object dragInfo) { 289 final ItemInfo item = (ItemInfo) dragInfo; 290 return willAcceptItem(item); 291 } 292 293 public void addItem(ShortcutInfo item) { 294 mInfo.add(item); 295 LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); 296 } 297 298 public void onDragEnter(Object dragInfo) { 299 if (!willAcceptItem((ItemInfo) dragInfo)) return; 300 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); 301 CellLayout layout = (CellLayout) getParent().getParent(); 302 mFolderRingAnimator.setCell(lp.cellX, lp.cellY); 303 mFolderRingAnimator.setCellLayout(layout); 304 mFolderRingAnimator.animateToAcceptState(); 305 layout.showFolderAccept(mFolderRingAnimator); 306 } 307 308 public void onDragOver(Object dragInfo) { 309 } 310 311 public void performCreateAnimation(final ShortcutInfo destInfo, final View destView, 312 final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect, 313 float scaleRelativeToDragLayer, Runnable postAnimationRunnable) { 314 315 Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1]; 316 computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth()); 317 318 // This will animate the dragView (srcView) into the new folder 319 onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null); 320 321 // This will animate the first item from it's position as an icon into its 322 // position as the first item in the preview 323 animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION); 324 addItem(destInfo); 325 } 326 327 public void onDragExit(Object dragInfo) { 328 onDragExit(); 329 } 330 331 public void onDragExit() { 332 mFolderRingAnimator.animateToNaturalState(); 333 } 334 335 private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect, 336 float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable, 337 DragObject d) { 338 item.cellX = -1; 339 item.cellY = -1; 340 341 // Typically, the animateView corresponds to the DragView; however, if this is being done 342 // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we 343 // will not have a view to animate 344 if (animateView != null) { 345 DragLayer dragLayer = mLauncher.getDragLayer(); 346 Rect from = new Rect(); 347 dragLayer.getViewRectRelativeToSelf(animateView, from); 348 Rect to = finalRect; 349 if (to == null) { 350 to = new Rect(); 351 Workspace workspace = mLauncher.getWorkspace(); 352 // Set cellLayout and this to it's final state to compute final animation locations 353 workspace.setFinalTransitionTransform((CellLayout) getParent().getParent()); 354 float scaleX = getScaleX(); 355 float scaleY = getScaleY(); 356 setScaleX(1.0f); 357 setScaleY(1.0f); 358 scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to); 359 // Finished computing final animation locations, restore current state 360 setScaleX(scaleX); 361 setScaleY(scaleY); 362 workspace.resetTransitionTransform((CellLayout) getParent().getParent()); 363 } 364 365 int[] center = new int[2]; 366 float scale = getLocalCenterForIndex(index, center); 367 center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); 368 center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); 369 370 to.offset(center[0] - animateView.getMeasuredWidth() / 2, 371 center[1] - animateView.getMeasuredHeight() / 2); 372 373 float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f; 374 375 float finalScale = scale * scaleRelativeToDragLayer; 376 dragLayer.animateView(animateView, from, to, finalAlpha, 377 1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION, 378 new DecelerateInterpolator(2), new AccelerateInterpolator(2), 379 postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); 380 postDelayed(new Runnable() { 381 public void run() { 382 addItem(item); 383 } 384 }, DROP_IN_ANIMATION_DURATION); 385 } else { 386 addItem(item); 387 } 388 } 389 390 public void onDrop(DragObject d) { 391 ShortcutInfo item; 392 if (d.dragInfo instanceof ApplicationInfo) { 393 // Came from all apps -- make a copy 394 item = ((ApplicationInfo) d.dragInfo).makeShortcut(); 395 } else { 396 item = (ShortcutInfo) d.dragInfo; 397 } 398 mFolder.notifyDrop(); 399 onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d); 400 } 401 402 public DropTarget getDropTargetDelegate(DragObject d) { 403 return null; 404 } 405 406 private void computePreviewDrawingParams(int drawableSize, int totalSize) { 407 if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) { 408 mIntrinsicIconSize = drawableSize; 409 mTotalWidth = totalSize; 410 411 final int previewSize = FolderRingAnimator.sPreviewSize; 412 final int previewPadding = FolderRingAnimator.sPreviewPadding; 413 414 mAvailableSpaceInPreview = (previewSize - 2 * previewPadding); 415 // cos(45) = 0.707 + ~= 0.1) = 0.8f 416 int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f)); 417 418 int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR)); 419 mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight); 420 421 mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale); 422 mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR; 423 424 mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2; 425 mPreviewOffsetY = previewPadding; 426 } 427 } 428 429 private void computePreviewDrawingParams(Drawable d) { 430 computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth()); 431 } 432 433 class PreviewItemDrawingParams { 434 PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) { 435 this.transX = transX; 436 this.transY = transY; 437 this.scale = scale; 438 this.overlayAlpha = overlayAlpha; 439 } 440 float transX; 441 float transY; 442 float scale; 443 int overlayAlpha; 444 Drawable drawable; 445 } 446 447 private float getLocalCenterForIndex(int index, int[] center) { 448 mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams); 449 450 mParams.transX += mPreviewOffsetX; 451 mParams.transY += mPreviewOffsetY; 452 float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2; 453 float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2; 454 455 center[0] = (int) Math.round(offsetX); 456 center[1] = (int) Math.round(offsetY); 457 return mParams.scale; 458 } 459 460 private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, 461 PreviewItemDrawingParams params) { 462 index = NUM_ITEMS_IN_PREVIEW - index - 1; 463 float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1); 464 float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r)); 465 466 float offset = (1 - r) * mMaxPerspectiveShift; 467 float scaledSize = scale * mBaselineIconSize; 468 float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize; 469 470 // We want to imagine our coordinates from the bottom left, growing up and to the 471 // right. This is natural for the x-axis, but for the y-axis, we have to invert things. 472 float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection); 473 float transX = offset + scaleOffsetCorrection; 474 float totalScale = mBaselineIconScale * scale; 475 final int overlayAlpha = (int) (80 * (1 - r)); 476 477 if (params == null) { 478 params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha); 479 } else { 480 params.transX = transX; 481 params.transY = transY; 482 params.scale = totalScale; 483 params.overlayAlpha = overlayAlpha; 484 } 485 return params; 486 } 487 488 private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) { 489 canvas.save(); 490 canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY); 491 canvas.scale(params.scale, params.scale); 492 Drawable d = params.drawable; 493 494 if (d != null) { 495 d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize); 496 d.setFilterBitmap(true); 497 d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP); 498 d.draw(canvas); 499 d.clearColorFilter(); 500 d.setFilterBitmap(false); 501 } 502 canvas.restore(); 503 } 504 505 @Override 506 protected void dispatchDraw(Canvas canvas) { 507 super.dispatchDraw(canvas); 508 509 if (mFolder == null) return; 510 if (mFolder.getItemCount() == 0 && !mAnimating) return; 511 512 ArrayList<View> items = mFolder.getItemsInReadingOrder(false); 513 Drawable d; 514 TextView v; 515 516 // Update our drawing parameters if necessary 517 if (mAnimating) { 518 computePreviewDrawingParams(mAnimParams.drawable); 519 } else { 520 v = (TextView) items.get(0); 521 d = v.getCompoundDrawables()[1]; 522 computePreviewDrawingParams(d); 523 } 524 525 int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW); 526 if (!mAnimating) { 527 for (int i = nItemsInPreview - 1; i >= 0; i--) { 528 v = (TextView) items.get(i); 529 d = v.getCompoundDrawables()[1]; 530 531 mParams = computePreviewItemDrawingParams(i, mParams); 532 mParams.drawable = d; 533 drawPreviewItem(canvas, mParams); 534 } 535 } else { 536 drawPreviewItem(canvas, mAnimParams); 537 } 538 } 539 540 private void animateFirstItem(final Drawable d, int duration) { 541 computePreviewDrawingParams(d); 542 final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null); 543 544 final float scale0 = 1.0f; 545 final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2; 546 final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2; 547 mAnimParams.drawable = d; 548 549 ValueAnimator va = ValueAnimator.ofFloat(0f, 1.0f); 550 va.addUpdateListener(new AnimatorUpdateListener(){ 551 public void onAnimationUpdate(ValueAnimator animation) { 552 float progress = (Float) animation.getAnimatedValue(); 553 554 mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0); 555 mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0); 556 mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0); 557 invalidate(); 558 } 559 }); 560 va.addListener(new AnimatorListenerAdapter() { 561 @Override 562 public void onAnimationStart(Animator animation) { 563 mAnimating = true; 564 } 565 @Override 566 public void onAnimationEnd(Animator animation) { 567 mAnimating = false; 568 } 569 }); 570 va.setDuration(duration); 571 va.start(); 572 } 573 574 public void setTextVisible(boolean visible) { 575 if (visible) { 576 mFolderName.setVisibility(VISIBLE); 577 } else { 578 mFolderName.setVisibility(INVISIBLE); 579 } 580 } 581 582 public boolean getTextVisible() { 583 return mFolderName.getVisibility() == VISIBLE; 584 } 585 586 public void onItemsChanged() { 587 invalidate(); 588 requestLayout(); 589 } 590 591 public void onAdd(ShortcutInfo item) { 592 invalidate(); 593 requestLayout(); 594 } 595 596 public void onRemove(ShortcutInfo item) { 597 invalidate(); 598 requestLayout(); 599 } 600 601 public void onTitleChanged(CharSequence title) { 602 mFolderName.setText(title.toString()); 603 setContentDescription(String.format(getContext().getString(R.string.folder_name_format), 604 title)); 605 } 606 607 @Override 608 public boolean onTouchEvent(MotionEvent event) { 609 // Call the superclass onTouchEvent first, because sometimes it changes the state to 610 // isPressed() on an ACTION_UP 611 boolean result = super.onTouchEvent(event); 612 613 switch (event.getAction()) { 614 case MotionEvent.ACTION_DOWN: 615 mLongPressHelper.postCheckForLongPress(); 616 break; 617 case MotionEvent.ACTION_CANCEL: 618 case MotionEvent.ACTION_UP: 619 mLongPressHelper.cancelLongPress(); 620 break; 621 } 622 return result; 623 } 624 625 @Override 626 public void cancelLongPress() { 627 super.cancelLongPress(); 628 629 mLongPressHelper.cancelLongPress(); 630 } 631 } 632