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