Merge "Adding spring effect for folder icons when dragging similar to adaptive icons" into ub-launcher3-dorval-polish2

This commit is contained in:
Sunny Goyal 2017-07-26 21:05:01 +00:00 committed by Android (Google) Code Review
commit f9a9c3f69c
6 changed files with 256 additions and 38 deletions

View File

@ -1465,23 +1465,25 @@ public class Launcher extends BaseActivity
mWorkspace.addInScreen(view, info);
} else {
// Adding a shortcut to a Folder.
final long folderIconId = container;
FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View view) {
return info != null && info.id == folderIconId;
}
});
FolderIcon folderIcon = findFolderIcon(container);
if (folderIcon != null) {
FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
folderInfo.add(info, args.rank, false);
} else {
Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut.");
Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
}
}
}
public FolderIcon findFolderIcon(final long folderIconId) {
return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View view) {
return info != null && info.id == folderIconId;
}
});
}
/**
* Add a widget to the workspace.
*

View File

@ -78,6 +78,7 @@ public class DragView extends View {
@Thunk static float sDragAlpha = 1f;
private boolean mDrawBitmap = true;
private Bitmap mBitmap;
private Bitmap mCrossFadeBitmap;
@Thunk Paint mPaint;
@ -187,7 +188,8 @@ public class DragView extends View {
}
/**
* Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder).
* Initialize {@code #mIconDrawable} if the item can be represented using
* an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
*/
@TargetApi(Build.VERSION_CODES.O)
public void setItemInfo(final ItemInfo info) {
@ -195,7 +197,8 @@ public class DragView extends View {
return;
}
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return;
}
// Load the adaptive icon on a background thread and add the view in ui thread.
@ -205,7 +208,7 @@ public class DragView extends View {
public void run() {
LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
Object[] outObj = new Object[1];
Drawable dr = getFullDrawable(info, appState, outObj);
final Drawable dr = getFullDrawable(info, appState, outObj);
if (dr instanceof AdaptiveIconDrawable) {
int w = mBitmap.getWidth();
@ -249,6 +252,9 @@ public class DragView extends View {
// Assign the variable on the UI thread to avoid race conditions.
mScaledMaskPath = mask;
// Do not draw the background in case of folder as its translucent
mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
if (info.isDisabled()) {
FastBitmapDrawable d = new FastBitmapDrawable(null);
d.setIsDisabled(true);
@ -323,6 +329,14 @@ public class DragView extends View {
return sm.getShortcutIconDrawable(si.get(0),
appState.getInvariantDeviceProfile().fillResIconDpi);
}
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight()));
if (icon == null) {
return null;
}
outObj[0] = icon;
return icon;
} else {
return null;
}
@ -350,6 +364,8 @@ public class DragView extends View {
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
insetFraction, insetFraction, 0, 0);
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return ((FolderAdaptiveIcon) obj).getBadge();
} else {
return mLauncher.getPackageManager()
.getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
@ -405,21 +421,24 @@ public class DragView extends View {
@Override
protected void onDraw(Canvas canvas) {
mHasDrawn = true;
// Always draw the bitmap to mask anti aliasing due to clipPath
boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
if (crossFade) {
int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
mPaint.setAlpha(alpha);
}
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
if (crossFade) {
mPaint.setAlpha((int) (255 * mCrossFadeProgress));
final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
canvas.scale(sX, sY);
canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
canvas.restoreToCount(saveCount);
if (mDrawBitmap) {
// Always draw the bitmap to mask anti aliasing due to clipPath
boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
if (crossFade) {
int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
mPaint.setAlpha(alpha);
}
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
if (crossFade) {
mPaint.setAlpha((int) (255 * mCrossFadeProgress));
final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
canvas.scale(sX, sY);
canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
canvas.restoreToCount(saveCount);
}
}
if (mScaledMaskPath != null) {

View File

@ -0,0 +1,175 @@
/*
* Copyright (C) 2017 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.dragndrop;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import com.android.launcher3.Launcher;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.util.Preconditions;
import java.util.concurrent.Callable;
/**
* {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
*/
@TargetApi(Build.VERSION_CODES.O)
public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
private static final String TAG = "FolderAdaptiveIcon";
private final Drawable mBadge;
private final Path mMask;
private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
super(bg, fg);
mBadge = badge;
mMask = mask;
}
@Override
public Path getIconMask() {
return mMask;
}
public Drawable getBadge() {
return mBadge;
}
public static FolderAdaptiveIcon createFolderAdaptiveIcon(
final Launcher launcher, final long folderId, Point dragViewSize) {
Preconditions.assertNonUiThread();
int margin = launcher.getResources()
.getDimensionPixelSize(R.dimen.blur_size_medium_outline);
// Allocate various bitmaps on the background thread, because why not!
final Bitmap badge = Bitmap.createBitmap(
dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
// The bitmap for the preview is generated larger than needed to allow for the spring effect
float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
final Bitmap preview = Bitmap.createBitmap(
(int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor),
Bitmap.Config.ARGB_8888);
// Create the actual drawable on the UI thread to avoid race conditions with
// FolderIcon draw pass
try {
return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() {
@Override
public FolderAdaptiveIcon call() throws Exception {
FolderIcon icon = launcher.findFolderIcon(folderId);
return icon == null ? null : createDrawableOnUiThread(icon, badge, preview);
}
}).get();
} catch (Exception e) {
Log.e(TAG, "Unable to create folder icon", e);
return null;
}
}
/**
* Initializes various bitmaps on the UI thread and returns the final drawable.
*/
private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
Bitmap badgeBitmap, Bitmap previewBitmap) {
Preconditions.assertUIThread();
float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
Canvas c = new Canvas();
PreviewBackground bg = icon.getFolderBackground();
// Initialize badge
c.setBitmap(badgeBitmap);
bg.drawShadow(c);
bg.drawBackgroundStroke(c);
icon.drawBadge(c);
// Initialize preview
float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() /
(1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
float previewShiftX = shiftFactor * previewBitmap.getWidth();
float previewShiftY = shiftFactor * previewBitmap.getHeight();
c.setBitmap(previewBitmap);
c.translate(previewShiftX, previewShiftY);
icon.getPreviewItemManager().draw(c);
c.setBitmap(null);
// Initialize mask
Path mask = new Path();
Matrix m = new Matrix();
m.setTranslate(margin, margin);
bg.getClipPath().transform(m, mask);
ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
margin - previewShiftX, margin - previewShiftY);
return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
}
/**
* A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
*/
private static class ShiftedBitmapDrawable extends Drawable {
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
private final Bitmap mBitmap;
private final float mShiftX;
private final float mShiftY;
ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
mBitmap = bitmap;
mShiftX = shiftX;
mShiftY = shiftY;
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
}
@Override
public void setAlpha(int i) { }
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
}

View File

@ -440,6 +440,14 @@ public class FolderIcon extends FrameLayout implements FolderListener {
invalidate();
}
public PreviewBackground getFolderBackground() {
return mBackground;
}
public PreviewItemManager getPreviewItemManager() {
return mPreviewItemManager;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
@ -463,14 +471,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
if (mPreviewLayoutRule.clipToBackground()) {
mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
}
}
// The items are drawn in coordinates relative to the preview offset
canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
mPreviewItemManager.draw(canvas);
canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
mBackground.clipCanvasHardware(canvas);
@ -481,6 +486,10 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mBackground.drawBackgroundStroke(canvas);
}
drawBadge(canvas);
}
public void drawBadge(Canvas canvas) {
if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
int offsetX = mBackground.getOffsetX();
int offsetY = mBackground.getOffsetY();

View File

@ -195,19 +195,28 @@ public class PreviewBackground {
invalidate();
}
public int getBgColor() {
int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
return ColorUtils.setAlphaComponent(mBgColor, alpha);
}
public void drawBackground(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
mPaint.setColor(getBgColor());
drawCircle(canvas, 0 /* deltaRadius */);
// Draw shadow.
drawShadow(canvas);
}
public void drawShadow(Canvas canvas) {
if (mShadowShader == null) {
return;
}
float radius = getScaledRadius();
float shadowRadius = radius + mStrokeWidth;
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLACK);
int offsetX = getOffsetX();
int offsetY = getOffsetY();
@ -219,7 +228,7 @@ public class PreviewBackground {
} else {
saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
canvas.clipPath(getClipPath(), Region.Op.DIFFERENCE);
}
mShaderMatrix.setScale(shadowRadius, shadowRadius);
@ -295,12 +304,11 @@ public class PreviewBackground {
radius - deltaRadius, mPaint);
}
// It is the callers responsibility to save and restore the canvas layers.
void clipCanvasSoftware(Canvas canvas, Region.Op op) {
public Path getClipPath() {
mPath.reset();
float r = getScaledRadius();
mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
canvas.clipPath(mPath, op);
return mPath;
}
// It is the callers responsibility to save and restore the canvas layers.

View File

@ -146,6 +146,10 @@ public class PreviewItemManager {
}
public void draw(Canvas canvas) {
// The items are drawn in coordinates relative to the preview offset
PreviewBackground bg = mIcon.getFolderBackground();
canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY);
float firstPageItemsTransX = 0;
if (mShouldSlideInFirstPage) {
drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX);
@ -154,6 +158,7 @@ public class PreviewItemManager {
}
drawParams(canvas, mFirstPageParams, firstPageItemsTransX);
canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY);
}
public void onParamsChanged() {