blob: 6b0694578c78f9b7c197a0d74887572848ff223e [file] [log] [blame]
Winson Chung303e1ff2014-03-07 15:06:19 -08001/*
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
Winson Chungc6011de2014-06-30 18:04:55 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Winson Chunga26fb782014-06-12 17:52:39 -070021import android.animation.ObjectAnimator;
Winson Chung14926462014-04-14 18:57:14 -070022import android.animation.TimeInterpolator;
23import android.animation.ValueAnimator;
Winson Chung303e1ff2014-03-07 15:06:19 -080024import android.content.Context;
Winson Chung14926462014-04-14 18:57:14 -070025import android.graphics.Canvas;
Winson Chung96e3bc12014-05-06 16:44:12 -070026import android.graphics.Outline;
Winson Chungc9567c02014-06-16 20:25:51 -070027import android.graphics.Paint;
Winson Chung14926462014-04-14 18:57:14 -070028import android.graphics.Path;
Winson Chung9f9679d2014-04-11 16:49:09 -070029import android.graphics.Point;
Winson Chung303e1ff2014-03-07 15:06:19 -080030import android.graphics.Rect;
Winson Chung14926462014-04-14 18:57:14 -070031import android.graphics.RectF;
Winson Chung37c8d8e2014-03-24 14:53:07 -070032import android.util.AttributeSet;
Winson Chung9f9679d2014-04-11 16:49:09 -070033import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080034import android.view.View;
Winson Chungb5ddfc32014-06-13 17:23:12 -070035import android.view.ViewPropertyAnimator;
Winson Chung14926462014-04-14 18:57:14 -070036import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080037import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070038import com.android.systemui.R;
Winson Chungd42a6cf2014-06-03 16:24:04 -070039import com.android.systemui.recents.Console;
Winson Chung14926462014-04-14 18:57:14 -070040import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080041import com.android.systemui.recents.RecentsConfiguration;
42import com.android.systemui.recents.model.Task;
Winson Chung303e1ff2014-03-07 15:06:19 -080043
Winson Chung303e1ff2014-03-07 15:06:19 -080044
Winson Chung37c8d8e2014-03-24 14:53:07 -070045/* A task view */
Winson Chung6cb485f2014-05-19 10:30:43 -070046public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
47 View.OnLongClickListener {
Winson Chung37c8d8e2014-03-24 14:53:07 -070048 /** The TaskView callbacks */
49 interface TaskViewCallbacks {
Winson Chung7aceb9a2014-07-03 13:38:01 -070050 public void onTaskViewAppIconClicked(TaskView tv);
51 public void onTaskViewAppInfoClicked(TaskView tv);
52 public void onTaskViewDismissed(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070053 }
54
Winson Chungd42a6cf2014-06-03 16:24:04 -070055 RecentsConfiguration mConfig;
56
Winson Chung14926462014-04-14 18:57:14 -070057 int mDim;
58 int mMaxDim;
59 TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
60
Winson Chung303e1ff2014-03-07 15:06:19 -080061 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070062 boolean mTaskDataLoaded;
Winson Chung1e8d71b2014-05-16 17:05:22 -070063 boolean mIsFocused;
Winson Chung5a9b0b02014-05-20 17:32:03 -070064 boolean mClipViewInStack;
Winson Chungd42a6cf2014-06-03 16:24:04 -070065 Rect mTmpRect = new Rect();
Winson Chungc9567c02014-06-16 20:25:51 -070066 Paint mLayerPaint = new Paint();
Winson Chung37c8d8e2014-03-24 14:53:07 -070067
68 TaskThumbnailView mThumbnailView;
69 TaskBarView mBarView;
70 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080071
Winson Chungd42a6cf2014-06-03 16:24:04 -070072 // Optimizations
73 ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
74 new ValueAnimator.AnimatorUpdateListener() {
75 @Override
76 public void onAnimationUpdate(ValueAnimator animation) {
77 updateDimOverlayFromScale();
78 }
79 };
Winson Chung743d5c92014-06-13 10:14:53 -070080 Runnable mEnableThumbnailClip = new Runnable() {
81 @Override
82 public void run() {
83 mThumbnailView.updateTaskBarClip(mBarView);
84 }
85 };
86 Runnable mDisableThumbnailClip = new Runnable() {
87 @Override
88 public void run() {
89 mThumbnailView.disableClipTaskBarView();
90 }
91 };
Winson Chungd42a6cf2014-06-03 16:24:04 -070092
Winson Chung37c8d8e2014-03-24 14:53:07 -070093
94 public TaskView(Context context) {
95 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -080096 }
97
Winson Chung37c8d8e2014-03-24 14:53:07 -070098 public TaskView(Context context, AttributeSet attrs) {
99 this(context, attrs, 0);
100 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800101
Winson Chung37c8d8e2014-03-24 14:53:07 -0700102 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
103 this(context, attrs, defStyleAttr, 0);
104 }
105
106 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
107 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700108 mConfig = RecentsConfiguration.getInstance();
Winson Chung14926462014-04-14 18:57:14 -0700109 setWillNotDraw(false);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700110 setClipToOutline(true);
Winson Chunga26fb782014-06-12 17:52:39 -0700111 setDim(getDim());
Winson Chung37c8d8e2014-03-24 14:53:07 -0700112 }
113
114 @Override
115 protected void onFinishInflate() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700116 mMaxDim = mConfig.taskStackMaxDim;
Winson Chung14926462014-04-14 18:57:14 -0700117
Winson Chung5a9b0b02014-05-20 17:32:03 -0700118 // By default, all views are clipped to other views in their stack
119 mClipViewInStack = true;
120
Winson Chung37c8d8e2014-03-24 14:53:07 -0700121 // Bind the views
Winson Chung37c8d8e2014-03-24 14:53:07 -0700122 mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
Winson Chung743d5c92014-06-13 10:14:53 -0700123 mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
Winson Chung9f9679d2014-04-11 16:49:09 -0700124
Winson Chung37c8d8e2014-03-24 14:53:07 -0700125 if (mTaskDataLoaded) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700126 onTaskDataLoaded();
Winson Chung303e1ff2014-03-07 15:06:19 -0800127 }
128 }
129
Winson Chung9f9679d2014-04-11 16:49:09 -0700130 @Override
Winson Chung14926462014-04-14 18:57:14 -0700131 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
132 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
133
Winson Chung96e3bc12014-05-06 16:44:12 -0700134 // Update the outline
135 Outline o = new Outline();
Winson Chung602de032014-05-27 12:19:28 -0700136 o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
Winson Chung7aceb9a2014-07-03 13:38:01 -0700137 mConfig.taskViewShadowOutlineBottomInsetPx, mConfig.taskViewRoundedCornerRadiusPx);
Winson Chung96e3bc12014-05-06 16:44:12 -0700138 setOutline(o);
Winson Chung14926462014-04-14 18:57:14 -0700139 }
140
Winson Chung04dfe0d2014-03-14 14:06:29 -0700141 /** Set callback */
142 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800143 mCb = cb;
144 }
145
Winson Chung303e1ff2014-03-07 15:06:19 -0800146 /** Gets the task */
147 Task getTask() {
148 return mTask;
149 }
150
151 /** Synchronizes this view's properties with the task's transform */
Winson Chungb5ddfc32014-06-13 17:23:12 -0700152 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700153 if (Console.Enabled) {
154 Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
155 "duration: " + duration, Console.AnsiPurple);
156 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700157
Winson Chung54e297a2014-05-09 17:15:32 -0700158 // Update the bar view
Winson Chungb5ddfc32014-06-13 17:23:12 -0700159 mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
Winson Chung54e297a2014-05-09 17:15:32 -0700160
Winson Chungb5ddfc32014-06-13 17:23:12 -0700161 // Check to see if any properties have changed, and update the task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800162 if (duration > 0) {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700163 ViewPropertyAnimator anim = animate();
164 boolean useLayers = false;
165
166 // Animate to the final state
167 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
168 anim.translationY(toTransform.translationY);
Winson Chung303e1ff2014-03-07 15:06:19 -0800169 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700170 if (Constants.DebugFlags.App.EnableShadows &&
171 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
172 anim.translationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700173 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700174 if (toTransform.hasScaleChangedFrom(getScaleX())) {
175 anim.scaleX(toTransform.scale)
Winson Chungc6a16232014-04-01 14:04:48 -0700176 .scaleY(toTransform.scale)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700177 .setUpdateListener(mUpdateDimListener);
178 useLayers = true;
179 }
180 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
181 // Use layers if we animate alpha
182 anim.alpha(toTransform.alpha);
183 useLayers = true;
184 }
185 if (useLayers) {
186 anim.withLayer();
187 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700188 anim.setStartDelay(toTransform.startDelay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700189 .setDuration(duration)
190 .setInterpolator(mConfig.fastOutSlowInInterpolator)
191 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800192 } else {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700193 // Set the changed properties
194 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
195 setTranslationY(toTransform.translationY);
196 }
197 if (Constants.DebugFlags.App.EnableShadows &&
198 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
Winson Chungb13c46e2014-06-09 18:33:08 -0700199 setTranslationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700200 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700201 if (toTransform.hasScaleChangedFrom(getScaleX())) {
202 setScaleX(toTransform.scale);
203 setScaleY(toTransform.scale);
204 updateDimOverlayFromScale();
205 }
206 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
207 setAlpha(toTransform.alpha);
208 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800209 }
210 }
211
212 /** Resets this view's properties */
213 void resetViewProperties() {
214 setTranslationX(0f);
215 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700216 if (Constants.DebugFlags.App.EnableShadows) {
217 setTranslationZ(0f);
218 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800219 setScaleX(1f);
220 setScaleY(1f);
221 setAlpha(1f);
Winson Chunga26fb782014-06-12 17:52:39 -0700222 setDim(0);
Winson Chung14926462014-04-14 18:57:14 -0700223 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800224 }
225
Winson Chung11ca76a52014-04-10 17:29:13 -0700226 /**
227 * When we are un/filtering, this method will set up the transform that we are animating to,
228 * in order to hide the task.
229 */
Winson Chungc6a16232014-04-01 14:04:48 -0700230 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
231 // Fade the view out and slide it away
232 toTransform.alpha = 0f;
233 toTransform.translationY += 200;
Winson Chung7ab650c2014-06-18 14:25:34 -0700234 toTransform.translationZ = 0;
Winson Chungc6a16232014-04-01 14:04:48 -0700235 }
236
Winson Chung11ca76a52014-04-10 17:29:13 -0700237 /**
238 * When we are un/filtering, this method will setup the transform that we are animating from,
239 * in order to show the task.
240 */
Winson Chungc6a16232014-04-01 14:04:48 -0700241 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
242 // Fade the view in
243 fromTransform.alpha = 0f;
244 }
245
Winson Chung24cf1522014-05-29 12:03:33 -0700246 /** Prepares this task view for the enter-recents animations. This is called earlier in the
247 * first layout because the actual animation into recents may take a long time. */
Winson Chung969f5862014-06-16 17:08:24 -0700248 public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
249 Rect taskRect) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700250 if (mConfig.launchedFromAppWithScreenshot) {
251 if (isTaskViewFrontMost) {
252 // Hide the task view as we are going to animate the full screenshot into view
253 // and then replace it with this view once we are done
254 setVisibility(View.INVISIBLE);
255 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700256 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700257 } else {
258 // Top align the task views
259 setTranslationY(offsetY);
260 setScaleX(1f);
261 setScaleY(1f);
262 }
263
264 } else if (mConfig.launchedFromAppWithThumbnail) {
265 if (isTaskViewFrontMost) {
266 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700267 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700268 // Set the dim to 0 so we can animate it in
269 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700270 }
271
272 } else if (mConfig.launchedFromHome) {
273 // Move the task view off screen (below) so we can animate it in
274 setTranslationY(offscreenY);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700275 if (Constants.DebugFlags.App.EnableShadows) {
276 setTranslationZ(0);
277 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700278 setScaleX(1f);
279 setScaleY(1f);
280 }
Winson Chung24cf1522014-05-29 12:03:33 -0700281 }
282
Winson Chung303e1ff2014-03-07 15:06:19 -0800283 /** Animates this task view as it enters recents */
Winson Chungc6011de2014-06-30 18:04:55 -0700284 public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700285 TaskViewTransform transform = ctx.transform;
286
287 if (mConfig.launchedFromAppWithScreenshot) {
288 if (ctx.isFrontMost) {
289 // Animate the full screenshot down first, before swapping with this task view
290 ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
291 @Override
292 public void run() {
293 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700294 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700295 setVisibility(View.VISIBLE);
Winson Chungc6011de2014-06-30 18:04:55 -0700296 // Decrement the post animation trigger
297 ctx.postAnimationTrigger.decrement();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700298 }
299 });
300 } else {
301 // Animate the tasks down behind the full screenshot
302 animate()
303 .scaleX(transform.scale)
304 .scaleY(transform.scale)
305 .translationY(transform.translationY)
306 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700307 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700308 .setInterpolator(mConfig.linearOutSlowInInterpolator)
309 .setDuration(475)
310 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700311 .withEndAction(new Runnable() {
312 @Override
313 public void run() {
314 mEnableThumbnailClip.run();
315 // Decrement the post animation trigger
316 ctx.postAnimationTrigger.decrement();
317 }
318 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700319 .start();
320 }
Winson Chungc6011de2014-06-30 18:04:55 -0700321 ctx.postAnimationTrigger.increment();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700322
323 } else if (mConfig.launchedFromAppWithThumbnail) {
324 if (ctx.isFrontMost) {
325 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700326 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700327
Winson Chunga26fb782014-06-12 17:52:39 -0700328 // Animate the dim into view as well
329 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
330 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
331 anim.setDuration(mConfig.taskBarEnterAnimDuration);
332 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
Winson Chungc6011de2014-06-30 18:04:55 -0700333 anim.addListener(new AnimatorListenerAdapter() {
334 @Override
335 public void onAnimationEnd(Animator animation) {
336 // Decrement the post animation trigger
337 ctx.postAnimationTrigger.decrement();
338 }
339 });
Winson Chunga26fb782014-06-12 17:52:39 -0700340 anim.start();
Winson Chungc6011de2014-06-30 18:04:55 -0700341 ctx.postAnimationTrigger.increment();
Winson Chung743d5c92014-06-13 10:14:53 -0700342 } else {
343 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700344 }
345
346 } else if (mConfig.launchedFromHome) {
347 // Animate the tasks up
348 int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
349 int delay = mConfig.taskBarEnterAnimDelay +
350 frontIndex * mConfig.taskViewEnterFromHomeDelay;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700351 if (Constants.DebugFlags.App.EnableShadows) {
352 animate().translationZ(transform.translationZ);
353 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700354 animate()
355 .scaleX(transform.scale)
356 .scaleY(transform.scale)
357 .translationY(transform.translationY)
358 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700359 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700360 .setInterpolator(mConfig.quintOutInterpolator)
361 .setDuration(mConfig.taskViewEnterFromHomeDuration)
362 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700363 .withEndAction(new Runnable() {
364 @Override
365 public void run() {
366 mEnableThumbnailClip.run();
367 // Decrement the post animation trigger
368 ctx.postAnimationTrigger.decrement();
369 }
370 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700371 .start();
Winson Chungc6011de2014-06-30 18:04:55 -0700372 ctx.postAnimationTrigger.increment();
Winson Chung7aceb9a2014-07-03 13:38:01 -0700373 } else {
374 // Otherwise, just enable the thumbnail clip
375 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700376 }
377 }
378
Winson Chung969f5862014-06-16 17:08:24 -0700379 /** Animates this task view as it leaves recents by pressing home. */
380 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700381 animate()
382 .translationY(ctx.offscreenTranslationY)
383 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700384 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700385 .setInterpolator(mConfig.fastOutLinearInInterpolator)
386 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700387 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700388 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700389 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700390 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800391 }
392
393 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700394 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
395 if (isLaunchingTask) {
396 // Disable the thumbnail clip and animate the bar out
397 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700398
Winson Chung969f5862014-06-16 17:08:24 -0700399 // Animate the dim
400 if (mDim > 0) {
401 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
402 anim.setDuration(mConfig.taskBarExitAnimDuration);
403 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
404 anim.start();
405 }
406 } else {
407 // Hide the dismiss button
408 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700409 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800410 }
411
Winson Chung9f49df92014-05-07 18:08:34 -0700412 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700413 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700414 // Disabling clipping with the stack while the view is animating away
415 setClipViewInStack(false);
416
Winson Chungd42a6cf2014-06-03 16:24:04 -0700417 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700418 .alpha(0f)
419 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700420 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700421 .setInterpolator(mConfig.fastOutSlowInInterpolator)
422 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700423 .withLayer()
424 .withEndAction(new Runnable() {
425 @Override
426 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700427 // We just throw this into a runnable because starting a view property
428 // animation using layers can cause inconsisten results if we try and
429 // update the layers while the animation is running. In some cases,
430 // the runnabled passed in may start an animation which also uses layers
431 // so we defer all this by posting this.
432 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700433
434 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700435 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700436 }
437 })
438 .start();
439 }
440
Winson Chung969f5862014-06-16 17:08:24 -0700441 /** Animates this task view if the user does not interact with the stack after a certain time. */
442 public void startNoUserInteractionAnimation() {
443 mBarView.startNoUserInteractionAnimation();
444 }
445
446 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
447 public void setNoUserInteractionState() {
448 mBarView.setNoUserInteractionState();
449 }
450
Winson Chung303e1ff2014-03-07 15:06:19 -0800451 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700452 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800453 getHitRect(outRect);
454 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
455 outRect.right = outRect.left + mThumbnailView.getRight();
456 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800457 return outRect;
458 }
459
460 /** Enable the hw layers on this task view */
461 void enableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700462 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700463 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800464 }
465
466 /** Disable the hw layers on this task view */
467 void disableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700468 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700469 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800470 }
471
Winson Chung5a9b0b02014-05-20 17:32:03 -0700472 /**
473 * Returns whether this view should be clipped, or any views below should clip against this
474 * view.
475 */
476 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700477 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700478 }
479
480 /** Sets whether this view should be clipped, or clipped against. */
481 void setClipViewInStack(boolean clip) {
482 if (clip != mClipViewInStack) {
483 mClipViewInStack = clip;
484 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700485 getHitRect(mTmpRect);
486 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700487 }
488 }
489 }
490
Winson Chunga26fb782014-06-12 17:52:39 -0700491 /** Returns the current dim. */
492 public void setDim(int dim) {
493 mDim = dim;
494 postInvalidateOnAnimation();
495 }
496
497 /** Returns the current dim. */
498 public int getDim() {
499 return mDim;
500 }
501
502 /** Compute the dim as a function of the scale of this view. */
503 int getDimOverlayFromScale() {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700504 float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
Winson Chung14926462014-04-14 18:57:14 -0700505 float scaleRange = 1f - minScale;
506 float dim = (1f - getScaleX()) / scaleRange;
507 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700508 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
509 }
510
511 /** Update the dim as a function of the scale of this view. */
512 void updateDimOverlayFromScale() {
513 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700514 }
515
516 @Override
517 public void draw(Canvas canvas) {
Winson Chung14926462014-04-14 18:57:14 -0700518 super.draw(canvas);
519
520 // Apply the dim if necessary
521 if (mDim > 0) {
522 canvas.drawColor(mDim << 24);
523 }
524 }
525
Winson Chung1e8d71b2014-05-16 17:05:22 -0700526 /**
527 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
528 * if the view is not currently visible, or we are in touch state (where we still want to keep
529 * track of focus).
530 */
531 public void setFocusedTask() {
532 mIsFocused = true;
Winson Chungc6011de2014-06-30 18:04:55 -0700533 // Workaround, we don't always want it focusable in touch mode, but we want the first task
534 // to be focused after the enter-recents animation, which can be triggered from either touch
535 // or keyboard
536 setFocusableInTouchMode(true);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700537 requestFocus();
Winson Chungc6011de2014-06-30 18:04:55 -0700538 setFocusableInTouchMode(false);
Winson Chung47a3e652014-05-21 16:03:42 -0700539 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700540 }
541
542 /**
543 * Updates the explicitly focused state when the view focus changes.
544 */
545 @Override
546 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
547 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
548 if (!gainFocus) {
549 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700550 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700551 }
552 }
553
554 /**
555 * Returns whether we have explicitly been focused.
556 */
557 public boolean isFocusedTask() {
558 return mIsFocused || isFocused();
559 }
560
Winson Chung4d7b0922014-03-13 17:14:17 -0700561 /**** TaskCallbacks Implementation ****/
562
Winson Chung04dfe0d2014-03-14 14:06:29 -0700563 /** Binds this task view to the task */
564 public void onTaskBound(Task t) {
565 mTask = t;
566 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800567 }
568
569 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700570 public void onTaskDataLoaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700571 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700572 // Bind each of the views to the new task data
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700573 mThumbnailView.rebindToTask(mTask);
574 mBarView.rebindToTask(mTask);
Winson Chung9f9679d2014-04-11 16:49:09 -0700575 // Rebind any listeners
576 mBarView.mApplicationIcon.setOnClickListener(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700577 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700578 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700579 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700580 mBarView.mApplicationIcon.setOnLongClickListener(this);
581 }
582 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700583 }
584 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700585 }
586
587 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700588 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700589 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700590 // Unbind each of the views from the task data and remove the task callback
591 mTask.setCallbacks(null);
592 mThumbnailView.unbindFromTask();
593 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700594 // Unbind any listeners
595 mBarView.mApplicationIcon.setOnClickListener(null);
Winson Chung6cb485f2014-05-19 10:30:43 -0700596 mBarView.mDismissButton.setOnClickListener(null);
597 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
598 mBarView.mApplicationIcon.setOnLongClickListener(null);
599 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700600 }
601 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700602 }
603
604 @Override
Winson Chung7ab650c2014-06-18 14:25:34 -0700605 public void onClick(final View v) {
606 // We purposely post the handler delayed to allow for the touch feedback to draw
607 final TaskView tv = this;
608 postDelayed(new Runnable() {
609 @Override
610 public void run() {
611 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700612 mCb.onTaskViewAppIconClicked(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700613 } else if (v == mBarView.mDismissButton) {
614 // Animate out the view and call the callback
615 startDeleteTaskAnimation(new Runnable() {
616 @Override
617 public void run() {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700618 mCb.onTaskViewDismissed(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700619 }
620 });
Winson Chung54e297a2014-05-09 17:15:32 -0700621 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700622 }
623 }, 125);
Winson Chung303e1ff2014-03-07 15:06:19 -0800624 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700625
626 @Override
627 public boolean onLongClick(View v) {
628 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700629 mCb.onTaskViewAppInfoClicked(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700630 return true;
631 }
632 return false;
633 }
Winson Chung7bb18852014-05-20 23:25:41 +0000634}