blob: 0b191622a716bfad7b0b82877674683ee3bf34c9 [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) {
126 onTaskDataLoaded(false);
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 }
205 anim.setStartDelay(0)
206 .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;
251 }
252
Winson Chung11ca76a52014-04-10 17:29:13 -0700253 /**
254 * When we are un/filtering, this method will setup the transform that we are animating from,
255 * in order to show the task.
256 */
Winson Chungc6a16232014-04-01 14:04:48 -0700257 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
258 // Fade the view in
259 fromTransform.alpha = 0f;
260 }
261
Winson Chung24cf1522014-05-29 12:03:33 -0700262 /** Prepares this task view for the enter-recents animations. This is called earlier in the
263 * first layout because the actual animation into recents may take a long time. */
Winson Chung969f5862014-06-16 17:08:24 -0700264 public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
265 Rect taskRect) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700266 if (mConfig.launchedFromAppWithScreenshot) {
267 if (isTaskViewFrontMost) {
268 // Hide the task view as we are going to animate the full screenshot into view
269 // and then replace it with this view once we are done
270 setVisibility(View.INVISIBLE);
271 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700272 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700273 } else {
274 // Top align the task views
275 setTranslationY(offsetY);
276 setScaleX(1f);
277 setScaleY(1f);
278 }
279
280 } else if (mConfig.launchedFromAppWithThumbnail) {
281 if (isTaskViewFrontMost) {
282 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700283 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700284 // Set the dim to 0 so we can animate it in
285 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700286 }
287
288 } else if (mConfig.launchedFromHome) {
289 // Move the task view off screen (below) so we can animate it in
290 setTranslationY(offscreenY);
Winson Chung969f5862014-06-16 17:08:24 -0700291 setTranslationZ(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700292 setScaleX(1f);
293 setScaleY(1f);
294 }
Winson Chung24cf1522014-05-29 12:03:33 -0700295 }
296
Winson Chung303e1ff2014-03-07 15:06:19 -0800297 /** Animates this task view as it enters recents */
Winson Chung969f5862014-06-16 17:08:24 -0700298 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700299 TaskViewTransform transform = ctx.transform;
300
301 if (mConfig.launchedFromAppWithScreenshot) {
302 if (ctx.isFrontMost) {
303 // Animate the full screenshot down first, before swapping with this task view
304 ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
305 @Override
306 public void run() {
307 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700308 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700309 setVisibility(View.VISIBLE);
310 }
311 });
312 } else {
313 // Animate the tasks down behind the full screenshot
314 animate()
315 .scaleX(transform.scale)
316 .scaleY(transform.scale)
317 .translationY(transform.translationY)
318 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700319 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700320 .setInterpolator(mConfig.linearOutSlowInInterpolator)
321 .setDuration(475)
322 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700323 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700324 .start();
325 }
326
327 } else if (mConfig.launchedFromAppWithThumbnail) {
328 if (ctx.isFrontMost) {
329 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700330 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700331
Winson Chunga26fb782014-06-12 17:52:39 -0700332 // Animate the dim into view as well
333 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
334 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
335 anim.setDuration(mConfig.taskBarEnterAnimDuration);
336 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
337 anim.start();
Winson Chung743d5c92014-06-13 10:14:53 -0700338 } else {
339 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700340 }
341
342 } else if (mConfig.launchedFromHome) {
343 // Animate the tasks up
344 int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
345 int delay = mConfig.taskBarEnterAnimDelay +
346 frontIndex * mConfig.taskViewEnterFromHomeDelay;
347 animate()
348 .scaleX(transform.scale)
349 .scaleY(transform.scale)
350 .translationY(transform.translationY)
Winson Chung969f5862014-06-16 17:08:24 -0700351 .translationZ(transform.translationZ)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700352 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700353 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700354 .setInterpolator(mConfig.quintOutInterpolator)
355 .setDuration(mConfig.taskViewEnterFromHomeDuration)
356 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700357 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700358 .start();
359 }
360 }
361
Winson Chung969f5862014-06-16 17:08:24 -0700362 /** Animates this task view as it leaves recents by pressing home. */
363 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700364 animate()
365 .translationY(ctx.offscreenTranslationY)
366 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700367 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700368 .setInterpolator(mConfig.fastOutLinearInInterpolator)
369 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700370 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700371 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700372 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700373 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800374 }
375
376 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700377 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
378 if (isLaunchingTask) {
379 // Disable the thumbnail clip and animate the bar out
380 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700381
Winson Chung969f5862014-06-16 17:08:24 -0700382 // Animate the dim
383 if (mDim > 0) {
384 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
385 anim.setDuration(mConfig.taskBarExitAnimDuration);
386 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
387 anim.start();
388 }
389 } else {
390 // Hide the dismiss button
391 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700392 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800393 }
394
Winson Chung9f49df92014-05-07 18:08:34 -0700395 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700396 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700397 // Disabling clipping with the stack while the view is animating away
398 setClipViewInStack(false);
399
Winson Chungd42a6cf2014-06-03 16:24:04 -0700400 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700401 .alpha(0f)
402 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700403 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700404 .setInterpolator(mConfig.fastOutSlowInInterpolator)
405 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700406 .withLayer()
407 .withEndAction(new Runnable() {
408 @Override
409 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700410 // We just throw this into a runnable because starting a view property
411 // animation using layers can cause inconsisten results if we try and
412 // update the layers while the animation is running. In some cases,
413 // the runnabled passed in may start an animation which also uses layers
414 // so we defer all this by posting this.
415 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700416
417 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700418 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700419 }
420 })
421 .start();
422 }
423
Winson Chung969f5862014-06-16 17:08:24 -0700424 /** Animates this task view if the user does not interact with the stack after a certain time. */
425 public void startNoUserInteractionAnimation() {
426 mBarView.startNoUserInteractionAnimation();
427 }
428
429 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
430 public void setNoUserInteractionState() {
431 mBarView.setNoUserInteractionState();
432 }
433
Winson Chung303e1ff2014-03-07 15:06:19 -0800434 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700435 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800436 getHitRect(outRect);
437 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
438 outRect.right = outRect.left + mThumbnailView.getRight();
439 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800440 return outRect;
441 }
442
443 /** Enable the hw layers on this task view */
444 void enableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700445 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700446 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800447 }
448
449 /** Disable the hw layers on this task view */
450 void disableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700451 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700452 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800453 }
454
Winson Chung5a9b0b02014-05-20 17:32:03 -0700455 /**
456 * Returns whether this view should be clipped, or any views below should clip against this
457 * view.
458 */
459 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700460 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700461 }
462
463 /** Sets whether this view should be clipped, or clipped against. */
464 void setClipViewInStack(boolean clip) {
465 if (clip != mClipViewInStack) {
466 mClipViewInStack = clip;
467 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700468 getHitRect(mTmpRect);
469 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700470 }
471 }
472 }
473
Winson Chunga26fb782014-06-12 17:52:39 -0700474 /** Returns the current dim. */
475 public void setDim(int dim) {
476 mDim = dim;
477 postInvalidateOnAnimation();
478 }
479
480 /** Returns the current dim. */
481 public int getDim() {
482 return mDim;
483 }
484
485 /** Compute the dim as a function of the scale of this view. */
486 int getDimOverlayFromScale() {
Winson Chung14926462014-04-14 18:57:14 -0700487 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
488 float scaleRange = 1f - minScale;
489 float dim = (1f - getScaleX()) / scaleRange;
490 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700491 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
492 }
493
494 /** Update the dim as a function of the scale of this view. */
495 void updateDimOverlayFromScale() {
496 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700497 }
498
499 @Override
500 public void draw(Canvas canvas) {
Winson Chung969f5862014-06-16 17:08:24 -0700501 int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
Winson Chung14926462014-04-14 18:57:14 -0700502 // Apply the rounded rect clip path on the whole view
503 canvas.clipPath(mRoundedRectClipPath);
Winson Chung14926462014-04-14 18:57:14 -0700504 super.draw(canvas);
Winson Chung969f5862014-06-16 17:08:24 -0700505 canvas.restoreToCount(restoreCount);
Winson Chung14926462014-04-14 18:57:14 -0700506
507 // Apply the dim if necessary
508 if (mDim > 0) {
509 canvas.drawColor(mDim << 24);
510 }
511 }
512
Winson Chung1e8d71b2014-05-16 17:05:22 -0700513 /**
514 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
515 * if the view is not currently visible, or we are in touch state (where we still want to keep
516 * track of focus).
517 */
518 public void setFocusedTask() {
519 mIsFocused = true;
520 requestFocus();
Winson Chung47a3e652014-05-21 16:03:42 -0700521 invalidate();
522 mCb.onTaskFocused(this);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700523 }
524
525 /**
526 * Updates the explicitly focused state when the view focus changes.
527 */
528 @Override
529 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
530 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
531 if (!gainFocus) {
532 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700533 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700534 }
535 }
536
537 /**
538 * Returns whether we have explicitly been focused.
539 */
540 public boolean isFocusedTask() {
541 return mIsFocused || isFocused();
542 }
543
Winson Chung4d7b0922014-03-13 17:14:17 -0700544 /**** TaskCallbacks Implementation ****/
545
Winson Chung04dfe0d2014-03-14 14:06:29 -0700546 /** Binds this task view to the task */
547 public void onTaskBound(Task t) {
548 mTask = t;
549 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800550 }
551
552 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700553 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung863db8a2014-05-20 14:27:39 -0700554 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700555 // Bind each of the views to the new task data
556 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
557 mBarView.rebindToTask(mTask, reloadingTaskData);
Winson Chung9f9679d2014-04-11 16:49:09 -0700558 // Rebind any listeners
559 mBarView.mApplicationIcon.setOnClickListener(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700560 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700561 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700562 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700563 mBarView.mApplicationIcon.setOnLongClickListener(this);
564 }
565 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700566 }
567 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700568 }
569
570 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700571 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700572 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700573 // Unbind each of the views from the task data and remove the task callback
574 mTask.setCallbacks(null);
575 mThumbnailView.unbindFromTask();
576 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700577 // Unbind any listeners
578 mBarView.mApplicationIcon.setOnClickListener(null);
Winson Chung6cb485f2014-05-19 10:30:43 -0700579 mBarView.mDismissButton.setOnClickListener(null);
580 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
581 mBarView.mApplicationIcon.setOnLongClickListener(null);
582 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700583 }
584 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700585 }
586
587 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -0800588 public void onClick(View v) {
Winson Chung863db8a2014-05-20 14:27:39 -0700589 if (v == mBarView.mApplicationIcon) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700590 mCb.onTaskIconClicked(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700591 } else if (v == mBarView.mDismissButton) {
592 // Animate out the view and call the callback
593 final TaskView tv = this;
Winson Chung969f5862014-06-16 17:08:24 -0700594 startDeleteTaskAnimation(new Runnable() {
Winson Chung54e297a2014-05-09 17:15:32 -0700595 @Override
596 public void run() {
597 mCb.onTaskDismissed(tv);
598 }
599 });
Winson Chung9f9679d2014-04-11 16:49:09 -0700600 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800601 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700602
603 @Override
604 public boolean onLongClick(View v) {
605 if (v == mBarView.mApplicationIcon) {
606 mCb.onTaskAppInfoClicked(this);
607 return true;
608 }
609 return false;
610 }
Winson Chung7bb18852014-05-20 23:25:41 +0000611}