blob: b7e834b4f83daddcef8ec8000e4a032f55fcfe91 [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 Chungd42a6cf2014-06-03 16:24:04 -0700262 public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
263 Rect taskRect) {
264 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
270 mBarView.prepareAnimateEnterRecents();
271 } 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
281 mBarView.prepareAnimateEnterRecents();
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);
289 setScaleX(1f);
290 setScaleY(1f);
291 }
Winson Chung24cf1522014-05-29 12:03:33 -0700292 }
293
Winson Chung303e1ff2014-03-07 15:06:19 -0800294 /** Animates this task view as it enters recents */
Winson Chungd42a6cf2014-06-03 16:24:04 -0700295 public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
296 TaskViewTransform transform = ctx.transform;
297
298 if (mConfig.launchedFromAppWithScreenshot) {
299 if (ctx.isFrontMost) {
300 // Animate the full screenshot down first, before swapping with this task view
301 ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
302 @Override
303 public void run() {
304 // Animate the task bar of the first task view
Winson Chung743d5c92014-06-13 10:14:53 -0700305 mBarView.animateOnEnterRecents(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700306 setVisibility(View.VISIBLE);
307 }
308 });
309 } else {
310 // Animate the tasks down behind the full screenshot
311 animate()
312 .scaleX(transform.scale)
313 .scaleY(transform.scale)
314 .translationY(transform.translationY)
315 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700316 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700317 .setInterpolator(mConfig.linearOutSlowInInterpolator)
318 .setDuration(475)
319 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700320 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700321 .start();
322 }
323
324 } else if (mConfig.launchedFromAppWithThumbnail) {
325 if (ctx.isFrontMost) {
326 // Animate the task bar of the first task view
Winson Chung743d5c92014-06-13 10:14:53 -0700327 mBarView.animateOnEnterRecents(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
328
Winson Chunga26fb782014-06-12 17:52:39 -0700329 // Animate the dim into view as well
330 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
331 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
332 anim.setDuration(mConfig.taskBarEnterAnimDuration);
333 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
334 anim.start();
Winson Chung743d5c92014-06-13 10:14:53 -0700335 } else {
336 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700337 }
338
339 } else if (mConfig.launchedFromHome) {
340 // Animate the tasks up
341 int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
342 int delay = mConfig.taskBarEnterAnimDelay +
343 frontIndex * mConfig.taskViewEnterFromHomeDelay;
344 animate()
345 .scaleX(transform.scale)
346 .scaleY(transform.scale)
347 .translationY(transform.translationY)
348 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700349 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700350 .setInterpolator(mConfig.quintOutInterpolator)
351 .setDuration(mConfig.taskViewEnterFromHomeDuration)
352 .withLayer()
Winson Chung743d5c92014-06-13 10:14:53 -0700353 .withEndAction(mEnableThumbnailClip)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700354 .start();
355 }
356 }
357
358 /** Animates this task view as it leaves recents */
359 public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
360 animate()
361 .translationY(ctx.offscreenTranslationY)
362 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700363 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700364 .setInterpolator(mConfig.fastOutLinearInInterpolator)
365 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700366 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700367 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700368 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700369 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800370 }
371
Winson Chunga26fb782014-06-12 17:52:39 -0700372 /** Animates this task view if the user does not interact with the stack after a certain time. */
373 public void animateOnNoUserInteraction() {
374 mBarView.animateOnNoUserInteraction();
375 }
376
377 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
378 public void setOnNoUserInteraction() {
379 mBarView.setOnNoUserInteraction();
380 }
381
Winson Chung303e1ff2014-03-07 15:06:19 -0800382 /** Animates this task view as it exits recents */
Winson Chungd42a6cf2014-06-03 16:24:04 -0700383 public void animateOnLaunchingTask(final Runnable r) {
Winson Chung743d5c92014-06-13 10:14:53 -0700384 // Disable the thumbnail clip and animate the bar out
385 mBarView.animateOnLaunchingTask(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700386
387 // Animate the dim
388 if (mDim > 0) {
389 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
390 anim.setDuration(mConfig.taskBarExitAnimDuration);
391 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
392 anim.start();
393 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800394 }
395
Winson Chung9f49df92014-05-07 18:08:34 -0700396 /** Animates the deletion of this task view */
397 public void animateRemoval(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 Chung303e1ff2014-03-07 15:06:19 -0800425 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700426 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800427 getHitRect(outRect);
428 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
429 outRect.right = outRect.left + mThumbnailView.getRight();
430 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800431 return outRect;
432 }
433
434 /** Enable the hw layers on this task view */
435 void enableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800436 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700437 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800438 }
439
440 /** Disable the hw layers on this task view */
441 void disableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800442 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700443 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800444 }
445
Winson Chung5a9b0b02014-05-20 17:32:03 -0700446 /**
447 * Returns whether this view should be clipped, or any views below should clip against this
448 * view.
449 */
450 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700451 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700452 }
453
454 /** Sets whether this view should be clipped, or clipped against. */
455 void setClipViewInStack(boolean clip) {
456 if (clip != mClipViewInStack) {
457 mClipViewInStack = clip;
458 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700459 getHitRect(mTmpRect);
460 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700461 }
462 }
463 }
464
Winson Chunga26fb782014-06-12 17:52:39 -0700465 /** Returns the current dim. */
466 public void setDim(int dim) {
467 mDim = dim;
468 postInvalidateOnAnimation();
469 }
470
471 /** Returns the current dim. */
472 public int getDim() {
473 return mDim;
474 }
475
476 /** Compute the dim as a function of the scale of this view. */
477 int getDimOverlayFromScale() {
Winson Chung14926462014-04-14 18:57:14 -0700478 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
479 float scaleRange = 1f - minScale;
480 float dim = (1f - getScaleX()) / scaleRange;
481 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700482 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
483 }
484
485 /** Update the dim as a function of the scale of this view. */
486 void updateDimOverlayFromScale() {
487 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700488 }
489
490 @Override
491 public void draw(Canvas canvas) {
492 // Apply the rounded rect clip path on the whole view
493 canvas.clipPath(mRoundedRectClipPath);
494
495 super.draw(canvas);
496
497 // Apply the dim if necessary
498 if (mDim > 0) {
499 canvas.drawColor(mDim << 24);
500 }
501 }
502
Winson Chung1e8d71b2014-05-16 17:05:22 -0700503 /**
504 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
505 * if the view is not currently visible, or we are in touch state (where we still want to keep
506 * track of focus).
507 */
508 public void setFocusedTask() {
509 mIsFocused = true;
510 requestFocus();
Winson Chung47a3e652014-05-21 16:03:42 -0700511 invalidate();
512 mCb.onTaskFocused(this);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700513 }
514
515 /**
516 * Updates the explicitly focused state when the view focus changes.
517 */
518 @Override
519 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
520 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
521 if (!gainFocus) {
522 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700523 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700524 }
525 }
526
527 /**
528 * Returns whether we have explicitly been focused.
529 */
530 public boolean isFocusedTask() {
531 return mIsFocused || isFocused();
532 }
533
Winson Chung4d7b0922014-03-13 17:14:17 -0700534 /**** TaskCallbacks Implementation ****/
535
Winson Chung04dfe0d2014-03-14 14:06:29 -0700536 /** Binds this task view to the task */
537 public void onTaskBound(Task t) {
538 mTask = t;
539 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800540 }
541
542 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700543 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung863db8a2014-05-20 14:27:39 -0700544 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700545 // Bind each of the views to the new task data
546 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
547 mBarView.rebindToTask(mTask, reloadingTaskData);
Winson Chung9f9679d2014-04-11 16:49:09 -0700548 // Rebind any listeners
549 mBarView.mApplicationIcon.setOnClickListener(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700550 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700551 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700552 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700553 mBarView.mApplicationIcon.setOnLongClickListener(this);
554 }
555 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700556 }
557 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700558 }
559
560 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700561 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700562 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700563 // Unbind each of the views from the task data and remove the task callback
564 mTask.setCallbacks(null);
565 mThumbnailView.unbindFromTask();
566 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700567 // Unbind any listeners
568 mBarView.mApplicationIcon.setOnClickListener(null);
Winson Chung6cb485f2014-05-19 10:30:43 -0700569 mBarView.mDismissButton.setOnClickListener(null);
570 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
571 mBarView.mApplicationIcon.setOnLongClickListener(null);
572 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700573 }
574 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700575 }
576
577 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -0800578 public void onClick(View v) {
Winson Chung863db8a2014-05-20 14:27:39 -0700579 if (v == mBarView.mApplicationIcon) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700580 mCb.onTaskIconClicked(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700581 } else if (v == mBarView.mDismissButton) {
582 // Animate out the view and call the callback
583 final TaskView tv = this;
584 animateRemoval(new Runnable() {
585 @Override
586 public void run() {
587 mCb.onTaskDismissed(tv);
588 }
589 });
Winson Chung9f9679d2014-04-11 16:49:09 -0700590 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800591 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700592
593 @Override
594 public boolean onLongClick(View v) {
595 if (v == mBarView.mApplicationIcon) {
596 mCb.onTaskAppInfoClicked(this);
597 return true;
598 }
599 return false;
600 }
Winson Chung7bb18852014-05-20 23:25:41 +0000601}