1 /* 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 17 package com.android.systemui.recents.views; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.content.Context; 23 import android.graphics.Bitmap; 24 import android.graphics.BitmapShader; 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.LightingColorFilter; 28 import android.graphics.Matrix; 29 import android.graphics.Paint; 30 import android.graphics.Rect; 31 import android.graphics.RectF; 32 import android.graphics.Shader; 33 import android.util.AttributeSet; 34 import android.view.View; 35 import com.android.systemui.recents.RecentsConfiguration; 36 import com.android.systemui.recents.misc.Utilities; 37 import com.android.systemui.recents.model.Task; 38 39 40 /** 41 * The task thumbnail view. It implements an image view that allows for animating the dim and 42 * alpha of the thumbnail image. 43 */ 44 public class TaskViewThumbnail extends View { 45 46 RecentsConfiguration mConfig; 47 48 // Drawing 49 float mDimAlpha; 50 Matrix mScaleMatrix = new Matrix(); 51 Paint mDrawPaint = new Paint(); 52 RectF mBitmapRect = new RectF(); 53 RectF mLayoutRect = new RectF(); 54 BitmapShader mBitmapShader; 55 LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0); 56 57 // Thumbnail alpha 58 float mThumbnailAlpha; 59 ValueAnimator mThumbnailAlphaAnimator; 60 ValueAnimator.AnimatorUpdateListener mThumbnailAlphaUpdateListener 61 = new ValueAnimator.AnimatorUpdateListener() { 62 @Override 63 public void onAnimationUpdate(ValueAnimator animation) { 64 mThumbnailAlpha = (float) animation.getAnimatedValue(); 65 updateThumbnailPaintFilter(); 66 } 67 }; 68 69 // Task bar clipping, the top of this thumbnail can be clipped against the opaque header 70 // bar that overlaps this thumbnail 71 View mTaskBar; 72 Rect mClipRect = new Rect(); 73 74 // Visibility optimization, if the thumbnail height is less than the height of the header 75 // bar for the task view, then just mark this thumbnail view as invisible 76 boolean mInvisible; 77 78 public TaskViewThumbnail(Context context) { 79 this(context, null); 80 } 81 82 public TaskViewThumbnail(Context context, AttributeSet attrs) { 83 this(context, attrs, 0); 84 } 85 86 public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) { 87 this(context, attrs, defStyleAttr, 0); 88 } 89 90 public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 91 super(context, attrs, defStyleAttr, defStyleRes); 92 mConfig = RecentsConfiguration.getInstance(); 93 mDrawPaint.setColorFilter(mLightingColorFilter); 94 mDrawPaint.setFilterBitmap(true); 95 mDrawPaint.setAntiAlias(true); 96 } 97 98 @Override 99 protected void onFinishInflate() { 100 mThumbnailAlpha = mConfig.taskViewThumbnailAlpha; 101 updateThumbnailPaintFilter(); 102 } 103 104 @Override 105 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 106 super.onLayout(changed, left, top, right, bottom); 107 if (changed) { 108 mLayoutRect.set(0, 0, getWidth(), getHeight()); 109 updateThumbnailScale(); 110 } 111 } 112 113 @Override 114 protected void onDraw(Canvas canvas) { 115 if (mInvisible) { 116 return; 117 } 118 // Draw the thumbnail with the rounded corners 119 canvas.drawRoundRect(0, 0, getWidth(), getHeight(), 120 mConfig.taskViewRoundedCornerRadiusPx, 121 mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint); 122 } 123 124 /** Sets the thumbnail to a given bitmap. */ 125 void setThumbnail(Bitmap bm) { 126 if (bm != null) { 127 mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, 128 Shader.TileMode.CLAMP); 129 mDrawPaint.setShader(mBitmapShader); 130 mBitmapRect.set(0, 0, bm.getWidth(), bm.getHeight()); 131 updateThumbnailScale(); 132 } else { 133 mBitmapShader = null; 134 mDrawPaint.setShader(null); 135 } 136 updateThumbnailPaintFilter(); 137 } 138 139 /** Updates the paint to draw the thumbnail. */ 140 void updateThumbnailPaintFilter() { 141 if (mInvisible) { 142 return; 143 } 144 int mul = (int) ((1.0f - mDimAlpha) * mThumbnailAlpha * 255); 145 int add = (int) ((1.0f - mDimAlpha) * (1 - mThumbnailAlpha) * 255); 146 if (mBitmapShader != null) { 147 mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul)); 148 mLightingColorFilter.setColorAdd(Color.argb(0, add, add, add)); 149 mDrawPaint.setColorFilter(mLightingColorFilter); 150 mDrawPaint.setColor(0xffffffff); 151 } else { 152 int grey = mul + add; 153 mDrawPaint.setColorFilter(null); 154 mDrawPaint.setColor(Color.argb(255, grey, grey, grey)); 155 } 156 invalidate(); 157 } 158 159 /** Updates the thumbnail shader's scale transform. */ 160 void updateThumbnailScale() { 161 if (mBitmapShader != null) { 162 mScaleMatrix.setRectToRect(mBitmapRect, mLayoutRect, Matrix.ScaleToFit.FILL); 163 mBitmapShader.setLocalMatrix(mScaleMatrix); 164 } 165 } 166 167 /** Updates the clip rect based on the given task bar. */ 168 void updateClipToTaskBar(View taskBar) { 169 mTaskBar = taskBar; 170 int top = (int) Math.max(0, taskBar.getTranslationY() + 171 taskBar.getMeasuredHeight() - 1); 172 mClipRect.set(0, top, getMeasuredWidth(), getMeasuredHeight()); 173 setClipBounds(mClipRect); 174 } 175 176 /** Updates the visibility of the the thumbnail. */ 177 void updateThumbnailVisibility(int clipBottom) { 178 boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight(); 179 if (invisible != mInvisible) { 180 mInvisible = invisible; 181 if (!mInvisible) { 182 updateThumbnailPaintFilter(); 183 } 184 invalidate(); 185 } 186 } 187 188 /** 189 * Sets the dim alpha, only used when we are not using hardware layers. 190 * (see RecentsConfiguration.useHardwareLayers) 191 */ 192 public void setDimAlpha(float dimAlpha) { 193 mDimAlpha = dimAlpha; 194 updateThumbnailPaintFilter(); 195 } 196 197 /** Binds the thumbnail view to the task */ 198 void rebindToTask(Task t) { 199 if (t.thumbnail != null) { 200 setThumbnail(t.thumbnail); 201 } else { 202 setThumbnail(null); 203 } 204 } 205 206 /** Unbinds the thumbnail view from the task */ 207 void unbindFromTask() { 208 setThumbnail(null); 209 } 210 211 /** Handles focus changes. */ 212 void onFocusChanged(boolean focused) { 213 if (focused) { 214 if (Float.compare(getAlpha(), 1f) != 0) { 215 startFadeAnimation(1f, 0, 150, null); 216 } 217 } else { 218 if (Float.compare(getAlpha(), mConfig.taskViewThumbnailAlpha) != 0) { 219 startFadeAnimation(mConfig.taskViewThumbnailAlpha, 0, 150, null); 220 } 221 } 222 } 223 224 /** 225 * Prepares for the enter recents animation, this gets called before the the view 226 * is first visible and will be followed by a startEnterRecentsAnimation() call. 227 */ 228 void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask) { 229 if (isTaskViewLaunchTargetTask) { 230 mThumbnailAlpha = 1f; 231 } else { 232 mThumbnailAlpha = mConfig.taskViewThumbnailAlpha; 233 } 234 updateThumbnailPaintFilter(); 235 } 236 237 /** Animates this task thumbnail as it enters Recents. */ 238 void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) { 239 startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay, 240 mConfig.taskViewEnterFromAppDuration, postAnimRunnable); 241 } 242 243 /** Animates this task thumbnail as it exits Recents. */ 244 void startLaunchTaskAnimation(Runnable postAnimRunnable) { 245 startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable); 246 } 247 248 /** Starts a new thumbnail alpha animation. */ 249 void startFadeAnimation(float finalAlpha, int delay, int duration, final Runnable postAnimRunnable) { 250 Utilities.cancelAnimationWithoutCallbacks(mThumbnailAlphaAnimator); 251 mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha); 252 mThumbnailAlphaAnimator.setStartDelay(delay); 253 mThumbnailAlphaAnimator.setDuration(duration); 254 mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator); 255 mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener); 256 if (postAnimRunnable != null) { 257 mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() { 258 @Override 259 public void onAnimationEnd(Animator animation) { 260 postAnimRunnable.run(); 261 } 262 }); 263 } 264 mThumbnailAlphaAnimator.start(); 265 } 266 } 267