• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.annotation.TargetApi;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.ColorFilter;
23 import android.graphics.Matrix;
24 import android.graphics.Paint;
25 import android.graphics.Path;
26 import android.graphics.PixelFormat;
27 import android.graphics.Point;
28 import android.graphics.drawable.AdaptiveIconDrawable;
29 import android.graphics.drawable.ColorDrawable;
30 import android.graphics.drawable.Drawable;
31 import android.os.Build;
32 import android.util.Log;
33 
34 import com.android.launcher3.Launcher;
35 import com.android.launcher3.MainThreadExecutor;
36 import com.android.launcher3.R;
37 import com.android.launcher3.folder.FolderIcon;
38 import com.android.launcher3.folder.PreviewBackground;
39 import com.android.launcher3.graphics.BitmapRenderer;
40 import com.android.launcher3.util.Preconditions;
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 
FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask)52     private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
53         super(bg, fg);
54         mBadge = badge;
55         mMask = mask;
56     }
57 
58     @Override
getIconMask()59     public Path getIconMask() {
60         return mMask;
61     }
62 
getBadge()63     public Drawable getBadge() {
64         return mBadge;
65     }
66 
createFolderAdaptiveIcon( Launcher launcher, long folderId, Point dragViewSize)67     public static FolderAdaptiveIcon createFolderAdaptiveIcon(
68             Launcher launcher, long folderId, Point dragViewSize) {
69         Preconditions.assertNonUiThread();
70         int margin = launcher.getResources()
71                 .getDimensionPixelSize(R.dimen.blur_size_medium_outline);
72 
73         // Allocate various bitmaps on the background thread, because why not!
74         final Bitmap badge = Bitmap.createBitmap(
75                 dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
76 
77         // Create the actual drawable on the UI thread to avoid race conditions with
78         // FolderIcon draw pass
79         try {
80             return new MainThreadExecutor().submit(() -> {
81                 FolderIcon icon = launcher.findFolderIcon(folderId);
82                 return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize);
83             }).get();
84         } catch (Exception e) {
85             Log.e(TAG, "Unable to create folder icon", e);
86             return null;
87         }
88     }
89 
90     /**
91      * Initializes various bitmaps on the UI thread and returns the final drawable.
92      */
createDrawableOnUiThread(FolderIcon icon, Bitmap badgeBitmap, Point dragViewSize)93     private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
94             Bitmap badgeBitmap, Point dragViewSize) {
95         Preconditions.assertUIThread();
96         float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
97 
98         Canvas c = new Canvas();
99         PreviewBackground bg = icon.getFolderBackground();
100 
101         // Initialize badge
102         c.setBitmap(badgeBitmap);
103         bg.drawShadow(c);
104         bg.drawBackgroundStroke(c);
105         icon.drawBadge(c);
106 
107         // Initialize preview
108         final float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
109         final int previewWidth = (int) (dragViewSize.x * sizeScaleFactor);
110         final int previewHeight = (int) (dragViewSize.y * sizeScaleFactor);
111 
112         final float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() / sizeScaleFactor;
113         final float previewShiftX = shiftFactor * previewWidth;
114         final float previewShiftY = shiftFactor * previewHeight;
115 
116         Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight,
117                 (canvas) -> {
118                     int count = canvas.save();
119                     canvas.translate(previewShiftX, previewShiftY);
120                     icon.getPreviewItemManager().draw(canvas);
121                     canvas.restoreToCount(count);
122                 });
123 
124         // Initialize mask
125         Path mask = new Path();
126         Matrix m = new Matrix();
127         m.setTranslate(margin, margin);
128         bg.getClipPath().transform(m, mask);
129 
130         ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
131         ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
132                 margin - previewShiftX, margin - previewShiftY);
133 
134         return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
135     }
136 
137     /**
138      * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
139      */
140     private static class ShiftedBitmapDrawable extends Drawable {
141 
142         private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
143         private final Bitmap mBitmap;
144         private final float mShiftX;
145         private final float mShiftY;
146 
ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY)147         ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
148             mBitmap = bitmap;
149             mShiftX = shiftX;
150             mShiftY = shiftY;
151         }
152 
153         @Override
draw(Canvas canvas)154         public void draw(Canvas canvas) {
155             canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
156         }
157 
158         @Override
setAlpha(int i)159         public void setAlpha(int i) { }
160 
161         @Override
setColorFilter(ColorFilter colorFilter)162         public void setColorFilter(ColorFilter colorFilter) {
163             mPaint.setColorFilter(colorFilter);
164         }
165 
166         @Override
getOpacity()167         public int getOpacity() {
168             return PixelFormat.TRANSLUCENT;
169         }
170     }
171 }
172