Home | History | Annotate | Download | only in dragndrop
      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 
     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
     59     public Path getIconMask() {
     60         return mMask;
     61     }
     62 
     63     public Drawable getBadge() {
     64         return mBadge;
     65     }
     66 
     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      */
     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 
    147         ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
    148             mBitmap = bitmap;
    149             mShiftX = shiftX;
    150             mShiftY = shiftY;
    151         }
    152 
    153         @Override
    154         public void draw(Canvas canvas) {
    155             canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
    156         }
    157 
    158         @Override
    159         public void setAlpha(int i) { }
    160 
    161         @Override
    162         public void setColorFilter(ColorFilter colorFilter) {
    163             mPaint.setColorFilter(colorFilter);
    164         }
    165 
    166         @Override
    167         public int getOpacity() {
    168             return PixelFormat.TRANSLUCENT;
    169         }
    170     }
    171 }
    172