Use custom drawable for Recents Go thumbnails (1/2)

In order to support rotated thumbnails based off the device orientation,
we should create a custom drawable that supports drawing a rotated
bitmap based off some requested orientation. This CL adds this class,
and we'll use it in a later CL.

By baking this directly into the drawable, we prevent unnecessary
allocations of rotated bitmaps.

Bug: 114136250
Bug: 131095241
Test: Builds
Change-Id: Ib4e83e3619028c583e10f1b8d743a365ec310346
(cherry picked from commit 518ff10856a60c9fe7a6ccdbd7e6c6a88cba3c6e)
diff --git a/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java b/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java
new file mode 100644
index 0000000..6ef9039
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Bitmap backed drawable that supports rotating the thumbnail bitmap depending on if the
+ * orientation the thumbnail was taken in matches the desired orientation. In addition, the
+ * thumbnail always fills into the containing bounds.
+ */
+public final class ThumbnailDrawable extends Drawable {
+
+    private final Paint mPaint = new Paint();
+    private final Matrix mMatrix = new Matrix();
+    private final ThumbnailData mThumbnailData;
+    private int mRequestedOrientation;
+
+    public ThumbnailDrawable(@NonNull ThumbnailData thumbnailData, int requestedOrientation) {
+        mThumbnailData = thumbnailData;
+        mRequestedOrientation = requestedOrientation;
+        updateMatrix();
+    }
+
+    /**
+     * Set the requested orientation.
+     *
+     * @param orientation the orientation we want the thumbnail to be in
+     */
+    public void setRequestedOrientation(int orientation) {
+        if (mRequestedOrientation != orientation) {
+            mRequestedOrientation = orientation;
+            updateMatrix();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mThumbnailData.thumbnail == null) {
+            return;
+        }
+        canvas.drawBitmap(mThumbnailData.thumbnail, mMatrix, mPaint);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        updateMatrix();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final int oldAlpha = mPaint.getAlpha();
+        if (alpha != oldAlpha) {
+            mPaint.setAlpha(alpha);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public int getAlpha() {
+        return mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    private void updateMatrix() {
+        if (mThumbnailData.thumbnail == null) {
+            return;
+        }
+        mMatrix.reset();
+        float scaleX;
+        float scaleY;
+        Rect bounds = getBounds();
+        Bitmap thumbnail = mThumbnailData.thumbnail;
+        if (mRequestedOrientation != mThumbnailData.orientation) {
+            // Rotate and translate so that top left is the same.
+            mMatrix.postRotate(90, 0, 0);
+            mMatrix.postTranslate(thumbnail.getHeight(), 0);
+
+            scaleX = (float) bounds.width() / thumbnail.getHeight();
+            scaleY = (float) bounds.height() / thumbnail.getWidth();
+        } else {
+            scaleX = (float) bounds.width() / thumbnail.getWidth();
+            scaleY = (float) bounds.height() / thumbnail.getHeight();
+        }
+        // Scale to fill.
+        mMatrix.postScale(scaleX, scaleY);
+    }
+}