content: /*
   * Copyright (C) 2008 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.lb.launcher;
  
  import android.animation.Animator;
  import android.animation.AnimatorListenerAdapter;
  import android.animation.ValueAnimator;
  import android.animation.ValueAnimator.AnimatorUpdateListener;
  import android.content.Context;
  import android.content.res.Resources;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.PorterDuff;
  import android.graphics.Rect;
  import android.graphics.drawable.Drawable;
  import android.os.Looper;
  import android.os.Parcelable;
  import android.util.AttributeSet;
  import android.view.LayoutInflater;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewConfiguration;
  import android.view.ViewGroup;
  import android.view.animation.AccelerateInterpolator;
  import android.view.animation.DecelerateInterpolator;
  import android.widget.FrameLayout;
  import android.widget.ImageView;
  import android.widget.TextView;
  import com.lb.launcher.R;
  import com.lb.launcher.DropTarget.DragObject;
  import com.lb.launcher.FolderInfo.FolderListener;
  import java.util.ArrayList;
  
  /**
   * An icon that can appear on in the workspace representing an {@link UserFolder}.
   */
  public class FolderIcon extends FrameLayout implements FolderListener {
      private Launcher mLauncher;
      private Folder mFolder;
      private FolderInfo mInfo;
      private static boolean sStaticValuesDirty = true;
  
      private CheckLongPressHelper mLongPressHelper;
  
      // The number of icons to display in the
      private static final int NUM_ITEMS_IN_PREVIEW = 3;
      private static final int CONSUMPTION_ANIMATION_DURATION = 100;
      private static final int DROP_IN_ANIMATION_DURATION = 400;
      private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
      private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
  
      // The degree to which the inner ring grows when accepting drop
      private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
  
      // The degree to which the outer ring is scaled in its natural state
      private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
  
      // The amount of vertical spread between items in the stack [0...1]
      private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
  
      // Flag as to whether or not to draw an outer ring. Currently none is designed.
      public static final boolean HAS_OUTER_RING = true;
  
      // Flag whether the folder should open itself when an item is dragged over is enabled.
      public static final boolean SPRING_LOADING_ENABLED = true;
  
      // The degree to which the item in the back of the stack is scaled [0...1]
      // (0 means it's not scaled at all, 1 means it's scaled to nothing)
      private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
  
      // Delay when drag enters until the folder opens, in miliseconds.
      private static final int ON_OPEN_DELAY = 800;
  
      public static Drawable sSharedFolderLeaveBehind = null;
  
      private ImageView mPreviewBackground;
      private BubbleTextView mFolderName;
  
      FolderRingAnimator mFolderRingAnimator = null;
  
      // These variables are all associated with the drawing of the preview; they are stored
      // as member variables for shared usage and to avoid computation on each frame
      private int mIntrinsicIconSize;
      private float mBaselineIconScale;
      private int mBaselineIconSize;
      private int mAvailableSpaceInPreview;
      private int mTotalWidth = -1;
      private int mPreviewOffsetX;
      private int mPreviewOffsetY;
      private float mMaxPerspectiveShift;
      boolean mAnimating = false;
      private Rect mOldBounds = new Rect();
  
      private float mSlop;
  
      private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
      private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
      private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
  
      private Alarm mOpenAlarm = new Alarm();
      private ItemInfo mDragInfo;
  
      public FolderIcon(Context context, AttributeSet attrs) {
          super(context, attrs);
          init();
      }
  
      public FolderIcon(Context context) {
          super(context);
          init();
      }
  
      private void init() {
          mLongPressHelper = new CheckLongPressHelper(this);
      }
  
      public boolean isDropEnabled() {
          final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
          final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
          final Workspace workspace = (Workspace) cellLayout.getParent();
          return !workspace.workspaceInModalState();
      }
  
      static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
              FolderInfo folderInfo, IconCache iconCache) {
          @SuppressWarnings("all") // suppress dead code warning
          final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
          if (error) {
              throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
                      "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
                      "is dependent on this");
          }
          LauncherAppState app = LauncherAppState.getInstance();
          DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
  
          FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
          icon.setClipToPadding(false);
          icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
          icon.mFolderName.setText(folderInfo.title);
          icon.mFolderName.setCompoundDrawablePadding(0);
          FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
          lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
  
          // Offset the preview background to center this view accordingly
          icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
          lp = (FrameLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
          lp.topMargin = grid.folderBackgroundOffset;
          lp.width = grid.folderIconSizePx;
          lp.height = grid.folderIconSizePx;
  
          icon.setTag(folderInfo);
          icon.setOnClickListener(launcher);
          icon.mInfo = folderInfo;
          icon.mLauncher = launcher;
          icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
                  folderInfo.title));
          Folder folder = Folder.fromXml(launcher);
          folder.setDragController(launcher.getDragController());
          folder.setFolderIcon(icon);
          folder.bind(folderInfo);
          icon.mFolder = folder;
  
          icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
          folderInfo.addListener(icon);
  
          icon.setOnFocusChangeListener(launcher.mFocusHandler);
          return icon;
      }
  
      @Override
      protected Parcelable onSaveInstanceState() {
          sStaticValuesDirty = true;
          return super.onSaveInstanceState();
      }
  
      public static class FolderRingAnimator {
          public int mCellX;
          public int mCellY;
          private CellLayout mCellLayout;
          public float mOuterRingSize;
          public float mInnerRingSize;
          public FolderIcon mFolderIcon = null;
          public static Drawable sSharedOuterRingDrawable = null;
          public static Drawable sSharedInnerRingDrawable = null;
          public static int sPreviewSize = -1;
          public static int sPreviewPadding = -1;
  
          private ValueAnimator mAcceptAnimator;
          private ValueAnimator mNeutralAnimator;
  
          public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
              mFolderIcon = folderIcon;
              Resources res = launcher.getResources();
  
              // We need to reload the static values when configuration changes in case they are
              // different in another configuration
              if (sStaticValuesDirty) {
                  if (Looper.myLooper() != Looper.getMainLooper()) {
                      throw new RuntimeException("FolderRingAnimator loading drawables on non-UI thread "
                              + Thread.currentThread());
                  }
  
                  LauncherAppState app = LauncherAppState.getInstance();
                  DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
                  sPreviewSize = grid.folderIconSizePx;
                  sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
                  sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
                  sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_nolip_holo);
                  sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest);
                  sStaticValuesDirty = false;
              }
          }
  
          public void animateToAcceptState() {
              if (mNeutralAnimator != null) {
                  mNeutralAnimator.cancel();
              }
              mAcceptAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f);
              mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
  
              final int previewSize = sPreviewSize;
              mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
                  public void onAnimationUpdate(ValueAnimator animation) {
                      final float percent = (Float) animation.getAnimatedValue();
                      mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * previewSize;
                      mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * previewSize;
                      if (mCellLayout != null) {
                          mCellLayout.invalidate();
                      }
                  }
              });
              mAcceptAnimator.addListener(new AnimatorListenerAdapter() {
                  @Override
                  public void onAnimationStart(Animator animation) {
                      if (mFolderIcon != null) {
                          mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE);
                      }
                  }
              });
              mAcceptAnimator.start();
          }
  
          public void animateToNaturalState() {
              if (mAcceptAnimator != null) {
                  mAcceptAnimator.cancel();
              }
              mNeutralAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f);
              mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
  
              final int previewSize = sPreviewSize;
              mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
                  public void onAnimationUpdate(ValueAnimator animation) {
                      final float percent = (Float) animation.getAnimatedValue();
                      mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * previewSize;
                      mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * previewSize;
                      if (mCellLayout != null) {
                          mCellLayout.invalidate();
                      }
                  }
              });
              mNeutralAnimator.addListener(new AnimatorListenerAdapter() {
                  @Override
                  public void onAnimationEnd(Animator animation) {
                      if (mCellLayout != null) {
                          mCellLayout.hideFolderAccept(FolderRingAnimator.this);
                      }
                      if (mFolderIcon != null) {
                          mFolderIcon.mPreviewBackground.setVisibility(VISIBLE);
                      }
                  }
              });
              mNeutralAnimator.start();
          }
  
          // Location is expressed in window coordinates
          public void getCell(int[] loc) {
              loc[0] = mCellX;
              loc[1] = mCellY;
          }
  
          // Location is expressed in window coordinates
          public void setCell(int x, int y) {
              mCellX = x;
              mCellY = y;
          }
  
          public void setCellLayout(CellLayout layout) {
              mCellLayout = layout;
          }
  
          public float getOuterRingSize() {
              return mOuterRingSize;
          }
  
          public float getInnerRingSize() {
              return mInnerRingSize;
          }
      }
  
      public Folder getFolder() {
          return mFolder;
      }
  
      FolderInfo getFolderInfo() {
          return mInfo;
      }
  
      private boolean willAcceptItem(ItemInfo item) {
          final int itemType = item.itemType;
          return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                  itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
                  !mFolder.isFull() && item != mInfo && !mInfo.opened);
      }
  
      public boolean acceptDrop(Object dragInfo) {
          final ItemInfo item = (ItemInfo) dragInfo;
          return !mFolder.isDestroyed() && willAcceptItem(item);
      }
  
      public void addItem(ShortcutInfo item) {
          mInfo.add(item);
      }
  
      public void onDragEnter(Object dragInfo) {
          if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return;
          CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
          CellLayout layout = (CellLayout) getParent().getParent();
          mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
          mFolderRingAnimator.setCellLayout(layout);
          mFolderRingAnimator.animateToAcceptState();
          layout.showFolderAccept(mFolderRingAnimator);
          mOpenAlarm.setOnAlarmListener(mOnOpenListener);
          if (SPRING_LOADING_ENABLED &&
                  ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
              // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
              // though widget-style shortcuts can be added to folders. The issue is that we need
              // to deal with configuration activities which are currently handled in
              // Workspace#onDropExternal.
              mOpenAlarm.setAlarm(ON_OPEN_DELAY);
          }
          mDragInfo = (ItemInfo) dragInfo;
      }
  
      public void onDragOver(Object dragInfo) {
      }
  
      OnAlarmListener mOnOpenListener = new OnAlarmListener() {
          public void onAlarm(Alarm alarm) {
              ShortcutInfo item;
              if (mDragInfo instanceof AppInfo) {
                  // Came from all apps -- make a copy.
                  item = ((AppInfo) mDragInfo).makeShortcut();
                  item.spanX = 1;
                  item.spanY = 1;
              } else {
                  // ShortcutInfo
                  item = (ShortcutInfo) mDragInfo;
              }
              mFolder.beginExternalDrag(item);
              mLauncher.openFolder(FolderIcon.this);
          }
      };
  
      public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
              final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
              float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
  
          // These correspond two the drawable and view that the icon was dropped _onto_
          Drawable animateDrawable = getTopDrawable((TextView) destView);
          computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
                  destView.getMeasuredWidth());
  
          // This will animate the first item from it's position as an icon into its
          // position as the first item in the preview
          animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
          addItem(destInfo);
  
          // This will animate the dragView (srcView) into the new folder
          onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
      }
  
      public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
          Drawable animateDrawable = getTopDrawable((TextView) finalView);
          computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
                  finalView.getMeasuredWidth());
  
          // This will animate the first item from it's position as an icon into its
          // position as the first item in the preview
          animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
                  onCompleteRunnable);
      }
  
      public void onDragExit(Object dragInfo) {
          onDragExit();
      }
  
      public void onDragExit() {
          mFolderRingAnimator.animateToNaturalState();
          mOpenAlarm.cancelAlarm();
      }
  
      private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
              float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
              DragObject d) {
          item.cellX = -1;
          item.cellY = -1;
  
          // Typically, the animateView corresponds to the DragView; however, if this is being done
          // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
          // will not have a view to animate
          if (animateView != null) {
              DragLayer dragLayer = mLauncher.getDragLayer();
              Rect from = new Rect();
              dragLayer.getViewRectRelativeToSelf(animateView, from);
              Rect to = finalRect;
              if (to == null) {
                  to = new Rect();
                  Workspace workspace = mLauncher.getWorkspace();
                  // Set cellLayout and this to it's final state to compute final animation locations
                  workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
                  float scaleX = getScaleX();
                  float scaleY = getScaleY();
                  setScaleX(1.0f);
                  setScaleY(1.0f);
                  scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
                  // Finished computing final animation locations, restore current state
                  setScaleX(scaleX);
                  setScaleY(scaleY);
                  workspace.resetTransitionTransform((CellLayout) getParent().getParent());
              }
  
              int[] center = new int[2];
              float scale = getLocalCenterForIndex(index, center);
              center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
              center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
  
              to.offset(center[0] - animateView.getMeasuredWidth() / 2,
                        center[1] - animateView.getMeasuredHeight() / 2);
  
              float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
  
              float finalScale = scale * scaleRelativeToDragLayer;
              dragLayer.animateView(animateView, from, to, finalAlpha,
                      1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
                      new DecelerateInterpolator(2), new AccelerateInterpolator(2),
                      postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
              addItem(item);
              mHiddenItems.add(item);
              mFolder.hideItem(item);
              postDelayed(new Runnable() {
                  public void run() {
                      mHiddenItems.remove(item);
                      mFolder.showItem(item);
                      invalidate();
                  }
              }, DROP_IN_ANIMATION_DURATION);
          } else {
              addItem(item);
          }
      }
  
      public void onDrop(DragObject d) {
          ShortcutInfo item;
          if (d.dragInfo instanceof AppInfo) {
              // Came from all apps -- make a copy
              item = ((AppInfo) d.dragInfo).makeShortcut();
          } else {
              item = (ShortcutInfo) d.dragInfo;
          }
          mFolder.notifyDrop();
          onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
      }
  
      private void computePreviewDrawingParams(int drawableSize, int totalSize) {
          if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
              LauncherAppState app = LauncherAppState.getInstance();
              DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
  
              mIntrinsicIconSize = drawableSize;
              mTotalWidth = totalSize;
  
              final int previewSize = mPreviewBackground.getLayoutParams().height;
              final int previewPadding = FolderRingAnimator.sPreviewPadding;
  
              mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
              // cos(45) = 0.707  + ~= 0.1) = 0.8f
              int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
  
              int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
  
              mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
  
              mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
              mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
  
              mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
              mPreviewOffsetY = previewPadding + grid.folderBackgroundOffset;
          }
      }
  
      private void computePreviewDrawingParams(Drawable d) {
          computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
      }
  
      class PreviewItemDrawingParams {
          PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) {
              this.transX = transX;
              this.transY = transY;
              this.scale = scale;
              this.overlayAlpha = overlayAlpha;
          }
          float transX;
          float transY;
          float scale;
          int overlayAlpha;
          Drawable drawable;
      }
  
      private float getLocalCenterForIndex(int index, int[] center) {
          mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams);
  
          mParams.transX += mPreviewOffsetX;
          mParams.transY += mPreviewOffsetY;
          float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
          float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
  
          center[0] = (int) Math.round(offsetX);
          center[1] = (int) Math.round(offsetY);
          return mParams.scale;
      }
  
      private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
              PreviewItemDrawingParams params) {
          index = NUM_ITEMS_IN_PREVIEW - index - 1;
          float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
          float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
  
          float offset = (1 - r) * mMaxPerspectiveShift;
          float scaledSize = scale * mBaselineIconSize;
          float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
  
          // We want to imagine our coordinates from the bottom left, growing up and to the
          // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
          float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection) + getPaddingTop();
          float transX = (mAvailableSpaceInPreview - scaledSize) / 2;
          float totalScale = mBaselineIconScale * scale;
          final int overlayAlpha = (int) (80 * (1 - r));
  
          if (params == null) {
              params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
          } else {
              params.transX = transX;
              params.transY = transY;
              params.scale = totalScale;
              params.overlayAlpha = overlayAlpha;
          }
          return params;
      }
  
      private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
          canvas.save();
          canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY);
          canvas.scale(params.scale, params.scale);
          Drawable d = params.drawable;
  
          if (d != null) {
              mOldBounds.set(d.getBounds());
              d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
              if (d instanceof FastBitmapDrawable) {
                  FastBitmapDrawable fd = (FastBitmapDrawable) d;
                  int oldBrightness = fd.getBrightness();
                  fd.setBrightness(params.overlayAlpha);
                  d.draw(canvas);
                  fd.setBrightness(oldBrightness);
              } else {
                  d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
                          PorterDuff.Mode.SRC_ATOP);
                  d.draw(canvas);
                  d.clearColorFilter();
              }
              d.setBounds(mOldBounds);
          }
          canvas.restore();
      }
  
      @Override
      protected void dispatchDraw(Canvas canvas) {
          super.dispatchDraw(canvas);
  
          if (mFolder == null) return;
          if (mFolder.getItemCount() == 0 && !mAnimating) return;
  
          ArrayList<View> items = mFolder.getItemsInReadingOrder();
          Drawable d;
          TextView v;
  
          // Update our drawing parameters if necessary
          if (mAnimating) {
              computePreviewDrawingParams(mAnimParams.drawable);
          } else {
              v = (TextView) items.get(0);
              d = getTopDrawable(v);
              computePreviewDrawingParams(d);
          }
  
          int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
          if (!mAnimating) {
              for (int i = nItemsInPreview - 1; i >= 0; i--) {
                  v = (TextView) items.get(i);
                  if (!mHiddenItems.contains(v.getTag())) {
                      d = getTopDrawable(v);
                      mParams = computePreviewItemDrawingParams(i, mParams);
                      mParams.drawable = d;
                      drawPreviewItem(canvas, mParams);
                  }
              }
          } else {
              drawPreviewItem(canvas, mAnimParams);
          }
      }
  
      private Drawable getTopDrawable(TextView v) {
          Drawable d = v.getCompoundDrawables()[1];
          return (d instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) d).mIcon : d;
      }
  
      private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
              final Runnable onCompleteRunnable) {
          final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
  
          final float scale0 = 1.0f;
          final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2;
          final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2 + getPaddingTop();
          mAnimParams.drawable = d;
  
          ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f);
          va.addUpdateListener(new AnimatorUpdateListener(){
              public void onAnimationUpdate(ValueAnimator animation) {
                  float progress = (Float) animation.getAnimatedValue();
                  if (reverse) {
                      progress = 1 - progress;
                      mPreviewBackground.setAlpha(progress);
                  }
  
                  mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0);
                  mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0);
                  mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0);
                  invalidate();
              }
          });
          va.addListener(new AnimatorListenerAdapter() {
              @Override
              public void onAnimationStart(Animator animation) {
                  mAnimating = true;
              }
              @Override
              public void onAnimationEnd(Animator animation) {
                  mAnimating = false;
                  if (onCompleteRunnable != null) {
                      onCompleteRunnable.run();
                  }
              }
          });
          va.setDuration(duration);
          va.start();
      }
  
      public void setTextVisible(boolean visible) {
          if (visible) {
              mFolderName.setVisibility(VISIBLE);
          } else {
              mFolderName.setVisibility(INVISIBLE);
          }
      }
  
      public boolean getTextVisible() {
          return mFolderName.getVisibility() == VISIBLE;
      }
  
      public void onItemsChanged() {
          invalidate();
          requestLayout();
      }
  
      public void onAdd(ShortcutInfo item) {
          invalidate();
          requestLayout();
      }
  
      public void onRemove(ShortcutInfo item) {
          invalidate();
          requestLayout();
      }
  
      public void onTitleChanged(CharSequence title) {
          mFolderName.setText(title.toString());
          setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
                  title));
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          // Call the superclass onTouchEvent first, because sometimes it changes the state to
          // isPressed() on an ACTION_UP
          boolean result = super.onTouchEvent(event);
  
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
                  mLongPressHelper.postCheckForLongPress();
                  break;
              case MotionEvent.ACTION_CANCEL:
              case MotionEvent.ACTION_UP:
                  mLongPressHelper.cancelLongPress();
                  break;
              case MotionEvent.ACTION_MOVE:
                  if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
                      mLongPressHelper.cancelLongPress();
                  }
                  break;
          }
          return result;
      }
  
      @Override
      protected void onAttachedToWindow() {
          super.onAttachedToWindow();
          mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
      }
  
      @Override
      public void cancelLongPress() {
          super.cancelLongPress();
  
          mLongPressHelper.cancelLongPress();
      }
  }
  