blob: b440847438969f8a4eca76a860cbf1c2014f7d11 [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;
Winson5b7dd532015-12-01 16:02:12 -080020import android.animation.AnimatorSet;
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;
Winson35f30502015-09-28 11:24:36 -070024import android.content.res.Resources;
Winson35f30502015-09-28 11:24:36 -070025import android.graphics.Outline;
Winsonbe7607a2015-10-01 17:24:51 -070026import android.graphics.Point;
Winson5b7dd532015-12-01 16:02:12 -080027import android.graphics.Rect;
Winson Chung37c8d8e2014-03-24 14:53:07 -070028import android.util.AttributeSet;
Winsonf24f2162016-01-05 12:11:55 -080029import android.util.FloatProperty;
Winsonf24f2162016-01-05 12:11:55 -080030import android.util.Property;
Winsonbe7607a2015-10-01 17:24:51 -070031import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080032import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080033import android.view.ViewDebug;
Winson Chungf95c7f52014-08-18 17:09:45 -070034import android.view.ViewOutlineProvider;
Winson9b001392016-04-08 14:54:02 -070035import android.widget.TextView;
Winson8be16342016-02-09 11:53:27 -080036import android.widget.Toast;
Winsonc0d70582016-01-29 10:24:39 -080037
Winson42329522016-02-05 10:39:46 -080038import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010039import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080040import com.android.systemui.Interpolators;
Winson Chung37c8d8e2014-03-24 14:53:07 -070041import com.android.systemui.R;
Winson0d14d4d2015-10-26 17:05:04 -070042import com.android.systemui.recents.Recents;
Winsonbe7607a2015-10-01 17:24:51 -070043import com.android.systemui.recents.RecentsActivity;
Winson Chung303e1ff2014-03-07 15:06:19 -080044import com.android.systemui.recents.RecentsConfiguration;
Winson2536c7e2015-10-01 15:49:31 -070045import com.android.systemui.recents.events.EventBus;
Winson Chung48f2cda2015-12-11 13:20:12 -050046import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winson0d14d4d2015-10-26 17:05:04 -070047import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
Winsonef064132016-01-05 12:11:31 -080048import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
Winson27c28f82016-05-05 16:16:50 -070049import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
Winsonbe7607a2015-10-01 17:24:51 -070050import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
51import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
Winsonf24f2162016-01-05 12:11:55 -080052import com.android.systemui.recents.misc.ReferenceCountedTrigger;
Winson0d14d4d2015-10-26 17:05:04 -070053import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung6519c1b2017-10-13 17:12:56 -070054import com.android.systemui.shared.recents.utilities.AnimationProps;
55import com.android.systemui.shared.recents.utilities.Utilities;
56import com.android.systemui.shared.recents.model.Task;
57import com.android.systemui.shared.recents.model.ThumbnailData;
Winson Chung303e1ff2014-03-07 15:06:19 -080058
Winson Chungeb2206c2017-01-03 12:37:08 -080059import java.io.PrintWriter;
Winsonf24f2162016-01-05 12:11:55 -080060import java.util.ArrayList;
61
Winson3c107162016-01-22 15:53:00 -080062/**
63 * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
64 * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
65 * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
66 * with the previous bounds if any child requests layout).
67 */
68public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
Winsonf24f2162016-01-05 12:11:55 -080069 TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
Winson0d14d4d2015-10-26 17:05:04 -070070
Winson Chung37c8d8e2014-03-24 14:53:07 -070071 /** The TaskView callbacks */
72 interface TaskViewCallbacks {
Winson Chung48f2cda2015-12-11 13:20:12 -050073 void onTaskViewClipStateChanged(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070074 }
75
Winsonf24f2162016-01-05 12:11:55 -080076 /**
77 * The dim overlay is generally calculated from the task progress, but occasionally (like when
Winsone693aaf2016-03-01 12:05:59 -080078 * launching) needs to be animated independently of the task progress. This call is only used
79 * when animating the task into Recents, when the header dim is already applied
80 */
81 public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
82 new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
83 @Override
84 public void setValue(TaskView tv, float dimAlpha) {
85 tv.setDimAlphaWithoutHeader(dimAlpha);
86 }
87
88 @Override
89 public Float get(TaskView tv) {
90 return tv.getDimAlpha();
91 }
92 };
93
94 /**
95 * The dim overlay is generally calculated from the task progress, but occasionally (like when
Winsonf24f2162016-01-05 12:11:55 -080096 * launching) needs to be animated independently of the task progress.
97 */
Winson1bcf3c42016-02-10 13:29:39 -080098 public static final Property<TaskView, Float> DIM_ALPHA =
Winson14991502016-02-15 15:40:08 -080099 new FloatProperty<TaskView>("dimAlpha") {
Winsonf24f2162016-01-05 12:11:55 -0800100 @Override
Winson1bcf3c42016-02-10 13:29:39 -0800101 public void setValue(TaskView tv, float dimAlpha) {
102 tv.setDimAlpha(dimAlpha);
Winsonf24f2162016-01-05 12:11:55 -0800103 }
104
105 @Override
106 public Float get(TaskView tv) {
Winson1bcf3c42016-02-10 13:29:39 -0800107 return tv.getDimAlpha();
Winsonf24f2162016-01-05 12:11:55 -0800108 }
109 };
110
Winson14991502016-02-15 15:40:08 -0800111 /**
112 * The dim overlay is generally calculated from the task progress, but occasionally (like when
113 * launching) needs to be animated independently of the task progress.
114 */
115 public static final Property<TaskView, Float> VIEW_OUTLINE_ALPHA =
116 new FloatProperty<TaskView>("viewOutlineAlpha") {
117 @Override
118 public void setValue(TaskView tv, float alpha) {
119 tv.getViewBounds().setAlpha(alpha);
120 }
121
122 @Override
123 public Float get(TaskView tv) {
124 return tv.getViewBounds().getAlpha();
125 }
126 };
127
Winson231bc9c2016-02-09 12:31:00 -0800128 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700129 private float mDimAlpha;
130 private float mActionButtonTranslationZ;
Winson Chung14926462014-04-14 18:57:14 -0700131
Winson231bc9c2016-02-09 12:31:00 -0800132 @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
Winsonfc48b072016-04-21 11:20:11 -0700133 private Task mTask;
Winson Chungb69258b2017-06-07 16:53:03 -0700134 private boolean mTaskBound;
Winson231bc9c2016-02-09 12:31:00 -0800135 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700136 private boolean mClipViewInStack = true;
Winson231bc9c2016-02-09 12:31:00 -0800137 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700138 private boolean mTouchExplorationEnabled;
Winson231bc9c2016-02-09 12:31:00 -0800139 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700140 private boolean mIsDisabledInSafeMode;
Winson231bc9c2016-02-09 12:31:00 -0800141 @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
Winsonfc48b072016-04-21 11:20:11 -0700142 private AnimateableViewBounds mViewBounds;
Winsonf24f2162016-01-05 12:11:55 -0800143
144 private AnimatorSet mTransformAnimation;
Winsona6b81e02016-04-07 14:23:02 -0700145 private ObjectAnimator mDimAnimator;
146 private ObjectAnimator mOutlineAnimator;
Jorim Jaggi899327f2016-02-25 20:44:18 -0500147 private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
Winsonf24f2162016-01-05 12:11:55 -0800148 private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700149
Winson231bc9c2016-02-09 12:31:00 -0800150 @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
Manu Cornet5f615362016-12-16 16:06:33 -0800151 protected TaskViewThumbnail mThumbnailView;
Winson231bc9c2016-02-09 12:31:00 -0800152 @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
Manu Cornet017a3ac2017-02-06 17:47:42 -0800153 protected TaskViewHeader mHeaderView;
Winsonfc48b072016-04-21 11:20:11 -0700154 private View mActionButtonView;
155 private View mIncompatibleAppToastView;
156 private TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -0800157
Winson231bc9c2016-02-09 12:31:00 -0800158 @ViewDebug.ExportedProperty(category="recents")
Winsonfc48b072016-04-21 11:20:11 -0700159 private Point mDownTouchPos = new Point();
Winsonbe7607a2015-10-01 17:24:51 -0700160
Winson8be16342016-02-09 11:53:27 -0800161 private Toast mDisabledAppToast;
162
Winson Chung37c8d8e2014-03-24 14:53:07 -0700163 public TaskView(Context context) {
164 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -0800165 }
166
Winson Chung37c8d8e2014-03-24 14:53:07 -0700167 public TaskView(Context context, AttributeSet attrs) {
168 this(context, attrs, 0);
169 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800170
Winson Chung37c8d8e2014-03-24 14:53:07 -0700171 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
172 this(context, attrs, defStyleAttr, 0);
173 }
174
175 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
176 super(context, attrs, defStyleAttr, defStyleRes);
Winson53ec42c2015-10-28 15:55:35 -0700177 RecentsConfiguration config = Recents.getConfiguration();
Winson35f30502015-09-28 11:24:36 -0700178 Resources res = context.getResources();
Jiaquan He5a2eb832016-12-13 14:36:37 -0800179 mViewBounds = createOutlineProvider();
Winson53ec42c2015-10-28 15:55:35 -0700180 if (config.fakeShadows) {
181 setBackground(new FakeShadowDrawable(res, config));
Jorim Jaggicb557032014-09-16 23:09:24 +0200182 }
183 setOutlineProvider(mViewBounds);
Winson Chung931c51f2015-12-17 17:08:55 -0500184 setOnLongClickListener(this);
Matthew Ngb7035f32017-01-19 11:04:50 -0800185 setAccessibilityDelegate(new TaskViewAccessibilityDelegate(this));
Winson Chung14926462014-04-14 18:57:14 -0700186 }
187
Winson Chung04dfe0d2014-03-14 14:06:29 -0700188 /** Set callback */
189 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800190 mCb = cb;
191 }
192
Winsona1ededd2016-03-25 12:23:12 -0700193 /**
194 * Called from RecentsActivity when it is relaunched.
195 */
196 void onReload(boolean isResumingFromVisible) {
Winson Chung247f1d42017-10-06 15:53:08 -0700197 resetNoUserInteractionState();
Winson88737542016-02-17 13:27:33 -0800198 if (!isResumingFromVisible) {
199 resetViewProperties();
Winson88737542016-02-17 13:27:33 -0800200 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700201 }
202
Winson Chung303e1ff2014-03-07 15:06:19 -0800203 /** Gets the task */
Winsonb1e71d02015-11-23 12:40:23 -0800204 public Task getTask() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800205 return mTask;
206 }
207
Jiaquan He5a2eb832016-12-13 14:36:37 -0800208 /* Create an outline provider to clip and outline the view */
209 protected AnimateableViewBounds createOutlineProvider() {
210 return new AnimateableViewBounds(this, mContext.getResources().getDimensionPixelSize(
211 R.dimen.recents_task_view_shadow_rounded_corners_radius));
212 }
213
Winson Chungdcfa7972014-07-22 12:27:13 -0700214 /** Returns the view bounds. */
215 AnimateableViewBounds getViewBounds() {
216 return mViewBounds;
217 }
218
219 @Override
220 protected void onFinishInflate() {
221 // Bind the views
Alan Viverette51efddb2017-04-05 10:00:01 -0400222 mHeaderView = findViewById(R.id.task_view_bar);
223 mThumbnailView = findViewById(R.id.task_view_thumbnail);
Winson67c79572016-04-13 14:02:18 -0700224 mThumbnailView.updateClipToTaskBar(mHeaderView);
Winson Chung2915b9d2014-07-28 11:01:57 -0700225 mActionButtonView = findViewById(R.id.lock_to_app_fab);
Winson Chungf95c7f52014-08-18 17:09:45 -0700226 mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
227 @Override
228 public void getOutline(View view, Outline outline) {
229 // Set the outline to match the FAB background
Winson Chungbf5dbf12014-09-16 00:58:25 +0200230 outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
Winson Chung931c51f2015-12-17 17:08:55 -0500231 outline.setAlpha(0.35f);
Winson Chungf95c7f52014-08-18 17:09:45 -0700232 }
233 });
Winson Chung931c51f2015-12-17 17:08:55 -0500234 mActionButtonView.setOnClickListener(this);
Winson Chung86677c92014-11-14 11:30:39 -0800235 mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
Winson Chungdcfa7972014-07-22 12:27:13 -0700236 }
237
Winson619e40c2016-03-25 16:12:35 -0700238 /**
239 * Update the task view when the configuration changes.
240 */
Manu Cornet5f615362016-12-16 16:06:33 -0800241 protected void onConfigurationChanged() {
Winson619e40c2016-03-25 16:12:35 -0700242 mHeaderView.onConfigurationChanged();
243 }
244
Winson Chungdcfa7972014-07-22 12:27:13 -0700245 @Override
Winsonde0591a2015-12-04 17:24:35 -0800246 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
247 super.onSizeChanged(w, h, oldw, oldh);
Winson94a0e892016-01-08 16:06:50 -0800248 if (w > 0 && h > 0) {
249 mHeaderView.onTaskViewSizeChanged(w, h);
250 mThumbnailView.onTaskViewSizeChanged(w, h);
Winson67c79572016-04-13 14:02:18 -0700251
252 mActionButtonView.setTranslationX(w - getMeasuredWidth());
253 mActionButtonView.setTranslationY(h - getMeasuredHeight());
Winson94a0e892016-01-08 16:06:50 -0800254 }
Winsonde0591a2015-12-04 17:24:35 -0800255 }
256
257 @Override
Winsonad66efa2016-01-08 16:40:30 -0800258 public boolean hasOverlappingRendering() {
259 return false;
260 }
261
262 @Override
Winsonbe7607a2015-10-01 17:24:51 -0700263 public boolean onInterceptTouchEvent(MotionEvent ev) {
264 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
265 mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
266 }
267 return super.onInterceptTouchEvent(ev);
268 }
269
Winson3c107162016-01-22 15:53:00 -0800270 @Override
271 protected void measureContents(int width, int height) {
Jorim Jaggicb557032014-09-16 23:09:24 +0200272 int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
273 int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
Winson67c79572016-04-13 14:02:18 -0700274 int widthSpec = MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY);
275 int heightSpec = MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY);
Winson Chungbf5dbf12014-09-16 00:58:25 +0200276
277 // Measure the content
Winson67c79572016-04-13 14:02:18 -0700278 measureChildren(widthSpec, heightSpec);
Winson5b7dd532015-12-01 16:02:12 -0800279
Winson Chungdcfa7972014-07-22 12:27:13 -0700280 setMeasuredDimension(width, height);
281 }
282
Winsonf24f2162016-01-05 12:11:55 -0800283 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
Winson40a22732016-02-02 18:07:00 -0800284 AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
Winson53ec42c2015-10-28 15:55:35 -0700285 RecentsConfiguration config = Recents.getConfiguration();
Winsonbe8e6962016-02-01 14:27:52 -0800286 cancelTransformAnimation();
Winson53ec42c2015-10-28 15:55:35 -0700287
Winsonf24f2162016-01-05 12:11:55 -0800288 // Compose the animations for the transform
289 mTmpAnimators.clear();
Winsonad66efa2016-01-08 16:40:30 -0800290 toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
Winsonf24f2162016-01-05 12:11:55 -0800291 if (toAnimation.isImmediate()) {
Winson1bcf3c42016-02-10 13:29:39 -0800292 if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
293 setDimAlpha(toTransform.dimAlpha);
Winson55003902016-01-12 12:00:37 -0800294 }
Winson14991502016-02-15 15:40:08 -0800295 if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
296 mViewBounds.setAlpha(toTransform.viewOutlineAlpha);
297 }
Winsonc5fd3502016-01-18 15:18:37 -0800298 // Manually call back to the animator listener and update callback
Winsonbe8e6962016-02-01 14:27:52 -0800299 if (toAnimation.getListener() != null) {
300 toAnimation.getListener().onAnimationEnd(null);
Winsonf24f2162016-01-05 12:11:55 -0800301 }
Winsonc5fd3502016-01-18 15:18:37 -0800302 if (updateCallback != null) {
303 updateCallback.onAnimationUpdate(null);
304 }
Winson Chung012ef362014-07-31 18:36:25 -0700305 } else {
Winson40a22732016-02-02 18:07:00 -0800306 // Both the progress and the update are a function of the bounds movement of the task
Winson1bcf3c42016-02-10 13:29:39 -0800307 if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
Winsona6b81e02016-04-07 14:23:02 -0700308 mDimAnimator = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
Winson1bcf3c42016-02-10 13:29:39 -0800309 toTransform.dimAlpha);
Winsona6b81e02016-04-07 14:23:02 -0700310 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mDimAnimator));
Winsonf24f2162016-01-05 12:11:55 -0800311 }
Winson14991502016-02-15 15:40:08 -0800312 if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
Winsona6b81e02016-04-07 14:23:02 -0700313 mOutlineAnimator = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
Winson14991502016-02-15 15:40:08 -0800314 mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
Winsona6b81e02016-04-07 14:23:02 -0700315 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mOutlineAnimator));
Winson14991502016-02-15 15:40:08 -0800316 }
Winsonc5fd3502016-01-18 15:18:37 -0800317 if (updateCallback != null) {
318 ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
319 updateCallbackAnim.addUpdateListener(updateCallback);
Winson40a22732016-02-02 18:07:00 -0800320 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim));
Winsonc5fd3502016-01-18 15:18:37 -0800321 }
Winsonf24f2162016-01-05 12:11:55 -0800322
323 // Create the animator
324 mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
Winsonf24f2162016-01-05 12:11:55 -0800325 mTransformAnimation.start();
Jorim Jaggi899327f2016-02-25 20:44:18 -0500326 mTargetAnimationTransform.copyFrom(toTransform);
Winson Chung012ef362014-07-31 18:36:25 -0700327 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800328 }
329
330 /** Resets this view's properties */
331 void resetViewProperties() {
Winsonbe8e6962016-02-01 14:27:52 -0800332 cancelTransformAnimation();
Winson1bcf3c42016-02-10 13:29:39 -0800333 setDimAlpha(0);
Winson479f7442015-11-25 15:16:27 -0800334 setVisibility(View.VISIBLE);
335 getViewBounds().reset();
Winson4c84a8c2016-01-21 18:14:12 -0800336 getHeaderView().reset();
Winson Chungdcfa7972014-07-22 12:27:13 -0700337 TaskViewTransform.reset(this);
Winsonf24f2162016-01-05 12:11:55 -0800338
339 mActionButtonView.setScaleX(1f);
340 mActionButtonView.setScaleY(1f);
Winson6ea25882016-01-13 10:59:04 -0800341 mActionButtonView.setAlpha(0f);
Winson67c79572016-04-13 14:02:18 -0700342 mActionButtonView.setTranslationX(0f);
343 mActionButtonView.setTranslationY(0f);
Winsonf24f2162016-01-05 12:11:55 -0800344 mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
Winson9b001392016-04-08 14:54:02 -0700345 if (mIncompatibleAppToastView != null) {
346 mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
347 }
Winson Chungc6a16232014-04-01 14:04:48 -0700348 }
349
Winsonf24f2162016-01-05 12:11:55 -0800350 /**
Jorim Jaggi899327f2016-02-25 20:44:18 -0500351 * @return whether we are animating towards {@param transform}
352 */
353 boolean isAnimatingTo(TaskViewTransform transform) {
354 return mTransformAnimation != null && mTransformAnimation.isStarted()
355 && mTargetAnimationTransform.isSame(transform);
356 }
357
358 /**
Winsonf24f2162016-01-05 12:11:55 -0800359 * Cancels any current transform animations.
360 */
361 public void cancelTransformAnimation() {
Jon Miranda635c1152017-04-12 17:47:47 -0700362 cancelDimAnimationIfExists();
Winsonad66efa2016-01-08 16:40:30 -0800363 Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
Winsona6b81e02016-04-07 14:23:02 -0700364 Utilities.cancelAnimationWithoutCallbacks(mOutlineAnimator);
Winson Chung9f49df92014-05-07 18:08:34 -0700365 }
366
Jon Miranda635c1152017-04-12 17:47:47 -0700367 private void cancelDimAnimationIfExists() {
368 if (mDimAnimator != null) {
369 mDimAnimator.cancel();
370 }
371 }
372
Winson Chungd16c5652015-01-26 16:11:07 -0800373 /** Enables/disables handling touch on this task view. */
Manu Cornet1afcea42016-10-18 18:18:21 +0800374 public void setTouchEnabled(boolean enabled) {
Winson Chungd16c5652015-01-26 16:11:07 -0800375 setOnClickListener(enabled ? this : null);
376 }
377
Winson Chung969f5862014-06-16 17:08:24 -0700378 /** Animates this task view if the user does not interact with the stack after a certain time. */
Manu Cornet1afcea42016-10-18 18:18:21 +0800379 public void startNoUserInteractionAnimation() {
Winson Chunga0e88b52014-08-11 19:25:42 -0700380 mHeaderView.startNoUserInteractionAnimation();
Winson Chung969f5862014-06-16 17:08:24 -0700381 }
382
383 /** Mark this task view that the user does has not interacted with the stack after a certain time. */
Winson Chunga0e88b52014-08-11 19:25:42 -0700384 void setNoUserInteractionState() {
385 mHeaderView.setNoUserInteractionState();
386 }
387
Winson Chungb0a28ea2014-10-28 15:21:35 -0700388 /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
389 void resetNoUserInteractionState() {
390 mHeaderView.resetNoUserInteractionState();
391 }
392
Winson Chunga0e88b52014-08-11 19:25:42 -0700393 /** Dismisses this task. */
394 void dismissTask() {
395 // Animate out the view and call the callback
396 final TaskView tv = this;
Winson3b6ba1a2016-03-22 15:37:54 -0700397 DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv);
Winsonef064132016-01-05 12:11:31 -0800398 dismissEvent.addPostAnimationCallback(new Runnable() {
Winson Chunga0e88b52014-08-11 19:25:42 -0700399 @Override
400 public void run() {
Winson61560f02016-05-02 16:36:20 -0700401 EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv,
402 new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
403 Interpolators.FAST_OUT_SLOW_IN)));
Winson Chunga0e88b52014-08-11 19:25:42 -0700404 }
Winsonef064132016-01-05 12:11:31 -0800405 });
406 EventBus.getDefault().send(dismissEvent);
Winson Chung303e1ff2014-03-07 15:06:19 -0800407 }
408
Winson Chung5a9b0b02014-05-20 17:32:03 -0700409 /**
410 * Returns whether this view should be clipped, or any views below should clip against this
411 * view.
412 */
413 boolean shouldClipViewInStack() {
Winson Chung247f1d42017-10-06 15:53:08 -0700414 if (getVisibility() != View.VISIBLE || Recents.getConfiguration().isLowRamDevice) {
Winson36a5a2c2015-10-29 18:04:39 -0700415 return false;
416 }
417 return mClipViewInStack;
Winson Chung5a9b0b02014-05-20 17:32:03 -0700418 }
419
420 /** Sets whether this view should be clipped, or clipped against. */
421 void setClipViewInStack(boolean clip) {
422 if (clip != mClipViewInStack) {
423 mClipViewInStack = clip;
Winson Chungfe03b402014-11-17 11:47:09 -0800424 if (mCb != null) {
425 mCb.onTaskViewClipStateChanged(this);
426 }
Winson Chung93748a12014-07-13 17:43:31 -0700427 }
428 }
429
Peter Schillerb124d562015-12-11 21:31:17 -0800430 public TaskViewHeader getHeaderView() {
431 return mHeaderView;
432 }
433
Winson1bcf3c42016-02-10 13:29:39 -0800434 /**
435 * Sets the current dim.
436 */
437 public void setDimAlpha(float dimAlpha) {
Winson1bcf3c42016-02-10 13:29:39 -0800438 mDimAlpha = dimAlpha;
Winsone693aaf2016-03-01 12:05:59 -0800439 mThumbnailView.setDimAlpha(dimAlpha);
440 mHeaderView.setDimAlpha(dimAlpha);
441 }
442
443 /**
444 * Sets the current dim without updating the header's dim.
445 */
446 public void setDimAlphaWithoutHeader(float dimAlpha) {
447 mDimAlpha = dimAlpha;
448 mThumbnailView.setDimAlpha(dimAlpha);
Winson Chunga26fb782014-06-12 17:52:39 -0700449 }
450
Winson1bcf3c42016-02-10 13:29:39 -0800451 /**
452 * Returns the current dim.
453 */
454 public float getDimAlpha() {
Winson Chung353c0b92014-10-16 17:43:23 -0700455 return mDimAlpha;
Winson Chunga26fb782014-06-12 17:52:39 -0700456 }
457
Winson1bcf3c42016-02-10 13:29:39 -0800458 /**
Winson0d14d4d2015-10-26 17:05:04 -0700459 * Explicitly sets the focused state of this task.
Winson Chung1e8d71b2014-05-16 17:05:22 -0700460 */
Winsonf24f2162016-01-05 12:11:55 -0800461 public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
Winson0d14d4d2015-10-26 17:05:04 -0700462 if (isFocused) {
463 if (requestViewFocus && !isFocused()) {
464 requestFocus();
465 }
Winson0d14d4d2015-10-26 17:05:04 -0700466 } else {
Winsonf0009882016-06-01 12:22:55 -0700467 if (isAccessibilityFocused() && mTouchExplorationEnabled) {
Winson0d14d4d2015-10-26 17:05:04 -0700468 clearAccessibilityFocus();
469 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700470 }
471 }
472
Winson Chung931c51f2015-12-17 17:08:55 -0500473 /**
474 * Shows the action button.
475 * @param fadeIn whether or not to animate the action button in.
476 * @param fadeInDuration the duration of the action button animation, only used if
477 * {@param fadeIn} is true.
478 */
479 public void showActionButton(boolean fadeIn, int fadeInDuration) {
480 mActionButtonView.setVisibility(View.VISIBLE);
481
Winson65c851e2016-01-20 12:43:35 -0800482 if (fadeIn && mActionButtonView.getAlpha() < 1f) {
483 mActionButtonView.animate()
484 .alpha(1f)
485 .scaleX(1f)
486 .scaleY(1f)
487 .setDuration(fadeInDuration)
Winsonc0d70582016-01-29 10:24:39 -0800488 .setInterpolator(Interpolators.ALPHA_IN)
Winson65c851e2016-01-20 12:43:35 -0800489 .start();
Winson Chung931c51f2015-12-17 17:08:55 -0500490 } else {
Winsonf24f2162016-01-05 12:11:55 -0800491 mActionButtonView.setScaleX(1f);
492 mActionButtonView.setScaleY(1f);
Winson Chung931c51f2015-12-17 17:08:55 -0500493 mActionButtonView.setAlpha(1f);
Winsonf24f2162016-01-05 12:11:55 -0800494 mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
Winson Chung931c51f2015-12-17 17:08:55 -0500495 }
496 }
497
498 /**
499 * Immediately hides the action button.
Winsonf24f2162016-01-05 12:11:55 -0800500 *
501 * @param fadeOut whether or not to animate the action button out.
Winson Chung931c51f2015-12-17 17:08:55 -0500502 */
Winsonf24f2162016-01-05 12:11:55 -0800503 public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown,
504 final Animator.AnimatorListener animListener) {
Winson65c851e2016-01-20 12:43:35 -0800505 if (fadeOut && mActionButtonView.getAlpha() > 0f) {
506 if (scaleDown) {
507 float toScale = 0.9f;
Winsonf24f2162016-01-05 12:11:55 -0800508 mActionButtonView.animate()
Winson65c851e2016-01-20 12:43:35 -0800509 .scaleX(toScale)
510 .scaleY(toScale);
Winsonf24f2162016-01-05 12:11:55 -0800511 }
Winson65c851e2016-01-20 12:43:35 -0800512 mActionButtonView.animate()
513 .alpha(0f)
514 .setDuration(fadeOutDuration)
Winsonc0d70582016-01-29 10:24:39 -0800515 .setInterpolator(Interpolators.ALPHA_OUT)
Winson65c851e2016-01-20 12:43:35 -0800516 .withEndAction(new Runnable() {
517 @Override
518 public void run() {
519 if (animListener != null) {
520 animListener.onAnimationEnd(null);
521 }
522 mActionButtonView.setVisibility(View.INVISIBLE);
523 }
524 })
525 .start();
Winsonf24f2162016-01-05 12:11:55 -0800526 } else {
527 mActionButtonView.setAlpha(0f);
528 mActionButtonView.setVisibility(View.INVISIBLE);
529 if (animListener != null) {
530 animListener.onAnimationEnd(null);
531 }
532 }
533 }
534
535 /**** TaskStackAnimationHelper.Callbacks Implementation ****/
536
537 @Override
538 public void onPrepareLaunchTargetForEnterAnimation() {
539 // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
Winsone693aaf2016-03-01 12:05:59 -0800540 setDimAlphaWithoutHeader(0);
Winsonf24f2162016-01-05 12:11:55 -0800541 mActionButtonView.setAlpha(0f);
Winson9b001392016-04-08 14:54:02 -0700542 if (mIncompatibleAppToastView != null &&
543 mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
544 mIncompatibleAppToastView.setAlpha(0f);
545 }
Winsonf24f2162016-01-05 12:11:55 -0800546 }
547
548 @Override
Winsone693aaf2016-03-01 12:05:59 -0800549 public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
550 boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
Jon Miranda635c1152017-04-12 17:47:47 -0700551 cancelDimAnimationIfExists();
Winsona6b81e02016-04-07 14:23:02 -0700552
Winsone693aaf2016-03-01 12:05:59 -0800553 // Dim the view after the app window transitions down into recents
Winsonf24f2162016-01-05 12:11:55 -0800554 postAnimationTrigger.increment();
Winsone693aaf2016-03-01 12:05:59 -0800555 AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
Winsona6b81e02016-04-07 14:23:02 -0700556 mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
Winsone693aaf2016-03-01 12:05:59 -0800557 DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
Winsona6b81e02016-04-07 14:23:02 -0700558 mDimAnimator.addListener(postAnimationTrigger.decrementOnAnimationEnd());
559 mDimAnimator.start();
Winsonf24f2162016-01-05 12:11:55 -0800560
561 if (screenPinningEnabled) {
562 showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
563 }
Winson9b001392016-04-08 14:54:02 -0700564
565 if (mIncompatibleAppToastView != null &&
566 mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
567 mIncompatibleAppToastView.animate()
568 .alpha(1f)
569 .setDuration(duration)
570 .setInterpolator(Interpolators.ALPHA_IN)
571 .start();
572 }
Winsonf24f2162016-01-05 12:11:55 -0800573 }
574
575 @Override
576 public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
577 ReferenceCountedTrigger postAnimationTrigger) {
Winsona6b81e02016-04-07 14:23:02 -0700578 Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
579
Winson68088812016-02-12 16:06:04 -0800580 // Un-dim the view before/while launching the target
Winson1bcf3c42016-02-10 13:29:39 -0800581 AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
Winsona6b81e02016-04-07 14:23:02 -0700582 mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
Winsone693aaf2016-03-01 12:05:59 -0800583 DIM_ALPHA, getDimAlpha(), 0));
Winsona6b81e02016-04-07 14:23:02 -0700584 mDimAnimator.start();
Winsonf24f2162016-01-05 12:11:55 -0800585
586 postAnimationTrigger.increment();
587 hideActionButton(true /* fadeOut */, duration,
588 !screenPinningRequested /* scaleDown */,
589 postAnimationTrigger.decrementOnAnimationEnd());
Winson Chung931c51f2015-12-17 17:08:55 -0500590 }
591
Winson59924fe2016-03-17 14:13:18 -0700592 @Override
593 public void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled) {
594 if (screenPinningEnabled) {
595 showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
596 }
597 }
598
Winson Chung4d7b0922014-03-13 17:14:17 -0700599 /**** TaskCallbacks Implementation ****/
600
Winsonfc48b072016-04-21 11:20:11 -0700601 public void onTaskBound(Task t, boolean touchExplorationEnabled, int displayOrientation,
602 Rect displayRect) {
Winson8be16342016-02-09 11:53:27 -0800603 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonfc48b072016-04-21 11:20:11 -0700604 mTouchExplorationEnabled = touchExplorationEnabled;
Winson Chung04dfe0d2014-03-14 14:06:29 -0700605 mTask = t;
Winson Chungb69258b2017-06-07 16:53:03 -0700606 mTaskBound = true;
Winson Chungb169ebd2015-12-14 13:57:36 -0500607 mTask.addCallback(this);
Winson8be16342016-02-09 11:53:27 -0800608 mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
Winsonfc48b072016-04-21 11:20:11 -0700609 mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode, displayOrientation, displayRect);
Winsond2a03062016-04-15 11:19:07 -0700610 mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
Winson9b001392016-04-08 14:54:02 -0700611
612 if (!t.isDockable && ssp.hasDockedTask()) {
613 if (mIncompatibleAppToastView == null) {
614 mIncompatibleAppToastView = Utilities.findViewStubById(this,
615 R.id.incompatible_app_toast_stub).inflate();
Winson Chung80f7b012017-03-28 21:33:28 -0700616 TextView msg = findViewById(com.android.internal.R.id.message);
617 msg.setText(R.string.dock_non_resizeble_failed_to_dock_text);
Winson9b001392016-04-08 14:54:02 -0700618 }
619 mIncompatibleAppToastView.setVisibility(View.VISIBLE);
620 } else if (mIncompatibleAppToastView != null) {
621 mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
622 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800623 }
624
625 @Override
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100626 public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
Winson Chungb69258b2017-06-07 16:53:03 -0700627 if (mTaskBound) {
628 // Update each of the views to the new task data
629 mThumbnailView.onTaskDataLoaded(thumbnailData);
630 mHeaderView.onTaskDataLoaded();
631 }
Winson Chung4d7b0922014-03-13 17:14:17 -0700632 }
633
634 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700635 public void onTaskDataUnloaded() {
Winsond2a03062016-04-15 11:19:07 -0700636 // Unbind each of the views from the task and remove the task callback
Winsonf24f2162016-01-05 12:11:55 -0800637 mTask.removeCallback(this);
638 mThumbnailView.unbindFromTask();
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000639 mHeaderView.unbindFromTask(mTouchExplorationEnabled);
Winson Chungb69258b2017-06-07 16:53:03 -0700640 mTaskBound = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700641 }
642
Winson Chungd16c5652015-01-26 16:11:07 -0800643 @Override
Wale Ogunwale89be5762017-10-04 13:27:49 -0700644 public void onTaskWindowingModeChanged() {
Winsond2a03062016-04-15 11:19:07 -0700645 // Force rebind the header, the thumbnail does not change due to stack changes
646 mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
647 mHeaderView.onTaskDataLoaded();
Winson Chung1f24c7e2014-07-11 17:06:48 -0700648 }
649
Winson Chungdcfa7972014-07-22 12:27:13 -0700650 /**** View.OnClickListener Implementation ****/
651
Winson Chung4d7b0922014-03-13 17:14:17 -0700652 @Override
Winson Chung7ea62ba2014-07-22 16:30:37 -0700653 public void onClick(final View v) {
Winson8be16342016-02-09 11:53:27 -0800654 if (mIsDisabledInSafeMode) {
655 Context context = getContext();
656 String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
657 if (mDisabledAppToast != null) {
658 mDisabledAppToast.cancel();
659 }
660 mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
661 mDisabledAppToast.show();
662 return;
663 }
664
Winson Chung48f2cda2015-12-11 13:20:12 -0500665 boolean screenPinningRequested = false;
Winson2536c7e2015-10-01 15:49:31 -0700666 if (v == mActionButtonView) {
667 // Reset the translation of the action button before we animate it out
668 mActionButtonView.setTranslationZ(0f);
Winson Chung48f2cda2015-12-11 13:20:12 -0500669 screenPinningRequested = true;
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200670 }
Wale Ogunwale0568aed2017-09-08 13:29:37 -0700671 EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, screenPinningRequested));
Winson42329522016-02-05 10:39:46 -0800672
Chris Wren7c516842016-03-01 16:44:32 -0500673 MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
Winson42329522016-02-05 10:39:46 -0800674 mTask.key.getComponent().toString());
Winson Chung6cb485f2014-05-19 10:30:43 -0700675 }
Winsonbe7607a2015-10-01 17:24:51 -0700676
677 /**** View.OnLongClickListener Implementation ****/
678
679 @Override
680 public boolean onLongClick(View v) {
Matthew Ng43db6d22017-06-27 15:29:39 -0700681 if (!Recents.getConfiguration().dragToSplitEnabled) {
682 return false;
683 }
Winson37fc5132015-11-30 14:53:12 -0800684 SystemServicesProxy ssp = Recents.getSystemServices();
Manu Cornet23861dc2016-11-07 14:45:47 -0800685 boolean inBounds = false;
Winson5b7dd532015-12-01 16:02:12 -0800686 Rect clipBounds = new Rect(mViewBounds.mClipBounds);
Manu Cornet23861dc2016-11-07 14:45:47 -0800687 if (!clipBounds.isEmpty()) {
688 // If we are clipping the view to the bounds, manually do the hit test.
689 clipBounds.scale(getScaleX());
690 inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
691 } else {
692 // Otherwise just make sure we're within the view's bounds.
693 inBounds = mDownTouchPos.x <= getWidth() && mDownTouchPos.y <= getHeight();
694 }
Winson5b7dd532015-12-01 16:02:12 -0800695 if (v == this && inBounds && !ssp.hasDockedTask()) {
Winsonbe7607a2015-10-01 17:24:51 -0700696 // Start listening for drag events
697 setClipViewInStack(false);
698
Winson479f7442015-11-25 15:16:27 -0800699 mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
700 mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
Winsoneca4ab62015-11-04 10:50:28 -0800701
Winsonbe7607a2015-10-01 17:24:51 -0700702 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
Winson479f7442015-11-25 15:16:27 -0800703 EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
Winsonbe7607a2015-10-01 17:24:51 -0700704 return true;
705 }
706 return false;
707 }
708
709 /**** Events ****/
710
711 public final void onBusEvent(DragEndEvent event) {
Winson Chung6519c1b2017-10-13 17:12:56 -0700712 if (!(event.dropTarget instanceof DockState)) {
Winson27c28f82016-05-05 16:16:50 -0700713 event.addPostAnimationCallback(() -> {
714 // Reset the clip state for the drag view after the end animation completes
715 setClipViewInStack(true);
Winsoneca4ab62015-11-04 10:50:28 -0800716 });
717 }
Winsonbe7607a2015-10-01 17:24:51 -0700718 EventBus.getDefault().unregister(this);
719 }
Winson27c28f82016-05-05 16:16:50 -0700720
721 public final void onBusEvent(DragEndCancelledEvent event) {
722 // Reset the clip state for the drag view after the cancel animation completes
723 event.addPostAnimationCallback(() -> {
724 setClipViewInStack(true);
725 });
726 }
Winson Chungeb2206c2017-01-03 12:37:08 -0800727
728 public void dump(String prefix, PrintWriter writer) {
729 String innerPrefix = prefix + " ";
730
731 writer.print(prefix); writer.print("TaskView");
732 writer.print(" mTask="); writer.print(mTask.key.id);
733 writer.println();
734
735 mThumbnailView.dump(innerPrefix, writer);
736 }
Winson Chung7bb18852014-05-20 23:25:41 +0000737}