1de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar/*
2de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * Copyright (C) 2014 The Android Open Source Project
3de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar *
4de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * Licensed under the Apache License, Version 2.0 (the "License");
5de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * you may not use this file except in compliance with the License.
6de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * You may obtain a copy of the License at
7de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar *
8de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar *      http://www.apache.org/licenses/LICENSE-2.0
9de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar *
10de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * Unless required by applicable law or agreed to in writing, software
11de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * distributed under the License is distributed on an "AS IS" BASIS,
12de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * See the License for the specific language governing permissions and
14de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * limitations under the License.
15de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar */
16de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
17de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarpackage com.android.systemui.recents.views;
18de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
19de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.content.Context;
20de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.content.res.Configuration;
21de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.content.res.Resources;
22de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Bitmap;
23de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.BitmapShader;
24de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Canvas;
25de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Color;
26de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.ColorMatrix;
27de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.ColorMatrixColorFilter;
28de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.LightingColorFilter;
29de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Matrix;
30de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Paint;
31de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Rect;
32de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.graphics.Shader;
33de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.util.AttributeSet;
34de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.view.View;
35de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport android.view.ViewDebug;
36de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
37de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport com.android.systemui.R;
38de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport com.android.systemui.recents.events.EventBus;
39de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
40de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport com.android.systemui.recents.misc.Utilities;
41de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport com.android.systemui.recents.model.Task;
42de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport com.android.systemui.recents.model.ThumbnailData;
43de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarimport java.io.PrintWriter;
44de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
45de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
46de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar/**
47de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * The task thumbnail view.  It implements an image view that allows for animating the dim and
48de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar * alpha of the thumbnail image.
49de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar */
50de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainarpublic class TaskViewThumbnail extends View {
51de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
52de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
53de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
54de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
55de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private Task mTask;
56de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
57de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
58de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private Rect mDisplayRect = new Rect();
59de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
60de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    // Drawing
61de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @ViewDebug.ExportedProperty(category="recents")
62de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected Rect mTaskViewRect = new Rect();
63de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @ViewDebug.ExportedProperty(category="recents")
64de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected Rect mThumbnailRect = new Rect();
65de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @ViewDebug.ExportedProperty(category="recents")
66de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected float mThumbnailScale;
67de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private float mFullscreenThumbnailScale = 1f;
68de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    /** The height, in pixels, of the task view's title bar. */
69de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private int mTitleBarHeight;
70de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private boolean mSizeToFit = false;
71de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private boolean mOverlayHeaderOnThumbnailActionBar = true;
72de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private ThumbnailData mThumbnailData;
73de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
74de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected int mCornerRadius;
75de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @ViewDebug.ExportedProperty(category="recents")
76de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private float mDimAlpha;
77de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private Matrix mMatrix = new Matrix();
78de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private Paint mDrawPaint = new Paint();
79de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected Paint mLockedPaint = new Paint();
80de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected Paint mBgFillPaint = new Paint();
81de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected BitmapShader mBitmapShader;
82de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected boolean mUserLocked = false;
83de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
84de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
85de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    // Clip the top of the thumbnail against the opaque header bar that overlaps this view
86de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private View mTaskBar;
87de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
88de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    // Visibility optimization, if the thumbnail height is less than the height of the header
89de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    // bar for the task view, then just mark this thumbnail view as invisible
90de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @ViewDebug.ExportedProperty(category="recents")
91de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private boolean mInvisible;
92de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
93de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @ViewDebug.ExportedProperty(category="recents")
94de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    private boolean mDisabledInSafeMode;
95de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
96de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    public TaskViewThumbnail(Context context) {
97de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        this(context, null);
98de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    }
99de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
100de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    public TaskViewThumbnail(Context context, AttributeSet attrs) {
101de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        this(context, attrs, 0);
102de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    }
103de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
104de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
105de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        this(context, attrs, defStyleAttr, 0);
106de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    }
107de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
108de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
109de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        super(context, attrs, defStyleAttr, defStyleRes);
110de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mDrawPaint.setColorFilter(mLightingColorFilter);
111de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mDrawPaint.setFilterBitmap(true);
112de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mDrawPaint.setAntiAlias(true);
113de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        Resources res = getResources();
114de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
115de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mBgFillPaint.setColor(Color.WHITE);
116de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mLockedPaint.setColor(Color.WHITE);
117de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
118de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    }
119de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
120de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    /**
121de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar     * Called when the task view frame changes, allowing us to move the contents of the header
122de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar     * to match the frame changes.
123de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar     */
124de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    public void onTaskViewSizeChanged(int width, int height) {
125de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        // Return early if the bounds have not changed
126de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
127de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar            return;
128de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        }
129de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
130de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        mTaskViewRect.set(0, 0, width, height);
131de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        setLeftTopRightBottom(0, 0, width, height);
132de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        updateThumbnailMatrix();
133de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    }
134de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
135de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    @Override
136de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar    protected void onDraw(Canvas canvas) {
137de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        if (mInvisible) {
138de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar            return;
139de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        }
140de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
141de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        int viewWidth = mTaskViewRect.width();
142de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        int viewHeight = mTaskViewRect.height();
143de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        int thumbnailWidth = Math.min(viewWidth,
144de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar                (int) (mThumbnailRect.width() * mThumbnailScale));
145de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        int thumbnailHeight = Math.min(viewHeight,
146de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar                (int) (mThumbnailRect.height() * mThumbnailScale));
147de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar
148de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar        if (mUserLocked) {
149de2d8694e25a814696358e95141f4b1aa4d8847ePirama Arumuga Nainar            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
150                    mLockedPaint);
151        } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
152            int topOffset = 0;
153            if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
154                topOffset = mTaskBar.getHeight() - mCornerRadius;
155            }
156
157            // Draw the background, there will be some small overdraw with the thumbnail
158            if (thumbnailWidth < viewWidth) {
159                // Portrait thumbnail on a landscape task view
160                canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), topOffset,
161                        viewWidth, viewHeight,
162                        mCornerRadius, mCornerRadius, mBgFillPaint);
163            }
164            if (thumbnailHeight < viewHeight) {
165                // Landscape thumbnail on a portrait task view
166                canvas.drawRoundRect(0, Math.max(topOffset, thumbnailHeight - mCornerRadius),
167                        viewWidth, viewHeight,
168                        mCornerRadius, mCornerRadius, mBgFillPaint);
169            }
170
171            // Draw the thumbnail
172            canvas.drawRoundRect(0, topOffset, thumbnailWidth, thumbnailHeight,
173                    mCornerRadius, mCornerRadius, mDrawPaint);
174        } else {
175            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
176                    mBgFillPaint);
177        }
178    }
179
180    /** Sets the thumbnail to a given bitmap. */
181    void setThumbnail(ThumbnailData thumbnailData) {
182        if (thumbnailData != null && thumbnailData.thumbnail != null) {
183            Bitmap bm = thumbnailData.thumbnail;
184            bm.prepareToDraw();
185            mFullscreenThumbnailScale = thumbnailData.scale;
186            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
187            mDrawPaint.setShader(mBitmapShader);
188            mThumbnailRect.set(0, 0,
189                    bm.getWidth() - thumbnailData.insets.left - thumbnailData.insets.right,
190                    bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom);
191            mThumbnailData = thumbnailData;
192            updateThumbnailMatrix();
193            updateThumbnailPaintFilter();
194        } else {
195            mBitmapShader = null;
196            mDrawPaint.setShader(null);
197            mThumbnailRect.setEmpty();
198            mThumbnailData = null;
199        }
200    }
201
202    /** Updates the paint to draw the thumbnail. */
203    void updateThumbnailPaintFilter() {
204        if (mInvisible) {
205            return;
206        }
207        int mul = (int) ((1.0f - mDimAlpha) * 255);
208        if (mBitmapShader != null) {
209            if (mDisabledInSafeMode) {
210                // Brightness: C-new = C-old*(1-amount) + amount
211                TMP_FILTER_COLOR_MATRIX.setSaturation(0);
212                float scale = 1f - mDimAlpha;
213                float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
214                mat[0] = scale;
215                mat[6] = scale;
216                mat[12] = scale;
217                mat[4] = mDimAlpha * 255f;
218                mat[9] = mDimAlpha * 255f;
219                mat[14] = mDimAlpha * 255f;
220                TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
221                ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
222                mDrawPaint.setColorFilter(filter);
223                mBgFillPaint.setColorFilter(filter);
224                mLockedPaint.setColorFilter(filter);
225            } else {
226                mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
227                mDrawPaint.setColorFilter(mLightingColorFilter);
228                mDrawPaint.setColor(0xFFffffff);
229                mBgFillPaint.setColorFilter(mLightingColorFilter);
230                mLockedPaint.setColorFilter(mLightingColorFilter);
231            }
232        } else {
233            int grey = mul;
234            mDrawPaint.setColorFilter(null);
235            mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
236        }
237        if (!mInvisible) {
238            invalidate();
239        }
240    }
241
242    /**
243     * Updates the scale of the bitmap relative to this view.
244     */
245    public void updateThumbnailMatrix() {
246        mThumbnailScale = 1f;
247        if (mBitmapShader != null && mThumbnailData != null) {
248            // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
249            // dragged into the stack from the freeform workspace
250            boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
251            int xOffset, yOffset = 0;
252            if (mTaskViewRect.isEmpty()) {
253                // If we haven't measured , skip the thumbnail drawing and only draw the background
254                // color
255                mThumbnailScale = 0f;
256            } else if (mSizeToFit) {
257                // Make sure we fill the entire space regardless of the orientation.
258                float viewAspectRatio = (float) mTaskViewRect.width() /
259                        (float) (mTaskViewRect.height() - mTitleBarHeight);
260                float thumbnailAspectRatio =
261                        (float) mThumbnailRect.width() / (float) mThumbnailRect.height();
262                if (viewAspectRatio > thumbnailAspectRatio) {
263                    mThumbnailScale =
264                            (float) mTaskViewRect.width() / (float) mThumbnailRect.width();
265                } else {
266                    mThumbnailScale = (float) (mTaskViewRect.height() - mTitleBarHeight)
267                            / (float) mThumbnailRect.height();
268                }
269            } else if (isStackTask) {
270                float invThumbnailScale = 1f / mFullscreenThumbnailScale;
271                if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
272                    if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
273                        // If we are in the same orientation as the screenshot, just scale it to the
274                        // width of the task view
275                        mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
276                    } else {
277                        // Scale the landscape thumbnail up to app size, then scale that to the task
278                        // view size to match other portrait screenshots
279                        mThumbnailScale = invThumbnailScale *
280                                ((float) mTaskViewRect.width() / mDisplayRect.width());
281                    }
282                } else {
283                    // Otherwise, scale the screenshot to fit 1:1 in the current orientation
284                    mThumbnailScale = invThumbnailScale;
285                }
286            } else {
287                // Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
288                // to fit the entire bitmap into the task bounds
289                mThumbnailScale = Math.min(
290                        (float) mTaskViewRect.width() / mThumbnailRect.width(),
291                        (float) mTaskViewRect.height() / mThumbnailRect.height());
292            }
293            mMatrix.setTranslate(-mThumbnailData.insets.left * mFullscreenThumbnailScale,
294                    -mThumbnailData.insets.top * mFullscreenThumbnailScale);
295            mMatrix.postScale(mThumbnailScale, mThumbnailScale);
296            mBitmapShader.setLocalMatrix(mMatrix);
297        }
298        if (!mInvisible) {
299            invalidate();
300        }
301    }
302
303    /** Sets whether the thumbnail should be resized to fit the task view in all orientations. */
304    public void setSizeToFit(boolean flag) {
305        mSizeToFit = flag;
306    }
307
308    /**
309     * Sets whether the header should overlap (and hide) the action bar in the thumbnail, or
310     * be stacked just above it.
311     */
312    public void setOverlayHeaderOnThumbnailActionBar(boolean flag) {
313        mOverlayHeaderOnThumbnailActionBar = flag;
314    }
315
316    /** Updates the clip rect based on the given task bar. */
317    void updateClipToTaskBar(View taskBar) {
318        mTaskBar = taskBar;
319        invalidate();
320    }
321
322    /** Updates the visibility of the the thumbnail. */
323    void updateThumbnailVisibility(int clipBottom) {
324        boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
325        if (invisible != mInvisible) {
326            mInvisible = invisible;
327            if (!mInvisible) {
328                updateThumbnailPaintFilter();
329            }
330        }
331    }
332
333    /**
334     * Sets the dim alpha, only used when we are not using hardware layers.
335     * (see RecentsConfiguration.useHardwareLayers)
336     */
337    public void setDimAlpha(float dimAlpha) {
338        mDimAlpha = dimAlpha;
339        updateThumbnailPaintFilter();
340    }
341
342    /**
343     * Returns the {@link Paint} used to draw a task screenshot, or {@link #mLockedPaint} if the
344     * thumbnail shouldn't be drawn because it belongs to a locked user.
345     */
346    protected Paint getDrawPaint() {
347        if (mUserLocked) {
348            return mLockedPaint;
349        }
350        return mDrawPaint;
351    }
352
353    /**
354     * Binds the thumbnail view to the task.
355     */
356    void bindToTask(Task t, boolean disabledInSafeMode, int displayOrientation, Rect displayRect) {
357        mTask = t;
358        mDisabledInSafeMode = disabledInSafeMode;
359        mDisplayOrientation = displayOrientation;
360        mDisplayRect.set(displayRect);
361        if (t.colorBackground != 0) {
362            mBgFillPaint.setColor(t.colorBackground);
363        }
364        if (t.colorPrimary != 0) {
365            mLockedPaint.setColor(t.colorPrimary);
366        }
367        mUserLocked = t.isLocked;
368        EventBus.getDefault().register(this);
369    }
370
371    /**
372     * Called when the bound task's data has loaded and this view should update to reflect the
373     * changes.
374     */
375    void onTaskDataLoaded(ThumbnailData thumbnailData) {
376        setThumbnail(thumbnailData);
377    }
378
379    /** Unbinds the thumbnail view from the task */
380    void unbindFromTask() {
381        mTask = null;
382        setThumbnail(null);
383        EventBus.getDefault().unregister(this);
384    }
385
386    public final void onBusEvent(TaskSnapshotChangedEvent event) {
387        if (mTask == null || event.taskId != mTask.key.id || event.thumbnailData == null
388                || event.thumbnailData.thumbnail == null) {
389            return;
390        }
391        setThumbnail(event.thumbnailData);
392    }
393
394    public void dump(String prefix, PrintWriter writer) {
395        writer.print(prefix); writer.print("TaskViewThumbnail");
396        writer.print(" mTaskViewRect="); writer.print(Utilities.dumpRect(mTaskViewRect));
397        writer.print(" mThumbnailRect="); writer.print(Utilities.dumpRect(mThumbnailRect));
398        writer.print(" mThumbnailScale="); writer.print(mThumbnailScale);
399        writer.print(" mDimAlpha="); writer.print(mDimAlpha);
400        writer.println();
401    }
402}
403