ed66b2bac7
-> When a widget has no configuration activity, we bind and inflate it when the user picks it up. This allows us to smoothly transition between it's preview and some actual state of the widget when it is dropped. -> When a widget has a configuration activity, we delay the above process until the configuration activity has been run at which time we transition between the preview and the actual widget. Change-Id: I5265cd98400d70e5e75c3dcd21e322ed0b352d7b
594 lines
23 KiB
Java
594 lines
23 KiB
Java
/*
|
|
* 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.launcher2;
|
|
|
|
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.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.launcher.R;
|
|
import com.android.launcher2.DropTarget.DragObject;
|
|
import com.android.launcher2.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;
|
|
Folder mFolder;
|
|
FolderInfo mInfo;
|
|
private static boolean sStaticValuesDirty = true;
|
|
|
|
// 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;
|
|
|
|
// 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);
|
|
|
|
public FolderIcon(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
}
|
|
|
|
public FolderIcon(Context context) {
|
|
super(context);
|
|
}
|
|
|
|
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) {
|
|
|
|
if (INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION) {
|
|
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 = ValueAnimator.ofFloat(0f, 1f);
|
|
mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
|
|
mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
|
|
public void onAnimationUpdate(ValueAnimator animation) {
|
|
final float percent = (Float) animation.getAnimatedValue();
|
|
mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
|
|
mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
|
|
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 = ValueAnimator.ofFloat(0f, 1f);
|
|
mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
|
|
mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
|
|
public void onAnimationUpdate(ValueAnimator animation) {
|
|
final float percent = (Float) animation.getAnimatedValue();
|
|
mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
|
|
mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 willAcceptItem(item);
|
|
}
|
|
|
|
public void addItem(ShortcutInfo item) {
|
|
mInfo.add(item);
|
|
LauncherModel.addOrMoveItemInDatabase(mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
|
|
}
|
|
|
|
public void onDragEnter(Object dragInfo) {
|
|
if (!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 View srcView, Rect dstRect,
|
|
float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
|
|
|
|
Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
|
|
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth());
|
|
|
|
// This will animate the dragView (srcView) into the new folder
|
|
onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
|
|
|
|
// 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);
|
|
|
|
postDelayed(new Runnable() {
|
|
public void run() {
|
|
addItem(destInfo);
|
|
}
|
|
}, INITIAL_ITEM_ANIMATION_DURATION);
|
|
}
|
|
|
|
public void onDragExit(Object dragInfo) {
|
|
if (!willAcceptItem((ItemInfo) dragInfo)) return;
|
|
mFolderRingAnimator.animateToNaturalState();
|
|
}
|
|
|
|
private void onDrop(final ShortcutInfo item, View animateView, Rect finalRect,
|
|
float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
|
|
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);
|
|
postDelayed(new Runnable() {
|
|
public void run() {
|
|
addItem(item);
|
|
}
|
|
}, 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);
|
|
}
|
|
|
|
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(false);
|
|
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);
|
|
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) {
|
|
computePreviewDrawingParams(d);
|
|
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 = ValueAnimator.ofFloat(0f, 1.0f);
|
|
va.addUpdateListener(new AnimatorUpdateListener(){
|
|
public void onAnimationUpdate(ValueAnimator animation) {
|
|
float progress = (Float) animation.getAnimatedValue();
|
|
|
|
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;
|
|
}
|
|
});
|
|
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(mContext.getString(R.string.folder_name_format),
|
|
title));
|
|
}
|
|
}
|