blob: ab148632698c5b28c6851fcbdadcf1eeaa781ea3 [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;
Chris Craik7b7ca3c2014-07-11 13:32:19 -070030import android.view.ViewOutlineProvider;
Winson Chungb5ddfc32014-06-13 17:23:12 -070031import android.view.ViewPropertyAnimator;
Winson Chung14926462014-04-14 18:57:14 -070032import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080033import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070034import com.android.systemui.R;
Winson Chungffa2ec62014-07-03 15:54:42 -070035import com.android.systemui.recents.misc.Console;
Winson Chung14926462014-04-14 18:57:14 -070036import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080037import com.android.systemui.recents.RecentsConfiguration;
38import com.android.systemui.recents.model.Task;
Winson Chung1f24c7e2014-07-11 17:06:48 -070039import com.android.systemui.recents.model.TaskStack;
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 {
Winson Chung7aceb9a2014-07-03 13:38:01 -070047 public void onTaskViewAppIconClicked(TaskView tv);
48 public void onTaskViewAppInfoClicked(TaskView tv);
Winson Chung93748a12014-07-13 17:43:31 -070049 public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
Winson Chung7aceb9a2014-07-03 13:38:01 -070050 public void onTaskViewDismissed(TaskView tv);
Winson Chung93748a12014-07-13 17:43:31 -070051 public void onTaskViewClipStateChanged(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070052 }
53
Winson Chungd42a6cf2014-06-03 16:24:04 -070054 RecentsConfiguration mConfig;
55
Winson Chung1f24c7e2014-07-11 17:06:48 -070056 int mFooterHeight;
57 int mMaxFooterHeight;
58 ObjectAnimator mFooterAnimator;
59
Winson Chung14926462014-04-14 18:57:14 -070060 int mDim;
61 int mMaxDim;
Winson Chung6057c912014-07-08 14:39:08 -070062 AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
Winson Chung14926462014-04-14 18:57:14 -070063
Winson Chung303e1ff2014-03-07 15:06:19 -080064 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070065 boolean mTaskDataLoaded;
Winson Chung1e8d71b2014-05-16 17:05:22 -070066 boolean mIsFocused;
Winson Chungffa2ec62014-07-03 15:54:42 -070067 boolean mIsStub;
Winson Chung5a9b0b02014-05-20 17:32:03 -070068 boolean mClipViewInStack;
Winson Chung93748a12014-07-13 17:43:31 -070069 int mClipFromBottom;
Winson Chungc9567c02014-06-16 20:25:51 -070070 Paint mLayerPaint = new Paint();
Winson Chung37c8d8e2014-03-24 14:53:07 -070071
72 TaskThumbnailView mThumbnailView;
73 TaskBarView mBarView;
Winson Chung1f24c7e2014-07-11 17:06:48 -070074 View mLockToAppButtonView;
Winson Chung37c8d8e2014-03-24 14:53:07 -070075 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080076
Winson Chungd42a6cf2014-06-03 16:24:04 -070077 // Optimizations
78 ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
79 new ValueAnimator.AnimatorUpdateListener() {
80 @Override
81 public void onAnimationUpdate(ValueAnimator animation) {
82 updateDimOverlayFromScale();
83 }
84 };
Winson Chung743d5c92014-06-13 10:14:53 -070085 Runnable mEnableThumbnailClip = new Runnable() {
86 @Override
87 public void run() {
88 mThumbnailView.updateTaskBarClip(mBarView);
89 }
90 };
91 Runnable mDisableThumbnailClip = new Runnable() {
92 @Override
93 public void run() {
94 mThumbnailView.disableClipTaskBarView();
95 }
96 };
Winson Chungd42a6cf2014-06-03 16:24:04 -070097
Winson Chung37c8d8e2014-03-24 14:53:07 -070098
99 public TaskView(Context context) {
100 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -0800101 }
102
Winson Chung37c8d8e2014-03-24 14:53:07 -0700103 public TaskView(Context context, AttributeSet attrs) {
104 this(context, attrs, 0);
105 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800106
Winson Chung37c8d8e2014-03-24 14:53:07 -0700107 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
108 this(context, attrs, defStyleAttr, 0);
109 }
110
111 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
112 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700113 mConfig = RecentsConfiguration.getInstance();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700114 mMaxFooterHeight = mConfig.taskViewLockToAppButtonHeight;
Winson Chung14926462014-04-14 18:57:14 -0700115 setWillNotDraw(false);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700116 setClipToOutline(true);
Winson Chunga26fb782014-06-12 17:52:39 -0700117 setDim(getDim());
Winson Chung1f24c7e2014-07-11 17:06:48 -0700118 setFooterHeight(getFooterHeight());
Chris Craik7b7ca3c2014-07-11 13:32:19 -0700119 setOutlineProvider(new ViewOutlineProvider() {
120 @Override
121 public boolean getOutline(View view, Outline outline) {
Winson Chung93748a12014-07-13 17:43:31 -0700122 // The current height is measured with the footer, so account for the footer height
123 // and the current clip (in the stack)
124 int height = getMeasuredHeight() - mClipFromBottom - mMaxFooterHeight + mFooterHeight;
Chris Craik7b7ca3c2014-07-11 13:32:19 -0700125 outline.setRoundRect(0, 0, getWidth(), height,
126 mConfig.taskViewRoundedCornerRadiusPx);
127 return true;
128 }
129 });
Winson Chung37c8d8e2014-03-24 14:53:07 -0700130 }
131
132 @Override
133 protected void onFinishInflate() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700134 mMaxDim = mConfig.taskStackMaxDim;
Winson Chung14926462014-04-14 18:57:14 -0700135
Winson Chung5a9b0b02014-05-20 17:32:03 -0700136 // By default, all views are clipped to other views in their stack
137 mClipViewInStack = true;
138
Winson Chung37c8d8e2014-03-24 14:53:07 -0700139 // Bind the views
Winson Chung37c8d8e2014-03-24 14:53:07 -0700140 mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
Winson Chung743d5c92014-06-13 10:14:53 -0700141 mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700142 mLockToAppButtonView = findViewById(R.id.lock_to_app);
Winson Chung9f9679d2014-04-11 16:49:09 -0700143
Winson Chung37c8d8e2014-03-24 14:53:07 -0700144 if (mTaskDataLoaded) {
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700145 onTaskDataLoaded();
Winson Chung303e1ff2014-03-07 15:06:19 -0800146 }
147 }
148
Winson Chung9f9679d2014-04-11 16:49:09 -0700149 @Override
Winson Chung14926462014-04-14 18:57:14 -0700150 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Winson Chung1f24c7e2014-07-11 17:06:48 -0700151 int width = MeasureSpec.getSize(widthMeasureSpec);
152 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chung14926462014-04-14 18:57:14 -0700153
Winson Chung1f24c7e2014-07-11 17:06:48 -0700154 // Measure the bar view, thumbnail, and lock-to-app buttons
155 mBarView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
156 MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
157 mLockToAppButtonView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
158 MeasureSpec.makeMeasureSpec(mConfig.taskViewLockToAppButtonHeight,
159 MeasureSpec.EXACTLY));
160 // Measure the thumbnail height to be the same as the width
161 mThumbnailView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
162 MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY));
163 setMeasuredDimension(width, height);
Winson Chung14926462014-04-14 18:57:14 -0700164 }
165
Winson Chung04dfe0d2014-03-14 14:06:29 -0700166 /** Set callback */
167 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800168 mCb = cb;
169 }
170
Winson Chung303e1ff2014-03-07 15:06:19 -0800171 /** Gets the task */
172 Task getTask() {
173 return mTask;
174 }
175
176 /** Synchronizes this view's properties with the task's transform */
Winson Chungb5ddfc32014-06-13 17:23:12 -0700177 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700178 if (Console.Enabled) {
179 Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
180 "duration: " + duration, Console.AnsiPurple);
181 }
Winson Chung96e3bc12014-05-06 16:44:12 -0700182
Winson Chung54e297a2014-05-09 17:15:32 -0700183 // Update the bar view
Winson Chungb5ddfc32014-06-13 17:23:12 -0700184 mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
Winson Chung54e297a2014-05-09 17:15:32 -0700185
Winson Chungb5ddfc32014-06-13 17:23:12 -0700186 // Check to see if any properties have changed, and update the task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800187 if (duration > 0) {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700188 ViewPropertyAnimator anim = animate();
189 boolean useLayers = false;
190
191 // Animate to the final state
192 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
193 anim.translationY(toTransform.translationY);
Winson Chung303e1ff2014-03-07 15:06:19 -0800194 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700195 if (Constants.DebugFlags.App.EnableShadows &&
196 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
197 anim.translationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700198 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700199 if (toTransform.hasScaleChangedFrom(getScaleX())) {
200 anim.scaleX(toTransform.scale)
Winson Chungc6a16232014-04-01 14:04:48 -0700201 .scaleY(toTransform.scale)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700202 .setUpdateListener(mUpdateDimListener);
203 useLayers = true;
204 }
205 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
206 // Use layers if we animate alpha
207 anim.alpha(toTransform.alpha);
208 useLayers = true;
209 }
210 if (useLayers) {
211 anim.withLayer();
212 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700213 anim.setStartDelay(toTransform.startDelay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700214 .setDuration(duration)
215 .setInterpolator(mConfig.fastOutSlowInInterpolator)
216 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800217 } else {
Winson Chungb5ddfc32014-06-13 17:23:12 -0700218 // Set the changed properties
219 if (toTransform.hasTranslationYChangedFrom(getTranslationY())) {
220 setTranslationY(toTransform.translationY);
221 }
222 if (Constants.DebugFlags.App.EnableShadows &&
223 toTransform.hasTranslationZChangedFrom(getTranslationZ())) {
Winson Chungb13c46e2014-06-09 18:33:08 -0700224 setTranslationZ(toTransform.translationZ);
Winson Chung814086d2014-05-07 15:01:14 -0700225 }
Winson Chungb5ddfc32014-06-13 17:23:12 -0700226 if (toTransform.hasScaleChangedFrom(getScaleX())) {
227 setScaleX(toTransform.scale);
228 setScaleY(toTransform.scale);
229 updateDimOverlayFromScale();
230 }
231 if (toTransform.hasAlphaChangedFrom(getAlpha())) {
232 setAlpha(toTransform.alpha);
233 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800234 }
235 }
236
237 /** Resets this view's properties */
238 void resetViewProperties() {
239 setTranslationX(0f);
240 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700241 if (Constants.DebugFlags.App.EnableShadows) {
242 setTranslationZ(0f);
243 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800244 setScaleX(1f);
245 setScaleY(1f);
246 setAlpha(1f);
Winson Chunga26fb782014-06-12 17:52:39 -0700247 setDim(0);
Winson Chung14926462014-04-14 18:57:14 -0700248 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800249 }
250
Winson Chung11ca76a52014-04-10 17:29:13 -0700251 /**
252 * When we are un/filtering, this method will set up the transform that we are animating to,
253 * in order to hide the task.
254 */
Winson Chungc6a16232014-04-01 14:04:48 -0700255 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
256 // Fade the view out and slide it away
257 toTransform.alpha = 0f;
258 toTransform.translationY += 200;
Winson Chung7ab650c2014-06-18 14:25:34 -0700259 toTransform.translationZ = 0;
Winson Chungc6a16232014-04-01 14:04:48 -0700260 }
261
Winson Chung11ca76a52014-04-10 17:29:13 -0700262 /**
263 * When we are un/filtering, this method will setup the transform that we are animating from,
264 * in order to show the task.
265 */
Winson Chungc6a16232014-04-01 14:04:48 -0700266 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
267 // Fade the view in
268 fromTransform.alpha = 0f;
269 }
270
Winson Chung24cf1522014-05-29 12:03:33 -0700271 /** Prepares this task view for the enter-recents animations. This is called earlier in the
272 * first layout because the actual animation into recents may take a long time. */
Winson Chung083baf92014-07-11 10:32:42 -0700273 public void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, int offsetY,
274 int offscreenY) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700275 if (mConfig.launchedFromAppWithScreenshot) {
Winson Chung083baf92014-07-11 10:32:42 -0700276 if (isTaskViewLaunchTargetTask) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700277 // Hide the task view as we are going to animate the full screenshot into view
278 // and then replace it with this view once we are done
279 setVisibility(View.INVISIBLE);
280 // Also hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700281 mBarView.prepareEnterRecentsAnimation();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700282 } else {
283 // Top align the task views
284 setTranslationY(offsetY);
285 setScaleX(1f);
286 setScaleY(1f);
287 }
288
289 } else if (mConfig.launchedFromAppWithThumbnail) {
Winson Chung083baf92014-07-11 10:32:42 -0700290 if (isTaskViewLaunchTargetTask) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700291 // Hide the front most task bar view so we can animate it in
Winson Chung969f5862014-06-16 17:08:24 -0700292 mBarView.prepareEnterRecentsAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700293 // Set the dim to 0 so we can animate it in
294 setDim(0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700295 }
296
297 } else if (mConfig.launchedFromHome) {
298 // Move the task view off screen (below) so we can animate it in
299 setTranslationY(offscreenY);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700300 if (Constants.DebugFlags.App.EnableShadows) {
301 setTranslationZ(0);
302 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700303 setScaleX(1f);
304 setScaleY(1f);
305 }
Winson Chung24cf1522014-05-29 12:03:33 -0700306 }
307
Winson Chung303e1ff2014-03-07 15:06:19 -0800308 /** Animates this task view as it enters recents */
Winson Chungc6011de2014-06-30 18:04:55 -0700309 public void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
Winson Chung6057c912014-07-08 14:39:08 -0700310 TaskViewTransform transform = ctx.currentTaskTransform;
Winson Chungd42a6cf2014-06-03 16:24:04 -0700311
312 if (mConfig.launchedFromAppWithScreenshot) {
Winson Chung083baf92014-07-11 10:32:42 -0700313 if (ctx.isCurrentTaskLaunchTarget) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700314 // Animate the full screenshot down first, before swapping with this task view
Winson Chung6057c912014-07-08 14:39:08 -0700315 ctx.fullScreenshotView.animateOnEnterRecents(ctx, new Runnable() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700316 @Override
317 public void run() {
318 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700319 mBarView.startEnterRecentsAnimation(0, mEnableThumbnailClip);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700320 setVisibility(View.VISIBLE);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700321 // Animate the footer into view
322 animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration, 0);
Winson Chungc6011de2014-06-30 18:04:55 -0700323 // Decrement the post animation trigger
324 ctx.postAnimationTrigger.decrement();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700325 }
326 });
327 } else {
328 // Animate the tasks down behind the full screenshot
329 animate()
330 .scaleX(transform.scale)
331 .scaleY(transform.scale)
332 .translationY(transform.translationY)
333 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700334 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700335 .setInterpolator(mConfig.linearOutSlowInInterpolator)
336 .setDuration(475)
337 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700338 .withEndAction(new Runnable() {
339 @Override
340 public void run() {
341 mEnableThumbnailClip.run();
342 // Decrement the post animation trigger
343 ctx.postAnimationTrigger.decrement();
344 }
345 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700346 .start();
347 }
Winson Chungc6011de2014-06-30 18:04:55 -0700348 ctx.postAnimationTrigger.increment();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700349
350 } else if (mConfig.launchedFromAppWithThumbnail) {
Winson Chung083baf92014-07-11 10:32:42 -0700351 if (ctx.isCurrentTaskLaunchTarget) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700352 // Animate the task bar of the first task view
Winson Chung969f5862014-06-16 17:08:24 -0700353 mBarView.startEnterRecentsAnimation(mConfig.taskBarEnterAnimDelay, mEnableThumbnailClip);
Winson Chung743d5c92014-06-13 10:14:53 -0700354
Winson Chunga26fb782014-06-12 17:52:39 -0700355 // Animate the dim into view as well
356 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
357 anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
358 anim.setDuration(mConfig.taskBarEnterAnimDuration);
359 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
Winson Chungc6011de2014-06-30 18:04:55 -0700360 anim.addListener(new AnimatorListenerAdapter() {
361 @Override
362 public void onAnimationEnd(Animator animation) {
363 // Decrement the post animation trigger
364 ctx.postAnimationTrigger.decrement();
365 }
366 });
Winson Chunga26fb782014-06-12 17:52:39 -0700367 anim.start();
Winson Chungc6011de2014-06-30 18:04:55 -0700368 ctx.postAnimationTrigger.increment();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700369
370 // Animate the footer into view
371 animateFooterVisibility(true, mConfig.taskBarEnterAnimDuration,
372 mConfig.taskBarEnterAnimDelay);
Winson Chung743d5c92014-06-13 10:14:53 -0700373 } else {
374 mEnableThumbnailClip.run();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700375 }
376
377 } else if (mConfig.launchedFromHome) {
378 // Animate the tasks up
Winson Chung6057c912014-07-08 14:39:08 -0700379 int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700380 int delay = mConfig.taskBarEnterAnimDelay +
381 frontIndex * mConfig.taskViewEnterFromHomeDelay;
Winson Chung7aceb9a2014-07-03 13:38:01 -0700382 if (Constants.DebugFlags.App.EnableShadows) {
383 animate().translationZ(transform.translationZ);
384 }
Winson Chungd42a6cf2014-06-03 16:24:04 -0700385 animate()
386 .scaleX(transform.scale)
387 .scaleY(transform.scale)
388 .translationY(transform.translationY)
389 .setStartDelay(delay)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700390 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700391 .setInterpolator(mConfig.quintOutInterpolator)
392 .setDuration(mConfig.taskViewEnterFromHomeDuration)
393 .withLayer()
Winson Chungc6011de2014-06-30 18:04:55 -0700394 .withEndAction(new Runnable() {
395 @Override
396 public void run() {
397 mEnableThumbnailClip.run();
398 // Decrement the post animation trigger
399 ctx.postAnimationTrigger.decrement();
400 }
401 })
Winson Chungd42a6cf2014-06-03 16:24:04 -0700402 .start();
Winson Chungc6011de2014-06-30 18:04:55 -0700403 ctx.postAnimationTrigger.increment();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700404
405 // Animate the footer into view
406 animateFooterVisibility(true, mConfig.taskViewEnterFromHomeDuration,
407 mConfig.taskBarEnterAnimDelay);
Winson Chung7aceb9a2014-07-03 13:38:01 -0700408 } else {
409 // Otherwise, just enable the thumbnail clip
410 mEnableThumbnailClip.run();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700411
412 // Animate the footer into view
413 animateFooterVisibility(true, 0, 0);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700414 }
415 }
416
Winson Chung969f5862014-06-16 17:08:24 -0700417 /** Animates this task view as it leaves recents by pressing home. */
418 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700419 animate()
420 .translationY(ctx.offscreenTranslationY)
421 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700422 .setUpdateListener(null)
Winson Chungad6f2762014-06-13 09:41:09 -0700423 .setInterpolator(mConfig.fastOutLinearInInterpolator)
424 .setDuration(mConfig.taskViewExitToHomeDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700425 .withLayer()
Winson Chungd42a6cf2014-06-03 16:24:04 -0700426 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
Winson Chung37c8d8e2014-03-24 14:53:07 -0700427 .start();
Winson Chungd42a6cf2014-06-03 16:24:04 -0700428 ctx.postAnimationTrigger.increment();
Winson Chung303e1ff2014-03-07 15:06:19 -0800429 }
430
431 /** Animates this task view as it exits recents */
Winson Chung969f5862014-06-16 17:08:24 -0700432 public void startLaunchTaskAnimation(final Runnable r, boolean isLaunchingTask) {
433 if (isLaunchingTask) {
434 // Disable the thumbnail clip and animate the bar out
435 mBarView.startLaunchTaskAnimation(mDisableThumbnailClip, r);
Winson Chunga26fb782014-06-12 17:52:39 -0700436
Winson Chung969f5862014-06-16 17:08:24 -0700437 // Animate the dim
438 if (mDim > 0) {
439 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
440 anim.setDuration(mConfig.taskBarExitAnimDuration);
441 anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
442 anim.start();
443 }
444 } else {
445 // Hide the dismiss button
446 mBarView.startLaunchTaskDismissAnimation();
Winson Chunga26fb782014-06-12 17:52:39 -0700447 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800448 }
449
Winson Chung9f49df92014-05-07 18:08:34 -0700450 /** Animates the deletion of this task view */
Winson Chung969f5862014-06-16 17:08:24 -0700451 public void startDeleteTaskAnimation(final Runnable r) {
Winson Chung5a9b0b02014-05-20 17:32:03 -0700452 // Disabling clipping with the stack while the view is animating away
453 setClipViewInStack(false);
454
Winson Chungd42a6cf2014-06-03 16:24:04 -0700455 animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
Winson Chung9f49df92014-05-07 18:08:34 -0700456 .alpha(0f)
457 .setStartDelay(0)
Winson Chungb5ddfc32014-06-13 17:23:12 -0700458 .setUpdateListener(null)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700459 .setInterpolator(mConfig.fastOutSlowInInterpolator)
460 .setDuration(mConfig.taskViewRemoveAnimDuration)
Winson Chung9f49df92014-05-07 18:08:34 -0700461 .withLayer()
462 .withEndAction(new Runnable() {
463 @Override
464 public void run() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700465 // We just throw this into a runnable because starting a view property
466 // animation using layers can cause inconsisten results if we try and
467 // update the layers while the animation is running. In some cases,
468 // the runnabled passed in may start an animation which also uses layers
469 // so we defer all this by posting this.
470 r.run();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700471
472 // Re-enable clipping with the stack (we will reuse this view)
Winson Chungd42a6cf2014-06-03 16:24:04 -0700473 setClipViewInStack(true);
Winson Chung9f49df92014-05-07 18:08:34 -0700474 }
475 })
476 .start();
477 }
478
Winson Chung969f5862014-06-16 17:08:24 -0700479 /** Animates this task view if the user does not interact with the stack after a certain time. */
480 public void startNoUserInteractionAnimation() {
481 mBarView.startNoUserInteractionAnimation();
482 }
483
484 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
485 public void setNoUserInteractionState() {
486 mBarView.setNoUserInteractionState();
487 }
488
Winson Chung303e1ff2014-03-07 15:06:19 -0800489 /** Enable the hw layers on this task view */
490 void enableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700491 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700492 mBarView.enableHwLayers();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700493 mLockToAppButtonView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
Winson Chung303e1ff2014-03-07 15:06:19 -0800494 }
495
496 /** Disable the hw layers on this task view */
497 void disableHwLayers() {
Winson Chungc9567c02014-06-16 20:25:51 -0700498 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chungd42a6cf2014-06-03 16:24:04 -0700499 mBarView.disableHwLayers();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700500 mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
Winson Chung303e1ff2014-03-07 15:06:19 -0800501 }
502
Winson Chung93748a12014-07-13 17:43:31 -0700503 /** Sets the stubbed state of this task view.
Winson Chungffa2ec62014-07-03 15:54:42 -0700504 void setStubState(boolean isStub) {
505 if (!mIsStub && isStub) {
506 // This is now a stub task view, so clip to the bar height, hide the thumbnail
507 setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
508 mThumbnailView.setVisibility(View.INVISIBLE);
509 // Temporary
510 mBarView.mActivityDescription.setText("Stub");
511 } else if (mIsStub && !isStub) {
512 setClipBounds(null);
513 mThumbnailView.setVisibility(View.VISIBLE);
514 }
515 mIsStub = isStub;
Winson Chung93748a12014-07-13 17:43:31 -0700516 } */
Winson Chungffa2ec62014-07-03 15:54:42 -0700517
Winson Chung5a9b0b02014-05-20 17:32:03 -0700518 /**
519 * Returns whether this view should be clipped, or any views below should clip against this
520 * view.
521 */
522 boolean shouldClipViewInStack() {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700523 return mClipViewInStack && (getVisibility() == View.VISIBLE);
Winson Chung5a9b0b02014-05-20 17:32:03 -0700524 }
525
526 /** Sets whether this view should be clipped, or clipped against. */
527 void setClipViewInStack(boolean clip) {
528 if (clip != mClipViewInStack) {
529 mClipViewInStack = clip;
Winson Chung93748a12014-07-13 17:43:31 -0700530 mCb.onTaskViewClipStateChanged(this);
531 }
532 }
533
534 void setClipFromBottom(int clipFromBottom) {
535 clipFromBottom = Math.max(0, Math.min(getMeasuredHeight(), clipFromBottom));
536 if (mClipFromBottom != clipFromBottom) {
537 mClipFromBottom = clipFromBottom;
538 invalidateOutline();
Winson Chung5a9b0b02014-05-20 17:32:03 -0700539 }
540 }
541
Winson Chung1f24c7e2014-07-11 17:06:48 -0700542 /** Sets the footer height. */
Winson Chung93748a12014-07-13 17:43:31 -0700543 public void setFooterHeight(int footerHeight) {
544 if (footerHeight != mFooterHeight) {
545 mFooterHeight = footerHeight;
546 invalidateOutline();
547 invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
548 getMeasuredHeight());
549 }
Winson Chung1f24c7e2014-07-11 17:06:48 -0700550 }
551
552 /** Gets the footer height. */
553 public int getFooterHeight() {
554 return mFooterHeight;
555 }
556
557 /** Animates the footer into and out of view. */
558 public void animateFooterVisibility(boolean visible, int duration, int delay) {
559 if (!mTask.canLockToTask) return;
560 if (mMaxFooterHeight <= 0) return;
561
562 if (mFooterAnimator != null) {
563 mFooterAnimator.removeAllListeners();
564 mFooterAnimator.cancel();
565 }
566 int height = visible ? mMaxFooterHeight : 0;
567 if (visible && mLockToAppButtonView.getVisibility() != View.VISIBLE) {
568 if (duration > 0) {
569 setFooterHeight(0);
570 } else {
571 setFooterHeight(mMaxFooterHeight);
572 }
573 mLockToAppButtonView.setVisibility(View.VISIBLE);
574 }
575 if (duration > 0) {
576 mFooterAnimator = ObjectAnimator.ofInt(this, "footerHeight", height);
577 mFooterAnimator.setDuration(duration);
578 mFooterAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
579 if (!visible) {
580 mFooterAnimator.addListener(new AnimatorListenerAdapter() {
581 @Override
582 public void onAnimationEnd(Animator animation) {
583 mLockToAppButtonView.setVisibility(View.INVISIBLE);
584 }
585 });
586 }
587 mFooterAnimator.start();
588 } else {
589 if (!visible) {
590 mLockToAppButtonView.setVisibility(View.INVISIBLE);
591 }
592 }
593 }
594
Winson Chunga26fb782014-06-12 17:52:39 -0700595 /** Returns the current dim. */
596 public void setDim(int dim) {
597 mDim = dim;
598 postInvalidateOnAnimation();
599 }
600
601 /** Returns the current dim. */
602 public int getDim() {
603 return mDim;
604 }
605
606 /** Compute the dim as a function of the scale of this view. */
607 int getDimOverlayFromScale() {
Winson Chungd7b2cb12014-06-26 15:08:50 -0700608 float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
Winson Chung14926462014-04-14 18:57:14 -0700609 float scaleRange = 1f - minScale;
610 float dim = (1f - getScaleX()) / scaleRange;
611 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
Winson Chunga26fb782014-06-12 17:52:39 -0700612 return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
613 }
614
615 /** Update the dim as a function of the scale of this view. */
616 void updateDimOverlayFromScale() {
617 setDim(getDimOverlayFromScale());
Winson Chung14926462014-04-14 18:57:14 -0700618 }
619
620 @Override
621 public void draw(Canvas canvas) {
Winson Chung14926462014-04-14 18:57:14 -0700622 super.draw(canvas);
623
624 // Apply the dim if necessary
625 if (mDim > 0) {
626 canvas.drawColor(mDim << 24);
627 }
628 }
629
Winson Chungbdbb87d2014-07-09 14:29:13 -0700630 @Override
631 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
632 if (mIsStub && (child == mThumbnailView)) {
633 // Skip the thumbnail view if we are in stub mode
634 return false;
635 }
636 return super.drawChild(canvas, child, drawingTime);
637 }
638
Winson Chung1e8d71b2014-05-16 17:05:22 -0700639 /**
640 * Sets the focused task explicitly. We need a separate flag because requestFocus() won't happen
641 * if the view is not currently visible, or we are in touch state (where we still want to keep
642 * track of focus).
643 */
644 public void setFocusedTask() {
645 mIsFocused = true;
Winson Chungc6011de2014-06-30 18:04:55 -0700646 // Workaround, we don't always want it focusable in touch mode, but we want the first task
647 // to be focused after the enter-recents animation, which can be triggered from either touch
648 // or keyboard
649 setFocusableInTouchMode(true);
Winson Chung1e8d71b2014-05-16 17:05:22 -0700650 requestFocus();
Winson Chungc6011de2014-06-30 18:04:55 -0700651 setFocusableInTouchMode(false);
Winson Chung47a3e652014-05-21 16:03:42 -0700652 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700653 }
654
655 /**
656 * Updates the explicitly focused state when the view focus changes.
657 */
658 @Override
659 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
660 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
661 if (!gainFocus) {
662 mIsFocused = false;
Winson Chung47a3e652014-05-21 16:03:42 -0700663 invalidate();
Winson Chung1e8d71b2014-05-16 17:05:22 -0700664 }
665 }
666
667 /**
668 * Returns whether we have explicitly been focused.
669 */
670 public boolean isFocusedTask() {
671 return mIsFocused || isFocused();
672 }
673
Winson Chung4d7b0922014-03-13 17:14:17 -0700674 /**** TaskCallbacks Implementation ****/
675
Winson Chung04dfe0d2014-03-14 14:06:29 -0700676 /** Binds this task view to the task */
677 public void onTaskBound(Task t) {
678 mTask = t;
679 mTask.setCallbacks(this);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700680 if (getMeasuredWidth() == 0) {
Winson Chung93748a12014-07-13 17:43:31 -0700681 // If we haven't yet measured, we should just set the footer height with any animation
Winson Chung1f24c7e2014-07-11 17:06:48 -0700682 animateFooterVisibility(t.canLockToTask, 0, 0);
683 } else {
684 animateFooterVisibility(t.canLockToTask, mConfig.taskViewLockToAppLongAnimDuration, 0);
685 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800686 }
687
688 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700689 public void onTaskDataLoaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700690 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700691 // Bind each of the views to the new task data
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700692 mThumbnailView.rebindToTask(mTask);
693 mBarView.rebindToTask(mTask);
Winson Chung9f9679d2014-04-11 16:49:09 -0700694 // Rebind any listeners
Winson Chungffa2ec62014-07-03 15:54:42 -0700695 if (Constants.DebugFlags.App.EnableTaskFiltering) {
696 mBarView.mApplicationIcon.setOnClickListener(this);
697 }
Winson Chung54e297a2014-05-09 17:15:32 -0700698 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700699 mLockToAppButtonView.setOnClickListener(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700700 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
Winson Chungd42a6cf2014-06-03 16:24:04 -0700701 if (mConfig.developerOptionsEnabled) {
Winson Chung6cb485f2014-05-19 10:30:43 -0700702 mBarView.mApplicationIcon.setOnLongClickListener(this);
703 }
704 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700705 }
706 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700707 }
708
709 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700710 public void onTaskDataUnloaded() {
Winson Chung863db8a2014-05-20 14:27:39 -0700711 if (mThumbnailView != null && mBarView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700712 // Unbind each of the views from the task data and remove the task callback
713 mTask.setCallbacks(null);
714 mThumbnailView.unbindFromTask();
715 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700716 // Unbind any listeners
Winson Chungffa2ec62014-07-03 15:54:42 -0700717 if (Constants.DebugFlags.App.EnableTaskFiltering) {
718 mBarView.mApplicationIcon.setOnClickListener(null);
719 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700720 mBarView.mDismissButton.setOnClickListener(null);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700721 mLockToAppButtonView.setOnClickListener(null);
Winson Chung6cb485f2014-05-19 10:30:43 -0700722 if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
723 mBarView.mApplicationIcon.setOnLongClickListener(null);
724 }
Winson Chung37c8d8e2014-03-24 14:53:07 -0700725 }
726 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700727 }
728
Winson Chung1f24c7e2014-07-11 17:06:48 -0700729 /** Enables/disables handling touch on this task view. */
730 void setTouchEnabled(boolean enabled) {
731 setOnClickListener(enabled ? this : null);
732 }
733
Winson Chung4d7b0922014-03-13 17:14:17 -0700734 @Override
Winson Chung7ab650c2014-06-18 14:25:34 -0700735 public void onClick(final View v) {
736 // We purposely post the handler delayed to allow for the touch feedback to draw
737 final TaskView tv = this;
738 postDelayed(new Runnable() {
739 @Override
740 public void run() {
741 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700742 mCb.onTaskViewAppIconClicked(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700743 } else if (v == mBarView.mDismissButton) {
744 // Animate out the view and call the callback
745 startDeleteTaskAnimation(new Runnable() {
746 @Override
747 public void run() {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700748 mCb.onTaskViewDismissed(tv);
Winson Chung7ab650c2014-06-18 14:25:34 -0700749 }
750 });
Winson Chung1f24c7e2014-07-11 17:06:48 -0700751 // Hide the footer
752 tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration, 0);
753 } else if (v == tv || v == mLockToAppButtonView) {
754 mCb.onTaskViewClicked(tv, tv.getTask(), (v == mLockToAppButtonView));
Winson Chung54e297a2014-05-09 17:15:32 -0700755 }
Winson Chung7ab650c2014-06-18 14:25:34 -0700756 }
757 }, 125);
Winson Chung303e1ff2014-03-07 15:06:19 -0800758 }
Winson Chung6cb485f2014-05-19 10:30:43 -0700759
760 @Override
761 public boolean onLongClick(View v) {
762 if (v == mBarView.mApplicationIcon) {
Winson Chung7aceb9a2014-07-03 13:38:01 -0700763 mCb.onTaskViewAppInfoClicked(this);
Winson Chung6cb485f2014-05-19 10:30:43 -0700764 return true;
765 }
766 return false;
767 }
Winson Chung7bb18852014-05-20 23:25:41 +0000768}