blob: b1fc7003c4b6e36da09de7d27e4005038f1e506f [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 Chunga26fb782014-06-12 17:52:39 -070019import android.animation.ObjectAnimator;
Winson Chung14926462014-04-14 18:57:14 -070020import android.animation.TimeInterpolator;
21import android.animation.ValueAnimator;
Winson Chung303e1ff2014-03-07 15:06:19 -080022import android.content.Context;
Winson Chung14926462014-04-14 18:57:14 -070023import android.graphics.Canvas;
Winson Chung96e3bc12014-05-06 16:44:12 -070024import android.graphics.Outline;
Winson Chungc9567c02014-06-16 20:25:51 -070025import android.graphics.Paint;
Winson Chung14926462014-04-14 18:57:14 -070026import android.graphics.Path;
Winson Chung9f9679d2014-04-11 16:49:09 -070027import android.graphics.Point;
Winson Chung303e1ff2014-03-07 15:06:19 -080028import android.graphics.Rect;
Winson Chung14926462014-04-14 18:57:14 -070029import android.graphics.RectF;
Winson Chung37c8d8e2014-03-24 14:53:07 -070030import android.util.AttributeSet;
Winson Chung9f9679d2014-04-11 16:49:09 -070031import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080032import android.view.View;
Winson Chungb5ddfc32014-06-13 17:23:12 -070033import android.view.ViewPropertyAnimator;
Winson Chung14926462014-04-14 18:57:14 -070034import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080035import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070036import com.android.systemui.R;
Winson Chungd42a6cf2014-06-03 16:24:04 -070037import com.android.systemui.recents.Console;
Winson Chung14926462014-04-14 18:57:14 -070038import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080039import com.android.systemui.recents.RecentsConfiguration;
40import com.android.systemui.recents.model.Task;
Winson Chung303e1ff2014-03-07 15:06:19 -080041
Winson Chung303e1ff2014-03-07 15:06:19 -080042
Winson Chung37c8d8e2014-03-24 14:53:07 -070043/* A task view */
Winson Chung6cb485f2014-05-19 10:30:43 -070044public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
45 View.OnLongClickListener {
Winson Chung37c8d8e2014-03-24 14:53:07 -070046 /** The TaskView callbacks */
47 interface TaskViewCallbacks {
48 public void onTaskIconClicked(TaskView tv);
Winson Chung9f9679d2014-04-11 16:49:09 -070049 public void onTaskAppInfoClicked(TaskView tv);
Winson Chung47a3e652014-05-21 16:03:42 -070050 public void onTaskFocused(TaskView tv);
Winson Chung54e297a2014-05-09 17:15:32 -070051 public void onTaskDismissed(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070052 }
53
Winson Chungd42a6cf2014-06-03 16:24:04 -070054 RecentsConfiguration mConfig;
55
Winson Chung14926462014-04-14 18:57:14 -070056 int mDim;
57 int mMaxDim;
58 TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
59
Winson Chung303e1ff2014-03-07 15:06:19 -080060 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070061 boolean mTaskDataLoaded;
Winson Chung1e8d71b2014-05-16 17:05:22 -070062 boolean mIsFocused;
Winson Chung5a9b0b02014-05-20 17:32:03 -070063 boolean mClipViewInStack;
Winson Chung9f9679d2014-04-11 16:49:09 -070064 Point mLastTouchDown = new Point();
Winson Chung14926462014-04-14 18:57:14 -070065 Path mRoundedRectClipPath = new Path();
Winson Chungd42a6cf2014-06-03 16:24:04 -070066 Rect mTmpRect = new Rect();
Winson Chungc9567c02014-06-16 20:25:51 -070067 Paint mLayerPaint = new Paint();
Winson Chung37c8d8e2014-03-24 14:53:07 -070068
69 TaskThumbnailView mThumbnailView;
70 TaskBarView mBarView;
71 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080072
Winson Chungd42a6cf2014-06-03 16:24:04 -070073 // Optimizations
74 ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
75 new ValueAnimator.AnimatorUpdateListener() {
76 @Override
77 public void onAnimationUpdate(ValueAnimator animation) {
78 updateDimOverlayFromScale();
79 }
80 };
Winson Chung743d5c92014-06-13 10:14:53 -070081 Runnable mEnableThumbnailClip = new Runnable() {
82 @Override
83 public void run() {
84 mThumbnailView.updateTaskBarClip(mBarView);
85 }
86 };
87 Runnable mDisableThumbnailClip = new Runnable() {
88 @Override
89 public void run() {
90 mThumbnailView.disableClipTaskBarView();
91 }
92 };
Winson Chungd42a6cf2014-06-03 16:24:04 -070093
Winson Chung37c8d8e2014-03-24 14:53:07 -070094
95 public TaskView(Context context) {
96 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -080097 }
98
Winson Chung37c8d8e2014-03-24 14:53:07 -070099 public TaskView(Context context, AttributeSet attrs) {
100 this(context, attrs, 0);
101 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800102
Winson Chung37c8d8e2014-03-24 14:53:07 -0700103 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
104 this(context, attrs, defStyleAttr, 0);
105 }
106
107 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
108 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700109 mConfig = RecentsConfiguration.getInstance();
Winson Chung14926462014-04-14 18:57:14 -0700110 setWillNotDraw(false);
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
134 // Update the rounded rect clip path
Winson Chungd42a6cf2014-06-03 16:24:04 -0700135 float radius = mConfig.taskViewRoundedCornerRadiusPx;
Winson Chung14926462014-04-14 18:57:14 -0700136 mRoundedRectClipPath.reset();
137 mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
138 radius, radius, Path.Direction.CW);
Winson Chung96e3bc12014-05-06 16:44:12 -0700139
140 // Update the outline
141 Outline o = new Outline();
Winson Chung602de032014-05-27 12:19:28 -0700142 o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
Winson Chungd42a6cf2014-06-03 16:24:04 -0700143 mConfig.taskViewShadowOutlineBottomInsetPx, radius);
Winson Chung96e3bc12014-05-06 16:44:12 -0700144 setOutline(o);
Winson Chung14926462014-04-14 18:57:14 -0700145 }
146
147 @Override
Winson Chung9f9679d2014-04-11 16:49:09 -0700148 public boolean onInterceptTouchEvent(MotionEvent ev) {
149 switch (ev.getAction()) {
150 case MotionEvent.ACTION_DOWN:
151 case MotionEvent.ACTION_MOVE:
152 mLastTouchDown.set((int) ev.getX(), (int) ev.getY());
153 break;
154 }
155 return super.onInterceptTouchEvent(ev);
156 }
157
Winson Chung04dfe0d2014-03-14 14:06:29 -0700158 /** Set callback */
159 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800160 mCb = cb;
161 }
162
Winson Chung303e1ff2014-03-07 15:06:19 -0800163 /** Gets the task */
164 Task getTask() {
165 return mTask;
166 }
167
168 /** Synchronizes this view's properties with the task's transform */
Winson Chungb5ddfc32014-06-13 17:23:12 -0700169 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700170 if (Console.Enabled) {
171 Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
172 "duration: " + duration, Console.AnsiPurple);
173 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700174
Winson Chung54e297a2014-05-09 17:15:32 -0700175 // Update the bar view
Winson Chungb5ddfc32014-06-13 17:23:12 -0700176 mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
Winson Chung54e297a2014-05-09 17:15:32 -0700177
Winson Chungb5ddfc32014-06-13 17:23:12 -0700178 // Check to see if any properties have changed, and update the task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800179 if (duration > 0) {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700180 ViewPropertyAnimator anim = animate();
181 boolean useLayers = false;
182
183 // Animate to the final state
184 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
185 anim.translationY(toTransform.translationY);
Winson Chung303e1ff2014-03-07 15:06:19 -0800186 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700187 if (Constants.DebugFlags.App.EnableShadows &&
188 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
189 anim.translationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700190 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700191 if (toTransform.hasScaleChangedFrom(getScaleX())) {
192 anim.scaleX(toTransform.scale)
Winson Chungc6a16232014-04-01 14:04:48 -0700193 .scaleY(toTransform.scale)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700194 .setUpdateListener(mUpdateDimListener);
195 useLayers = true;
196 }
197 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
198 // Use layers if we animate alpha
199 anim.alpha(toTransform.alpha);
200 useLayers = true;
201 }
202 if (useLayers) {
203 anim.withLayer();
204 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700205 anim.setStartDelay(toTransform.startDelay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700206 .setDuration(duration)
207 .setInterpolator(mConfig.fastOutSlowInInterpolator)
208 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800209 } else {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700210 // Set the changed properties
211 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
212 setTranslationY(toTransform.translationY);
213 }
214 if (Constants.DebugFlags.App.EnableShadows &&
215 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
Winson Chungb13c46e2014-06-09 18:33:08 -0700216 setTranslationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700217 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700218 if (toTransform.hasScaleChangedFrom(getScaleX())) {
219 setScaleX(toTransform.scale);
220 setScaleY(toTransform.scale);
221 updateDimOverlayFromScale();
222 }
223 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
224 setAlpha(toTransform.alpha);
225 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800226 }
227 }
228
229 /** Resets this view's properties */
230 void resetViewProperties() {
231 setTranslationX(0f);
232 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700233 if (Constants.DebugFlags.App.EnableShadows) {
234 setTranslationZ(0f);
235 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800236 setScaleX(1f);
237 setScaleY(1f);
238 setAlpha(1f);
Winson Chunga26fb782014-06-12 17:52:39 -0700239 setDim(0);
Winson Chung14926462014-04-14 18:57:14 -0700240 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800241 }
242
Winson Chung11ca76a52014-04-10 17:29:13 -0700243 /**
244 * When we are un/filtering, this method will set up the transform that we are animating to,
245 * in order to hide the task.
246 */
Winson Chungc6a16232014-04-01 14:04:48 -0700247 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
248 // Fade the view out and slide it away
249 toTransform.alpha = 0f;
250 toTransform.translationY += 200;
Winson Chung7ab650c2014-06-18 14:25:34 -0700251 toTransform.translationZ = 0;
Winson Chungc6a16232014-04-01 14:04:48 -0700252 }
253
Winson Chung11ca76a52014-04-10 17:29:13 -0700254 /**
255 * When we are un/filtering, this method will setup the transform that we are animating from,
256 * in order to show the task.
257 */
Winson Chungc6a16232014-04-01 14:04:48 -0700258 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
259 // Fade the view in
260 fromTransform.alpha = 0f;
261 }
262
Winson Chung24cf1522014-05-29 12:03:33 -0700263 /** Prepares this task view for the enter-recents animations. This is called earlier in the
264 * first layout because the actual animation into recents may take a long time. */
Winson Chung969f5862014-06-16 17:08:24 -0700265 public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
266 Rect taskRect) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700267 if (mConfig.launchedFromAppWithScreenshot) {
268 if (isTaskViewFrontMost) {
269 // Hide the task view as we are going to animate the full screenshot into view
270 // and then replace it with this view once we are done
271 setVisibility(View.INVISIBLE);
272 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700273 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700274 } else {
275 // Top align the task views
276 setTranslationY(offsetY);
277 setScaleX(1f);
278 setScaleY(1f);
279 }
280
281 } else if (mConfig.launchedFromAppWithThumbnail) {
282 if (isTaskViewFrontMost) {
283 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700284 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700285 // Set the dim to 0 so we can animate it in
286 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700287 }
288
289 } else if (mConfig.launchedFromHome) {
290 // Move the task view off screen (below) so we can animate it in
291 setTranslationY(offscreenY);
Winson Chung969f5862014-06-16 17:08:24 -0700292 setTranslationZ(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700293 setScaleX(1f);
294 setScaleY(1f);
295 }
Winson Chung24cf1522014-05-29 12:03:33 -0700296 }
297
Winson Chung303e1ff2014-03-07 15:06:19 -0800298 /** Animates this task view as it enters recents */
Winson Chung969f5862014-06-16 17:08:24 -0700299 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700300 TaskViewTransform transform = ctx.transform;
301
302 if (mConfig.launchedFromAppWithScreenshot) {
303 if (ctx.isFrontMost) {
304 // Animate the full screenshot down first, before swapping with this task view
305 ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
306 @Override
307 public void run() {
308 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700309 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700310 setVisibility(View.VISIBLE);
311 }
312 });
313 } else {
314 // Animate the tasks down behind the full screenshot
315 animate()
316 .scaleX(transform.scale)
317 .scaleY(transform.scale)
318 .translationY(transform.translationY)
319 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700320 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700321 .setInterpolator(mConfig.linearOutSlowInInterpolator)
322 .setDuration(475)
323 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700324 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700325 .start();
326 }
327
328 } else if (mConfig.launchedFromAppWithThumbnail) {
329 if (ctx.isFrontMost) {
330 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700331 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700332
Winson Chunga26fb782014-06-12 17:52:39 -0700333 // Animate the dim into view as well
334 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
335 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
336 anim.setDuration(mConfig.taskBarEnterAnimDuration);
337 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
338 anim.start();
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
345 int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
346 int delay = mConfig.taskBarEnterAnimDelay +
347 frontIndex * mConfig.taskViewEnterFromHomeDelay;
348 animate()
349 .scaleX(transform.scale)
350 .scaleY(transform.scale)
351 .translationY(transform.translationY)
Winson Chung969f5862014-06-16 17:08:24 -0700352 .translationZ(transform.translationZ)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700353 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700354 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700355 .setInterpolator(mConfig.quintOutInterpolator)
356 .setDuration(mConfig.taskViewEnterFromHomeDuration)
357 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700358 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700359 .start();
360 }
361 }
362
Winson Chung969f5862014-06-16 17:08:24 -0700363 /** Animates this task view as it leaves recents by pressing home. */
364 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700365 animate()
366 .translationY(ctx.offscreenTranslationY)
367 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700368 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700369 .setInterpolator(mConfig.fastOutLinearInInterpolator)
370 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700371 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700372 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700373 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700374 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800375 }
376
377 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700378 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
379 if (isLaunchingTask) {
380 // Disable the thumbnail clip and animate the bar out
381 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700382
Winson Chung969f5862014-06-16 17:08:24 -0700383 // Animate the dim
384 if (mDim > 0) {
385 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
386 anim.setDuration(mConfig.taskBarExitAnimDuration);
387 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
388 anim.start();
389 }
390 } else {
391 // Hide the dismiss button
392 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700393 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800394 }
395
Winson Chung9f49df92014-05-07 18:08:34 -0700396 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700397 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700398 // Disabling clipping with the stack while the view is animating away
399 setClipViewInStack(false);
400
Winson Chungd42a6cf2014-06-03 16:24:04 -0700401 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700402 .alpha(0f)
403 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700404 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700405 .setInterpolator(mConfig.fastOutSlowInInterpolator)
406 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700407 .withLayer()
408 .withEndAction(new Runnable() {
409 @Override
410 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700411 // We just throw this into a runnable because starting a view property
412 // animation using layers can cause inconsisten results if we try and
413 // update the layers while the animation is running. In some cases,
414 // the runnabled passed in may start an animation which also uses layers
415 // so we defer all this by posting this.
416 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700417
418 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700419 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700420 }
421 })
422 .start();
423 }
424
Winson Chung969f5862014-06-16 17:08:24 -0700425 /** Animates this task view if the user does not interact with the stack after a certain time. */
426 public void startNoUserInteractionAnimation() {
427 mBarView.startNoUserInteractionAnimation();
428 }
429
430 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
431 public void setNoUserInteractionState() {
432 mBarView.setNoUserInteractionState();
433 }
434
Winson Chung303e1ff2014-03-07 15:06:19 -0800435 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700436 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800437 getHitRect(outRect);
438 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
439 outRect.right = outRect.left + mThumbnailView.getRight();
440 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800441 return outRect;
442 }
443
444 /** Enable the hw layers on this task view */
445 void enableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700446 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700447 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800448 }
449
450 /** Disable the hw layers on this task view */
451 void disableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700452 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700453 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800454 }
455
Winson Chung5a9b0b02014-05-20 17:32:03 -0700456 /**
457 * Returns whether this view should be clipped, or any views below should clip against this
458 * view.
459 */
460 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700461 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700462 }
463
464 /** Sets whether this view should be clipped, or clipped against. */
465 void setClipViewInStack(boolean clip) {
466 if (clip != mClipViewInStack) {
467 mClipViewInStack = clip;
468 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700469 getHitRect(mTmpRect);
470 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700471 }
472 }
473 }
474
Winson Chunga26fb782014-06-12 17:52:39 -0700475 /** Returns the current dim. */
476 public void setDim(int dim) {
477 mDim = dim;
478 postInvalidateOnAnimation();
479 }
480
481 /** Returns the current dim. */
482 public int getDim() {
483 return mDim;
484 }
485
486 /** Compute the dim as a function of the scale of this view. */
487 int getDimOverlayFromScale() {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700488 float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
Winson Chung14926462014-04-14 18:57:14 -0700489 float scaleRange = 1f - minScale;
490 float dim = (1f - getScaleX()) / scaleRange;
491 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700492 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
493 }
494
495 /** Update the dim as a function of the scale of this view. */
496 void updateDimOverlayFromScale() {
497 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700498 }
499
500 @Override
501 public void draw(Canvas canvas) {
Winson Chung969f5862014-06-16 17:08:24 -0700502 int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
Winson Chung14926462014-04-14 18:57:14 -0700503 // Apply the rounded rect clip path on the whole view
504 canvas.clipPath(mRoundedRectClipPath);
Winson Chung14926462014-04-14 18:57:14 -0700505 super.draw(canvas);
Winson Chung969f5862014-06-16 17:08:24 -0700506 canvas.restoreToCount(restoreCount);
Winson Chung14926462014-04-14 18:57:14 -0700507
508 // Apply the dim if necessary
509 if (mDim > 0) {
510 canvas.drawColor(mDim << 24);
511 }
512 }
513
Winson Chung1e8d71b2014-05-16 17:05:22 -0700514 /**
515 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
516 * if the view is not currently visible, or we are in touch state (where we still want to keep
517 * track of focus).
518 */
519 public void setFocusedTask() {
520 mIsFocused = true;
521 requestFocus();
Winson Chung47a3e652014-05-21 16:03:42 -0700522 invalidate();
523 mCb.onTaskFocused(this);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700524 }
525
526 /**
527 * Updates the explicitly focused state when the view focus changes.
528 */
529 @Override
530 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
531 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
532 if (!gainFocus) {
533 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700534 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700535 }
536 }
537
538 /**
539 * Returns whether we have explicitly been focused.
540 */
541 public boolean isFocusedTask() {
542 return mIsFocused || isFocused();
543 }
544
Winson Chung4d7b0922014-03-13 17:14:17 -0700545 /**** TaskCallbacks Implementation ****/
546
Winson Chung04dfe0d2014-03-14 14:06:29 -0700547 /** Binds this task view to the task */
548 public void onTaskBound(Task t) {
549 mTask = t;
550 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800551 }
552
553 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700554 public void onTaskDataLoaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700555 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700556 // Bind each of the views to the new task data
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700557 mThumbnailView.rebindToTask(mTask);
558 mBarView.rebindToTask(mTask);
Winson Chung9f9679d2014-04-11 16:49:09 -0700559 // Rebind any listeners
560 mBarView.mApplicationIcon.setOnClickListener(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700561 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700562 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700563 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700564 mBarView.mApplicationIcon.setOnLongClickListener(this);
565 }
566 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700567 }
568 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700569 }
570
571 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700572 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700573 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700574 // Unbind each of the views from the task data and remove the task callback
575 mTask.setCallbacks(null);
576 mThumbnailView.unbindFromTask();
577 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700578 // Unbind any listeners
579 mBarView.mApplicationIcon.setOnClickListener(null);
Winson Chung6cb485f2014-05-19 10:30:43 -0700580 mBarView.mDismissButton.setOnClickListener(null);
581 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
582 mBarView.mApplicationIcon.setOnLongClickListener(null);
583 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700584 }
585 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700586 }
587
588 @Override
Winson Chung7ab650c2014-06-18 14:25:34 -0700589 public void onClick(final View v) {
590 // We purposely post the handler delayed to allow for the touch feedback to draw
591 final TaskView tv = this;
592 postDelayed(new Runnable() {
593 @Override
594 public void run() {
595 if (v == mBarView.mApplicationIcon) {
596 mCb.onTaskIconClicked(tv);
597 } else if (v == mBarView.mDismissButton) {
598 // Animate out the view and call the callback
599 startDeleteTaskAnimation(new Runnable() {
600 @Override
601 public void run() {
602 mCb.onTaskDismissed(tv);
603 }
604 });
Winson Chung54e297a2014-05-09 17:15:32 -0700605 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700606 }
607 }, 125);
Winson Chung303e1ff2014-03-07 15:06:19 -0800608 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700609
610 @Override
611 public boolean onLongClick(View v) {
612 if (v == mBarView.mApplicationIcon) {
613 mCb.onTaskAppInfoClicked(this);
614 return true;
615 }
616 return false;
617 }
Winson Chung7bb18852014-05-20 23:25:41 +0000618}