1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.dragndrop; 18 19 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 20 21 import android.annotation.TargetApi; 22 import android.graphics.Bitmap; 23 import android.graphics.Matrix; 24 import android.graphics.Paint; 25 import android.graphics.Path; 26 import android.graphics.Point; 27 import android.graphics.Rect; 28 import android.graphics.drawable.AdaptiveIconDrawable; 29 import android.graphics.drawable.Drawable; 30 import android.os.Build; 31 import android.util.Log; 32 33 import androidx.annotation.Nullable; 34 35 import com.android.launcher3.folder.FolderIcon; 36 import com.android.launcher3.folder.PreviewBackground; 37 import com.android.launcher3.graphics.ShiftedBitmapDrawable; 38 import com.android.launcher3.icons.BitmapRenderer; 39 import com.android.launcher3.util.Preconditions; 40 import com.android.launcher3.views.ActivityContext; 41 42 /** 43 * {@link AdaptiveIconDrawable} representation of a {@link FolderIcon} 44 */ 45 @TargetApi(Build.VERSION_CODES.O) 46 public class FolderAdaptiveIcon extends AdaptiveIconDrawable { 47 private static final String TAG = "FolderAdaptiveIcon"; 48 49 private final Drawable mBadge; 50 private final Path mMask; 51 private final ConstantState mConstantState; 52 private static final Rect sTmpRect = new Rect(); 53 FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask)54 private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) { 55 super(bg, fg); 56 mBadge = badge; 57 mMask = mask; 58 59 mConstantState = new MyConstantState(bg.getConstantState(), fg.getConstantState(), 60 badge.getConstantState(), mask); 61 } 62 63 @Override getIconMask()64 public Path getIconMask() { 65 return mMask; 66 } 67 getBadge()68 public Drawable getBadge() { 69 return mBadge; 70 } 71 createFolderAdaptiveIcon( ActivityContext activity, int folderId, Point dragViewSize)72 public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon( 73 ActivityContext activity, int folderId, Point dragViewSize) { 74 Preconditions.assertNonUiThread(); 75 76 // Create the actual drawable on the UI thread to avoid race conditions with 77 // FolderIcon draw pass 78 try { 79 return MAIN_EXECUTOR.submit(() -> { 80 FolderIcon icon = activity.findFolderIcon(folderId); 81 return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize); 82 83 }).get(); 84 } catch (Exception e) { 85 Log.e(TAG, "Unable to create folder icon", e); 86 return null; 87 } 88 } 89 createDrawableOnUiThread(FolderIcon icon, Point dragViewSize)90 private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon, 91 Point dragViewSize) { 92 Preconditions.assertUIThread(); 93 94 icon.getPreviewBounds(sTmpRect); 95 96 PreviewBackground bg = icon.getFolderBackground(); 97 98 // assume square 99 assert (dragViewSize.x == dragViewSize.y); 100 final int previewSize = sTmpRect.width(); 101 102 final int margin = (dragViewSize.x - previewSize) / 2; 103 final float previewShiftX = -sTmpRect.left + margin; 104 final float previewShiftY = -sTmpRect.top + margin; 105 106 // Initialize badge, which consists of the outline stroke, shadow and dot; these 107 // must be rendered above the foreground 108 Bitmap badgeBmp = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y, 109 (canvas) -> { 110 canvas.save(); 111 canvas.translate(previewShiftX, previewShiftY); 112 bg.drawShadow(canvas); 113 bg.drawBackgroundStroke(canvas); 114 icon.drawDot(canvas); 115 canvas.restore(); 116 }); 117 118 // Initialize mask 119 Path mask = new Path(); 120 Matrix m = new Matrix(); 121 m.setTranslate(previewShiftX, previewShiftY); 122 bg.getClipPath().transform(m, mask); 123 124 Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y, 125 (canvas) -> { 126 canvas.save(); 127 canvas.translate(previewShiftX, previewShiftY); 128 icon.getPreviewItemManager().draw(canvas); 129 canvas.restore(); 130 }); 131 132 Bitmap bgBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y, 133 (canvas) -> { 134 Paint p = new Paint(); 135 p.setColor(bg.getBgColor()); 136 137 canvas.drawCircle(dragViewSize.x / 2f, dragViewSize.y / 2f, bg.getRadius(), p); 138 }); 139 140 ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0); 141 ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0); 142 ShiftedBitmapDrawable background = new ShiftedBitmapDrawable(bgBitmap, 0, 0); 143 144 return new FolderAdaptiveIcon(background, foreground, badge, mask); 145 } 146 147 @Override getConstantState()148 public ConstantState getConstantState() { 149 return mConstantState; 150 } 151 152 private static class MyConstantState extends ConstantState { 153 private final ConstantState mBg; 154 private final ConstantState mFg; 155 private final ConstantState mBadge; 156 private final Path mMask; 157 MyConstantState(ConstantState bg, ConstantState fg, ConstantState badge, Path mask)158 MyConstantState(ConstantState bg, ConstantState fg, ConstantState badge, Path mask) { 159 mBg = bg; 160 mFg = fg; 161 mBadge = badge; 162 mMask = mask; 163 } 164 165 @Override newDrawable()166 public Drawable newDrawable() { 167 return new FolderAdaptiveIcon(mBg.newDrawable(), mFg.newDrawable(), 168 mBadge.newDrawable(), mMask); 169 } 170 171 @Override getChangingConfigurations()172 public int getChangingConfigurations() { 173 return mBg.getChangingConfigurations() & mFg.getChangingConfigurations() 174 & mBadge.getChangingConfigurations(); 175 } 176 } 177 } 178