blob: 5fad6298640bd676349cb9b68f4f2fedbfd6512e [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 Chung14926462014-04-14 18:57:14 -070019import android.animation.TimeInterpolator;
20import android.animation.ValueAnimator;
Winson Chung303e1ff2014-03-07 15:06:19 -080021import android.content.Context;
Winson Chung14926462014-04-14 18:57:14 -070022import android.graphics.Canvas;
Winson Chung96e3bc12014-05-06 16:44:12 -070023import android.graphics.Outline;
Winson Chung14926462014-04-14 18:57:14 -070024import android.graphics.Path;
Winson Chung9f9679d2014-04-11 16:49:09 -070025import android.graphics.Point;
Winson Chung303e1ff2014-03-07 15:06:19 -080026import android.graphics.Rect;
Winson Chung14926462014-04-14 18:57:14 -070027import android.graphics.RectF;
Winson Chung37c8d8e2014-03-24 14:53:07 -070028import android.util.AttributeSet;
Winson Chung9f9679d2014-04-11 16:49:09 -070029import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080030import android.view.View;
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 Chung14926462014-04-14 18:57:14 -070034import com.android.systemui.recents.Constants;
Winson Chung303e1ff2014-03-07 15:06:19 -080035import com.android.systemui.recents.RecentsConfiguration;
36import com.android.systemui.recents.model.Task;
Winson Chung303e1ff2014-03-07 15:06:19 -080037
Winson Chung303e1ff2014-03-07 15:06:19 -080038
Winson Chung37c8d8e2014-03-24 14:53:07 -070039/* A task view */
Winson Chung9f9679d2014-04-11 16:49:09 -070040public class TaskView extends FrameLayout implements View.OnClickListener,
41 Task.TaskCallbacks {
Winson Chung37c8d8e2014-03-24 14:53:07 -070042 /** The TaskView callbacks */
43 interface TaskViewCallbacks {
44 public void onTaskIconClicked(TaskView tv);
Winson Chung9f9679d2014-04-11 16:49:09 -070045 public void onTaskInfoPanelShown(TaskView tv);
46 public void onTaskInfoPanelHidden(TaskView tv);
47 public void onTaskAppInfoClicked(TaskView tv);
Winson Chung54e297a2014-05-09 17:15:32 -070048 public void onTaskDismissed(TaskView tv);
Winson Chung9f9679d2014-04-11 16:49:09 -070049
Winson Chung37c8d8e2014-03-24 14:53:07 -070050 // public void onTaskViewReboundToTask(TaskView tv, Task t);
51 }
52
Winson Chung14926462014-04-14 18:57:14 -070053 int mDim;
54 int mMaxDim;
55 TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
56
Winson Chung303e1ff2014-03-07 15:06:19 -080057 Task mTask;
Winson Chung37c8d8e2014-03-24 14:53:07 -070058 boolean mTaskDataLoaded;
Winson Chung9f9679d2014-04-11 16:49:09 -070059 boolean mTaskInfoPaneVisible;
60 Point mLastTouchDown = new Point();
Winson Chung14926462014-04-14 18:57:14 -070061 Path mRoundedRectClipPath = new Path();
Winson Chung37c8d8e2014-03-24 14:53:07 -070062
63 TaskThumbnailView mThumbnailView;
64 TaskBarView mBarView;
Winson Chung9f9679d2014-04-11 16:49:09 -070065 TaskInfoView mInfoView;
Winson Chung37c8d8e2014-03-24 14:53:07 -070066 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -080067
Winson Chung37c8d8e2014-03-24 14:53:07 -070068
69 public TaskView(Context context) {
70 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -080071 }
72
Winson Chung37c8d8e2014-03-24 14:53:07 -070073 public TaskView(Context context, AttributeSet attrs) {
74 this(context, attrs, 0);
75 }
Winson Chung303e1ff2014-03-07 15:06:19 -080076
Winson Chung37c8d8e2014-03-24 14:53:07 -070077 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
78 this(context, attrs, defStyleAttr, 0);
79 }
80
81 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
82 super(context, attrs, defStyleAttr, defStyleRes);
Winson Chung14926462014-04-14 18:57:14 -070083 setWillNotDraw(false);
Winson Chung37c8d8e2014-03-24 14:53:07 -070084 }
85
86 @Override
87 protected void onFinishInflate() {
Winson Chung14926462014-04-14 18:57:14 -070088 RecentsConfiguration config = RecentsConfiguration.getInstance();
89 mMaxDim = config.taskStackMaxDim;
90
Winson Chung37c8d8e2014-03-24 14:53:07 -070091 // Bind the views
92 mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail);
93 mBarView = (TaskBarView) findViewById(R.id.task_view_bar);
Winson Chung9f9679d2014-04-11 16:49:09 -070094 mInfoView = (TaskInfoView) findViewById(R.id.task_view_info_pane);
95
Winson Chung37c8d8e2014-03-24 14:53:07 -070096 if (mTaskDataLoaded) {
97 onTaskDataLoaded(false);
Winson Chung303e1ff2014-03-07 15:06:19 -080098 }
99 }
100
Winson Chung9f9679d2014-04-11 16:49:09 -0700101 @Override
Winson Chung14926462014-04-14 18:57:14 -0700102 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
103 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
104
105 // Update the rounded rect clip path
106 RecentsConfiguration config = RecentsConfiguration.getInstance();
107 float radius = config.taskViewRoundedCornerRadiusPx;
108 mRoundedRectClipPath.reset();
109 mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
110 radius, radius, Path.Direction.CW);
Winson Chung96e3bc12014-05-06 16:44:12 -0700111
112 // Update the outline
113 Outline o = new Outline();
114 o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), radius);
115 setOutline(o);
Winson Chung14926462014-04-14 18:57:14 -0700116 }
117
118 @Override
Winson Chung9f9679d2014-04-11 16:49:09 -0700119 public boolean onInterceptTouchEvent(MotionEvent ev) {
120 switch (ev.getAction()) {
121 case MotionEvent.ACTION_DOWN:
122 case MotionEvent.ACTION_MOVE:
123 mLastTouchDown.set((int) ev.getX(), (int) ev.getY());
124 break;
125 }
126 return super.onInterceptTouchEvent(ev);
127 }
128
Winson Chung04dfe0d2014-03-14 14:06:29 -0700129 /** Set callback */
130 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800131 mCb = cb;
132 }
133
Winson Chung303e1ff2014-03-07 15:06:19 -0800134 /** Gets the task */
135 Task getTask() {
136 return mTask;
137 }
138
139 /** Synchronizes this view's properties with the task's transform */
Winson Chungc6a16232014-04-01 14:04:48 -0700140 void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
141 TaskViewTransform toTransform, int duration) {
Winson Chung96e3bc12014-05-06 16:44:12 -0700142 RecentsConfiguration config = RecentsConfiguration.getInstance();
143 int minZ = config.taskViewTranslationZMinPx;
144 int incZ = config.taskViewTranslationZIncrementPx;
145
Winson Chung54e297a2014-05-09 17:15:32 -0700146 // Update the bar view
147 mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
148
149 // Update this task view
Winson Chung303e1ff2014-03-07 15:06:19 -0800150 if (duration > 0) {
151 if (animateFromTransform != null) {
152 setTranslationY(animateFromTransform.translationY);
Winson Chung814086d2014-05-07 15:01:14 -0700153 if (Constants.DebugFlags.App.EnableShadows) {
154 setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
155 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800156 setScaleX(animateFromTransform.scale);
157 setScaleY(animateFromTransform.scale);
Winson Chungc6a16232014-04-01 14:04:48 -0700158 setAlpha(animateFromTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800159 }
Winson Chung814086d2014-05-07 15:01:14 -0700160 if (Constants.DebugFlags.App.EnableShadows) {
161 animate().translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
162 }
Winson Chungc6a16232014-04-01 14:04:48 -0700163 animate().translationY(toTransform.translationY)
164 .scaleX(toTransform.scale)
165 .scaleY(toTransform.scale)
166 .alpha(toTransform.alpha)
Winson Chung303e1ff2014-03-07 15:06:19 -0800167 .setDuration(duration)
Winson Chung918c0722014-05-08 15:04:23 -0700168 .setInterpolator(config.defaultBezierInterpolator)
Winson Chungc6a16232014-04-01 14:04:48 -0700169 .withLayer()
Winson Chung14926462014-04-14 18:57:14 -0700170 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
171 @Override
172 public void onAnimationUpdate(ValueAnimator animation) {
173 updateDimOverlayFromScale();
174 }
175 })
Winson Chung303e1ff2014-03-07 15:06:19 -0800176 .start();
177 } else {
Winson Chungc6a16232014-04-01 14:04:48 -0700178 setTranslationY(toTransform.translationY);
Winson Chung814086d2014-05-07 15:01:14 -0700179 if (Constants.DebugFlags.App.EnableShadows) {
180 setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
181 }
Winson Chungc6a16232014-04-01 14:04:48 -0700182 setScaleX(toTransform.scale);
183 setScaleY(toTransform.scale);
184 setAlpha(toTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800185 }
Winson Chung14926462014-04-14 18:57:14 -0700186 updateDimOverlayFromScale();
187 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800188 }
189
190 /** Resets this view's properties */
191 void resetViewProperties() {
192 setTranslationX(0f);
193 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700194 if (Constants.DebugFlags.App.EnableShadows) {
195 setTranslationZ(0f);
196 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800197 setScaleX(1f);
198 setScaleY(1f);
199 setAlpha(1f);
Winson Chung14926462014-04-14 18:57:14 -0700200 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800201 }
202
Winson Chung11ca76a52014-04-10 17:29:13 -0700203 /**
204 * When we are un/filtering, this method will set up the transform that we are animating to,
205 * in order to hide the task.
206 */
Winson Chungc6a16232014-04-01 14:04:48 -0700207 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
208 // Fade the view out and slide it away
209 toTransform.alpha = 0f;
210 toTransform.translationY += 200;
211 }
212
Winson Chung11ca76a52014-04-10 17:29:13 -0700213 /**
214 * When we are un/filtering, this method will setup the transform that we are animating from,
215 * in order to show the task.
216 */
Winson Chungc6a16232014-04-01 14:04:48 -0700217 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
218 // Fade the view in
219 fromTransform.alpha = 0f;
220 }
221
Winson Chung303e1ff2014-03-07 15:06:19 -0800222 /** Animates this task view as it enters recents */
223 public void animateOnEnterRecents() {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700224 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700225 mBarView.setAlpha(0f);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700226 mBarView.animate()
227 .alpha(1f)
Winson Chung918c0722014-05-08 15:04:23 -0700228 .setStartDelay(250)
229 .setInterpolator(config.defaultBezierInterpolator)
Winson Chung0d767552014-04-09 14:33:23 -0700230 .setDuration(config.taskBarEnterAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700231 .withLayer()
232 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800233 }
234
235 /** Animates this task view as it exits recents */
236 public void animateOnLeavingRecents(final Runnable r) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700237 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700238 mBarView.animate()
239 .alpha(0f)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700240 .setStartDelay(0)
Winson Chung918c0722014-05-08 15:04:23 -0700241 .setInterpolator(config.defaultBezierInterpolator)
Winson Chungf5e22e72014-05-02 18:35:35 -0700242 .setDuration(config.taskBarExitAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700243 .withLayer()
Winson Chung0d767552014-04-09 14:33:23 -0700244 .withEndAction(new Runnable() {
245 @Override
246 public void run() {
247 post(r);
248 }
249 })
Winson Chung37c8d8e2014-03-24 14:53:07 -0700250 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800251 }
252
Winson Chung9f49df92014-05-07 18:08:34 -0700253 /** Animates the deletion of this task view */
254 public void animateRemoval(final Runnable r) {
255 RecentsConfiguration config = RecentsConfiguration.getInstance();
256 animate().translationX(config.taskViewRemoveAnimTranslationXPx)
257 .alpha(0f)
258 .setStartDelay(0)
Winson Chung918c0722014-05-08 15:04:23 -0700259 .setInterpolator(config.defaultBezierInterpolator)
Winson Chung9f49df92014-05-07 18:08:34 -0700260 .setDuration(config.taskViewRemoveAnimDuration)
261 .withLayer()
262 .withEndAction(new Runnable() {
263 @Override
264 public void run() {
265 post(r);
266 }
267 })
268 .start();
269 }
270
Winson Chung303e1ff2014-03-07 15:06:19 -0800271 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700272 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800273 getHitRect(outRect);
274 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
275 outRect.right = outRect.left + mThumbnailView.getRight();
276 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800277 return outRect;
278 }
279
Winson Chung9f9679d2014-04-11 16:49:09 -0700280 /** Returns whether this task has an info pane visible */
281 boolean isInfoPaneVisible() {
282 return mTaskInfoPaneVisible;
283 }
284
285 /** Shows the info pane if it is not visible. */
286 void showInfoPane(Rect taskVisibleRect) {
287 if (mTaskInfoPaneVisible) return;
288
289 // Remove the bar view from the visible rect and update the info pane contents
290 taskVisibleRect.top += mBarView.getMeasuredHeight();
291 mInfoView.updateContents(taskVisibleRect);
292
293 // Show the info pane and animate it into view
294 mInfoView.setVisibility(View.VISIBLE);
295 mInfoView.animateCircularClip(mLastTouchDown, 0f, 1f, null, true);
296 mInfoView.setOnClickListener(this);
297 mTaskInfoPaneVisible = true;
298
299 // Notify any callbacks
300 if (mCb != null) {
301 mCb.onTaskInfoPanelShown(this);
302 }
303 }
304
305 /** Hides the info pane if it is visible. */
306 void hideInfoPane() {
307 if (!mTaskInfoPaneVisible) return;
308 RecentsConfiguration config = RecentsConfiguration.getInstance();
309
310 // Cancel any circular clip animation
311 mInfoView.cancelCircularClipAnimation();
312
313 // Animate the info pane out
314 mInfoView.animate()
315 .alpha(0f)
316 .setDuration(config.taskViewInfoPaneAnimDuration)
Winson Chung918c0722014-05-08 15:04:23 -0700317 .setInterpolator(config.defaultBezierInterpolator)
Winson Chung9f9679d2014-04-11 16:49:09 -0700318 .withLayer()
319 .withEndAction(new Runnable() {
320 @Override
321 public void run() {
322 mInfoView.setVisibility(View.INVISIBLE);
323 mInfoView.setOnClickListener(null);
324
325 mInfoView.setAlpha(1f);
326 }
327 })
328 .start();
329 mTaskInfoPaneVisible = false;
330
331 // Notify any callbacks
332 if (mCb != null) {
333 mCb.onTaskInfoPanelHidden(this);
334 }
335 }
336
Winson Chung303e1ff2014-03-07 15:06:19 -0800337 /** Enable the hw layers on this task view */
338 void enableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800339 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
340 }
341
342 /** Disable the hw layers on this task view */
343 void disableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800344 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
345 }
346
Winson Chung14926462014-04-14 18:57:14 -0700347 /** Update the dim as a function of the scale of this view. */
348 void updateDimOverlayFromScale() {
349 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
350 float scaleRange = 1f - minScale;
351 float dim = (1f - getScaleX()) / scaleRange;
352 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
353 mDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
354 invalidate();
355 }
356
357 @Override
358 public void draw(Canvas canvas) {
359 // Apply the rounded rect clip path on the whole view
360 canvas.clipPath(mRoundedRectClipPath);
361
362 super.draw(canvas);
363
364 // Apply the dim if necessary
365 if (mDim > 0) {
366 canvas.drawColor(mDim << 24);
367 }
368 }
369
Winson Chung4d7b0922014-03-13 17:14:17 -0700370 /**** TaskCallbacks Implementation ****/
371
Winson Chung04dfe0d2014-03-14 14:06:29 -0700372 /** Binds this task view to the task */
373 public void onTaskBound(Task t) {
374 mTask = t;
375 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800376 }
377
378 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700379 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700380 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700381 // Bind each of the views to the new task data
382 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
383 mBarView.rebindToTask(mTask, reloadingTaskData);
Winson Chungf5e22e72014-05-02 18:35:35 -0700384 mInfoView.rebindToTask(mTask, reloadingTaskData);
Winson Chung9f9679d2014-04-11 16:49:09 -0700385 // Rebind any listeners
386 mBarView.mApplicationIcon.setOnClickListener(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700387 mBarView.mDismissButton.setOnClickListener(this);
Winson Chung9f9679d2014-04-11 16:49:09 -0700388 mInfoView.mAppInfoButton.setOnClickListener(this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700389 }
390 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700391 }
392
393 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700394 public void onTaskDataUnloaded() {
Winson Chung9f9679d2014-04-11 16:49:09 -0700395 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700396 // Unbind each of the views from the task data and remove the task callback
397 mTask.setCallbacks(null);
398 mThumbnailView.unbindFromTask();
399 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700400 // Unbind any listeners
401 mBarView.mApplicationIcon.setOnClickListener(null);
402 mInfoView.mAppInfoButton.setOnClickListener(null);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700403 }
404 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700405 }
406
407 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -0800408 public void onClick(View v) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700409 if (v == mInfoView) {
Winson Chung96e3bc12014-05-06 16:44:12 -0700410 hideInfoPane();
Winson Chung9f9679d2014-04-11 16:49:09 -0700411 } else if (v == mBarView.mApplicationIcon) {
412 mCb.onTaskIconClicked(this);
Winson Chung54e297a2014-05-09 17:15:32 -0700413 } else if (v == mBarView.mDismissButton) {
414 // Animate out the view and call the callback
415 final TaskView tv = this;
416 animateRemoval(new Runnable() {
417 @Override
418 public void run() {
419 mCb.onTaskDismissed(tv);
420 }
421 });
Winson Chung9f9679d2014-04-11 16:49:09 -0700422 } else if (v == mInfoView.mAppInfoButton) {
423 mCb.onTaskAppInfoClicked(this);
424 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800425 }
426}