blob: e5ac0d31ca8fb164a2392358cde1cc01785b75f3 [file] [log] [blame]
Winson Chung37c8d8e2014-03-24 14:53:07 -07001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.recents.views;
18
Winson21700932016-03-24 17:26:23 -070019import android.app.ActivityManager;
Winson Chung37c8d8e2014-03-24 14:53:07 -070020import android.content.Context;
Winson21700932016-03-24 17:26:23 -070021import android.content.res.Configuration;
Winson Chungdcfa7972014-07-22 12:27:13 -070022import android.graphics.Bitmap;
Selim Cineke8199c52014-09-17 04:03:52 +020023import android.graphics.BitmapShader;
24import android.graphics.Canvas;
25import android.graphics.Color;
Winson8be16342016-02-09 11:53:27 -080026import android.graphics.ColorMatrix;
27import android.graphics.ColorMatrixColorFilter;
Selim Cineke8199c52014-09-17 04:03:52 +020028import android.graphics.LightingColorFilter;
29import android.graphics.Matrix;
30import android.graphics.Paint;
Winson Chung743d5c92014-06-13 10:14:53 -070031import android.graphics.Rect;
Winson Chung1af8eda2016-02-05 17:55:56 +000032import android.graphics.Region;
Selim Cineke8199c52014-09-17 04:03:52 +020033import android.graphics.Shader;
Winson Chung37c8d8e2014-03-24 14:53:07 -070034import android.util.AttributeSet;
Winson Chung743d5c92014-06-13 10:14:53 -070035import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080036import android.view.ViewDebug;
Winsonc0d70582016-01-29 10:24:39 -080037
Winson2536c7e2015-10-01 15:49:31 -070038import com.android.systemui.R;
Winson21700932016-03-24 17:26:23 -070039import com.android.systemui.recents.Recents;
40import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung37c8d8e2014-03-24 14:53:07 -070041import com.android.systemui.recents.model.Task;
42
43
Winson Chung353c0b92014-10-16 17:43:23 -070044/**
45 * The task thumbnail view. It implements an image view that allows for animating the dim and
46 * alpha of the thumbnail image.
47 */
Selim Cineke8199c52014-09-17 04:03:52 +020048public class TaskViewThumbnail extends View {
Winson Chung743d5c92014-06-13 10:14:53 -070049
Winson8be16342016-02-09 11:53:27 -080050 private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
51 private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
52
Winson778f4952016-01-08 10:32:08 -080053 private Task mTask;
Winson479f7442015-11-25 15:16:27 -080054
Winson21700932016-03-24 17:26:23 -070055 private Rect mDisplayRect = new Rect();
56 private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
57
Winson Chung353c0b92014-10-16 17:43:23 -070058 // Drawing
Winson231bc9c2016-02-09 12:31:00 -080059 @ViewDebug.ExportedProperty(category="recents")
Winson21700932016-03-24 17:26:23 -070060 Rect mTaskViewRect = new Rect();
61 @ViewDebug.ExportedProperty(category="recents")
Winson778f4952016-01-08 10:32:08 -080062 Rect mThumbnailRect = new Rect();
Winson231bc9c2016-02-09 12:31:00 -080063 @ViewDebug.ExportedProperty(category="recents")
Winson21700932016-03-24 17:26:23 -070064 float mThumbnailScale;
65 float mFullscreenThumbnailScale;
66 ActivityManager.TaskThumbnailInfo mThumbnailInfo;
67
Winson35f30502015-09-28 11:24:36 -070068 int mCornerRadius;
Winson231bc9c2016-02-09 12:31:00 -080069 @ViewDebug.ExportedProperty(category="recents")
Winson Chung353c0b92014-10-16 17:43:23 -070070 float mDimAlpha;
71 Matrix mScaleMatrix = new Matrix();
Selim Cineke8199c52014-09-17 04:03:52 +020072 Paint mDrawPaint = new Paint();
Winson Chung1af8eda2016-02-05 17:55:56 +000073 Paint mBgFillPaint = new Paint();
Winson Chung353c0b92014-10-16 17:43:23 -070074 BitmapShader mBitmapShader;
Selim Cineke8199c52014-09-17 04:03:52 +020075 LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
Winson Chung353c0b92014-10-16 17:43:23 -070076
Winson Chung353c0b92014-10-16 17:43:23 -070077 // Task bar clipping, the top of this thumbnail can be clipped against the opaque header
78 // bar that overlaps this thumbnail
79 View mTaskBar;
Winson231bc9c2016-02-09 12:31:00 -080080 @ViewDebug.ExportedProperty(category="recents")
Winson Chung353c0b92014-10-16 17:43:23 -070081 Rect mClipRect = new Rect();
82
83 // Visibility optimization, if the thumbnail height is less than the height of the header
84 // bar for the task view, then just mark this thumbnail view as invisible
Winson231bc9c2016-02-09 12:31:00 -080085 @ViewDebug.ExportedProperty(category="recents")
Winson Chung353c0b92014-10-16 17:43:23 -070086 boolean mInvisible;
87
Winson231bc9c2016-02-09 12:31:00 -080088 @ViewDebug.ExportedProperty(category="recents")
Winson8be16342016-02-09 11:53:27 -080089 boolean mDisabledInSafeMode;
90
Winson Chungec396d62014-08-06 17:08:00 -070091 public TaskViewThumbnail(Context context) {
Winson Chung37c8d8e2014-03-24 14:53:07 -070092 this(context, null);
93 }
94
Winson Chungec396d62014-08-06 17:08:00 -070095 public TaskViewThumbnail(Context context, AttributeSet attrs) {
Winson Chung37c8d8e2014-03-24 14:53:07 -070096 this(context, attrs, 0);
97 }
98
Winson Chungec396d62014-08-06 17:08:00 -070099 public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700100 this(context, attrs, defStyleAttr, 0);
101 }
102
Winson Chungec396d62014-08-06 17:08:00 -0700103 public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700104 super(context, attrs, defStyleAttr, defStyleRes);
Selim Cineke8199c52014-09-17 04:03:52 +0200105 mDrawPaint.setColorFilter(mLightingColorFilter);
106 mDrawPaint.setFilterBitmap(true);
107 mDrawPaint.setAntiAlias(true);
Winson35f30502015-09-28 11:24:36 -0700108 mCornerRadius = getResources().getDimensionPixelSize(
109 R.dimen.recents_task_view_rounded_corners_radius);
Winson Chung1af8eda2016-02-05 17:55:56 +0000110 mBgFillPaint.setColor(Color.WHITE);
Winson21700932016-03-24 17:26:23 -0700111 mFullscreenThumbnailScale = context.getResources().getFraction(
112 com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
Selim Cineke8199c52014-09-17 04:03:52 +0200113 }
114
Winsonde0591a2015-12-04 17:24:35 -0800115 /**
116 * Called when the task view frame changes, allowing us to move the contents of the header
117 * to match the frame changes.
118 */
119 public void onTaskViewSizeChanged(int width, int height) {
Winson94a0e892016-01-08 16:06:50 -0800120 // Return early if the bounds have not changed
121 if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
122 return;
123 }
124
Winsonde0591a2015-12-04 17:24:35 -0800125 mTaskViewRect.set(0, 0, width, height);
Winson778f4952016-01-08 10:32:08 -0800126 updateThumbnailScale();
Winsonde0591a2015-12-04 17:24:35 -0800127 }
128
Selim Cineke8199c52014-09-17 04:03:52 +0200129 @Override
Winson21700932016-03-24 17:26:23 -0700130 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
131 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
132
133 SystemServicesProxy ssp = Recents.getSystemServices();
134 mOrientation = ssp.getDisplayOrientation();
135 mDisplayRect = ssp.getDisplayRect();
136 }
137
138 @Override
Winson Chung353c0b92014-10-16 17:43:23 -0700139 protected void onDraw(Canvas canvas) {
140 if (mInvisible) {
141 return;
142 }
Winson Chung1af8eda2016-02-05 17:55:56 +0000143
Winson9a54ac42016-04-01 12:17:02 -0700144 int viewWidth = mTaskViewRect.width();
145 int viewHeight = mTaskViewRect.height();
Winson21700932016-03-24 17:26:23 -0700146 if (mBitmapShader != null) {
Winson21700932016-03-24 17:26:23 -0700147
148 // We are drawing the thumbnail in the same orientation, so just fit the width
149 int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
150 int thumbnailHeight = (int) (mThumbnailRect.height() * mThumbnailScale);
151
152 if (thumbnailWidth >= viewWidth && thumbnailHeight >= viewHeight) {
153 // Thumbnail fills the full task view bounds, so just draw it
154 canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
155 mDrawPaint);
156 } else {
157 // Thumbnail does not fill the full task view bounds, so just draw it and fill the
158 // empty areas with the background color
159 int count = canvas.save();
Winson Chung1af8eda2016-02-05 17:55:56 +0000160
161 // Since we only want the top corners to be rounded, draw slightly beyond the
162 // thumbnail height, but clip to the thumbnail height
Winson21700932016-03-24 17:26:23 -0700163 canvas.clipRect(0, 0, thumbnailWidth, thumbnailHeight, Region.Op.REPLACE);
164 canvas.drawRoundRect(0, 0,
165 thumbnailWidth + (thumbnailWidth < viewWidth ? mCornerRadius : 0),
166 thumbnailHeight + (thumbnailHeight < viewHeight ? mCornerRadius : 0),
Winson Chung1af8eda2016-02-05 17:55:56 +0000167 mCornerRadius, mCornerRadius, mDrawPaint);
Winson Chung1af8eda2016-02-05 17:55:56 +0000168
Winson21700932016-03-24 17:26:23 -0700169 // In the remaining space, draw the background color
170 if (thumbnailWidth < viewWidth) {
171 canvas.clipRect(thumbnailWidth, 0, viewWidth, viewHeight, Region.Op.REPLACE);
172 canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
173 viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
174 }
175 if (thumbnailWidth > 0 && thumbnailHeight < viewHeight) {
176 canvas.clipRect(0, thumbnailHeight, viewWidth, viewHeight, Region.Op.REPLACE);
177 canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
178 viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
179 }
Winson Chung1af8eda2016-02-05 17:55:56 +0000180
Winson Chung1af8eda2016-02-05 17:55:56 +0000181 canvas.restoreToCount(count);
182 }
Winson9a54ac42016-04-01 12:17:02 -0700183 } else {
184 canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
185 mBgFillPaint);
Winson Chung1af8eda2016-02-05 17:55:56 +0000186 }
Winson Chung353c0b92014-10-16 17:43:23 -0700187 }
188
189 /** Sets the thumbnail to a given bitmap. */
Winson21700932016-03-24 17:26:23 -0700190 void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
Winson Chung353c0b92014-10-16 17:43:23 -0700191 if (bm != null) {
Winson21700932016-03-24 17:26:23 -0700192 mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Winson Chung353c0b92014-10-16 17:43:23 -0700193 mDrawPaint.setShader(mBitmapShader);
Winson778f4952016-01-08 10:32:08 -0800194 mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
Winson21700932016-03-24 17:26:23 -0700195 mThumbnailInfo = thumbnailInfo;
Winson778f4952016-01-08 10:32:08 -0800196 updateThumbnailScale();
Winson Chung353c0b92014-10-16 17:43:23 -0700197 } else {
198 mBitmapShader = null;
199 mDrawPaint.setShader(null);
Winson778f4952016-01-08 10:32:08 -0800200 mThumbnailRect.setEmpty();
Winson21700932016-03-24 17:26:23 -0700201 mThumbnailInfo = null;
Winson Chung353c0b92014-10-16 17:43:23 -0700202 }
Winson Chung353c0b92014-10-16 17:43:23 -0700203 }
204
205 /** Updates the paint to draw the thumbnail. */
206 void updateThumbnailPaintFilter() {
207 if (mInvisible) {
208 return;
209 }
Winson479f7442015-11-25 15:16:27 -0800210 int mul = (int) ((1.0f - mDimAlpha) * 255);
Winson Chung353c0b92014-10-16 17:43:23 -0700211 if (mBitmapShader != null) {
Winson8be16342016-02-09 11:53:27 -0800212 if (mDisabledInSafeMode) {
213 // Brightness: C-new = C-old*(1-amount) + amount
214 TMP_FILTER_COLOR_MATRIX.setSaturation(0);
215 float scale = 1f - mDimAlpha;
216 float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
217 mat[0] = scale;
218 mat[6] = scale;
219 mat[12] = scale;
Winson12858a62016-02-24 11:06:01 -0800220 mat[4] = mDimAlpha * 255f;
221 mat[9] = mDimAlpha * 255f;
222 mat[14] = mDimAlpha * 255f;
Winson8be16342016-02-09 11:53:27 -0800223 TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
224 ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
225 mDrawPaint.setColorFilter(filter);
226 mBgFillPaint.setColorFilter(filter);
227 } else {
228 mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
229 mDrawPaint.setColorFilter(mLightingColorFilter);
230 mDrawPaint.setColor(0xFFffffff);
231 mBgFillPaint.setColorFilter(mLightingColorFilter);
232 }
Winson Chung353c0b92014-10-16 17:43:23 -0700233 } else {
Winson479f7442015-11-25 15:16:27 -0800234 int grey = mul;
Winson Chung353c0b92014-10-16 17:43:23 -0700235 mDrawPaint.setColorFilter(null);
236 mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
237 }
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000238 if (!mInvisible) {
239 invalidate();
240 }
Winson Chung353c0b92014-10-16 17:43:23 -0700241 }
242
Winson479f7442015-11-25 15:16:27 -0800243 /**
Winson778f4952016-01-08 10:32:08 -0800244 * Updates the scale of the bitmap relative to this view.
Winson479f7442015-11-25 15:16:27 -0800245 */
Winson778f4952016-01-08 10:32:08 -0800246 public void updateThumbnailScale() {
Winson21700932016-03-24 17:26:23 -0700247 mThumbnailScale = 1f;
Winson479f7442015-11-25 15:16:27 -0800248 if (mBitmapShader != null) {
Winson21700932016-03-24 17:26:23 -0700249 // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
250 // dragged into the stack from the freeform workspace
251 boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
252 if (mTaskViewRect.isEmpty() || mThumbnailInfo == null ||
253 mThumbnailInfo.taskWidth == 0 || mThumbnailInfo.taskHeight == 0) {
254 // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
255 // and only draw the background color
256 mThumbnailScale = 0f;
257 } else if (isStackTask) {
258 float invThumbnailScale = 1f / mFullscreenThumbnailScale;
259 if (mOrientation == Configuration.ORIENTATION_PORTRAIT) {
260 if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
261 // If we are in the same orientation as the screenshot, just scale it to the
262 // width of the task view
263 mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
264 } else {
265 // Scale the landscape thumbnail up to app size, then scale that to the task
266 // view size to match other portrait screenshots
267 mThumbnailScale = invThumbnailScale *
268 ((float) mTaskViewRect.width() / mDisplayRect.width());
269 }
270 } else {
271 // Otherwise, scale the screenshot to fit 1:1 in the current orientation
272 mThumbnailScale = invThumbnailScale;
273 }
Winson778f4952016-01-08 10:32:08 -0800274 } else {
275 // Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
276 // to fit the entire bitmap into the task bounds
Winson21700932016-03-24 17:26:23 -0700277 mThumbnailScale = Math.min(
Winson778f4952016-01-08 10:32:08 -0800278 (float) mTaskViewRect.width() / mThumbnailRect.width(),
279 (float) mTaskViewRect.height() / mThumbnailRect.height());
280 }
Winson21700932016-03-24 17:26:23 -0700281 mScaleMatrix.setScale(mThumbnailScale, mThumbnailScale);
Selim Cineke8199c52014-09-17 04:03:52 +0200282 mBitmapShader.setLocalMatrix(mScaleMatrix);
283 }
Winson479f7442015-11-25 15:16:27 -0800284 if (!mInvisible) {
285 invalidate();
286 }
287 }
288
Winson Chung353c0b92014-10-16 17:43:23 -0700289 /** Updates the clip rect based on the given task bar. */
290 void updateClipToTaskBar(View taskBar) {
291 mTaskBar = taskBar;
292 int top = (int) Math.max(0, taskBar.getTranslationY() +
293 taskBar.getMeasuredHeight() - 1);
294 mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight());
295 setClipBounds(mClipRect);
296 }
297
298 /** Updates the visibility of the the thumbnail. */
299 void updateThumbnailVisibility(int clipBottom) {
300 boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
301 if (invisible != mInvisible) {
302 mInvisible = invisible;
303 if (!mInvisible) {
304 updateThumbnailPaintFilter();
305 }
Winson Chung353c0b92014-10-16 17:43:23 -0700306 }
307 }
308
309 /**
310 * Sets the dim alpha, only used when we are not using hardware layers.
311 * (see RecentsConfiguration.useHardwareLayers)
312 */
Selim Cineke8199c52014-09-17 04:03:52 +0200313 public void setDimAlpha(float dimAlpha) {
314 mDimAlpha = dimAlpha;
Winson Chung353c0b92014-10-16 17:43:23 -0700315 updateThumbnailPaintFilter();
316 }
317
318 /** Binds the thumbnail view to the task */
Winson21700932016-03-24 17:26:23 -0700319 void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo,
320 boolean disabledInSafeMode) {
Winson778f4952016-01-08 10:32:08 -0800321 mTask = t;
Winson8be16342016-02-09 11:53:27 -0800322 mDisabledInSafeMode = disabledInSafeMode;
Winson Chung353c0b92014-10-16 17:43:23 -0700323 if (t.thumbnail != null) {
Winson21700932016-03-24 17:26:23 -0700324 setThumbnail(t.thumbnail, thumbnailInfo);
Winson Chung353c0b92014-10-16 17:43:23 -0700325 } else {
Winson21700932016-03-24 17:26:23 -0700326 setThumbnail(null, null);
Winson Chung353c0b92014-10-16 17:43:23 -0700327 }
Winson9a54ac42016-04-01 12:17:02 -0700328 if (t.colorBackground != 0) {
329 mBgFillPaint.setColor(t.colorBackground);
330 }
Selim Cineke8199c52014-09-17 04:03:52 +0200331 }
332
Winson Chung37c8d8e2014-03-24 14:53:07 -0700333 /** Unbinds the thumbnail view from the task */
334 void unbindFromTask() {
Winson778f4952016-01-08 10:32:08 -0800335 mTask = null;
Winson21700932016-03-24 17:26:23 -0700336 setThumbnail(null, null);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700337 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700338}