blob: 801de245a497be30294c1f69d4ec3c47dfb2cbdf [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 Chung2f2ca082014-04-03 18:05:29 -070034import com.android.systemui.recents.BakedBezierInterpolator;
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 Chung9f9679d2014-04-11 16:49:09 -070041public class TaskView extends FrameLayout implements View.OnClickListener,
42 Task.TaskCallbacks {
Winson Chung37c8d8e2014-03-24 14:53:07 -070043 /** The TaskView callbacks */
44 interface TaskViewCallbacks {
45 public void onTaskIconClicked(TaskView tv);
Winson Chung9f9679d2014-04-11 16:49:09 -070046 public void onTaskInfoPanelShown(TaskView tv);
47 public void onTaskInfoPanelHidden(TaskView tv);
48 public void onTaskAppInfoClicked(TaskView tv);
49
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 Chung303e1ff2014-03-07 15:06:19 -0800146 if (duration > 0) {
147 if (animateFromTransform != null) {
148 setTranslationY(animateFromTransform.translationY);
Winson Chung96e3bc12014-05-06 16:44:12 -0700149 setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
Winson Chung303e1ff2014-03-07 15:06:19 -0800150 setScaleX(animateFromTransform.scale);
151 setScaleY(animateFromTransform.scale);
Winson Chungc6a16232014-04-01 14:04:48 -0700152 setAlpha(animateFromTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800153 }
Winson Chungc6a16232014-04-01 14:04:48 -0700154 animate().translationY(toTransform.translationY)
Winson Chung96e3bc12014-05-06 16:44:12 -0700155 .translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)))
Winson Chungc6a16232014-04-01 14:04:48 -0700156 .scaleX(toTransform.scale)
157 .scaleY(toTransform.scale)
158 .alpha(toTransform.alpha)
Winson Chung303e1ff2014-03-07 15:06:19 -0800159 .setDuration(duration)
Winson Chung2f2ca082014-04-03 18:05:29 -0700160 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chungc6a16232014-04-01 14:04:48 -0700161 .withLayer()
Winson Chung14926462014-04-14 18:57:14 -0700162 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
163 @Override
164 public void onAnimationUpdate(ValueAnimator animation) {
165 updateDimOverlayFromScale();
166 }
167 })
Winson Chung303e1ff2014-03-07 15:06:19 -0800168 .start();
169 } else {
Winson Chungc6a16232014-04-01 14:04:48 -0700170 setTranslationY(toTransform.translationY);
Winson Chung96e3bc12014-05-06 16:44:12 -0700171 setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
Winson Chungc6a16232014-04-01 14:04:48 -0700172 setScaleX(toTransform.scale);
173 setScaleY(toTransform.scale);
174 setAlpha(toTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800175 }
Winson Chung14926462014-04-14 18:57:14 -0700176 updateDimOverlayFromScale();
177 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800178 }
179
180 /** Resets this view's properties */
181 void resetViewProperties() {
182 setTranslationX(0f);
183 setTranslationY(0f);
Winson Chung96e3bc12014-05-06 16:44:12 -0700184 setTranslationZ(0f);
Winson Chung303e1ff2014-03-07 15:06:19 -0800185 setScaleX(1f);
186 setScaleY(1f);
187 setAlpha(1f);
Winson Chung14926462014-04-14 18:57:14 -0700188 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800189 }
190
Winson Chung11ca76a52014-04-10 17:29:13 -0700191 /**
192 * When we are un/filtering, this method will set up the transform that we are animating to,
193 * in order to hide the task.
194 */
Winson Chungc6a16232014-04-01 14:04:48 -0700195 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
196 // Fade the view out and slide it away
197 toTransform.alpha = 0f;
198 toTransform.translationY += 200;
199 }
200
Winson Chung11ca76a52014-04-10 17:29:13 -0700201 /**
202 * When we are un/filtering, this method will setup the transform that we are animating from,
203 * in order to show the task.
204 */
Winson Chungc6a16232014-04-01 14:04:48 -0700205 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
206 // Fade the view in
207 fromTransform.alpha = 0f;
208 }
209
Winson Chung303e1ff2014-03-07 15:06:19 -0800210 /** Animates this task view as it enters recents */
211 public void animateOnEnterRecents() {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700212 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700213 mBarView.setAlpha(0f);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700214 mBarView.animate()
215 .alpha(1f)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700216 .setStartDelay(235)
Winson Chung2f2ca082014-04-03 18:05:29 -0700217 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chung0d767552014-04-09 14:33:23 -0700218 .setDuration(config.taskBarEnterAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700219 .withLayer()
220 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800221 }
222
223 /** Animates this task view as it exits recents */
224 public void animateOnLeavingRecents(final Runnable r) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700225 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700226 mBarView.animate()
227 .alpha(0f)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700228 .setStartDelay(0)
Winson Chung2f2ca082014-04-03 18:05:29 -0700229 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chungf5e22e72014-05-02 18:35:35 -0700230 .setDuration(config.taskBarExitAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700231 .withLayer()
Winson Chung0d767552014-04-09 14:33:23 -0700232 .withEndAction(new Runnable() {
233 @Override
234 public void run() {
235 post(r);
236 }
237 })
Winson Chung37c8d8e2014-03-24 14:53:07 -0700238 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800239 }
240
241 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700242 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800243 getHitRect(outRect);
244 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
245 outRect.right = outRect.left + mThumbnailView.getRight();
246 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800247 return outRect;
248 }
249
Winson Chung9f9679d2014-04-11 16:49:09 -0700250 /** Returns whether this task has an info pane visible */
251 boolean isInfoPaneVisible() {
252 return mTaskInfoPaneVisible;
253 }
254
255 /** Shows the info pane if it is not visible. */
256 void showInfoPane(Rect taskVisibleRect) {
257 if (mTaskInfoPaneVisible) return;
258
259 // Remove the bar view from the visible rect and update the info pane contents
260 taskVisibleRect.top += mBarView.getMeasuredHeight();
261 mInfoView.updateContents(taskVisibleRect);
262
263 // Show the info pane and animate it into view
264 mInfoView.setVisibility(View.VISIBLE);
265 mInfoView.animateCircularClip(mLastTouchDown, 0f, 1f, null, true);
266 mInfoView.setOnClickListener(this);
267 mTaskInfoPaneVisible = true;
268
269 // Notify any callbacks
270 if (mCb != null) {
271 mCb.onTaskInfoPanelShown(this);
272 }
273 }
274
275 /** Hides the info pane if it is visible. */
276 void hideInfoPane() {
277 if (!mTaskInfoPaneVisible) return;
278 RecentsConfiguration config = RecentsConfiguration.getInstance();
279
280 // Cancel any circular clip animation
281 mInfoView.cancelCircularClipAnimation();
282
283 // Animate the info pane out
284 mInfoView.animate()
285 .alpha(0f)
286 .setDuration(config.taskViewInfoPaneAnimDuration)
287 .setInterpolator(BakedBezierInterpolator.INSTANCE)
288 .withLayer()
289 .withEndAction(new Runnable() {
290 @Override
291 public void run() {
292 mInfoView.setVisibility(View.INVISIBLE);
293 mInfoView.setOnClickListener(null);
294
295 mInfoView.setAlpha(1f);
296 }
297 })
298 .start();
299 mTaskInfoPaneVisible = false;
300
301 // Notify any callbacks
302 if (mCb != null) {
303 mCb.onTaskInfoPanelHidden(this);
304 }
305 }
306
Winson Chung303e1ff2014-03-07 15:06:19 -0800307 /** Enable the hw layers on this task view */
308 void enableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800309 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
310 }
311
312 /** Disable the hw layers on this task view */
313 void disableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800314 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
315 }
316
Winson Chung14926462014-04-14 18:57:14 -0700317 /** Update the dim as a function of the scale of this view. */
318 void updateDimOverlayFromScale() {
319 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
320 float scaleRange = 1f - minScale;
321 float dim = (1f - getScaleX()) / scaleRange;
322 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
323 mDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
324 invalidate();
325 }
326
327 @Override
328 public void draw(Canvas canvas) {
329 // Apply the rounded rect clip path on the whole view
330 canvas.clipPath(mRoundedRectClipPath);
331
332 super.draw(canvas);
333
334 // Apply the dim if necessary
335 if (mDim > 0) {
336 canvas.drawColor(mDim << 24);
337 }
338 }
339
Winson Chung4d7b0922014-03-13 17:14:17 -0700340 /**** TaskCallbacks Implementation ****/
341
Winson Chung04dfe0d2014-03-14 14:06:29 -0700342 /** Binds this task view to the task */
343 public void onTaskBound(Task t) {
344 mTask = t;
345 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800346 }
347
348 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700349 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700350 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700351 // Bind each of the views to the new task data
352 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
353 mBarView.rebindToTask(mTask, reloadingTaskData);
Winson Chungf5e22e72014-05-02 18:35:35 -0700354 mInfoView.rebindToTask(mTask, reloadingTaskData);
Winson Chung9f9679d2014-04-11 16:49:09 -0700355 // Rebind any listeners
356 mBarView.mApplicationIcon.setOnClickListener(this);
357 mInfoView.mAppInfoButton.setOnClickListener(this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700358 }
359 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700360 }
361
362 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700363 public void onTaskDataUnloaded() {
Winson Chung9f9679d2014-04-11 16:49:09 -0700364 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700365 // Unbind each of the views from the task data and remove the task callback
366 mTask.setCallbacks(null);
367 mThumbnailView.unbindFromTask();
368 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700369 // Unbind any listeners
370 mBarView.mApplicationIcon.setOnClickListener(null);
371 mInfoView.mAppInfoButton.setOnClickListener(null);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700372 }
373 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700374 }
375
376 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -0800377 public void onClick(View v) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700378 if (v == mInfoView) {
Winson Chung96e3bc12014-05-06 16:44:12 -0700379 hideInfoPane();
Winson Chung9f9679d2014-04-11 16:49:09 -0700380 } else if (v == mBarView.mApplicationIcon) {
381 mCb.onTaskIconClicked(this);
382 } else if (v == mInfoView.mAppInfoButton) {
383 mCb.onTaskAppInfoClicked(this);
384 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800385 }
386}