Switch all folder preview rendering to be programmatic (ie. no assets)
-> Refactored the preview background rendering to be much more self-contained. This cleans up a lot of code in the CellLayout, and keeps the logic in the right place. -> We switch to software rendering for performance and compatibility reasons. -> Removed all assets. -> FolderIcon accept animation includes animation of the clipped region. -> 1:1 hand-off of drawing of the FolderIcon background between the FolderIcon and the CellLayout. Unfortunately, CellLayout rendering is still required to work around clipping issues (due to use of software layer). We also need this to support folder creation feedback. Change-Id: Ib8f7fa6359dfedff8145f38dd50ba03849ca0d51
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 871 B |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 4.9 KiB |
|
@ -20,13 +20,6 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:focusable="true" >
|
||||
<ImageView
|
||||
android:id="@+id/preview_background"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:antialias="true"
|
||||
android:src="@drawable/portal_ring_inner"/>
|
||||
<com.android.launcher3.BubbleTextView
|
||||
style="@style/Icon"
|
||||
android:id="@+id/folder_icon_name"
|
||||
|
|
|
@ -55,7 +55,6 @@ import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
|
|||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.config.ProviderConfig;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.folder.FolderIcon.FolderRingAnimator;
|
||||
import com.android.launcher3.util.ParcelableSparseArray;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
|
@ -108,8 +107,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||
private OnTouchListener mInterceptTouchListener;
|
||||
private StylusEventHelper mStylusEventHelper;
|
||||
|
||||
private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>();
|
||||
private int[] mFolderLeaveBehindCell = {-1, -1};
|
||||
private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
|
||||
FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
|
||||
Paint mFolderBgPaint = new Paint();
|
||||
|
||||
private float mBackgroundAlpha;
|
||||
|
||||
|
@ -209,6 +209,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||
mPreviousReorderDirection[0] = INVALID_DIRECTION;
|
||||
mPreviousReorderDirection[1] = INVALID_DIRECTION;
|
||||
|
||||
mFolderLeaveBehind.delegateCellX = -1;
|
||||
mFolderLeaveBehind.delegateCellY = -1;
|
||||
|
||||
setAlwaysDrawnWithCacheEnabled(false);
|
||||
final Resources res = getResources();
|
||||
mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
|
||||
|
@ -501,88 +504,62 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
|||
}
|
||||
}
|
||||
|
||||
int previewOffset = FolderRingAnimator.sPreviewSize;
|
||||
|
||||
// The folder outer / inner ring image(s)
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
for (int i = 0; i < mFolderOuterRings.size(); i++) {
|
||||
FolderRingAnimator fra = mFolderOuterRings.get(i);
|
||||
|
||||
Drawable d;
|
||||
int width, height;
|
||||
cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
|
||||
View child = getChildAt(fra.mCellX, fra.mCellY);
|
||||
|
||||
if (child != null) {
|
||||
int centerX = mTempLocation[0] + mCellWidth / 2;
|
||||
int centerY = mTempLocation[1] + previewOffset / 2 +
|
||||
child.getPaddingTop() + grid.folderBackgroundOffset;
|
||||
|
||||
// Draw outer ring, if it exists
|
||||
if (FolderIcon.HAS_OUTER_RING) {
|
||||
d = FolderRingAnimator.sSharedOuterRingDrawable;
|
||||
width = (int) (fra.getOuterRingSize() * getChildrenScale());
|
||||
height = width;
|
||||
canvas.save();
|
||||
canvas.translate(centerX - width / 2, centerY - height / 2);
|
||||
d.setBounds(0, 0, width, height);
|
||||
d.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
// Draw inner ring
|
||||
d = FolderRingAnimator.sSharedInnerRingDrawable;
|
||||
width = (int) (fra.getInnerRingSize() * getChildrenScale());
|
||||
height = width;
|
||||
canvas.save();
|
||||
canvas.translate(centerX - width / 2, centerY - width / 2);
|
||||
d.setBounds(0, 0, width, height);
|
||||
d.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
for (int i = 0; i < mFolderBackgrounds.size(); i++) {
|
||||
FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
|
||||
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
|
||||
canvas.save();
|
||||
canvas.translate(mTempLocation[0], mTempLocation[1]);
|
||||
bg.drawBackground(canvas, mFolderBgPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
if (mFolderLeaveBehindCell[0] >= 0 && mFolderLeaveBehindCell[1] >= 0) {
|
||||
Drawable d = FolderIcon.sSharedFolderLeaveBehind;
|
||||
int width = d.getIntrinsicWidth();
|
||||
int height = d.getIntrinsicHeight();
|
||||
|
||||
cellToPoint(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1], mTempLocation);
|
||||
View child = getChildAt(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1]);
|
||||
if (child != null) {
|
||||
int centerX = mTempLocation[0] + mCellWidth / 2;
|
||||
int centerY = mTempLocation[1] + previewOffset / 2 +
|
||||
child.getPaddingTop() + grid.folderBackgroundOffset;
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(centerX - width / 2, centerY - width / 2);
|
||||
d.setBounds(0, 0, width, height);
|
||||
d.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
if (mFolderLeaveBehind.delegateCellX >= 0 && mFolderLeaveBehind.delegateCellY >= 0) {
|
||||
cellToPoint(mFolderLeaveBehind.delegateCellX,
|
||||
mFolderLeaveBehind.delegateCellY, mTempLocation);
|
||||
canvas.save();
|
||||
canvas.translate(mTempLocation[0], mTempLocation[1]);
|
||||
mFolderLeaveBehind.drawLeaveBehind(canvas, mFolderBgPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
public void showFolderAccept(FolderRingAnimator fra) {
|
||||
mFolderOuterRings.add(fra);
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
for (int i = 0; i < mFolderBackgrounds.size(); i++) {
|
||||
FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
|
||||
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
|
||||
canvas.save();
|
||||
canvas.translate(mTempLocation[0], mTempLocation[1]);
|
||||
bg.drawBackgroundStroke(canvas, mFolderBgPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
public void hideFolderAccept(FolderRingAnimator fra) {
|
||||
if (mFolderOuterRings.contains(fra)) {
|
||||
mFolderOuterRings.remove(fra);
|
||||
}
|
||||
invalidate();
|
||||
public void addFolderBackground(FolderIcon.PreviewBackground bg) {
|
||||
mFolderBackgrounds.add(bg);
|
||||
}
|
||||
public void removeFolderBackground(FolderIcon.PreviewBackground bg) {
|
||||
mFolderBackgrounds.remove(bg);
|
||||
}
|
||||
|
||||
public void setFolderLeaveBehindCell(int x, int y) {
|
||||
mFolderLeaveBehindCell[0] = x;
|
||||
mFolderLeaveBehindCell[1] = y;
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
View child = getChildAt(x, y);
|
||||
|
||||
mFolderLeaveBehind.setup(getResources().getDisplayMetrics(), grid, null,
|
||||
child.getMeasuredWidth(), child.getPaddingTop());
|
||||
|
||||
mFolderLeaveBehind.delegateCellX = x;
|
||||
mFolderLeaveBehind.delegateCellY = y;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void clearFolderLeaveBehind() {
|
||||
mFolderLeaveBehindCell[0] = -1;
|
||||
mFolderLeaveBehindCell[1] = -1;
|
||||
mFolderLeaveBehind.delegateCellX = -1;
|
||||
mFolderLeaveBehind.delegateCellY = -1;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ public class DeviceProfile {
|
|||
// Folder
|
||||
public int folderBackgroundOffset;
|
||||
public int folderIconSizePx;
|
||||
public int folderIconPreviewPadding;
|
||||
public int folderCellWidthPx;
|
||||
public int folderCellHeightPx;
|
||||
|
||||
|
@ -262,6 +263,7 @@ public class DeviceProfile {
|
|||
folderCellHeightPx = cellHeightPx + edgeMarginPx;
|
||||
folderBackgroundOffset = -edgeMarginPx;
|
||||
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
|
||||
folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3029,13 +3029,17 @@ public class Launcher extends Activity
|
|||
// We remove and re-draw the FolderIcon in-case it has changed
|
||||
mDragLayer.removeView(mFolderIconImageView);
|
||||
copyFolderIconToImage(fi);
|
||||
|
||||
if (cl != null) {
|
||||
cl.clearFolderLeaveBehind();
|
||||
}
|
||||
|
||||
ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(mFolderIconImageView, 1, 1, 1);
|
||||
oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
|
||||
oa.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (cl != null) {
|
||||
cl.clearFolderLeaveBehind();
|
||||
// Remove the ImageView copy of the FolderIcon and make the original visible.
|
||||
mDragLayer.removeView(mFolderIconImageView);
|
||||
fi.setVisibility(View.VISIBLE);
|
||||
|
|
|
@ -59,7 +59,6 @@ import android.widget.TextView;
|
|||
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.folder.FolderIcon.FolderRingAnimator;
|
||||
import com.android.launcher3.Launcher.CustomContentCallbacks;
|
||||
import com.android.launcher3.Launcher.LauncherOverlay;
|
||||
import com.android.launcher3.UninstallDropTarget.UninstallSource;
|
||||
|
@ -224,7 +223,7 @@ public class Workspace extends PagedView
|
|||
public static final int REORDER_TIMEOUT = 350;
|
||||
private final Alarm mFolderCreationAlarm = new Alarm();
|
||||
private final Alarm mReorderAlarm = new Alarm();
|
||||
@Thunk FolderRingAnimator mDragFolderRingAnimator = null;
|
||||
private FolderIcon.PreviewBackground mFolderCreateBg = new FolderIcon.PreviewBackground();
|
||||
private FolderIcon mDragOverFolderIcon = null;
|
||||
private boolean mCreateUserFolderOnDrop = false;
|
||||
private boolean mAddToExistingFolderOnDrop = false;
|
||||
|
@ -2493,6 +2492,10 @@ public class Workspace extends PagedView
|
|||
// If the dragView is null, we can't animate
|
||||
boolean animate = dragView != null;
|
||||
if (animate) {
|
||||
// In order to keep everything continuous, we hand off the currently rendered
|
||||
// folder background to the newly created icon. This preserves animation state.
|
||||
fi.setFolderBackground(mFolderCreateBg);
|
||||
mFolderCreateBg = new FolderIcon.PreviewBackground();
|
||||
fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
|
||||
postAnimationRunnable);
|
||||
} else {
|
||||
|
@ -2855,10 +2858,7 @@ public class Workspace extends PagedView
|
|||
}
|
||||
|
||||
private void cleanupFolderCreation() {
|
||||
if (mDragFolderRingAnimator != null) {
|
||||
mDragFolderRingAnimator.animateToNaturalState();
|
||||
mDragFolderRingAnimator = null;
|
||||
}
|
||||
mFolderCreateBg.animateToRest();
|
||||
mFolderCreationAlarm.setOnAlarmListener(null);
|
||||
mFolderCreationAlarm.cancelAlarm();
|
||||
}
|
||||
|
@ -3166,18 +3166,16 @@ public class Workspace extends PagedView
|
|||
this.layout = layout;
|
||||
this.cellX = cellX;
|
||||
this.cellY = cellY;
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
|
||||
|
||||
mFolderCreateBg.setup(getResources().getDisplayMetrics(), grid, null,
|
||||
cell.getMeasuredWidth(), cell.getPaddingTop());
|
||||
}
|
||||
|
||||
public void onAlarm(Alarm alarm) {
|
||||
if (mDragFolderRingAnimator != null) {
|
||||
// This shouldn't happen ever, but just in case, make sure we clean up the mess.
|
||||
mDragFolderRingAnimator.animateToNaturalState();
|
||||
}
|
||||
mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
|
||||
mDragFolderRingAnimator.setCell(cellX, cellY);
|
||||
mDragFolderRingAnimator.setCellLayout(layout);
|
||||
mDragFolderRingAnimator.animateToAcceptState();
|
||||
layout.showFolderAccept(mDragFolderRingAnimator);
|
||||
mFolderCreateBg.animateToAccept(layout, cellX, cellY);
|
||||
layout.clearDragOutlines();
|
||||
setDragMode(DRAG_MODE_CREATE_FOLDER);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public final class FeatureFlags {
|
|||
// As opposed to the new spring-loaded workspace.
|
||||
public static boolean LAUNCHER3_LEGACY_WORKSPACE_DND = false;
|
||||
public static boolean LAUNCHER3_ICON_NORMALIZATION = true;
|
||||
public static boolean LAUNCHER3_CLIPPED_FOLDER_ICON = false;
|
||||
public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
|
||||
public static boolean LAUNCHER3_LEGACY_LOGGING = false;
|
||||
public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = false;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule
|
|||
final float MIN_SCALE = 0.48f;
|
||||
final float MAX_SCALE = 0.58f;
|
||||
final float MAX_RADIUS_DILATION = 0.15f;
|
||||
final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
|
||||
|
||||
private float[] mTmpPoint = new float[2];
|
||||
|
||||
|
@ -22,27 +23,19 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule
|
|||
private float mRadius;
|
||||
private float mIconSize;
|
||||
private boolean mIsRtl;
|
||||
private Path mClipPath = new Path();
|
||||
|
||||
@Override
|
||||
public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
|
||||
mAvailableSpace = availableSpace;
|
||||
mRadius = 0.66f * availableSpace;
|
||||
mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f;
|
||||
mIconSize = intrinsicIconSize;
|
||||
mIsRtl = rtl;
|
||||
|
||||
// We make the clip radius just slightly smaller than the background drawable
|
||||
// TODO(adamcohen): this is hacky, needs cleanup (likely through programmatic drawing).
|
||||
int clipRadius = (int) mAvailableSpace / 2 - 1;
|
||||
|
||||
mClipPath.addCircle(mAvailableSpace / 2, mAvailableSpace / 2, clipRadius, Path.Direction.CW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
|
||||
int curNumItems, FolderIcon.PreviewItemDrawingParams params) {
|
||||
|
||||
|
||||
float totalScale = scaleForNumItems(curNumItems);
|
||||
float transX;
|
||||
float transY;
|
||||
|
@ -55,7 +48,6 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule
|
|||
getPosition(index, curNumItems, mTmpPoint);
|
||||
transX = mTmpPoint[0];
|
||||
transY = mTmpPoint[1];
|
||||
totalScale = scaleForNumItems(curNumItems);
|
||||
}
|
||||
|
||||
if (params == null) {
|
||||
|
@ -126,8 +118,8 @@ public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule
|
|||
}
|
||||
|
||||
@Override
|
||||
public Path getClipPath() {
|
||||
return mClipPath;
|
||||
public boolean clipToBackground() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,16 +21,17 @@ 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.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
@ -39,7 +40,6 @@ 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.android.launcher3.Alarm;
|
||||
|
@ -77,15 +77,14 @@ import java.util.ArrayList;
|
|||
* An icon that can appear on in the workspace representing an {@link Folder}.
|
||||
*/
|
||||
public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
@Thunk
|
||||
Launcher mLauncher;
|
||||
@Thunk Launcher mLauncher;
|
||||
@Thunk Folder mFolder;
|
||||
private FolderInfo mInfo;
|
||||
@Thunk static boolean sStaticValuesDirty = true;
|
||||
|
||||
public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_CLIPPED_FOLDER_ICON ?
|
||||
ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW :
|
||||
StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||
public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
|
||||
StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW :
|
||||
ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||
|
||||
private CheckLongPressHelper mLongPressHelper;
|
||||
private StylusEventHelper mStylusEventHelper;
|
||||
|
@ -96,36 +95,19 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
// Delay when drag enters until the folder opens, in miliseconds.
|
||||
private static final int ON_OPEN_DELAY = 800;
|
||||
|
||||
public static Drawable sSharedFolderLeaveBehind = null;
|
||||
|
||||
@Thunk ImageView mPreviewBackground;
|
||||
@Thunk
|
||||
BubbleTextView mFolderName;
|
||||
|
||||
FolderRingAnimator mFolderRingAnimator = null;
|
||||
@Thunk BubbleTextView mFolderName;
|
||||
|
||||
// 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 int mAvailableSpaceInPreview;
|
||||
private int mPreviewOffsetX;
|
||||
private int mPreviewOffsetY;
|
||||
private int mTotalWidth;
|
||||
PreviewBackground mBackground = new PreviewBackground();
|
||||
|
||||
private PreviewLayoutRule mPreviewLayoutRule;
|
||||
|
||||
|
@ -138,6 +120,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
|
||||
private Drawable mReferenceDrawable = null;
|
||||
|
||||
Paint mBgPaint = new Paint();
|
||||
|
||||
private Alarm mOpenAlarm = new Alarm();
|
||||
@Thunk
|
||||
ItemInfo mDragInfo;
|
||||
|
@ -155,17 +139,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
private void init() {
|
||||
mLongPressHelper = new CheckLongPressHelper(this);
|
||||
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
|
||||
mPreviewLayoutRule = FeatureFlags.LAUNCHER3_CLIPPED_FOLDER_ICON ?
|
||||
new ClippedFolderIconLayoutRule() :
|
||||
new StackFolderIconLayoutRule();
|
||||
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
|
||||
}
|
||||
mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
|
||||
new StackFolderIconLayoutRule() :
|
||||
new ClippedFolderIconLayoutRule();
|
||||
|
||||
public boolean isDropEnabled() {
|
||||
final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
|
||||
final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
|
||||
final Workspace workspace = (Workspace) cellLayout.getParent();
|
||||
return !workspace.workspaceInModalState();
|
||||
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
|
||||
}
|
||||
|
||||
public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
|
||||
|
@ -179,8 +157,13 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
}
|
||||
|
||||
DeviceProfile grid = launcher.getDeviceProfile();
|
||||
|
||||
FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
|
||||
|
||||
// For performance and compatibility reasons we render the preview using a software layer.
|
||||
// In particular, hardware path clipping has spotty ecosystem support and bad performance.
|
||||
// Software rendering also allows us to use shadow layers.
|
||||
icon.setLayerType(LAYER_TYPE_SOFTWARE, new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
|
||||
icon.setClipToPadding(false);
|
||||
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
|
||||
icon.mFolderName.setText(folderInfo.title);
|
||||
|
@ -188,13 +171,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
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;
|
||||
|
@ -206,7 +182,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
folder.bind(folderInfo);
|
||||
icon.setFolder(folder);
|
||||
|
||||
icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
|
||||
folderInfo.addListener(icon);
|
||||
|
||||
icon.setOnFocusChangeListener(launcher.mFocusHandler);
|
||||
|
@ -219,129 +194,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
return super.onSaveInstanceState();
|
||||
}
|
||||
|
||||
public static class FolderRingAnimator {
|
||||
public int mCellX;
|
||||
public int mCellY;
|
||||
@Thunk
|
||||
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());
|
||||
}
|
||||
|
||||
DeviceProfile grid = launcher.getDeviceProfile();
|
||||
sPreviewSize = grid.folderIconSizePx;
|
||||
sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
|
||||
sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer);
|
||||
sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_nolip);
|
||||
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 = animation.getAnimatedFraction();
|
||||
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;
|
||||
|
@ -375,11 +228,9 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
public void onDragEnter(ItemInfo dragInfo) {
|
||||
if (mFolder.isDestroyed() || !willAcceptItem(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);
|
||||
CellLayout cl = (CellLayout) getParent().getParent();
|
||||
|
||||
mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
|
||||
mOpenAlarm.setOnAlarmListener(mOnOpenListener);
|
||||
if (SPRING_LOADING_ENABLED &&
|
||||
((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
|
||||
|
@ -445,7 +296,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
}
|
||||
|
||||
public void onDragExit() {
|
||||
mFolderRingAnimator.animateToNaturalState();
|
||||
mBackground.animateToRest();
|
||||
mOpenAlarm.cancelAlarm();
|
||||
}
|
||||
|
||||
|
@ -531,16 +382,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
mIntrinsicIconSize = drawableSize;
|
||||
mTotalWidth = totalSize;
|
||||
|
||||
final int previewSize = FolderRingAnimator.sPreviewSize;
|
||||
final int previewPadding = FolderRingAnimator.sPreviewPadding;
|
||||
|
||||
mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
|
||||
|
||||
mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
|
||||
mPreviewOffsetY = previewPadding + grid.folderBackgroundOffset + getPaddingTop();
|
||||
|
||||
mPreviewLayoutRule.init(mAvailableSpaceInPreview, mIntrinsicIconSize,
|
||||
mBackground.setup(getResources().getDisplayMetrics(), grid, this, mTotalWidth,
|
||||
getPaddingTop());
|
||||
mPreviewLayoutRule.init(mBackground.previewSize, mIntrinsicIconSize,
|
||||
Utilities.isRtl(getResources()));
|
||||
|
||||
updateItemDrawingParams(false);
|
||||
}
|
||||
}
|
||||
|
@ -586,8 +432,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
mTmpParams = computePreviewItemDrawingParams(Math.min(mPreviewLayoutRule.numItems(), index),
|
||||
curNumItems, mTmpParams);
|
||||
|
||||
mTmpParams.transX += mPreviewOffsetX;
|
||||
mTmpParams.transY += mPreviewOffsetY;
|
||||
mTmpParams.transX += mBackground.basePreviewOffsetX;
|
||||
mTmpParams.transY += mBackground.basePreviewOffsetY;
|
||||
float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
|
||||
float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
|
||||
|
||||
|
@ -610,7 +456,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
float iconSize = mLauncher.getDeviceProfile().iconSizePx;
|
||||
|
||||
final float scale = iconSize / mReferenceDrawable.getIntrinsicWidth();
|
||||
final float trans = (mAvailableSpaceInPreview - iconSize) / 2;
|
||||
final float trans = (mBackground.previewSize - iconSize) / 2;
|
||||
|
||||
params.update(trans, trans, scale);
|
||||
return params;
|
||||
|
@ -642,24 +488,284 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
canvas.restore();
|
||||
}
|
||||
|
||||
public static class PreviewBackground {
|
||||
private float mScale = 1f;
|
||||
private float mColorMultiplier = 1f;
|
||||
private Path mClipPath = new Path();
|
||||
private int mStrokeWidth;
|
||||
private View mInvalidateDeligate;
|
||||
|
||||
public int previewSize;
|
||||
private int basePreviewOffsetX;
|
||||
private int basePreviewOffsetY;
|
||||
|
||||
private CellLayout mDrawingDelegate;
|
||||
public int delegateCellX;
|
||||
public int delegateCellY;
|
||||
|
||||
// Drawing / animation configurations
|
||||
private static final float ACCEPT_SCALE_FACTOR = 1.25f;
|
||||
private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
|
||||
|
||||
// Expressed on a scale from 0 to 255.
|
||||
private static final int BG_OPACITY = 160;
|
||||
private static final int MAX_BG_OPACITY = 225;
|
||||
private static final int BG_INTENSITY = 245;
|
||||
private static final int SHADOW_OPACITY = 80;
|
||||
|
||||
ValueAnimator mScaleAnimator;
|
||||
|
||||
public void setup(DisplayMetrics dm, DeviceProfile grid, View invalidateDeligate,
|
||||
int availableSpace, int topPadding) {
|
||||
mInvalidateDeligate = invalidateDeligate;
|
||||
|
||||
final int previewSize = grid.folderIconSizePx;
|
||||
final int previewPadding = grid.folderIconPreviewPadding;
|
||||
|
||||
this.previewSize = (previewSize - 2 * previewPadding);
|
||||
|
||||
basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
|
||||
basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
|
||||
|
||||
mStrokeWidth = Utilities.pxFromDp(1, dm);
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int getRadius() {
|
||||
return previewSize / 2;
|
||||
}
|
||||
|
||||
int getScaledRadius() {
|
||||
return (int) (mScale * getRadius());
|
||||
}
|
||||
|
||||
int getOffsetX() {
|
||||
return basePreviewOffsetX - (getScaledRadius() - getRadius());
|
||||
}
|
||||
|
||||
int getOffsetY() {
|
||||
return basePreviewOffsetY - (getScaledRadius() - getRadius());
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
int radius = getScaledRadius();
|
||||
mClipPath.reset();
|
||||
mClipPath.addCircle(radius, radius, radius, Path.Direction.CW);
|
||||
|
||||
if (mInvalidateDeligate != null) {
|
||||
mInvalidateDeligate.invalidate();
|
||||
}
|
||||
|
||||
if (mDrawingDelegate != null) {
|
||||
mDrawingDelegate.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void setInvalidateDeligate(View invalidateDeligate) {
|
||||
mInvalidateDeligate = invalidateDeligate;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void drawBackground(Canvas canvas, Paint paint) {
|
||||
canvas.save();
|
||||
canvas.translate(getOffsetX(), getOffsetY());
|
||||
|
||||
paint.reset();
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
paint.setXfermode(null);
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
|
||||
paint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
|
||||
|
||||
float radius = getScaledRadius();
|
||||
|
||||
canvas.drawCircle(radius, radius, radius, paint);
|
||||
canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
|
||||
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setColor(Color.TRANSPARENT);
|
||||
paint.setShadowLayer(mStrokeWidth, 0, mStrokeWidth, Color.argb(SHADOW_OPACITY, 0, 0, 0));
|
||||
canvas.drawCircle(radius, radius, radius, paint);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public void drawBackgroundStroke(Canvas canvas, Paint paint) {
|
||||
canvas.save();
|
||||
canvas.translate(getOffsetX(), getOffsetY());
|
||||
|
||||
paint.reset();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeWidth(mStrokeWidth);
|
||||
|
||||
float radius = getScaledRadius();
|
||||
canvas.drawCircle(radius, radius, radius - 1, paint);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public void drawLeaveBehind(Canvas canvas, Paint paint) {
|
||||
float originalScale = mScale;
|
||||
mScale = 0.5f;
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(getOffsetX(), getOffsetY());
|
||||
|
||||
paint.reset();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.argb(160, 245, 245, 245));
|
||||
|
||||
float radius = getScaledRadius();
|
||||
canvas.drawCircle(radius, radius, radius, paint);
|
||||
|
||||
canvas.restore();
|
||||
mScale = originalScale;
|
||||
}
|
||||
|
||||
// It is the callers responsibility to save and restore the canvas.
|
||||
private void clipCanvas(Canvas canvas) {
|
||||
canvas.translate(getOffsetX(), getOffsetY());
|
||||
canvas.clipPath(mClipPath);
|
||||
canvas.translate(-getOffsetX(), -getOffsetY());
|
||||
}
|
||||
|
||||
private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
|
||||
if (mDrawingDelegate != delegate) {
|
||||
delegate.addFolderBackground(this);
|
||||
}
|
||||
|
||||
mDrawingDelegate = delegate;
|
||||
delegateCellX = cellX;
|
||||
delegateCellY = cellY;
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void clearDrawingDelegate() {
|
||||
if (mDrawingDelegate != null) {
|
||||
mDrawingDelegate.removeFolderBackground(this);
|
||||
}
|
||||
|
||||
mDrawingDelegate = null;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private boolean drawingDelegated() {
|
||||
return mDrawingDelegate != null;
|
||||
}
|
||||
|
||||
private void animateScale(float finalScale, float finalMultiplier,
|
||||
final Runnable onStart, final Runnable onEnd) {
|
||||
final float scale0 = mScale;
|
||||
final float scale1 = finalScale;
|
||||
|
||||
final float bgMultiplier0 = mColorMultiplier;
|
||||
final float bgMultiplier1 = finalMultiplier;
|
||||
|
||||
if (mScaleAnimator != null) {
|
||||
mScaleAnimator.cancel();
|
||||
}
|
||||
|
||||
mScaleAnimator = LauncherAnimUtils.ofFloat(null, 0f, 1.0f);
|
||||
|
||||
mScaleAnimator.addUpdateListener(new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float prog = animation.getAnimatedFraction();
|
||||
mScale = prog * scale1 + (1 - prog) * scale0;
|
||||
mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
mScaleAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (onStart != null) {
|
||||
onStart.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (onEnd != null) {
|
||||
onEnd.run();
|
||||
}
|
||||
mScaleAnimator = null;
|
||||
}
|
||||
});
|
||||
|
||||
mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
|
||||
mScaleAnimator.start();
|
||||
}
|
||||
|
||||
public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
|
||||
Runnable onStart = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegateDrawing(cl, cellX, cellY);
|
||||
}
|
||||
};
|
||||
animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
|
||||
}
|
||||
|
||||
public void animateToRest() {
|
||||
// This can be called multiple times -- we need to make sure the drawing delegate
|
||||
// is saved and restored at the beginning of the animation, since cancelling the
|
||||
// existing animation can clear the delgate.
|
||||
final CellLayout cl = mDrawingDelegate;
|
||||
final int cellX = delegateCellX;
|
||||
final int cellY = delegateCellY;
|
||||
|
||||
Runnable onStart = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
delegateDrawing(cl, cellX, cellY);
|
||||
}
|
||||
};
|
||||
Runnable onEnd = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clearDrawingDelegate();
|
||||
}
|
||||
};
|
||||
animateScale(1f, 1f, onStart, onEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFolderBackground(PreviewBackground bg) {
|
||||
mBackground = bg;
|
||||
mBackground.setInvalidateDeligate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
if (mFolder == null) return;
|
||||
if (mFolder.getItemCount() == 0 && !mAnimating) return;
|
||||
|
||||
if (mReferenceDrawable != null) {
|
||||
computePreviewDrawingParams(mReferenceDrawable);
|
||||
}
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(mPreviewOffsetX, mPreviewOffsetY);
|
||||
Path clipPath = mPreviewLayoutRule.getClipPath();
|
||||
if (clipPath != null) {
|
||||
canvas.clipPath(clipPath);
|
||||
if (!mBackground.drawingDelegated()) {
|
||||
mBackground.drawBackground(canvas, mBgPaint);
|
||||
}
|
||||
|
||||
if (mFolder == null) return;
|
||||
if (mFolder.getItemCount() == 0 && !mAnimating) return;
|
||||
|
||||
canvas.save();
|
||||
|
||||
|
||||
if (mPreviewLayoutRule.clipToBackground()) {
|
||||
mBackground.clipCanvas(canvas);
|
||||
}
|
||||
|
||||
// The items are drawn in coordinates relative to the preview offset
|
||||
canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
|
||||
|
||||
// The first item should be drawn last (ie. on top of later items)
|
||||
for (int i = mDrawingParams.size() - 1; i >= 0; i--) {
|
||||
PreviewItemDrawingParams p = mDrawingParams.get(i);
|
||||
|
@ -668,6 +774,10 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
}
|
||||
}
|
||||
canvas.restore();
|
||||
|
||||
if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
|
||||
mBackground.drawBackgroundStroke(canvas, mBgPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getTopDrawable(TextView v) {
|
||||
|
@ -793,7 +903,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
PreviewItemDrawingParams p = mDrawingParams.get(i);
|
||||
p.drawable = getTopDrawable((TextView) items.get(i));
|
||||
|
||||
if (!animate || !FeatureFlags.LAUNCHER3_CLIPPED_FOLDER_ICON) {
|
||||
if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
|
||||
computePreviewItemDrawingParams(i, nItemsInPreview, p);
|
||||
if (mReferenceDrawable == null) {
|
||||
mReferenceDrawable = p.drawable;
|
||||
|
@ -884,6 +994,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
|||
public void init(int availableSpace, int intrinsicIconSize, boolean rtl);
|
||||
|
||||
public int numItems();
|
||||
public Path getClipPath();
|
||||
public boolean clipToBackground();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Path getClipPath() {
|
||||
return null;
|
||||
public boolean clipToBackground() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|