b922dccbf5
Test: Build with gradle Flag: NA Change-Id: I3ef17090741d14bebd76cf09cf3dc5ac4f11e686
251 lines
8.5 KiB
Java
251 lines
8.5 KiB
Java
/*
|
|
* 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Path;
|
|
import android.graphics.Path.Direction;
|
|
import android.graphics.Picture;
|
|
import android.graphics.PixelFormat;
|
|
import android.graphics.Point;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.AdaptiveIconDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build;
|
|
import android.util.Log;
|
|
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.UiThread;
|
|
|
|
import com.android.launcher3.folder.FolderIcon;
|
|
import com.android.launcher3.folder.PreviewBackground;
|
|
import com.android.launcher3.icons.BitmapRenderer;
|
|
import com.android.launcher3.util.Preconditions;
|
|
import com.android.launcher3.views.ActivityContext;
|
|
|
|
/**
|
|
* {@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 final ConstantState mConstantState;
|
|
private static final Rect sTmpRect = new Rect();
|
|
|
|
private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
|
|
super(bg, fg);
|
|
mBadge = badge;
|
|
mMask = mask;
|
|
|
|
mConstantState = new MyConstantState(bg.getConstantState(), fg.getConstantState(),
|
|
badge.getConstantState(), mask);
|
|
}
|
|
|
|
@Override
|
|
public Path getIconMask() {
|
|
return mMask;
|
|
}
|
|
|
|
public Drawable getBadge() {
|
|
return mBadge;
|
|
}
|
|
|
|
public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
|
|
ActivityContext activity, int folderId, Point size) {
|
|
Preconditions.assertNonUiThread();
|
|
|
|
// assume square
|
|
if (size.x != size.y) {
|
|
return null;
|
|
}
|
|
int requestedSize = size.x;
|
|
|
|
// Only use the size actually needed for drawing the folder icon
|
|
int drawingSize = activity.getDeviceProfile().folderIconSizePx;
|
|
int foregroundSize = Math.max(requestedSize, drawingSize);
|
|
float shift = foregroundSize - requestedSize;
|
|
|
|
Picture background = new Picture();
|
|
Picture foreground = new Picture();
|
|
Picture badge = new Picture();
|
|
|
|
Canvas bgCanvas = background.beginRecording(requestedSize, requestedSize);
|
|
Canvas badgeCanvas = badge.beginRecording(requestedSize, requestedSize);
|
|
|
|
Canvas fgCanvas = foreground.beginRecording(foregroundSize, foregroundSize);
|
|
fgCanvas.translate(shift, shift);
|
|
|
|
// Do not clip the folder drawing since the icon previews extend outside the background.
|
|
Path mask = new Path();
|
|
mask.addRect(-shift, -shift, requestedSize + shift, requestedSize + shift,
|
|
Direction.CCW);
|
|
|
|
// Initialize the actual draw commands on the UI thread to avoid race conditions with
|
|
// FolderIcon draw pass
|
|
try {
|
|
MAIN_EXECUTOR.submit(() -> {
|
|
FolderIcon icon = activity.findFolderIcon(folderId);
|
|
if (icon == null) {
|
|
throw new IllegalArgumentException("Folder not found with id: " + folderId);
|
|
}
|
|
initLayersOnUiThread(icon, requestedSize, bgCanvas, fgCanvas, badgeCanvas);
|
|
}).get();
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "Unable to create folder icon", e);
|
|
return null;
|
|
} finally {
|
|
background.endRecording();
|
|
foreground.endRecording();
|
|
badge.endRecording();
|
|
}
|
|
|
|
// Only convert foreground to a bitmap as it can contain multiple draw commands. Other
|
|
// layers either draw a nothing or a single draw call.
|
|
Bitmap fgBitmap = Bitmap.createBitmap(foreground);
|
|
Paint foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
|
|
// Do not use PictureDrawable as it moves the picture to the canvas bounds, whereas we want
|
|
// to draw it at (0,0)
|
|
return new FolderAdaptiveIcon(
|
|
new BitmapRendererDrawable(c -> c.drawPicture(background)),
|
|
new BitmapRendererDrawable(
|
|
c -> c.drawBitmap(fgBitmap, -shift, -shift, foregroundPaint)),
|
|
new BitmapRendererDrawable(c -> c.drawPicture(badge)),
|
|
mask);
|
|
}
|
|
|
|
@UiThread
|
|
private static void initLayersOnUiThread(FolderIcon icon, int size,
|
|
Canvas backgroundCanvas, Canvas foregroundCanvas, Canvas badgeCanvas) {
|
|
icon.getPreviewBounds(sTmpRect);
|
|
final int previewSize = sTmpRect.width();
|
|
|
|
PreviewBackground bg = icon.getFolderBackground();
|
|
final int margin = (size - previewSize) / 2;
|
|
final float previewShiftX = -sTmpRect.left + margin;
|
|
final float previewShiftY = -sTmpRect.top + margin;
|
|
|
|
// Initialize badge, which consists of the outline stroke, shadow and dot; these
|
|
// must be rendered above the foreground
|
|
badgeCanvas.save();
|
|
badgeCanvas.translate(previewShiftX, previewShiftY);
|
|
icon.drawDot(badgeCanvas);
|
|
badgeCanvas.restore();
|
|
|
|
// Draw foreground
|
|
foregroundCanvas.save();
|
|
foregroundCanvas.translate(previewShiftX, previewShiftY);
|
|
icon.getPreviewItemManager().draw(foregroundCanvas);
|
|
foregroundCanvas.restore();
|
|
|
|
// Draw background
|
|
Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
backgroundPaint.setColor(bg.getBgColor());
|
|
bg.drawShadow(backgroundCanvas);
|
|
backgroundCanvas.drawCircle(size / 2f, size / 2f, bg.getRadius(), backgroundPaint);
|
|
bg.drawBackgroundStroke(backgroundCanvas);
|
|
}
|
|
|
|
@Override
|
|
public ConstantState getConstantState() {
|
|
return mConstantState;
|
|
}
|
|
|
|
private static class MyConstantState extends ConstantState {
|
|
private final ConstantState mBg;
|
|
private final ConstantState mFg;
|
|
private final ConstantState mBadge;
|
|
private final Path mMask;
|
|
|
|
MyConstantState(ConstantState bg, ConstantState fg, ConstantState badge, Path mask) {
|
|
mBg = bg;
|
|
mFg = fg;
|
|
mBadge = badge;
|
|
mMask = mask;
|
|
}
|
|
|
|
@Override
|
|
public Drawable newDrawable() {
|
|
return new FolderAdaptiveIcon(mBg.newDrawable(), mFg.newDrawable(),
|
|
mBadge.newDrawable(), mMask);
|
|
}
|
|
|
|
@Override
|
|
public int getChangingConfigurations() {
|
|
return mBg.getChangingConfigurations() & mFg.getChangingConfigurations()
|
|
& mBadge.getChangingConfigurations();
|
|
}
|
|
}
|
|
|
|
private static class BitmapRendererDrawable extends Drawable {
|
|
|
|
private final BitmapRenderer mRenderer;
|
|
|
|
BitmapRendererDrawable(BitmapRenderer renderer) {
|
|
mRenderer = renderer;
|
|
}
|
|
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
mRenderer.draw(canvas);
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(int i) { }
|
|
|
|
@Override
|
|
public void setColorFilter(ColorFilter colorFilter) { }
|
|
|
|
@Override
|
|
public int getOpacity() {
|
|
return PixelFormat.TRANSLUCENT;
|
|
}
|
|
|
|
@Override
|
|
public ConstantState getConstantState() {
|
|
return new MyConstantState(mRenderer);
|
|
}
|
|
|
|
private static class MyConstantState extends ConstantState {
|
|
private final BitmapRenderer mRenderer;
|
|
|
|
MyConstantState(BitmapRenderer renderer) {
|
|
mRenderer = renderer;
|
|
}
|
|
|
|
@Override
|
|
public Drawable newDrawable() {
|
|
return new BitmapRendererDrawable(mRenderer);
|
|
}
|
|
|
|
@Override
|
|
public int getChangingConfigurations() {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|