summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/FolderIcon.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/FolderIcon.java')
-rw-r--r--src/com/android/launcher3/FolderIcon.java667
1 files changed, 667 insertions, 0 deletions
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
new file mode 100644
index 000000000..e11d7d18a
--- /dev/null
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -0,0 +1,667 @@
+/*
+ * 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.android.launcher3;
+
+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.Parcelable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.FolderInfo.FolderListener;
+
+import java.util.ArrayList;
+
+/**
+ * An icon that can appear on in the workspace representing an {@link UserFolder}.
+ */
+public class FolderIcon extends LinearLayout 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.24f;
+
+ // 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;
+
+ 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 PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+ private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+ private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
+
+ 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.isSmall();
+ }
+
+ 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");
+ }
+
+ FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
+
+ icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
+ icon.mFolderName.setText(folderInfo.title);
+ icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
+
+ 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);
+
+ 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 Drawable mOuterRingDrawable = null;
+ public Drawable mInnerRingDrawable = 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();
+ mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
+ mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
+
+ // We need to reload the static values when configuration changes in case they are
+ // different in another configuration
+ if (sStaticValuesDirty) {
+ sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
+ 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_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;
+ }
+ }
+
+ 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);
+ }
+
+ public void onDragOver(Object dragInfo) {
+ }
+
+ 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 = ((TextView) destView).getCompoundDrawables()[1];
+ 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 = ((TextView) finalView).getCompoundDrawables()[1];
+ 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();
+ }
+
+ 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 ApplicationInfo) {
+ // Came from all apps -- make a copy
+ item = ((ApplicationInfo) d.dragInfo).makeShortcut();
+ } else {
+ item = (ShortcutInfo) d.dragInfo;
+ }
+ mFolder.notifyDrop();
+ onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
+ }
+
+ public DropTarget getDropTargetDelegate(DragObject d) {
+ return null;
+ }
+
+ private void computePreviewDrawingParams(int drawableSize, int totalSize) {
+ if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
+ mIntrinsicIconSize = drawableSize;
+ mTotalWidth = totalSize;
+
+ final int previewSize = FolderRingAnimator.sPreviewSize;
+ 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;
+ }
+ }
+
+ 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);
+ float transX = offset + scaleOffsetCorrection;
+ 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) {
+ d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
+ d.setFilterBitmap(true);
+ d.setColorFilter(Color.argb(params.overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
+ d.draw(canvas);
+ d.clearColorFilter();
+ d.setFilterBitmap(false);
+ }
+ 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 = v.getCompoundDrawables()[1];
+ 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 = v.getCompoundDrawables()[1];
+ mParams = computePreviewItemDrawingParams(i, mParams);
+ mParams.drawable = d;
+ drawPreviewItem(canvas, mParams);
+ }
+ }
+ } else {
+ drawPreviewItem(canvas, mAnimParams);
+ }
+ }
+
+ 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;
+ 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;
+ }
+ return result;
+ }
+
+ @Override
+ public void cancelLongPress() {
+ super.cancelLongPress();
+
+ mLongPressHelper.cancelLongPress();
+ }
+}