blob: d3b79d648a4c7c6f679cff30398eb554d7e4dc69 [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;
23import android.graphics.Path;
Winson Chung9f9679d2014-04-11 16:49:09 -070024import android.graphics.Point;
Winson Chung303e1ff2014-03-07 15:06:19 -080025import android.graphics.Rect;
Winson Chung14926462014-04-14 18:57:14 -070026import android.graphics.RectF;
Winson Chung37c8d8e2014-03-24 14:53:07 -070027import android.util.AttributeSet;
Winson Chung9f9679d2014-04-11 16:49:09 -070028import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080029import android.view.View;
Winson Chung14926462014-04-14 18:57:14 -070030import android.view.animation.AccelerateInterpolator;
Winson Chung303e1ff2014-03-07 15:06:19 -080031import android.widget.FrameLayout;
Winson Chung37c8d8e2014-03-24 14:53:07 -070032import com.android.systemui.R;
Winson Chung2f2ca082014-04-03 18:05:29 -070033import com.android.systemui.recents.BakedBezierInterpolator;
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;
Winson Chung2f2ca082014-04-03 18:05:29 -070036import com.android.systemui.recents.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -080037import 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);
111 }
112
113 @Override
Winson Chung9f9679d2014-04-11 16:49:09 -0700114 public boolean onInterceptTouchEvent(MotionEvent ev) {
115 switch (ev.getAction()) {
116 case MotionEvent.ACTION_DOWN:
117 case MotionEvent.ACTION_MOVE:
118 mLastTouchDown.set((int) ev.getX(), (int) ev.getY());
119 break;
120 }
121 return super.onInterceptTouchEvent(ev);
122 }
123
Winson Chung04dfe0d2014-03-14 14:06:29 -0700124 /** Set callback */
125 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800126 mCb = cb;
127 }
128
Winson Chung303e1ff2014-03-07 15:06:19 -0800129 /** Gets the task */
130 Task getTask() {
131 return mTask;
132 }
133
134 /** Synchronizes this view's properties with the task's transform */
Winson Chungc6a16232014-04-01 14:04:48 -0700135 void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
136 TaskViewTransform toTransform, int duration) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800137 if (duration > 0) {
138 if (animateFromTransform != null) {
139 setTranslationY(animateFromTransform.translationY);
140 setScaleX(animateFromTransform.scale);
141 setScaleY(animateFromTransform.scale);
Winson Chungc6a16232014-04-01 14:04:48 -0700142 setAlpha(animateFromTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800143 }
Winson Chungc6a16232014-04-01 14:04:48 -0700144 animate().translationY(toTransform.translationY)
145 .scaleX(toTransform.scale)
146 .scaleY(toTransform.scale)
147 .alpha(toTransform.alpha)
Winson Chung303e1ff2014-03-07 15:06:19 -0800148 .setDuration(duration)
Winson Chung2f2ca082014-04-03 18:05:29 -0700149 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chungc6a16232014-04-01 14:04:48 -0700150 .withLayer()
Winson Chung14926462014-04-14 18:57:14 -0700151 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
152 @Override
153 public void onAnimationUpdate(ValueAnimator animation) {
154 updateDimOverlayFromScale();
155 }
156 })
Winson Chung303e1ff2014-03-07 15:06:19 -0800157 .start();
158 } else {
Winson Chungc6a16232014-04-01 14:04:48 -0700159 setTranslationY(toTransform.translationY);
160 setScaleX(toTransform.scale);
161 setScaleY(toTransform.scale);
162 setAlpha(toTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800163 }
Winson Chung14926462014-04-14 18:57:14 -0700164 updateDimOverlayFromScale();
165 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800166 }
167
168 /** Resets this view's properties */
169 void resetViewProperties() {
170 setTranslationX(0f);
171 setTranslationY(0f);
172 setScaleX(1f);
173 setScaleY(1f);
174 setAlpha(1f);
Winson Chung14926462014-04-14 18:57:14 -0700175 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800176 }
177
Winson Chung11ca76a52014-04-10 17:29:13 -0700178 /**
179 * When we are un/filtering, this method will set up the transform that we are animating to,
180 * in order to hide the task.
181 */
Winson Chungc6a16232014-04-01 14:04:48 -0700182 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
183 // Fade the view out and slide it away
184 toTransform.alpha = 0f;
185 toTransform.translationY += 200;
186 }
187
Winson Chung11ca76a52014-04-10 17:29:13 -0700188 /**
189 * When we are un/filtering, this method will setup the transform that we are animating from,
190 * in order to show the task.
191 */
Winson Chungc6a16232014-04-01 14:04:48 -0700192 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
193 // Fade the view in
194 fromTransform.alpha = 0f;
195 }
196
Winson Chung303e1ff2014-03-07 15:06:19 -0800197 /** Animates this task view as it enters recents */
198 public void animateOnEnterRecents() {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700199 RecentsConfiguration config = RecentsConfiguration.getInstance();
200 int translate = config.pxFromDp(10);
201 mBarView.setScaleX(1.25f);
202 mBarView.setScaleY(1.25f);
203 mBarView.setAlpha(0f);
204 mBarView.setTranslationX(translate / 2);
205 mBarView.setTranslationY(-translate);
206 mBarView.animate()
207 .alpha(1f)
208 .scaleX(1f)
209 .scaleY(1f)
210 .translationX(0)
211 .translationY(0)
212 .setStartDelay(235)
Winson Chung2f2ca082014-04-03 18:05:29 -0700213 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chung0d767552014-04-09 14:33:23 -0700214 .setDuration(config.taskBarEnterAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700215 .withLayer()
216 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800217 }
218
219 /** Animates this task view as it exits recents */
220 public void animateOnLeavingRecents(final Runnable r) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700221 RecentsConfiguration config = RecentsConfiguration.getInstance();
222 int translate = config.pxFromDp(10);
223 mBarView.animate()
224 .alpha(0f)
225 .scaleX(1.1f)
226 .scaleY(1.1f)
227 .translationX(translate / 2)
228 .translationY(-translate)
229 .setStartDelay(0)
Winson Chung2f2ca082014-04-03 18:05:29 -0700230 .setInterpolator(BakedBezierInterpolator.INSTANCE)
231 .setDuration(Utilities.calculateTranslationAnimationDuration(translate))
Winson Chung37c8d8e2014-03-24 14:53:07 -0700232 .withLayer()
Winson Chung0d767552014-04-09 14:33:23 -0700233 .withEndAction(new Runnable() {
234 @Override
235 public void run() {
236 post(r);
237 }
238 })
Winson Chung37c8d8e2014-03-24 14:53:07 -0700239 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800240 }
241
242 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700243 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800244 getHitRect(outRect);
245 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
246 outRect.right = outRect.left + mThumbnailView.getRight();
247 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800248 return outRect;
249 }
250
Winson Chung9f9679d2014-04-11 16:49:09 -0700251 /** Returns whether this task has an info pane visible */
252 boolean isInfoPaneVisible() {
253 return mTaskInfoPaneVisible;
254 }
255
256 /** Shows the info pane if it is not visible. */
257 void showInfoPane(Rect taskVisibleRect) {
258 if (mTaskInfoPaneVisible) return;
259
260 // Remove the bar view from the visible rect and update the info pane contents
261 taskVisibleRect.top += mBarView.getMeasuredHeight();
262 mInfoView.updateContents(taskVisibleRect);
263
264 // Show the info pane and animate it into view
265 mInfoView.setVisibility(View.VISIBLE);
266 mInfoView.animateCircularClip(mLastTouchDown, 0f, 1f, null, true);
267 mInfoView.setOnClickListener(this);
268 mTaskInfoPaneVisible = true;
269
270 // Notify any callbacks
271 if (mCb != null) {
272 mCb.onTaskInfoPanelShown(this);
273 }
274 }
275
276 /** Hides the info pane if it is visible. */
277 void hideInfoPane() {
278 if (!mTaskInfoPaneVisible) return;
279 RecentsConfiguration config = RecentsConfiguration.getInstance();
280
281 // Cancel any circular clip animation
282 mInfoView.cancelCircularClipAnimation();
283
284 // Animate the info pane out
285 mInfoView.animate()
286 .alpha(0f)
287 .setDuration(config.taskViewInfoPaneAnimDuration)
288 .setInterpolator(BakedBezierInterpolator.INSTANCE)
289 .withLayer()
290 .withEndAction(new Runnable() {
291 @Override
292 public void run() {
293 mInfoView.setVisibility(View.INVISIBLE);
294 mInfoView.setOnClickListener(null);
295
296 mInfoView.setAlpha(1f);
297 }
298 })
299 .start();
300 mTaskInfoPaneVisible = false;
301
302 // Notify any callbacks
303 if (mCb != null) {
304 mCb.onTaskInfoPanelHidden(this);
305 }
306 }
307
Winson Chung303e1ff2014-03-07 15:06:19 -0800308 /** Enable the hw layers on this task view */
309 void enableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800310 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
311 }
312
313 /** Disable the hw layers on this task view */
314 void disableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800315 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
316 }
317
Winson Chung14926462014-04-14 18:57:14 -0700318 /** Update the dim as a function of the scale of this view. */
319 void updateDimOverlayFromScale() {
320 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
321 float scaleRange = 1f - minScale;
322 float dim = (1f - getScaleX()) / scaleRange;
323 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
324 mDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
325 invalidate();
326 }
327
328 @Override
329 public void draw(Canvas canvas) {
330 // Apply the rounded rect clip path on the whole view
331 canvas.clipPath(mRoundedRectClipPath);
332
333 super.draw(canvas);
334
335 // Apply the dim if necessary
336 if (mDim > 0) {
337 canvas.drawColor(mDim << 24);
338 }
339 }
340
Winson Chung4d7b0922014-03-13 17:14:17 -0700341 /**** TaskCallbacks Implementation ****/
342
Winson Chung04dfe0d2014-03-14 14:06:29 -0700343 /** Binds this task view to the task */
344 public void onTaskBound(Task t) {
345 mTask = t;
346 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800347 }
348
349 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700350 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700351 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700352 // Bind each of the views to the new task data
353 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
354 mBarView.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) {
379 // Do nothing
380 } 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}