blob: 33e3f5858d2eebe36a9d55168a4268da7306eec5 [file] [log] [blame]
Winson Chung303e1ff2014-03-07 15:06:19 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.views;
18
Winson Chungc6011de2014-06-30 18:04:55 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Winson Chunga26fb782014-06-12 17:52:39 -070021import android.animation.ObjectAnimator;
Winson Chung14926462014-04-14 18:57:14 -070022import android.animation.ValueAnimator;
Winson Chung303e1ff2014-03-07 15:06:19 -080023import android.content.Context;
Winson Chung14926462014-04-14 18:57:14 -070024import android.graphics.Canvas;
Winson Chung96e3bc12014-05-06 16:44:12 -070025import android.graphics.Outline;
Winson Chungc9567c02014-06-16 20:25:51 -070026import android.graphics.Paint;
Winson Chung303e1ff2014-03-07 15:06:19 -080027import android.graphics.Rect;
Winson Chung37c8d8e2014-03-24 14:53:07 -070028import android.util.AttributeSet;
Winson Chung303e1ff2014-03-07 15:06:19 -080029import android.view.View;
Winson Chungb5ddfc32014-06-13 17:23:12 -070030import android.view.ViewPropertyAnimator;
Winson Chung14926462014-04-14 18:57:14 -070031import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080032import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070033import com.android.systemui.R;
Winson Chungffa2ec62014-07-03 15:54:42 -070034import com.android.systemui.recents.misc.Console;
Winson Chung14926462014-04-14 18:57:14 -070035import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080036import com.android.systemui.recents.RecentsConfiguration;
37import com.android.systemui.recents.model.Task;
Winson Chung303e1ff2014-03-07 15:06:19 -080038
Winson Chung303e1ff2014-03-07 15:06:19 -080039
Winson Chung37c8d8e2014-03-24 14:53:07 -070040/* A task view */
Winson Chung6cb485f2014-05-19 10:30:43 -070041public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.OnClickListener,
42 View.OnLongClickListener {
Winson Chung37c8d8e2014-03-24 14:53:07 -070043 /** The TaskView callbacks */
44 interface TaskViewCallbacks {
Winson Chung7aceb9a2014-07-03 13:38:01 -070045 public void onTaskViewAppIconClicked(TaskView tv);
46 public void onTaskViewAppInfoClicked(TaskView tv);
47 public void onTaskViewDismissed(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070048 }
49
Winson Chungd42a6cf2014-06-03 16:24:04 -070050 RecentsConfiguration mConfig;
51
Winson Chung14926462014-04-14 18:57:14 -070052 int mDim;
53 int mMaxDim;
Winson Chung6057c912014-07-08 14:39:08 -070054 AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
Winson Chung14926462014-04-14 18:57:14 -070055
Winson Chung303e1ff2014-03-07 15:06:19 -080056 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070057 boolean mTaskDataLoaded;
Winson Chung1e8d71b2014-05-16 17:05:22 -070058 boolean mIsFocused;
Winson Chungffa2ec62014-07-03 15:54:42 -070059 boolean mIsStub;
Winson Chung5a9b0b02014-05-20 17:32:03 -070060 boolean mClipViewInStack;
Winson Chungd42a6cf2014-06-03 16:24:04 -070061 Rect mTmpRect = new Rect();
Winson Chungc9567c02014-06-16 20:25:51 -070062 Paint mLayerPaint = new Paint();
Winson Chung37c8d8e2014-03-24 14:53:07 -070063
64 TaskThumbnailView mThumbnailView;
65 TaskBarView mBarView;
66 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080067
Winson Chungd42a6cf2014-06-03 16:24:04 -070068 // Optimizations
69 ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
70 new ValueAnimator.AnimatorUpdateListener() {
71 @Override
72 public void onAnimationUpdate(ValueAnimator animation) {
73 updateDimOverlayFromScale();
74 }
75 };
Winson Chung743d5c92014-06-13 10:14:53 -070076 Runnable mEnableThumbnailClip = new Runnable() {
77 @Override
78 public void run() {
79 mThumbnailView.updateTaskBarClip(mBarView);
80 }
81 };
82 Runnable mDisableThumbnailClip = new Runnable() {
83 @Override
84 public void run() {
85 mThumbnailView.disableClipTaskBarView();
86 }
87 };
Winson Chungd42a6cf2014-06-03 16:24:04 -070088
Winson Chung37c8d8e2014-03-24 14:53:07 -070089
90 public TaskView(Context context) {
91 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -080092 }
93
Winson Chung37c8d8e2014-03-24 14:53:07 -070094 public TaskView(Context context, AttributeSet attrs) {
95 this(context, attrs, 0);
96 }
Winson Chung303e1ff2014-03-07 15:06:19 -080097
Winson Chung37c8d8e2014-03-24 14:53:07 -070098 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
99 this(context, attrs, defStyleAttr, 0);
100 }
101
102 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
103 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700104 mConfig = RecentsConfiguration.getInstance();
Winson Chung14926462014-04-14 18:57:14 -0700105 setWillNotDraw(false);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700106 setClipToOutline(true);
Winson Chunga26fb782014-06-12 17:52:39 -0700107 setDim(getDim());
Winson Chung37c8d8e2014-03-24 14:53:07 -0700108 }
109
110 @Override
111 protected void onFinishInflate() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700112 mMaxDim = mConfig.taskStackMaxDim;
Winson Chung14926462014-04-14 18:57:14 -0700113
Winson Chung5a9b0b02014-05-20 17:32:03 -0700114 // By default, all views are clipped to other views in their stack
115 mClipViewInStack = true;
116
Winson Chung37c8d8e2014-03-24 14:53:07 -0700117 // Bind the views
Winson Chung37c8d8e2014-03-24 14:53:07 -0700118 mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
Winson Chung743d5c92014-06-13 10:14:53 -0700119 mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
Winson Chung9f9679d2014-04-11 16:49:09 -0700120
Winson Chung37c8d8e2014-03-24 14:53:07 -0700121 if (mTaskDataLoaded) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700122 onTaskDataLoaded();
Winson Chung303e1ff2014-03-07 15:06:19 -0800123 }
124 }
125
Winson Chung9f9679d2014-04-11 16:49:09 -0700126 @Override
Winson Chung14926462014-04-14 18:57:14 -0700127 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
128 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
129
Winson Chung96e3bc12014-05-06 16:44:12 -0700130 // Update the outline
131 Outline o = new Outline();
Winson Chungffa2ec62014-07-03 15:54:42 -0700132 o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
133 mConfig.taskViewRoundedCornerRadiusPx);
Winson Chung96e3bc12014-05-06 16:44:12 -0700134 setOutline(o);
Winson Chung14926462014-04-14 18:57:14 -0700135 }
136
Winson Chung04dfe0d2014-03-14 14:06:29 -0700137 /** Set callback */
138 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800139 mCb = cb;
140 }
141
Winson Chung303e1ff2014-03-07 15:06:19 -0800142 /** Gets the task */
143 Task getTask() {
144 return mTask;
145 }
146
147 /** Synchronizes this view's properties with the task's transform */
Winson Chungb5ddfc32014-06-13 17:23:12 -0700148 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700149 if (Console.Enabled) {
150 Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
151 "duration: " + duration, Console.AnsiPurple);
152 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700153
Winson Chung54e297a2014-05-09 17:15:32 -0700154 // Update the bar view
Winson Chungb5ddfc32014-06-13 17:23:12 -0700155 mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
Winson Chung54e297a2014-05-09 17:15:32 -0700156
Winson Chungb5ddfc32014-06-13 17:23:12 -0700157 // Check to see if any properties have changed, and update the task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800158 if (duration > 0) {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700159 ViewPropertyAnimator anim = animate();
160 boolean useLayers = false;
161
162 // Animate to the final state
163 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
164 anim.translationY(toTransform.translationY);
Winson Chung303e1ff2014-03-07 15:06:19 -0800165 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700166 if (Constants.DebugFlags.App.EnableShadows &&
167 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
168 anim.translationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700169 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700170 if (toTransform.hasScaleChangedFrom(getScaleX())) {
171 anim.scaleX(toTransform.scale)
Winson Chungc6a16232014-04-01 14:04:48 -0700172 .scaleY(toTransform.scale)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700173 .setUpdateListener(mUpdateDimListener);
174 useLayers = true;
175 }
176 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
177 // Use layers if we animate alpha
178 anim.alpha(toTransform.alpha);
179 useLayers = true;
180 }
181 if (useLayers) {
182 anim.withLayer();
183 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700184 anim.setStartDelay(toTransform.startDelay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700185 .setDuration(duration)
186 .setInterpolator(mConfig.fastOutSlowInInterpolator)
187 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800188 } else {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700189 // Set the changed properties
190 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
191 setTranslationY(toTransform.translationY);
192 }
193 if (Constants.DebugFlags.App.EnableShadows &&
194 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
Winson Chungb13c46e2014-06-09 18:33:08 -0700195 setTranslationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700196 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700197 if (toTransform.hasScaleChangedFrom(getScaleX())) {
198 setScaleX(toTransform.scale);
199 setScaleY(toTransform.scale);
200 updateDimOverlayFromScale();
201 }
202 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
203 setAlpha(toTransform.alpha);
204 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800205 }
206 }
207
208 /** Resets this view's properties */
209 void resetViewProperties() {
210 setTranslationX(0f);
211 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700212 if (Constants.DebugFlags.App.EnableShadows) {
213 setTranslationZ(0f);
214 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800215 setScaleX(1f);
216 setScaleY(1f);
217 setAlpha(1f);
Winson Chunga26fb782014-06-12 17:52:39 -0700218 setDim(0);
Winson Chung14926462014-04-14 18:57:14 -0700219 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800220 }
221
Winson Chung11ca76a52014-04-10 17:29:13 -0700222 /**
223 * When we are un/filtering, this method will set up the transform that we are animating to,
224 * in order to hide the task.
225 */
Winson Chungc6a16232014-04-01 14:04:48 -0700226 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
227 // Fade the view out and slide it away
228 toTransform.alpha = 0f;
229 toTransform.translationY += 200;
Winson Chung7ab650c2014-06-18 14:25:34 -0700230 toTransform.translationZ = 0;
Winson Chungc6a16232014-04-01 14:04:48 -0700231 }
232
Winson Chung11ca76a52014-04-10 17:29:13 -0700233 /**
234 * When we are un/filtering, this method will setup the transform that we are animating from,
235 * in order to show the task.
236 */
Winson Chungc6a16232014-04-01 14:04:48 -0700237 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
238 // Fade the view in
239 fromTransform.alpha = 0f;
240 }
241
Winson Chung24cf1522014-05-29 12:03:33 -0700242 /** Prepares this task view for the enter-recents animations. This is called earlier in the
243 * first layout because the actual animation into recents may take a long time. */
Winson Chung083baf92014-07-11 10:32:42 -0700244 public void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, int offsetY,
245 int offscreenY) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700246 if (mConfig.launchedFromAppWithScreenshot) {
Winson Chung083baf92014-07-11 10:32:42 -0700247 if (isTaskViewLaunchTargetTask) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700248 // Hide the task view as we are going to animate the full screenshot into view
249 // and then replace it with this view once we are done
250 setVisibility(View.INVISIBLE);
251 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700252 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700253 } else {
254 // Top align the task views
255 setTranslationY(offsetY);
256 setScaleX(1f);
257 setScaleY(1f);
258 }
259
260 } else if (mConfig.launchedFromAppWithThumbnail) {
Winson Chung083baf92014-07-11 10:32:42 -0700261 if (isTaskViewLaunchTargetTask) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700262 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700263 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700264 // Set the dim to 0 so we can animate it in
265 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700266 }
267
268 } else if (mConfig.launchedFromHome) {
269 // Move the task view off screen (below) so we can animate it in
270 setTranslationY(offscreenY);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700271 if (Constants.DebugFlags.App.EnableShadows) {
272 setTranslationZ(0);
273 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700274 setScaleX(1f);
275 setScaleY(1f);
276 }
Winson Chung24cf1522014-05-29 12:03:33 -0700277 }
278
Winson Chung303e1ff2014-03-07 15:06:19 -0800279 /** Animates this task view as it enters recents */
Winson Chungc6011de2014-06-30 18:04:55 -0700280 public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung6057c912014-07-08 14:39:08 -0700281 TaskViewTransform transform = ctx.currentTaskTransform;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700282
283 if (mConfig.launchedFromAppWithScreenshot) {
Winson Chung083baf92014-07-11 10:32:42 -0700284 if (ctx.isCurrentTaskLaunchTarget) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700285 // Animate the full screenshot down first, before swapping with this task view
Winson Chung6057c912014-07-08 14:39:08 -0700286 ctx.fullScreenshotView.animateOnEnterRecents(ctx, new Runnable() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700287 @Override
288 public void run() {
289 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700290 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700291 setVisibility(View.VISIBLE);
Winson Chungc6011de2014-06-30 18:04:55 -0700292 // Decrement the post animation trigger
293 ctx.postAnimationTrigger.decrement();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700294 }
295 });
296 } else {
297 // Animate the tasks down behind the full screenshot
298 animate()
299 .scaleX(transform.scale)
300 .scaleY(transform.scale)
301 .translationY(transform.translationY)
302 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700303 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700304 .setInterpolator(mConfig.linearOutSlowInInterpolator)
305 .setDuration(475)
306 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700307 .withEndAction(new Runnable() {
308 @Override
309 public void run() {
310 mEnableThumbnailClip.run();
311 // Decrement the post animation trigger
312 ctx.postAnimationTrigger.decrement();
313 }
314 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700315 .start();
316 }
Winson Chungc6011de2014-06-30 18:04:55 -0700317 ctx.postAnimationTrigger.increment();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700318
319 } else if (mConfig.launchedFromAppWithThumbnail) {
Winson Chung083baf92014-07-11 10:32:42 -0700320 if (ctx.isCurrentTaskLaunchTarget) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700321 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700322 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700323
Winson Chunga26fb782014-06-12 17:52:39 -0700324 // Animate the dim into view as well
325 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
326 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
327 anim.setDuration(mConfig.taskBarEnterAnimDuration);
328 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
Winson Chungc6011de2014-06-30 18:04:55 -0700329 anim.addListener(new AnimatorListenerAdapter() {
330 @Override
331 public void onAnimationEnd(Animator animation) {
332 // Decrement the post animation trigger
333 ctx.postAnimationTrigger.decrement();
334 }
335 });
Winson Chunga26fb782014-06-12 17:52:39 -0700336 anim.start();
Winson Chungc6011de2014-06-30 18:04:55 -0700337 ctx.postAnimationTrigger.increment();
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
Winson Chung6057c912014-07-08 14:39:08 -0700344 int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700345 int delay = mConfig.taskBarEnterAnimDelay +
346 frontIndex * mConfig.taskViewEnterFromHomeDelay;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700347 if (Constants.DebugFlags.App.EnableShadows) {
348 animate().translationZ(transform.translationZ);
349 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700350 animate()
351 .scaleX(transform.scale)
352 .scaleY(transform.scale)
353 .translationY(transform.translationY)
354 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700355 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700356 .setInterpolator(mConfig.quintOutInterpolator)
357 .setDuration(mConfig.taskViewEnterFromHomeDuration)
358 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700359 .withEndAction(new Runnable() {
360 @Override
361 public void run() {
362 mEnableThumbnailClip.run();
363 // Decrement the post animation trigger
364 ctx.postAnimationTrigger.decrement();
365 }
366 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700367 .start();
Winson Chungc6011de2014-06-30 18:04:55 -0700368 ctx.postAnimationTrigger.increment();
Winson Chung7aceb9a2014-07-03 13:38:01 -0700369 } else {
370 // Otherwise, just enable the thumbnail clip
371 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700372 }
373 }
374
Winson Chung969f5862014-06-16 17:08:24 -0700375 /** Animates this task view as it leaves recents by pressing home. */
376 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700377 animate()
378 .translationY(ctx.offscreenTranslationY)
379 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700380 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700381 .setInterpolator(mConfig.fastOutLinearInInterpolator)
382 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700383 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700384 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700385 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700386 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800387 }
388
389 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700390 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
391 if (isLaunchingTask) {
392 // Disable the thumbnail clip and animate the bar out
393 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700394
Winson Chung969f5862014-06-16 17:08:24 -0700395 // Animate the dim
396 if (mDim > 0) {
397 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
398 anim.setDuration(mConfig.taskBarExitAnimDuration);
399 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
400 anim.start();
401 }
402 } else {
403 // Hide the dismiss button
404 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700405 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800406 }
407
Winson Chung9f49df92014-05-07 18:08:34 -0700408 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700409 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700410 // Disabling clipping with the stack while the view is animating away
411 setClipViewInStack(false);
412
Winson Chungd42a6cf2014-06-03 16:24:04 -0700413 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700414 .alpha(0f)
415 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700416 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700417 .setInterpolator(mConfig.fastOutSlowInInterpolator)
418 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700419 .withLayer()
420 .withEndAction(new Runnable() {
421 @Override
422 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700423 // We just throw this into a runnable because starting a view property
424 // animation using layers can cause inconsisten results if we try and
425 // update the layers while the animation is running. In some cases,
426 // the runnabled passed in may start an animation which also uses layers
427 // so we defer all this by posting this.
428 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700429
430 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700431 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700432 }
433 })
434 .start();
435 }
436
Winson Chung969f5862014-06-16 17:08:24 -0700437 /** Animates this task view if the user does not interact with the stack after a certain time. */
438 public void startNoUserInteractionAnimation() {
439 mBarView.startNoUserInteractionAnimation();
440 }
441
442 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
443 public void setNoUserInteractionState() {
444 mBarView.setNoUserInteractionState();
445 }
446
Winson Chung303e1ff2014-03-07 15:06:19 -0800447 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700448 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800449 getHitRect(outRect);
450 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
451 outRect.right = outRect.left + mThumbnailView.getRight();
452 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800453 return outRect;
454 }
455
456 /** Enable the hw layers on this task view */
457 void enableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700458 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700459 mBarView.enableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800460 }
461
462 /** Disable the hw layers on this task view */
463 void disableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700464 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700465 mBarView.disableHwLayers();
Winson Chung303e1ff2014-03-07 15:06:19 -0800466 }
467
Winson Chungffa2ec62014-07-03 15:54:42 -0700468 /** Sets the stubbed state of this task view. */
469 void setStubState(boolean isStub) {
470 if (!mIsStub && isStub) {
471 // This is now a stub task view, so clip to the bar height, hide the thumbnail
472 setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
473 mThumbnailView.setVisibility(View.INVISIBLE);
474 // Temporary
475 mBarView.mActivityDescription.setText("Stub");
476 } else if (mIsStub && !isStub) {
477 setClipBounds(null);
478 mThumbnailView.setVisibility(View.VISIBLE);
479 }
480 mIsStub = isStub;
481 }
482
Winson Chung5a9b0b02014-05-20 17:32:03 -0700483 /**
484 * Returns whether this view should be clipped, or any views below should clip against this
485 * view.
486 */
487 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700488 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700489 }
490
491 /** Sets whether this view should be clipped, or clipped against. */
492 void setClipViewInStack(boolean clip) {
493 if (clip != mClipViewInStack) {
494 mClipViewInStack = clip;
495 if (getParent() instanceof View) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700496 getHitRect(mTmpRect);
497 ((View) getParent()).invalidate(mTmpRect);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700498 }
499 }
500 }
501
Winson Chunga26fb782014-06-12 17:52:39 -0700502 /** Returns the current dim. */
503 public void setDim(int dim) {
504 mDim = dim;
505 postInvalidateOnAnimation();
506 }
507
508 /** Returns the current dim. */
509 public int getDim() {
510 return mDim;
511 }
512
513 /** Compute the dim as a function of the scale of this view. */
514 int getDimOverlayFromScale() {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700515 float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
Winson Chung14926462014-04-14 18:57:14 -0700516 float scaleRange = 1f - minScale;
517 float dim = (1f - getScaleX()) / scaleRange;
518 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700519 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
520 }
521
522 /** Update the dim as a function of the scale of this view. */
523 void updateDimOverlayFromScale() {
524 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700525 }
526
527 @Override
528 public void draw(Canvas canvas) {
Winson Chung14926462014-04-14 18:57:14 -0700529 super.draw(canvas);
530
531 // Apply the dim if necessary
532 if (mDim > 0) {
533 canvas.drawColor(mDim << 24);
534 }
535 }
536
Winson Chungbdbb87d2014-07-09 14:29:13 -0700537 @Override
538 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
539 if (mIsStub && (child == mThumbnailView)) {
540 // Skip the thumbnail view if we are in stub mode
541 return false;
542 }
543 return super.drawChild(canvas, child, drawingTime);
544 }
545
Winson Chung1e8d71b2014-05-16 17:05:22 -0700546 /**
547 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
548 * if the view is not currently visible, or we are in touch state (where we still want to keep
549 * track of focus).
550 */
551 public void setFocusedTask() {
552 mIsFocused = true;
Winson Chungc6011de2014-06-30 18:04:55 -0700553 // Workaround, we don't always want it focusable in touch mode, but we want the first task
554 // to be focused after the enter-recents animation, which can be triggered from either touch
555 // or keyboard
556 setFocusableInTouchMode(true);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700557 requestFocus();
Winson Chungc6011de2014-06-30 18:04:55 -0700558 setFocusableInTouchMode(false);
Winson Chung47a3e652014-05-21 16:03:42 -0700559 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700560 }
561
562 /**
563 * Updates the explicitly focused state when the view focus changes.
564 */
565 @Override
566 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
567 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
568 if (!gainFocus) {
569 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700570 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700571 }
572 }
573
574 /**
575 * Returns whether we have explicitly been focused.
576 */
577 public boolean isFocusedTask() {
578 return mIsFocused || isFocused();
579 }
580
Winson Chung4d7b0922014-03-13 17:14:17 -0700581 /**** TaskCallbacks Implementation ****/
582
Winson Chung04dfe0d2014-03-14 14:06:29 -0700583 /** Binds this task view to the task */
584 public void onTaskBound(Task t) {
585 mTask = t;
586 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800587 }
588
589 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700590 public void onTaskDataLoaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700591 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700592 // Bind each of the views to the new task data
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700593 mThumbnailView.rebindToTask(mTask);
594 mBarView.rebindToTask(mTask);
Winson Chung9f9679d2014-04-11 16:49:09 -0700595 // Rebind any listeners
Winson Chungffa2ec62014-07-03 15:54:42 -0700596 if (Constants.DebugFlags.App.EnableTaskFiltering) {
597 mBarView.mApplicationIcon.setOnClickListener(this);
598 }
Winson Chung54e297a2014-05-09 17:15:32 -0700599 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700600 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700601 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700602 mBarView.mApplicationIcon.setOnLongClickListener(this);
603 }
604 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700605 }
606 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700607 }
608
609 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700610 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700611 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700612 // Unbind each of the views from the task data and remove the task callback
613 mTask.setCallbacks(null);
614 mThumbnailView.unbindFromTask();
615 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700616 // Unbind any listeners
Winson Chungffa2ec62014-07-03 15:54:42 -0700617 if (Constants.DebugFlags.App.EnableTaskFiltering) {
618 mBarView.mApplicationIcon.setOnClickListener(null);
619 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700620 mBarView.mDismissButton.setOnClickListener(null);
621 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
622 mBarView.mApplicationIcon.setOnLongClickListener(null);
623 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700624 }
625 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700626 }
627
628 @Override
Winson Chung7ab650c2014-06-18 14:25:34 -0700629 public void onClick(final View v) {
630 // We purposely post the handler delayed to allow for the touch feedback to draw
631 final TaskView tv = this;
632 postDelayed(new Runnable() {
633 @Override
634 public void run() {
635 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700636 mCb.onTaskViewAppIconClicked(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700637 } else if (v == mBarView.mDismissButton) {
638 // Animate out the view and call the callback
639 startDeleteTaskAnimation(new Runnable() {
640 @Override
641 public void run() {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700642 mCb.onTaskViewDismissed(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700643 }
644 });
Winson Chung54e297a2014-05-09 17:15:32 -0700645 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700646 }
647 }, 125);
Winson Chung303e1ff2014-03-07 15:06:19 -0800648 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700649
650 @Override
651 public boolean onLongClick(View v) {
652 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700653 mCb.onTaskViewAppInfoClicked(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700654 return true;
655 }
656 return false;
657 }
Winson Chung7bb18852014-05-20 23:25:41 +0000658}