blob: 09dc1c88dd1c65472cf08e182810d27866ee1593 [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 Chung14926462014-04-14 18:57:14 -070025import android.graphics.Path;
Winson Chung9f9679d2014-04-11 16:49:09 -070026import android.graphics.Point;
Winson Chung303e1ff2014-03-07 15:06:19 -080027import android.graphics.Rect;
Winson Chung14926462014-04-14 18:57:14 -070028import android.graphics.RectF;
Winson Chung37c8d8e2014-03-24 14:53:07 -070029import android.util.AttributeSet;
Winson Chung9f9679d2014-04-11 16:49:09 -070030import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080031import android.view.View;
Winson Chungb5ddfc32014-06-13 17:23:12 -070032import android.view.ViewPropertyAnimator;
Winson Chung14926462014-04-14 18:57:14 -070033import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080034import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070035import com.android.systemui.R;
Winson Chungd42a6cf2014-06-03 16:24:04 -070036import com.android.systemui.recents.Console;
Winson Chung14926462014-04-14 18:57:14 -070037import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080038import com.android.systemui.recents.RecentsConfiguration;
39import com.android.systemui.recents.model.Task;
Winson Chung303e1ff2014-03-07 15:06:19 -080040
Winson Chung303e1ff2014-03-07 15:06:19 -080041
Winson Chung37c8d8e2014-03-24 14:53:07 -070042/* A task view */
Winson Chung6cb485f2014-05-19 10:30:43 -070043public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
44 View.OnLongClickListener {
Winson Chung37c8d8e2014-03-24 14:53:07 -070045 /** The TaskView callbacks */
46 interface TaskViewCallbacks {
47 public void onTaskIconClicked(TaskView tv);
Winson Chung9f9679d2014-04-11 16:49:09 -070048 public void onTaskAppInfoClicked(TaskView tv);
Winson Chung47a3e652014-05-21 16:03:42 -070049 public void onTaskFocused(TaskView tv);
Winson Chung54e297a2014-05-09 17:15:32 -070050 public void onTaskDismissed(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070051 }
52
Winson Chungd42a6cf2014-06-03 16:24:04 -070053 RecentsConfiguration mConfig;
54
Winson Chung14926462014-04-14 18:57:14 -070055 int mDim;
56 int mMaxDim;
57 TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
58
Winson Chung303e1ff2014-03-07 15:06:19 -080059 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070060 boolean mTaskDataLoaded;
Winson Chung1e8d71b2014-05-16 17:05:22 -070061 boolean mIsFocused;
Winson Chung5a9b0b02014-05-20 17:32:03 -070062 boolean mClipViewInStack;
Winson Chung9f9679d2014-04-11 16:49:09 -070063 Point mLastTouchDown = new Point();
Winson Chung14926462014-04-14 18:57:14 -070064 Path mRoundedRectClipPath = new Path();
Winson Chungd42a6cf2014-06-03 16:24:04 -070065 Rect mTmpRect = new Rect();
Winson Chung37c8d8e2014-03-24 14:53:07 -070066
67 TaskThumbnailView mThumbnailView;
68 TaskBarView mBarView;
69 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080070
Winson Chungd42a6cf2014-06-03 16:24:04 -070071 // Optimizations
72 ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
73 new ValueAnimator.AnimatorUpdateListener() {
74 @Override
75 public void onAnimationUpdate(ValueAnimator animation) {
76 updateDimOverlayFromScale();
77 }
78 };
Winson Chung743d5c92014-06-13 10:14:53 -070079 Runnable mEnableThumbnailClip = new Runnable() {
80 @Override
81 public void run() {
82 mThumbnailView.updateTaskBarClip(mBarView);
83 }
84 };
85 Runnable mDisableThumbnailClip = new Runnable() {
86 @Override
87 public void run() {
88 mThumbnailView.disableClipTaskBarView();
89 }
90 };
Winson Chungd42a6cf2014-06-03 16:24:04 -070091
Winson Chung37c8d8e2014-03-24 14:53:07 -070092
93 public TaskView(Context context) {
94 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -080095 }
96
Winson Chung37c8d8e2014-03-24 14:53:07 -070097 public TaskView(Context context, AttributeSet attrs) {
98 this(context, attrs, 0);
99 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800100
Winson Chung37c8d8e2014-03-24 14:53:07 -0700101 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
102 this(context, attrs, defStyleAttr, 0);
103 }
104
105 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
106 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700107 mConfig = RecentsConfiguration.getInstance();
Winson Chung14926462014-04-14 18:57:14 -0700108 setWillNotDraw(false);
Winson Chunga26fb782014-06-12 17:52:39 -0700109 setDim(getDim());
Winson Chung37c8d8e2014-03-24 14:53:07 -0700110 }
111
112 @Override
113 protected void onFinishInflate() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700114 mMaxDim = mConfig.taskStackMaxDim;
Winson Chung14926462014-04-14 18:57:14 -0700115
Winson Chung5a9b0b02014-05-20 17:32:03 -0700116 // By default, all views are clipped to other views in their stack
117 mClipViewInStack = true;
118
Winson Chung37c8d8e2014-03-24 14:53:07 -0700119 // Bind the views
Winson Chung37c8d8e2014-03-24 14:53:07 -0700120 mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
Winson Chung743d5c92014-06-13 10:14:53 -0700121 mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
Winson Chung9f9679d2014-04-11 16:49:09 -0700122
Winson Chung37c8d8e2014-03-24 14:53:07 -0700123 if (mTaskDataLoaded) {
124 onTaskDataLoaded(false);
Winson Chung303e1ff2014-03-07 15:06:19 -0800125 }
126 }
127
Winson Chung9f9679d2014-04-11 16:49:09 -0700128 @Override
Winson Chung14926462014-04-14 18:57:14 -0700129 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
130 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
131
132 // Update the rounded rect clip path
Winson Chungd42a6cf2014-06-03 16:24:04 -0700133 float radius = mConfig.taskViewRoundedCornerRadiusPx;
Winson Chung14926462014-04-14 18:57:14 -0700134 mRoundedRectClipPath.reset();
135 mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
136 radius, radius, Path.Direction.CW);
Winson Chung96e3bc12014-05-06 16:44:12 -0700137
138 // Update the outline
139 Outline o = new Outline();
Winson Chung602de032014-05-27 12:19:28 -0700140 o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
Winson Chungd42a6cf2014-06-03 16:24:04 -0700141 mConfig.taskViewShadowOutlineBottomInsetPx, radius);
Winson Chung96e3bc12014-05-06 16:44:12 -0700142 setOutline(o);
Winson Chung14926462014-04-14 18:57:14 -0700143 }
144
145 @Override
Winson Chung9f9679d2014-04-11 16:49:09 -0700146 public boolean onInterceptTouchEvent(MotionEvent ev) {
147 switch (ev.getAction()) {
148 case MotionEvent.ACTION_DOWN:
149 case MotionEvent.ACTION_MOVE:
150 mLastTouchDown.set((int) ev.getX(), (int) ev.getY());
151 break;
152 }
153 return super.onInterceptTouchEvent(ev);
154 }
155
Winson Chung04dfe0d2014-03-14 14:06:29 -0700156 /** Set callback */
157 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800158 mCb = cb;
159 }
160
Winson Chung303e1ff2014-03-07 15:06:19 -0800161 /** Gets the task */
162 Task getTask() {
163 return mTask;
164 }
165
166 /** Synchronizes this view's properties with the task's transform */
Winson Chungb5ddfc32014-06-13 17:23:12 -0700167 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700168 if (Console.Enabled) {
169 Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
170 "duration: " + duration, Console.AnsiPurple);
171 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700172
Winson Chung54e297a2014-05-09 17:15:32 -0700173 // Update the bar view
Winson Chungb5ddfc32014-06-13 17:23:12 -0700174 mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
Winson Chung54e297a2014-05-09 17:15:32 -0700175
Winson Chungb5ddfc32014-06-13 17:23:12 -0700176 // Check to see if any properties have changed, and update the task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800177 if (duration > 0) {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700178 ViewPropertyAnimator anim = animate();
179 boolean useLayers = false;
180
181 // Animate to the final state
182 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
183 anim.translationY(toTransform.translationY);
Winson Chung303e1ff2014-03-07 15:06:19 -0800184 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700185 if (Constants.DebugFlags.App.EnableShadows &&
186 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
187 anim.translationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700188 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700189 if (toTransform.hasScaleChangedFrom(getScaleX())) {
190 anim.scaleX(toTransform.scale)
Winson Chungc6a16232014-04-01 14:04:48 -0700191 .scaleY(toTransform.scale)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700192 .setUpdateListener(mUpdateDimListener);
193 useLayers = true;
194 }
195 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
196 // Use layers if we animate alpha
197 anim.alpha(toTransform.alpha);
198 useLayers = true;
199 }
200 if (useLayers) {
201 anim.withLayer();
202 }
203 anim.setStartDelay(0)
204 .setDuration(duration)
205 .setInterpolator(mConfig.fastOutSlowInInterpolator)
206 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800207 } else {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700208 // Set the changed properties
209 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
210 setTranslationY(toTransform.translationY);
211 }
212 if (Constants.DebugFlags.App.EnableShadows &&
213 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
Winson Chungb13c46e2014-06-09 18:33:08 -0700214 setTranslationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700215 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700216 if (toTransform.hasScaleChangedFrom(getScaleX())) {
217 setScaleX(toTransform.scale);
218 setScaleY(toTransform.scale);
219 updateDimOverlayFromScale();
220 }
221 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
222 setAlpha(toTransform.alpha);
223 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800224 }
225 }
226
227 /** Resets this view's properties */
228 void resetViewProperties() {
229 setTranslationX(0f);
230 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700231 if (Constants.DebugFlags.App.EnableShadows) {
232 setTranslationZ(0f);
233 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800234 setScaleX(1f);
235 setScaleY(1f);
236 setAlpha(1f);
Winson Chunga26fb782014-06-12 17:52:39 -0700237 setDim(0);
Winson Chung14926462014-04-14 18:57:14 -0700238 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800239 }
240
Winson Chung11ca76a52014-04-10 17:29:13 -0700241 /**
242 * When we are un/filtering, this method will set up the transform that we are animating to,
243 * in order to hide the task.
244 */
Winson Chungc6a16232014-04-01 14:04:48 -0700245 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
246 // Fade the view out and slide it away
247 toTransform.alpha = 0f;
248 toTransform.translationY += 200;
249 }
250
Winson Chung11ca76a52014-04-10 17:29:13 -0700251 /**
252 * When we are un/filtering, this method will setup the transform that we are animating from,
253 * in order to show the task.
254 */
Winson Chungc6a16232014-04-01 14:04:48 -0700255 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
256 // Fade the view in
257 fromTransform.alpha = 0f;
258 }
259
Winson Chung24cf1522014-05-29 12:03:33 -0700260 /** Prepares this task view for the enter-recents animations. This is called earlier in the
261 * first layout because the actual animation into recents may take a long time. */
Winson Chung969f5862014-06-16 17:08:24 -0700262 public void prepareEnterRecentsAnimation(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
263 Rect taskRect) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700264 if (mConfig.launchedFromAppWithScreenshot) {
265 if (isTaskViewFrontMost) {
266 // Hide the task view as we are going to animate the full screenshot into view
267 // and then replace it with this view once we are done
268 setVisibility(View.INVISIBLE);
269 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700270 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700271 } else {
272 // Top align the task views
273 setTranslationY(offsetY);
274 setScaleX(1f);
275 setScaleY(1f);
276 }
277
278 } else if (mConfig.launchedFromAppWithThumbnail) {
279 if (isTaskViewFrontMost) {
280 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700281 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700282 // Set the dim to 0 so we can animate it in
283 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700284 }
285
286 } else if (mConfig.launchedFromHome) {
287 // Move the task view off screen (below) so we can animate it in
288 setTranslationY(offscreenY);
Winson Chung969f5862014-06-16 17:08:24 -0700289 setTranslationZ(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700290 setScaleX(1f);
291 setScaleY(1f);
292 }
Winson Chung24cf1522014-05-29 12:03:33 -0700293 }
294
Winson Chung303e1ff2014-03-07 15:06:19 -0800295 /** Animates this task view as it enters recents */
Winson Chung969f5862014-06-16 17:08:24 -0700296 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700297 TaskViewTransform transform = ctx.transform;
298
299 if (mConfig.launchedFromAppWithScreenshot) {
300 if (ctx.isFrontMost) {
301 // Animate the full screenshot down first, before swapping with this task view
302 ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
303 @Override
304 public void run() {
305 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700306 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700307 setVisibility(View.VISIBLE);
308 }
309 });
310 } else {
311 // Animate the tasks down behind the full screenshot
312 animate()
313 .scaleX(transform.scale)
314 .scaleY(transform.scale)
315 .translationY(transform.translationY)
316 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700317 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700318 .setInterpolator(mConfig.linearOutSlowInInterpolator)
319 .setDuration(475)
320 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700321 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700322 .start();
323 }
324
325 } else if (mConfig.launchedFromAppWithThumbnail) {
326 if (ctx.isFrontMost) {
327 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700328 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700329
Winson Chunga26fb782014-06-12 17:52:39 -0700330 // Animate the dim into view as well
331 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
332 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
333 anim.setDuration(mConfig.taskBarEnterAnimDuration);
334 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
335 anim.start();
Winson Chung743d5c92014-06-13 10:14:53 -0700336 } else {
337 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700338 }
339
340 } else if (mConfig.launchedFromHome) {
341 // Animate the tasks up
342 int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
343 int delay = mConfig.taskBarEnterAnimDelay +
344 frontIndex * mConfig.taskViewEnterFromHomeDelay;
345 animate()
346 .scaleX(transform.scale)
347 .scaleY(transform.scale)
348 .translationY(transform.translationY)
Winson Chung969f5862014-06-16 17:08:24 -0700349 .translationZ(transform.translationZ)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700350 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700351 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700352 .setInterpolator(mConfig.quintOutInterpolator)
353 .setDuration(mConfig.taskViewEnterFromHomeDuration)
354 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700355 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700356 .start();
357 }
358 }
359
Winson Chung969f5862014-06-16 17:08:24 -0700360 /** Animates this task view as it leaves recents by pressing home. */
361 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700362 animate()
363 .translationY(ctx.offscreenTranslationY)
364 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700365 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700366 .setInterpolator(mConfig.fastOutLinearInInterpolator)
367 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700368 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700369 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700370 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700371 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800372 }
373
374 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700375 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
376 if (isLaunchingTask) {
377 // Disable the thumbnail clip and animate the bar out
378 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700379
Winson Chung969f5862014-06-16 17:08:24 -0700380 // Animate the dim
381 if (mDim > 0) {
382 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
383 anim.setDuration(mConfig.taskBarExitAnimDuration);
384 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
385 anim.start();
386 }
387 } else {
388 // Hide the dismiss button
389 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700390 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800391 }
392
Winson Chung9f49df92014-05-07 18:08:34 -0700393 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700394 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700395 // Disabling clipping with the stack while the view is animating away
396 setClipViewInStack(false);
397
Winson Chungd42a6cf2014-06-03 16:24:04 -0700398 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700399 .alpha(0f)
400 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700401 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700402 .setInterpolator(mConfig.fastOutSlowInInterpolator)
403 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700404 .withLayer()
405 .withEndAction(new Runnable() {
406 @Override
407 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700408 // We just throw this into a runnable because starting a view property
409 // animation using layers can cause inconsisten results if we try and
410 // update the layers while the animation is running. In some cases,
411 // the runnabled passed in may start an animation which also uses layers
412 // so we defer all this by posting this.
413 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700414
415 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700416 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700417 }
418 })
419 .start();
420 }
421
Winson Chung969f5862014-06-16 17:08:24 -0700422 /** Animates this task view if the user does not interact with the stack after a certain time. */
423 public void startNoUserInteractionAnimation() {
424 mBarView.startNoUserInteractionAnimation();
425 }
426
427 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
428 public void setNoUserInteractionState() {
429 mBarView.setNoUserInteractionState();
430 }
431
Winson Chung303e1ff2014-03-07 15:06:19 -0800432 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700433 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800434 getHitRect(outRect);
435 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
436 outRect.right = outRect.left + mThumbnailView.getRight();
437 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800438 return outRect;
439 }
440
441 /** Enable the hw layers on this task view */
442 void enableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800443 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700444 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800445 }
446
447 /** Disable the hw layers on this task view */
448 void disableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800449 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700450 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800451 }
452
Winson Chung5a9b0b02014-05-20 17:32:03 -0700453 /**
454 * Returns whether this view should be clipped, or any views below should clip against this
455 * view.
456 */
457 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700458 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700459 }
460
461 /** Sets whether this view should be clipped, or clipped against. */
462 void setClipViewInStack(boolean clip) {
463 if (clip != mClipViewInStack) {
464 mClipViewInStack = clip;
465 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700466 getHitRect(mTmpRect);
467 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700468 }
469 }
470 }
471
Winson Chunga26fb782014-06-12 17:52:39 -0700472 /** Returns the current dim. */
473 public void setDim(int dim) {
474 mDim = dim;
475 postInvalidateOnAnimation();
476 }
477
478 /** Returns the current dim. */
479 public int getDim() {
480 return mDim;
481 }
482
483 /** Compute the dim as a function of the scale of this view. */
484 int getDimOverlayFromScale() {
Winson Chung14926462014-04-14 18:57:14 -0700485 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
486 float scaleRange = 1f - minScale;
487 float dim = (1f - getScaleX()) / scaleRange;
488 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700489 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
490 }
491
492 /** Update the dim as a function of the scale of this view. */
493 void updateDimOverlayFromScale() {
494 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700495 }
496
497 @Override
498 public void draw(Canvas canvas) {
Winson Chung969f5862014-06-16 17:08:24 -0700499 int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
Winson Chung14926462014-04-14 18:57:14 -0700500 // Apply the rounded rect clip path on the whole view
501 canvas.clipPath(mRoundedRectClipPath);
Winson Chung14926462014-04-14 18:57:14 -0700502 super.draw(canvas);
Winson Chung969f5862014-06-16 17:08:24 -0700503 canvas.restoreToCount(restoreCount);
Winson Chung14926462014-04-14 18:57:14 -0700504
505 // Apply the dim if necessary
506 if (mDim > 0) {
507 canvas.drawColor(mDim << 24);
508 }
509 }
510
Winson Chung1e8d71b2014-05-16 17:05:22 -0700511 /**
512 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
513 * if the view is not currently visible, or we are in touch state (where we still want to keep
514 * track of focus).
515 */
516 public void setFocusedTask() {
517 mIsFocused = true;
518 requestFocus();
Winson Chung47a3e652014-05-21 16:03:42 -0700519 invalidate();
520 mCb.onTaskFocused(this);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700521 }
522
523 /**
524 * Updates the explicitly focused state when the view focus changes.
525 */
526 @Override
527 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
528 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
529 if (!gainFocus) {
530 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700531 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700532 }
533 }
534
535 /**
536 * Returns whether we have explicitly been focused.
537 */
538 public boolean isFocusedTask() {
539 return mIsFocused || isFocused();
540 }
541
Winson Chung4d7b0922014-03-13 17:14:17 -0700542 /**** TaskCallbacks Implementation ****/
543
Winson Chung04dfe0d2014-03-14 14:06:29 -0700544 /** Binds this task view to the task */
545 public void onTaskBound(Task t) {
546 mTask = t;
547 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800548 }
549
550 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700551 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung863db8a2014-05-20 14:27:39 -0700552 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700553 // Bind each of the views to the new task data
554 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
555 mBarView.rebindToTask(mTask, reloadingTaskData);
Winson Chung9f9679d2014-04-11 16:49:09 -0700556 // Rebind any listeners
557 mBarView.mApplicationIcon.setOnClickListener(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700558 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700559 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700560 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700561 mBarView.mApplicationIcon.setOnLongClickListener(this);
562 }
563 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700564 }
565 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700566 }
567
568 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700569 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700570 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700571 // Unbind each of the views from the task data and remove the task callback
572 mTask.setCallbacks(null);
573 mThumbnailView.unbindFromTask();
574 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700575 // Unbind any listeners
576 mBarView.mApplicationIcon.setOnClickListener(null);
Winson Chung6cb485f2014-05-19 10:30:43 -0700577 mBarView.mDismissButton.setOnClickListener(null);
578 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
579 mBarView.mApplicationIcon.setOnLongClickListener(null);
580 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700581 }
582 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700583 }
584
585 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -0800586 public void onClick(View v) {
Winson Chung863db8a2014-05-20 14:27:39 -0700587 if (v == mBarView.mApplicationIcon) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700588 mCb.onTaskIconClicked(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700589 } else if (v == mBarView.mDismissButton) {
590 // Animate out the view and call the callback
591 final TaskView tv = this;
Winson Chung969f5862014-06-16 17:08:24 -0700592 startDeleteTaskAnimation(new Runnable() {
Winson Chung54e297a2014-05-09 17:15:32 -0700593 @Override
594 public void run() {
595 mCb.onTaskDismissed(tv);
596 }
597 });
Winson Chung9f9679d2014-04-11 16:49:09 -0700598 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800599 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700600
601 @Override
602 public boolean onLongClick(View v) {
603 if (v == mBarView.mApplicationIcon) {
604 mCb.onTaskAppInfoClicked(this);
605 return true;
606 }
607 return false;
608 }
Winson Chung7bb18852014-05-20 23:25:41 +0000609}