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