1 /* 2 * Copyright (C) 2019 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.quickstep; 18 19 import static android.graphics.Shader.TileMode.CLAMP; 20 21 import android.content.res.Resources; 22 import android.graphics.Bitmap; 23 import android.graphics.BitmapShader; 24 import android.graphics.Canvas; 25 import android.graphics.ColorFilter; 26 import android.graphics.Matrix; 27 import android.graphics.Paint; 28 import android.graphics.PixelFormat; 29 import android.graphics.Rect; 30 import android.graphics.RectF; 31 import android.graphics.drawable.Drawable; 32 33 import androidx.annotation.NonNull; 34 35 import com.android.launcher3.R; 36 import com.android.systemui.shared.recents.model.ThumbnailData; 37 38 /** 39 * Bitmap backed drawable that supports rotating the thumbnail bitmap depending on if the 40 * orientation the thumbnail was taken in matches the desired orientation. In addition, the 41 * thumbnail always fills into the containing bounds. 42 */ 43 public final class ThumbnailDrawable extends Drawable { 44 45 private final Paint mPaint = new Paint(); 46 private final Matrix mMatrix = new Matrix(); 47 private final ThumbnailData mThumbnailData; 48 private final BitmapShader mShader; 49 private final RectF mDestRect = new RectF(); 50 private final int mCornerRadius; 51 private int mRequestedOrientation; 52 ThumbnailDrawable(Resources res, @NonNull ThumbnailData thumbnailData, int requestedOrientation)53 public ThumbnailDrawable(Resources res, @NonNull ThumbnailData thumbnailData, 54 int requestedOrientation) { 55 mThumbnailData = thumbnailData; 56 mRequestedOrientation = requestedOrientation; 57 mCornerRadius = (int) res.getDimension(R.dimen.task_thumbnail_corner_radius); 58 mShader = new BitmapShader(mThumbnailData.thumbnail, CLAMP, CLAMP); 59 mPaint.setShader(mShader); 60 mPaint.setAntiAlias(true); 61 updateMatrix(); 62 } 63 64 /** 65 * Set the requested orientation. 66 * 67 * @param orientation the orientation we want the thumbnail to be in 68 */ setRequestedOrientation(int orientation)69 public void setRequestedOrientation(int orientation) { 70 if (mRequestedOrientation != orientation) { 71 mRequestedOrientation = orientation; 72 updateMatrix(); 73 } 74 } 75 76 @Override draw(Canvas canvas)77 public void draw(Canvas canvas) { 78 if (mThumbnailData.thumbnail == null) { 79 return; 80 } 81 canvas.drawRoundRect(mDestRect, mCornerRadius, mCornerRadius, mPaint); 82 } 83 84 @Override onBoundsChange(Rect bounds)85 protected void onBoundsChange(Rect bounds) { 86 super.onBoundsChange(bounds); 87 mDestRect.set(bounds); 88 updateMatrix(); 89 } 90 91 @Override setAlpha(int alpha)92 public void setAlpha(int alpha) { 93 final int oldAlpha = mPaint.getAlpha(); 94 if (alpha != oldAlpha) { 95 mPaint.setAlpha(alpha); 96 invalidateSelf(); 97 } 98 } 99 100 @Override getAlpha()101 public int getAlpha() { 102 return mPaint.getAlpha(); 103 } 104 105 @Override setColorFilter(ColorFilter colorFilter)106 public void setColorFilter(ColorFilter colorFilter) { 107 mPaint.setColorFilter(colorFilter); 108 invalidateSelf(); 109 } 110 111 @Override getColorFilter()112 public ColorFilter getColorFilter() { 113 return mPaint.getColorFilter(); 114 } 115 116 @Override getOpacity()117 public int getOpacity() { 118 return PixelFormat.TRANSLUCENT; 119 } 120 updateMatrix()121 private void updateMatrix() { 122 if (mThumbnailData.thumbnail == null) { 123 return; 124 } 125 mMatrix.reset(); 126 float scaleX; 127 float scaleY; 128 Rect bounds = getBounds(); 129 Bitmap thumbnail = mThumbnailData.thumbnail; 130 if (mRequestedOrientation != mThumbnailData.orientation) { 131 // Rotate and translate so that top left is the same. 132 mMatrix.postRotate(90, 0, 0); 133 mMatrix.postTranslate(thumbnail.getHeight(), 0); 134 135 scaleX = (float) bounds.width() / thumbnail.getHeight(); 136 scaleY = (float) bounds.height() / thumbnail.getWidth(); 137 } else { 138 scaleX = (float) bounds.width() / thumbnail.getWidth(); 139 scaleY = (float) bounds.height() / thumbnail.getHeight(); 140 } 141 // Scale to fill. 142 mMatrix.postScale(scaleX, scaleY); 143 mShader.setLocalMatrix(mMatrix); 144 } 145 } 146