blob: 45c2fae22cb93488629fbdea454d9ab3aa3b5cfc [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 Chung814086d2014-05-07 15:01:14 -0700149 if (Constants.DebugFlags.App.EnableShadows) {
150 setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
151 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800152 setScaleX(animateFromTransform.scale);
153 setScaleY(animateFromTransform.scale);
Winson Chungc6a16232014-04-01 14:04:48 -0700154 setAlpha(animateFromTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800155 }
Winson Chung814086d2014-05-07 15:01:14 -0700156 if (Constants.DebugFlags.App.EnableShadows) {
157 animate().translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
158 }
Winson Chungc6a16232014-04-01 14:04:48 -0700159 animate().translationY(toTransform.translationY)
160 .scaleX(toTransform.scale)
161 .scaleY(toTransform.scale)
162 .alpha(toTransform.alpha)
Winson Chung303e1ff2014-03-07 15:06:19 -0800163 .setDuration(duration)
Winson Chung2f2ca082014-04-03 18:05:29 -0700164 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chungc6a16232014-04-01 14:04:48 -0700165 .withLayer()
Winson Chung14926462014-04-14 18:57:14 -0700166 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
167 @Override
168 public void onAnimationUpdate(ValueAnimator animation) {
169 updateDimOverlayFromScale();
170 }
171 })
Winson Chung303e1ff2014-03-07 15:06:19 -0800172 .start();
173 } else {
Winson Chungc6a16232014-04-01 14:04:48 -0700174 setTranslationY(toTransform.translationY);
Winson Chung814086d2014-05-07 15:01:14 -0700175 if (Constants.DebugFlags.App.EnableShadows) {
176 setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
177 }
Winson Chungc6a16232014-04-01 14:04:48 -0700178 setScaleX(toTransform.scale);
179 setScaleY(toTransform.scale);
180 setAlpha(toTransform.alpha);
Winson Chung303e1ff2014-03-07 15:06:19 -0800181 }
Winson Chung14926462014-04-14 18:57:14 -0700182 updateDimOverlayFromScale();
183 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800184 }
185
186 /** Resets this view's properties */
187 void resetViewProperties() {
188 setTranslationX(0f);
189 setTranslationY(0f);
Winson Chung814086d2014-05-07 15:01:14 -0700190 if (Constants.DebugFlags.App.EnableShadows) {
191 setTranslationZ(0f);
192 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800193 setScaleX(1f);
194 setScaleY(1f);
195 setAlpha(1f);
Winson Chung14926462014-04-14 18:57:14 -0700196 invalidate();
Winson Chung303e1ff2014-03-07 15:06:19 -0800197 }
198
Winson Chung11ca76a52014-04-10 17:29:13 -0700199 /**
200 * When we are un/filtering, this method will set up the transform that we are animating to,
201 * in order to hide the task.
202 */
Winson Chungc6a16232014-04-01 14:04:48 -0700203 void prepareTaskTransformForFilterTaskHidden(TaskViewTransform toTransform) {
204 // Fade the view out and slide it away
205 toTransform.alpha = 0f;
206 toTransform.translationY += 200;
207 }
208
Winson Chung11ca76a52014-04-10 17:29:13 -0700209 /**
210 * When we are un/filtering, this method will setup the transform that we are animating from,
211 * in order to show the task.
212 */
Winson Chungc6a16232014-04-01 14:04:48 -0700213 void prepareTaskTransformForFilterTaskVisible(TaskViewTransform fromTransform) {
214 // Fade the view in
215 fromTransform.alpha = 0f;
216 }
217
Winson Chung303e1ff2014-03-07 15:06:19 -0800218 /** Animates this task view as it enters recents */
219 public void animateOnEnterRecents() {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700220 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700221 mBarView.setAlpha(0f);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700222 mBarView.animate()
223 .alpha(1f)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700224 .setStartDelay(235)
Winson Chung2f2ca082014-04-03 18:05:29 -0700225 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chung0d767552014-04-09 14:33:23 -0700226 .setDuration(config.taskBarEnterAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700227 .withLayer()
228 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800229 }
230
231 /** Animates this task view as it exits recents */
232 public void animateOnLeavingRecents(final Runnable r) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700233 RecentsConfiguration config = RecentsConfiguration.getInstance();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700234 mBarView.animate()
235 .alpha(0f)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700236 .setStartDelay(0)
Winson Chung2f2ca082014-04-03 18:05:29 -0700237 .setInterpolator(BakedBezierInterpolator.INSTANCE)
Winson Chungf5e22e72014-05-02 18:35:35 -0700238 .setDuration(config.taskBarExitAnimDuration)
Winson Chung37c8d8e2014-03-24 14:53:07 -0700239 .withLayer()
Winson Chung0d767552014-04-09 14:33:23 -0700240 .withEndAction(new Runnable() {
241 @Override
242 public void run() {
243 post(r);
244 }
245 })
Winson Chung37c8d8e2014-03-24 14:53:07 -0700246 .start();
Winson Chung303e1ff2014-03-07 15:06:19 -0800247 }
248
249 /** Returns the rect we want to clip (it may not be the full rect) */
Winson Chung0d767552014-04-09 14:33:23 -0700250 Rect getClippingRect(Rect outRect) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800251 getHitRect(outRect);
252 // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
253 outRect.right = outRect.left + mThumbnailView.getRight();
254 outRect.bottom = outRect.top + mThumbnailView.getBottom();
Winson Chung303e1ff2014-03-07 15:06:19 -0800255 return outRect;
256 }
257
Winson Chung9f9679d2014-04-11 16:49:09 -0700258 /** Returns whether this task has an info pane visible */
259 boolean isInfoPaneVisible() {
260 return mTaskInfoPaneVisible;
261 }
262
263 /** Shows the info pane if it is not visible. */
264 void showInfoPane(Rect taskVisibleRect) {
265 if (mTaskInfoPaneVisible) return;
266
267 // Remove the bar view from the visible rect and update the info pane contents
268 taskVisibleRect.top += mBarView.getMeasuredHeight();
269 mInfoView.updateContents(taskVisibleRect);
270
271 // Show the info pane and animate it into view
272 mInfoView.setVisibility(View.VISIBLE);
273 mInfoView.animateCircularClip(mLastTouchDown, 0f, 1f, null, true);
274 mInfoView.setOnClickListener(this);
275 mTaskInfoPaneVisible = true;
276
277 // Notify any callbacks
278 if (mCb != null) {
279 mCb.onTaskInfoPanelShown(this);
280 }
281 }
282
283 /** Hides the info pane if it is visible. */
284 void hideInfoPane() {
285 if (!mTaskInfoPaneVisible) return;
286 RecentsConfiguration config = RecentsConfiguration.getInstance();
287
288 // Cancel any circular clip animation
289 mInfoView.cancelCircularClipAnimation();
290
291 // Animate the info pane out
292 mInfoView.animate()
293 .alpha(0f)
294 .setDuration(config.taskViewInfoPaneAnimDuration)
295 .setInterpolator(BakedBezierInterpolator.INSTANCE)
296 .withLayer()
297 .withEndAction(new Runnable() {
298 @Override
299 public void run() {
300 mInfoView.setVisibility(View.INVISIBLE);
301 mInfoView.setOnClickListener(null);
302
303 mInfoView.setAlpha(1f);
304 }
305 })
306 .start();
307 mTaskInfoPaneVisible = false;
308
309 // Notify any callbacks
310 if (mCb != null) {
311 mCb.onTaskInfoPanelHidden(this);
312 }
313 }
314
Winson Chung303e1ff2014-03-07 15:06:19 -0800315 /** Enable the hw layers on this task view */
316 void enableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800317 mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
318 }
319
320 /** Disable the hw layers on this task view */
321 void disableHwLayers() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800322 mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
323 }
324
Winson Chung14926462014-04-14 18:57:14 -0700325 /** Update the dim as a function of the scale of this view. */
326 void updateDimOverlayFromScale() {
327 float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
328 float scaleRange = 1f - minScale;
329 float dim = (1f - getScaleX()) / scaleRange;
330 dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
331 mDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
332 invalidate();
333 }
334
335 @Override
336 public void draw(Canvas canvas) {
337 // Apply the rounded rect clip path on the whole view
338 canvas.clipPath(mRoundedRectClipPath);
339
340 super.draw(canvas);
341
342 // Apply the dim if necessary
343 if (mDim > 0) {
344 canvas.drawColor(mDim << 24);
345 }
346 }
347
Winson Chung4d7b0922014-03-13 17:14:17 -0700348 /**** TaskCallbacks Implementation ****/
349
Winson Chung04dfe0d2014-03-14 14:06:29 -0700350 /** Binds this task view to the task */
351 public void onTaskBound(Task t) {
352 mTask = t;
353 mTask.setCallbacks(this);
Winson Chung303e1ff2014-03-07 15:06:19 -0800354 }
355
356 @Override
Winson Chung4c71aef2014-03-21 15:15:11 -0700357 public void onTaskDataLoaded(boolean reloadingTaskData) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700358 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700359 // Bind each of the views to the new task data
360 mThumbnailView.rebindToTask(mTask, reloadingTaskData);
361 mBarView.rebindToTask(mTask, reloadingTaskData);
Winson Chungf5e22e72014-05-02 18:35:35 -0700362 mInfoView.rebindToTask(mTask, reloadingTaskData);
Winson Chung9f9679d2014-04-11 16:49:09 -0700363 // Rebind any listeners
364 mBarView.mApplicationIcon.setOnClickListener(this);
365 mInfoView.mAppInfoButton.setOnClickListener(this);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700366 }
367 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700368 }
369
370 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700371 public void onTaskDataUnloaded() {
Winson Chung9f9679d2014-04-11 16:49:09 -0700372 if (mThumbnailView != null && mBarView != null && mInfoView != null) {
Winson Chung37c8d8e2014-03-24 14:53:07 -0700373 // Unbind each of the views from the task data and remove the task callback
374 mTask.setCallbacks(null);
375 mThumbnailView.unbindFromTask();
376 mBarView.unbindFromTask();
Winson Chung9f9679d2014-04-11 16:49:09 -0700377 // Unbind any listeners
378 mBarView.mApplicationIcon.setOnClickListener(null);
379 mInfoView.mAppInfoButton.setOnClickListener(null);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700380 }
381 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700382 }
383
384 @Override
Winson Chung303e1ff2014-03-07 15:06:19 -0800385 public void onClick(View v) {
Winson Chung9f9679d2014-04-11 16:49:09 -0700386 if (v == mInfoView) {
Winson Chung96e3bc12014-05-06 16:44:12 -0700387 hideInfoPane();
Winson Chung9f9679d2014-04-11 16:49:09 -0700388 } else if (v == mBarView.mApplicationIcon) {
389 mCb.onTaskIconClicked(this);
390 } else if (v == mInfoView.mAppInfoButton) {
391 mCb.onTaskAppInfoClicked(this);
392 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800393 }
394}