blob: 8f4f235f28cf2c7a6a00b1645b941dd1f65e1e61 [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 Chung303e1ff2014-03-07 15:06:19 -080028import android.graphics.Rect;
Winson Chung37c8d8e2014-03-24 14:53:07 -070029import android.util.AttributeSet;
Winson Chung303e1ff2014-03-07 15:06:19 -080030import android.view.View;
Winson Chungb5ddfc32014-06-13 17:23:12 -070031import android.view.ViewPropertyAnimator;
Winson Chung14926462014-04-14 18:57:14 -070032import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080033import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070034import com.android.systemui.R;
Winson Chungffa2ec62014-07-03 15:54:42 -070035import com.android.systemui.recents.misc.Console;
Winson Chung14926462014-04-14 18:57:14 -070036import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080037import com.android.systemui.recents.RecentsConfiguration;
38import com.android.systemui.recents.model.Task;
Winson Chung303e1ff2014-03-07 15:06:19 -080039
Winson Chung303e1ff2014-03-07 15:06:19 -080040
Winson Chung37c8d8e2014-03-24 14:53:07 -070041/* A task view */
Winson Chung6cb485f2014-05-19 10:30:43 -070042public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
43 View.OnLongClickListener {
Winson Chung37c8d8e2014-03-24 14:53:07 -070044 /** The TaskView callbacks */
45 interface TaskViewCallbacks {
Winson Chung7aceb9a2014-07-03 13:38:01 -070046 public void onTaskViewAppIconClicked(TaskView tv);
47 public void onTaskViewAppInfoClicked(TaskView tv);
48 public void onTaskViewDismissed(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070049 }
50
Winson Chungd42a6cf2014-06-03 16:24:04 -070051 RecentsConfiguration mConfig;
52
Winson Chung14926462014-04-14 18:57:14 -070053 int mDim;
54 int mMaxDim;
Winson Chung6057c912014-07-08 14:39:08 -070055 AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
Winson Chung14926462014-04-14 18:57:14 -070056
Winson Chung303e1ff2014-03-07 15:06:19 -080057 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070058 boolean mTaskDataLoaded;
Winson Chung1e8d71b2014-05-16 17:05:22 -070059 boolean mIsFocused;
Winson Chungffa2ec62014-07-03 15:54:42 -070060 boolean mIsStub;
Winson Chung5a9b0b02014-05-20 17:32:03 -070061 boolean mClipViewInStack;
Winson Chungd42a6cf2014-06-03 16:24:04 -070062 Rect mTmpRect = new Rect();
Winson Chungc9567c02014-06-16 20:25:51 -070063 Paint mLayerPaint = new Paint();
Winson Chung37c8d8e2014-03-24 14:53:07 -070064
65 TaskThumbnailView mThumbnailView;
66 TaskBarView mBarView;
67 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080068
Winson Chungd42a6cf2014-06-03 16:24:04 -070069 // Optimizations
70 ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
71 new ValueAnimator.AnimatorUpdateListener() {
72 @Override
73 public void onAnimationUpdate(ValueAnimator animation) {
74 updateDimOverlayFromScale();
75 }
76 };
Winson Chung743d5c92014-06-13 10:14:53 -070077 Runnable mEnableThumbnailClip = new Runnable() {
78 @Override
79 public void run() {
80 mThumbnailView.updateTaskBarClip(mBarView);
81 }
82 };
83 Runnable mDisableThumbnailClip = new Runnable() {
84 @Override
85 public void run() {
86 mThumbnailView.disableClipTaskBarView();
87 }
88 };
Winson Chungd42a6cf2014-06-03 16:24:04 -070089
Winson Chung37c8d8e2014-03-24 14:53:07 -070090
91 public TaskView(Context context) {
92 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -080093 }
94
Winson Chung37c8d8e2014-03-24 14:53:07 -070095 public TaskView(Context context, AttributeSet attrs) {
96 this(context, attrs, 0);
97 }
Winson Chung303e1ff2014-03-07 15:06:19 -080098
Winson Chung37c8d8e2014-03-24 14:53:07 -070099 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
100 this(context, attrs, defStyleAttr, 0);
101 }
102
103 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
104 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700105 mConfig = RecentsConfiguration.getInstance();
Winson Chung14926462014-04-14 18:57:14 -0700106 setWillNotDraw(false);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700107 setClipToOutline(true);
Winson Chunga26fb782014-06-12 17:52:39 -0700108 setDim(getDim());
Winson Chung37c8d8e2014-03-24 14:53:07 -0700109 }
110
111 @Override
112 protected void onFinishInflate() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700113 mMaxDim = mConfig.taskStackMaxDim;
Winson Chung14926462014-04-14 18:57:14 -0700114
Winson Chung5a9b0b02014-05-20 17:32:03 -0700115 // By default, all views are clipped to other views in their stack
116 mClipViewInStack = true;
117
Winson Chung37c8d8e2014-03-24 14:53:07 -0700118 // Bind the views
Winson Chung37c8d8e2014-03-24 14:53:07 -0700119 mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
Winson Chung743d5c92014-06-13 10:14:53 -0700120 mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
Winson Chung9f9679d2014-04-11 16:49:09 -0700121
Winson Chung37c8d8e2014-03-24 14:53:07 -0700122 if (mTaskDataLoaded) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700123 onTaskDataLoaded();
Winson Chung303e1ff2014-03-07 15:06:19 -0800124 }
125 }
126
Winson Chung9f9679d2014-04-11 16:49:09 -0700127 @Override
Winson Chung14926462014-04-14 18:57:14 -0700128 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
129 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
130
Winson Chung96e3bc12014-05-06 16:44:12 -0700131 // Update the outline
132 Outline o = new Outline();
Winson Chungffa2ec62014-07-03 15:54:42 -0700133 o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
134 mConfig.taskViewRoundedCornerRadiusPx);
Winson Chung96e3bc12014-05-06 16:44:12 -0700135 setOutline(o);
Winson Chung14926462014-04-14 18:57:14 -0700136 }
137
Winson Chung04dfe0d2014-03-14 14:06:29 -0700138 /** Set callback */
139 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800140 mCb = cb;
141 }
142
Winson Chung303e1ff2014-03-07 15:06:19 -0800143 /** Gets the task */
144 Task getTask() {
145 return mTask;
146 }
147
148 /** Synchronizes this view's properties with the task's transform */
Winson Chungb5ddfc32014-06-13 17:23:12 -0700149 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700150 if (Console.Enabled) {
151 Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
152 "duration: " + duration, Console.AnsiPurple);
153 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700154
Winson Chung54e297a2014-05-09 17:15:32 -0700155 // Update the bar view
Winson Chungb5ddfc32014-06-13 17:23:12 -0700156 mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
Winson Chung54e297a2014-05-09 17:15:32 -0700157
Winson Chungb5ddfc32014-06-13 17:23:12 -0700158 // Check to see if any properties have changed, and update the task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800159 if (duration > 0) {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700160 ViewPropertyAnimator anim = animate();
161 boolean useLayers = false;
162
163 // Animate to the final state
164 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
165 anim.translationY(toTransform.translationY);
Winson Chung303e1ff2014-03-07 15:06:19 -0800166 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700167 if (Constants.DebugFlags.App.EnableShadows &&
168 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
169 anim.translationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700170 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700171 if (toTransform.hasScaleChangedFrom(getScaleX())) {
172 anim.scaleX(toTransform.scale)
Winson Chungc6a16232014-04-01 14:04:48 -0700173 .scaleY(toTransform.scale)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700174 .setUpdateListener(mUpdateDimListener);
175 useLayers = true;
176 }
177 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
178 // Use layers if we animate alpha
179 anim.alpha(toTransform.alpha);
180 useLayers = true;
181 }
182 if (useLayers) {
183 anim.withLayer();
184 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700185 anim.setStartDelay(toTransform.startDelay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700186 .setDuration(duration)
187 .setInterpolator(mConfig.fastOutSlowInInterpolator)
188 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800189 } else {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700190 // Set the changed properties
191 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
192 setTranslationY(toTransform.translationY);
193 }
194 if (Constants.DebugFlags.App.EnableShadows &&
195 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
Winson Chungb13c46e2014-06-09 18:33:08 -0700196 setTranslationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700197 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700198 if (toTransform.hasScaleChangedFrom(getScaleX())) {
199 setScaleX(toTransform.scale);
200 setScaleY(toTransform.scale);
201 updateDimOverlayFromScale();
202 }
203 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
204 setAlpha(toTransform.alpha);
205 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800206 }
207 }
208
209 /** Resets this view's properties */
210 void resetViewProperties() {
211 setTranslationX(0f);
212 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700213 if (Constants.DebugFlags.App.EnableShadows) {
214 setTranslationZ(0f);
215 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800216 setScaleX(1f);
217 setScaleY(1f);
218 setAlpha(1f);
Winson Chunga26fb782014-06-12 17:52:39 -0700219 setDim(0);
Winson Chung14926462014-04-14 18:57:14 -0700220 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800221 }
222
Winson Chung11ca76a52014-04-10 17:29:13 -0700223 /**
224 * When we are un/filtering, this method will set up the transform that we are animating to,
225 * in order to hide the task.
226 */
Winson Chungc6a16232014-04-01 14:04:48 -0700227 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
228 // Fade the view out and slide it away
229 toTransform.alpha = 0f;
230 toTransform.translationY += 200;
Winson Chung7ab650c2014-06-18 14:25:34 -0700231 toTransform.translationZ = 0;
Winson Chungc6a16232014-04-01 14:04:48 -0700232 }
233
Winson Chung11ca76a52014-04-10 17:29:13 -0700234 /**
235 * When we are un/filtering, this method will setup the transform that we are animating from,
236 * in order to show the task.
237 */
Winson Chungc6a16232014-04-01 14:04:48 -0700238 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
239 // Fade the view in
240 fromTransform.alpha = 0f;
241 }
242
Winson Chung24cf1522014-05-29 12:03:33 -0700243 /** Prepares this task view for the enter-recents animations. This is called earlier in the
244 * first layout because the actual animation into recents may take a long time. */
Winson Chung969f5862014-06-16 17:08:24 -0700245 public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
246 Rect taskRect) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700247 if (mConfig.launchedFromAppWithScreenshot) {
248 if (isTaskViewFrontMost) {
249 // Hide the task view as we are going to animate the full screenshot into view
250 // and then replace it with this view once we are done
251 setVisibility(View.INVISIBLE);
252 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700253 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700254 } else {
255 // Top align the task views
256 setTranslationY(offsetY);
257 setScaleX(1f);
258 setScaleY(1f);
259 }
260
261 } else if (mConfig.launchedFromAppWithThumbnail) {
262 if (isTaskViewFrontMost) {
263 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700264 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700265 // Set the dim to 0 so we can animate it in
266 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700267 }
268
269 } else if (mConfig.launchedFromHome) {
270 // Move the task view off screen (below) so we can animate it in
271 setTranslationY(offscreenY);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700272 if (Constants.DebugFlags.App.EnableShadows) {
273 setTranslationZ(0);
274 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700275 setScaleX(1f);
276 setScaleY(1f);
277 }
Winson Chung24cf1522014-05-29 12:03:33 -0700278 }
279
Winson Chung303e1ff2014-03-07 15:06:19 -0800280 /** Animates this task view as it enters recents */
Winson Chungc6011de2014-06-30 18:04:55 -0700281 public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung6057c912014-07-08 14:39:08 -0700282 TaskViewTransform transform = ctx.currentTaskTransform;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700283
284 if (mConfig.launchedFromAppWithScreenshot) {
Winson Chung6057c912014-07-08 14:39:08 -0700285 if (ctx.isCurrentTaskFrontMost) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700286 // Animate the full screenshot down first, before swapping with this task view
Winson Chung6057c912014-07-08 14:39:08 -0700287 ctx.fullScreenshotView.animateOnEnterRecents(ctx, new Runnable() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700288 @Override
289 public void run() {
290 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700291 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700292 setVisibility(View.VISIBLE);
Winson Chungc6011de2014-06-30 18:04:55 -0700293 // Decrement the post animation trigger
294 ctx.postAnimationTrigger.decrement();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700295 }
296 });
297 } else {
298 // Animate the tasks down behind the full screenshot
299 animate()
300 .scaleX(transform.scale)
301 .scaleY(transform.scale)
302 .translationY(transform.translationY)
303 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700304 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700305 .setInterpolator(mConfig.linearOutSlowInInterpolator)
306 .setDuration(475)
307 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700308 .withEndAction(new Runnable() {
309 @Override
310 public void run() {
311 mEnableThumbnailClip.run();
312 // Decrement the post animation trigger
313 ctx.postAnimationTrigger.decrement();
314 }
315 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700316 .start();
317 }
Winson Chungc6011de2014-06-30 18:04:55 -0700318 ctx.postAnimationTrigger.increment();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700319
320 } else if (mConfig.launchedFromAppWithThumbnail) {
Winson Chung6057c912014-07-08 14:39:08 -0700321 if (ctx.isCurrentTaskFrontMost) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700322 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700323 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700324
Winson Chunga26fb782014-06-12 17:52:39 -0700325 // Animate the dim into view as well
326 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
327 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
328 anim.setDuration(mConfig.taskBarEnterAnimDuration);
329 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
Winson Chungc6011de2014-06-30 18:04:55 -0700330 anim.addListener(new AnimatorListenerAdapter() {
331 @Override
332 public void onAnimationEnd(Animator animation) {
333 // Decrement the post animation trigger
334 ctx.postAnimationTrigger.decrement();
335 }
336 });
Winson Chunga26fb782014-06-12 17:52:39 -0700337 anim.start();
Winson Chungc6011de2014-06-30 18:04:55 -0700338 ctx.postAnimationTrigger.increment();
Winson Chung743d5c92014-06-13 10:14:53 -0700339 } else {
340 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700341 }
342
343 } else if (mConfig.launchedFromHome) {
344 // Animate the tasks up
Winson Chung6057c912014-07-08 14:39:08 -0700345 int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700346 int delay = mConfig.taskBarEnterAnimDelay +
347 frontIndex * mConfig.taskViewEnterFromHomeDelay;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700348 if (Constants.DebugFlags.App.EnableShadows) {
349 animate().translationZ(transform.translationZ);
350 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700351 animate()
352 .scaleX(transform.scale)
353 .scaleY(transform.scale)
354 .translationY(transform.translationY)
355 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700356 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700357 .setInterpolator(mConfig.quintOutInterpolator)
358 .setDuration(mConfig.taskViewEnterFromHomeDuration)
359 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700360 .withEndAction(new Runnable() {
361 @Override
362 public void run() {
363 mEnableThumbnailClip.run();
364 // Decrement the post animation trigger
365 ctx.postAnimationTrigger.decrement();
366 }
367 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700368 .start();
Winson Chungc6011de2014-06-30 18:04:55 -0700369 ctx.postAnimationTrigger.increment();
Winson Chung7aceb9a2014-07-03 13:38:01 -0700370 } else {
371 // Otherwise, just enable the thumbnail clip
372 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700373 }
374 }
375
Winson Chung969f5862014-06-16 17:08:24 -0700376 /** Animates this task view as it leaves recents by pressing home. */
377 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700378 animate()
379 .translationY(ctx.offscreenTranslationY)
380 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700381 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700382 .setInterpolator(mConfig.fastOutLinearInInterpolator)
383 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700384 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700385 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700386 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700387 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800388 }
389
390 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700391 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
392 if (isLaunchingTask) {
393 // Disable the thumbnail clip and animate the bar out
394 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700395
Winson Chung969f5862014-06-16 17:08:24 -0700396 // Animate the dim
397 if (mDim > 0) {
398 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
399 anim.setDuration(mConfig.taskBarExitAnimDuration);
400 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
401 anim.start();
402 }
403 } else {
404 // Hide the dismiss button
405 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700406 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800407 }
408
Winson Chung9f49df92014-05-07 18:08:34 -0700409 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700410 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700411 // Disabling clipping with the stack while the view is animating away
412 setClipViewInStack(false);
413
Winson Chungd42a6cf2014-06-03 16:24:04 -0700414 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700415 .alpha(0f)
416 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700417 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700418 .setInterpolator(mConfig.fastOutSlowInInterpolator)
419 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700420 .withLayer()
421 .withEndAction(new Runnable() {
422 @Override
423 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700424 // We just throw this into a runnable because starting a view property
425 // animation using layers can cause inconsisten results if we try and
426 // update the layers while the animation is running. In some cases,
427 // the runnabled passed in may start an animation which also uses layers
428 // so we defer all this by posting this.
429 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700430
431 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700432 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700433 }
434 })
435 .start();
436 }
437
Winson Chung969f5862014-06-16 17:08:24 -0700438 /** Animates this task view if the user does not interact with the stack after a certain time. */
439 public void startNoUserInteractionAnimation() {
440 mBarView.startNoUserInteractionAnimation();
441 }
442
443 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
444 public void setNoUserInteractionState() {
445 mBarView.setNoUserInteractionState();
446 }
447
Winson Chung303e1ff2014-03-07 15:06:19 -0800448 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700449 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800450 getHitRect(outRect);
451 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
452 outRect.right = outRect.left + mThumbnailView.getRight();
453 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800454 return outRect;
455 }
456
457 /** Enable the hw layers on this task view */
458 void enableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700459 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700460 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800461 }
462
463 /** Disable the hw layers on this task view */
464 void disableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700465 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700466 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800467 }
468
Winson Chungffa2ec62014-07-03 15:54:42 -0700469 /** Sets the stubbed state of this task view. */
470 void setStubState(boolean isStub) {
471 if (!mIsStub && isStub) {
472 // This is now a stub task view, so clip to the bar height, hide the thumbnail
473 setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
474 mThumbnailView.setVisibility(View.INVISIBLE);
475 // Temporary
476 mBarView.mActivityDescription.setText("Stub");
477 } else if (mIsStub && !isStub) {
478 setClipBounds(null);
479 mThumbnailView.setVisibility(View.VISIBLE);
480 }
481 mIsStub = isStub;
482 }
483
Winson Chung5a9b0b02014-05-20 17:32:03 -0700484 /**
485 * Returns whether this view should be clipped, or any views below should clip against this
486 * view.
487 */
488 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700489 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700490 }
491
492 /** Sets whether this view should be clipped, or clipped against. */
493 void setClipViewInStack(boolean clip) {
494 if (clip != mClipViewInStack) {
495 mClipViewInStack = clip;
496 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700497 getHitRect(mTmpRect);
498 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700499 }
500 }
501 }
502
Winson Chunga26fb782014-06-12 17:52:39 -0700503 /** Returns the current dim. */
504 public void setDim(int dim) {
505 mDim = dim;
506 postInvalidateOnAnimation();
507 }
508
509 /** Returns the current dim. */
510 public int getDim() {
511 return mDim;
512 }
513
514 /** Compute the dim as a function of the scale of this view. */
515 int getDimOverlayFromScale() {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700516 float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
Winson Chung14926462014-04-14 18:57:14 -0700517 float scaleRange = 1f - minScale;
518 float dim = (1f - getScaleX()) / scaleRange;
519 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700520 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
521 }
522
523 /** Update the dim as a function of the scale of this view. */
524 void updateDimOverlayFromScale() {
525 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700526 }
527
528 @Override
529 public void draw(Canvas canvas) {
Winson Chung14926462014-04-14 18:57:14 -0700530 super.draw(canvas);
531
532 // Apply the dim if necessary
533 if (mDim > 0) {
534 canvas.drawColor(mDim << 24);
535 }
536 }
537
Winson Chung1e8d71b2014-05-16 17:05:22 -0700538 /**
539 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
540 * if the view is not currently visible, or we are in touch state (where we still want to keep
541 * track of focus).
542 */
543 public void setFocusedTask() {
544 mIsFocused = true;
Winson Chungc6011de2014-06-30 18:04:55 -0700545 // Workaround, we don't always want it focusable in touch mode, but we want the first task
546 // to be focused after the enter-recents animation, which can be triggered from either touch
547 // or keyboard
548 setFocusableInTouchMode(true);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700549 requestFocus();
Winson Chungc6011de2014-06-30 18:04:55 -0700550 setFocusableInTouchMode(false);
Winson Chung47a3e652014-05-21 16:03:42 -0700551 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700552 }
553
554 /**
555 * Updates the explicitly focused state when the view focus changes.
556 */
557 @Override
558 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
559 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
560 if (!gainFocus) {
561 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700562 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700563 }
564 }
565
566 /**
567 * Returns whether we have explicitly been focused.
568 */
569 public boolean isFocusedTask() {
570 return mIsFocused || isFocused();
571 }
572
Winson Chung4d7b0922014-03-13 17:14:17 -0700573 /**** TaskCallbacks Implementation ****/
574
Winson Chung04dfe0d2014-03-14 14:06:29 -0700575 /** Binds this task view to the task */
576 public void onTaskBound(Task t) {
577 mTask = t;
578 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800579 }
580
581 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700582 public void onTaskDataLoaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700583 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700584 // Bind each of the views to the new task data
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700585 mThumbnailView.rebindToTask(mTask);
586 mBarView.rebindToTask(mTask);
Winson Chung9f9679d2014-04-11 16:49:09 -0700587 // Rebind any listeners
Winson Chungffa2ec62014-07-03 15:54:42 -0700588 if (Constants.DebugFlags.App.EnableTaskFiltering) {
589 mBarView.mApplicationIcon.setOnClickListener(this);
590 }
Winson Chung54e297a2014-05-09 17:15:32 -0700591 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700592 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700593 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700594 mBarView.mApplicationIcon.setOnLongClickListener(this);
595 }
596 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700597 }
598 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700599 }
600
601 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700602 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700603 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700604 // Unbind each of the views from the task data and remove the task callback
605 mTask.setCallbacks(null);
606 mThumbnailView.unbindFromTask();
607 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700608 // Unbind any listeners
Winson Chungffa2ec62014-07-03 15:54:42 -0700609 if (Constants.DebugFlags.App.EnableTaskFiltering) {
610 mBarView.mApplicationIcon.setOnClickListener(null);
611 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700612 mBarView.mDismissButton.setOnClickListener(null);
613 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
614 mBarView.mApplicationIcon.setOnLongClickListener(null);
615 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700616 }
617 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700618 }
619
620 @Override
Winson Chung7ab650c2014-06-18 14:25:34 -0700621 public void onClick(final View v) {
622 // We purposely post the handler delayed to allow for the touch feedback to draw
623 final TaskView tv = this;
624 postDelayed(new Runnable() {
625 @Override
626 public void run() {
627 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700628 mCb.onTaskViewAppIconClicked(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700629 } else if (v == mBarView.mDismissButton) {
630 // Animate out the view and call the callback
631 startDeleteTaskAnimation(new Runnable() {
632 @Override
633 public void run() {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700634 mCb.onTaskViewDismissed(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700635 }
636 });
Winson Chung54e297a2014-05-09 17:15:32 -0700637 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700638 }
639 }, 125);
Winson Chung303e1ff2014-03-07 15:06:19 -0800640 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700641
642 @Override
643 public boolean onLongClick(View v) {
644 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700645 mCb.onTaskViewAppInfoClicked(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700646 return true;
647 }
648 return false;
649 }
Winson Chung7bb18852014-05-20 23:25:41 +0000650}