blob: 0c47b13098916d1edea007568cd6ee6589dee1f7 [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
Winson59924fe2016-03-17 14:13:18 -070019import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
20
Winson Chungc6011de2014-06-30 18:04:55 -070021import android.animation.Animator;
Winson5b7dd532015-12-01 16:02:12 -080022import android.animation.AnimatorSet;
Winson Chunga26fb782014-06-12 17:52:39 -070023import android.animation.ObjectAnimator;
Winson Chung14926462014-04-14 18:57:14 -070024import android.animation.ValueAnimator;
Winson21700932016-03-24 17:26:23 -070025import android.app.ActivityManager;
Winson Chung303e1ff2014-03-07 15:06:19 -080026import android.content.Context;
Winson35f30502015-09-28 11:24:36 -070027import android.content.res.Resources;
Winson35f30502015-09-28 11:24:36 -070028import android.graphics.Outline;
29import android.graphics.Paint;
Winsonbe7607a2015-10-01 17:24:51 -070030import android.graphics.Point;
Winson35f30502015-09-28 11:24:36 -070031import android.graphics.PorterDuff;
32import android.graphics.PorterDuffColorFilter;
Winson5b7dd532015-12-01 16:02:12 -080033import android.graphics.Rect;
Winson Chung37c8d8e2014-03-24 14:53:07 -070034import android.util.AttributeSet;
Winsonf24f2162016-01-05 12:11:55 -080035import android.util.FloatProperty;
Winsonf24f2162016-01-05 12:11:55 -080036import android.util.Property;
Winsonbe7607a2015-10-01 17:24:51 -070037import android.view.MotionEvent;
Winson Chung303e1ff2014-03-07 15:06:19 -080038import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080039import android.view.ViewDebug;
Winson Chungf95c7f52014-08-18 17:09:45 -070040import android.view.ViewOutlineProvider;
Winson8be16342016-02-09 11:53:27 -080041import android.widget.Toast;
Winsonc0d70582016-01-29 10:24:39 -080042
Winson42329522016-02-05 10:39:46 -080043import com.android.internal.logging.MetricsLogger;
44import com.android.internal.logging.MetricsProto.MetricsEvent;
Winsonc0d70582016-01-29 10:24:39 -080045import com.android.systemui.Interpolators;
Winson Chung37c8d8e2014-03-24 14:53:07 -070046import com.android.systemui.R;
Winson0d14d4d2015-10-26 17:05:04 -070047import com.android.systemui.recents.Recents;
Winsonbe7607a2015-10-01 17:24:51 -070048import com.android.systemui.recents.RecentsActivity;
Winson Chung303e1ff2014-03-07 15:06:19 -080049import com.android.systemui.recents.RecentsConfiguration;
Winson2536c7e2015-10-01 15:49:31 -070050import com.android.systemui.recents.events.EventBus;
Winson Chung48f2cda2015-12-11 13:20:12 -050051import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winson0d14d4d2015-10-26 17:05:04 -070052import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
Winsonef064132016-01-05 12:11:31 -080053import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
Winsonbe7607a2015-10-01 17:24:51 -070054import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
55import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
Winsonf24f2162016-01-05 12:11:55 -080056import com.android.systemui.recents.misc.ReferenceCountedTrigger;
Winson0d14d4d2015-10-26 17:05:04 -070057import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung353c0b92014-10-16 17:43:23 -070058import com.android.systemui.recents.misc.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -080059import com.android.systemui.recents.model.Task;
Winsoneca4ab62015-11-04 10:50:28 -080060import com.android.systemui.recents.model.TaskStack;
Winson Chung303e1ff2014-03-07 15:06:19 -080061
Winsonf24f2162016-01-05 12:11:55 -080062import java.util.ArrayList;
63
Winson3c107162016-01-22 15:53:00 -080064/**
65 * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
66 * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
67 * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
68 * with the previous bounds if any child requests layout).
69 */
70public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
Winsonf24f2162016-01-05 12:11:55 -080071 TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
Winson0d14d4d2015-10-26 17:05:04 -070072
Winson Chung37c8d8e2014-03-24 14:53:07 -070073 /** The TaskView callbacks */
74 interface TaskViewCallbacks {
Winson Chung48f2cda2015-12-11 13:20:12 -050075 void onTaskViewClipStateChanged(TaskView tv);
Winson Chung37c8d8e2014-03-24 14:53:07 -070076 }
77
Winsonf24f2162016-01-05 12:11:55 -080078 /**
79 * The dim overlay is generally calculated from the task progress, but occasionally (like when
Winsone693aaf2016-03-01 12:05:59 -080080 * launching) needs to be animated independently of the task progress. This call is only used
81 * when animating the task into Recents, when the header dim is already applied
82 */
83 public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
84 new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
85 @Override
86 public void setValue(TaskView tv, float dimAlpha) {
87 tv.setDimAlphaWithoutHeader(dimAlpha);
88 }
89
90 @Override
91 public Float get(TaskView tv) {
92 return tv.getDimAlpha();
93 }
94 };
95
96 /**
97 * The dim overlay is generally calculated from the task progress, but occasionally (like when
Winsonf24f2162016-01-05 12:11:55 -080098 * launching) needs to be animated independently of the task progress.
99 */
Winson1bcf3c42016-02-10 13:29:39 -0800100 public static final Property<TaskView, Float> DIM_ALPHA =
Winson14991502016-02-15 15:40:08 -0800101 new FloatProperty<TaskView>("dimAlpha") {
Winsonf24f2162016-01-05 12:11:55 -0800102 @Override
Winson1bcf3c42016-02-10 13:29:39 -0800103 public void setValue(TaskView tv, float dimAlpha) {
104 tv.setDimAlpha(dimAlpha);
Winsonf24f2162016-01-05 12:11:55 -0800105 }
106
107 @Override
108 public Float get(TaskView tv) {
Winson1bcf3c42016-02-10 13:29:39 -0800109 return tv.getDimAlpha();
Winsonf24f2162016-01-05 12:11:55 -0800110 }
111 };
112
Winson14991502016-02-15 15:40:08 -0800113 /**
114 * The dim overlay is generally calculated from the task progress, but occasionally (like when
115 * launching) needs to be animated independently of the task progress.
116 */
117 public static final Property<TaskView, Float> VIEW_OUTLINE_ALPHA =
118 new FloatProperty<TaskView>("viewOutlineAlpha") {
119 @Override
120 public void setValue(TaskView tv, float alpha) {
121 tv.getViewBounds().setAlpha(alpha);
122 }
123
124 @Override
125 public Float get(TaskView tv) {
126 return tv.getViewBounds().getAlpha();
127 }
128 };
129
Winson231bc9c2016-02-09 12:31:00 -0800130 @ViewDebug.ExportedProperty(category="recents")
Winson1bcf3c42016-02-10 13:29:39 -0800131 float mDimAlpha;
Winson Chung86677c92014-11-14 11:30:39 -0800132 float mActionButtonTranslationZ;
Winson Chung14926462014-04-14 18:57:14 -0700133
Winson231bc9c2016-02-09 12:31:00 -0800134 @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
Winson Chung303e1ff2014-03-07 15:06:19 -0800135 Task mTask;
Winson231bc9c2016-02-09 12:31:00 -0800136 @ViewDebug.ExportedProperty(category="recents")
Winson Chung37c8d8e2014-03-24 14:53:07 -0700137 boolean mTaskDataLoaded;
Winson231bc9c2016-02-09 12:31:00 -0800138 @ViewDebug.ExportedProperty(category="recents")
Winsonf24f2162016-01-05 12:11:55 -0800139 boolean mClipViewInStack = true;
Winson231bc9c2016-02-09 12:31:00 -0800140 @ViewDebug.ExportedProperty(category="recents")
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000141 boolean mTouchExplorationEnabled;
Winson231bc9c2016-02-09 12:31:00 -0800142 @ViewDebug.ExportedProperty(category="recents")
Winson8be16342016-02-09 11:53:27 -0800143 boolean mIsDisabledInSafeMode;
Winson231bc9c2016-02-09 12:31:00 -0800144 @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
Winson Chungdcfa7972014-07-22 12:27:13 -0700145 AnimateableViewBounds mViewBounds;
Winsonf24f2162016-01-05 12:11:55 -0800146
147 private AnimatorSet mTransformAnimation;
Jorim Jaggi899327f2016-02-25 20:44:18 -0500148 private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
Winsonf24f2162016-01-05 12:11:55 -0800149 private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
Winson Chung37c8d8e2014-03-24 14:53:07 -0700150
Winson Chungbf5dbf12014-09-16 00:58:25 +0200151 View mContent;
Winson231bc9c2016-02-09 12:31:00 -0800152 @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
Winson Chungec396d62014-08-06 17:08:00 -0700153 TaskViewThumbnail mThumbnailView;
Winson231bc9c2016-02-09 12:31:00 -0800154 @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
Winson Chunga0e88b52014-08-11 19:25:42 -0700155 TaskViewHeader mHeaderView;
Winson Chung2915b9d2014-07-28 11:01:57 -0700156 View mActionButtonView;
Winson Chung37c8d8e2014-03-24 14:53:07 -0700157 TaskViewCallbacks mCb;
Winson Chung303e1ff2014-03-07 15:06:19 -0800158
Winson231bc9c2016-02-09 12:31:00 -0800159 @ViewDebug.ExportedProperty(category="recents")
Winsonbe7607a2015-10-01 17:24:51 -0700160 Point mDownTouchPos = new Point();
161
Winson8be16342016-02-09 11:53:27 -0800162 private Toast mDisabledAppToast;
163
Winson Chung37c8d8e2014-03-24 14:53:07 -0700164 public TaskView(Context context) {
165 this(context, null);
Winson Chung303e1ff2014-03-07 15:06:19 -0800166 }
167
Winson Chung37c8d8e2014-03-24 14:53:07 -0700168 public TaskView(Context context, AttributeSet attrs) {
169 this(context, attrs, 0);
170 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800171
Winson Chung37c8d8e2014-03-24 14:53:07 -0700172 public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
173 this(context, attrs, defStyleAttr, 0);
174 }
175
176 public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
177 super(context, attrs, defStyleAttr, defStyleRes);
Winson53ec42c2015-10-28 15:55:35 -0700178 RecentsConfiguration config = Recents.getConfiguration();
Winson35f30502015-09-28 11:24:36 -0700179 Resources res = context.getResources();
Winson35f30502015-09-28 11:24:36 -0700180 mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
Winson14991502016-02-15 15:40:08 -0800181 R.dimen.recents_task_view_shadow_rounded_corners_radius));
Winson53ec42c2015-10-28 15:55:35 -0700182 if (config.fakeShadows) {
183 setBackground(new FakeShadowDrawable(res, config));
Jorim Jaggicb557032014-09-16 23:09:24 +0200184 }
185 setOutlineProvider(mViewBounds);
Winson Chung931c51f2015-12-17 17:08:55 -0500186 setOnLongClickListener(this);
Winson Chung14926462014-04-14 18:57:14 -0700187 }
188
Winson Chung04dfe0d2014-03-14 14:06:29 -0700189 /** Set callback */
190 void setCallbacks(TaskViewCallbacks cb) {
Winson Chung303e1ff2014-03-07 15:06:19 -0800191 mCb = cb;
192 }
193
Winson Chungb0a28ea2014-10-28 15:21:35 -0700194 /** Resets this TaskView for reuse. */
Winson88737542016-02-17 13:27:33 -0800195 void onResume(boolean isResumingFromVisible) {
Winson Chungb0a28ea2014-10-28 15:21:35 -0700196 resetNoUserInteractionState();
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000197 readSystemFlags();
Winson88737542016-02-17 13:27:33 -0800198 if (!isResumingFromVisible) {
199 resetViewProperties();
200 setClipViewInStack(false);
201 }
Winson Chungb0a28ea2014-10-28 15:21:35 -0700202 setCallbacks(null);
203 }
204
Winson Chung303e1ff2014-03-07 15:06:19 -0800205 /** Gets the task */
Winsonb1e71d02015-11-23 12:40:23 -0800206 public Task getTask() {
Winson Chung303e1ff2014-03-07 15:06:19 -0800207 return mTask;
208 }
209
Winson Chungdcfa7972014-07-22 12:27:13 -0700210 /** Returns the view bounds. */
211 AnimateableViewBounds getViewBounds() {
212 return mViewBounds;
213 }
214
215 @Override
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000216 protected void onAttachedToWindow() {
217 super.onAttachedToWindow();
218 readSystemFlags();
219 }
220
221 @Override
Winson Chungdcfa7972014-07-22 12:27:13 -0700222 protected void onFinishInflate() {
223 // Bind the views
Winson Chungbf5dbf12014-09-16 00:58:25 +0200224 mContent = findViewById(R.id.task_view_content);
Winson Chunga0e88b52014-08-11 19:25:42 -0700225 mHeaderView = (TaskViewHeader) findViewById(R.id.task_view_bar);
Winson Chungec396d62014-08-06 17:08:00 -0700226 mThumbnailView = (TaskViewThumbnail) findViewById(R.id.task_view_thumbnail);
Winson Chung2915b9d2014-07-28 11:01:57 -0700227 mActionButtonView = findViewById(R.id.lock_to_app_fab);
Winson Chungf95c7f52014-08-18 17:09:45 -0700228 mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
229 @Override
230 public void getOutline(View view, Outline outline) {
231 // Set the outline to match the FAB background
Winson Chungbf5dbf12014-09-16 00:58:25 +0200232 outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
Winson Chung931c51f2015-12-17 17:08:55 -0500233 outline.setAlpha(0.35f);
Winson Chungf95c7f52014-08-18 17:09:45 -0700234 }
235 });
Winson Chung931c51f2015-12-17 17:08:55 -0500236 mActionButtonView.setOnClickListener(this);
Winson Chung86677c92014-11-14 11:30:39 -0800237 mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
Winson Chungdcfa7972014-07-22 12:27:13 -0700238 }
239
240 @Override
Winsonde0591a2015-12-04 17:24:35 -0800241 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
242 super.onSizeChanged(w, h, oldw, oldh);
Winson94a0e892016-01-08 16:06:50 -0800243 if (w > 0 && h > 0) {
244 mHeaderView.onTaskViewSizeChanged(w, h);
245 mThumbnailView.onTaskViewSizeChanged(w, h);
246 }
Winsonde0591a2015-12-04 17:24:35 -0800247 }
248
249 @Override
Winsonad66efa2016-01-08 16:40:30 -0800250 public boolean hasOverlappingRendering() {
251 return false;
252 }
253
254 @Override
Winsonbe7607a2015-10-01 17:24:51 -0700255 public boolean onInterceptTouchEvent(MotionEvent ev) {
256 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
257 mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
258 }
259 return super.onInterceptTouchEvent(ev);
260 }
261
Winson Chungdcfa7972014-07-22 12:27:13 -0700262
Winson3c107162016-01-22 15:53:00 -0800263 @Override
264 protected void measureContents(int width, int height) {
Jorim Jaggicb557032014-09-16 23:09:24 +0200265 int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
266 int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
Winson Chungbf5dbf12014-09-16 00:58:25 +0200267
268 // Measure the content
269 mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
Winson8b1871d2015-11-20 09:56:20 -0800270 MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY));
Winson Chungbf5dbf12014-09-16 00:58:25 +0200271
Winson3c107162016-01-22 15:53:00 -0800272 // Optimization: Prevent overdraw of the thumbnail under the header view
Winsond477d532015-10-13 15:13:40 -0700273 mThumbnailView.updateClipToTaskBar(mHeaderView);
Winson5b7dd532015-12-01 16:02:12 -0800274
Winson Chungdcfa7972014-07-22 12:27:13 -0700275 setMeasuredDimension(width, height);
276 }
277
Winsonf24f2162016-01-05 12:11:55 -0800278 void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
Winson40a22732016-02-02 18:07:00 -0800279 AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
Winson53ec42c2015-10-28 15:55:35 -0700280 RecentsConfiguration config = Recents.getConfiguration();
Winsonbe8e6962016-02-01 14:27:52 -0800281 cancelTransformAnimation();
Winson53ec42c2015-10-28 15:55:35 -0700282
Winsonf24f2162016-01-05 12:11:55 -0800283 // Compose the animations for the transform
284 mTmpAnimators.clear();
Winsonad66efa2016-01-08 16:40:30 -0800285 toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
Winsonf24f2162016-01-05 12:11:55 -0800286 if (toAnimation.isImmediate()) {
Winson1bcf3c42016-02-10 13:29:39 -0800287 if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
288 setDimAlpha(toTransform.dimAlpha);
Winson55003902016-01-12 12:00:37 -0800289 }
Winson14991502016-02-15 15:40:08 -0800290 if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
291 mViewBounds.setAlpha(toTransform.viewOutlineAlpha);
292 }
Winsonc5fd3502016-01-18 15:18:37 -0800293 // Manually call back to the animator listener and update callback
Winsonbe8e6962016-02-01 14:27:52 -0800294 if (toAnimation.getListener() != null) {
295 toAnimation.getListener().onAnimationEnd(null);
Winsonf24f2162016-01-05 12:11:55 -0800296 }
Winsonc5fd3502016-01-18 15:18:37 -0800297 if (updateCallback != null) {
298 updateCallback.onAnimationUpdate(null);
299 }
Winson Chung012ef362014-07-31 18:36:25 -0700300 } else {
Winson40a22732016-02-02 18:07:00 -0800301 // Both the progress and the update are a function of the bounds movement of the task
Winson1bcf3c42016-02-10 13:29:39 -0800302 if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
303 ObjectAnimator anim = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
304 toTransform.dimAlpha);
Winson40a22732016-02-02 18:07:00 -0800305 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
Winsonf24f2162016-01-05 12:11:55 -0800306 }
Winson14991502016-02-15 15:40:08 -0800307 if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
308 ObjectAnimator anim = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
309 mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
310 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
311 }
Winsonc5fd3502016-01-18 15:18:37 -0800312 if (updateCallback != null) {
313 ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
314 updateCallbackAnim.addUpdateListener(updateCallback);
Winson40a22732016-02-02 18:07:00 -0800315 mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim));
Winsonc5fd3502016-01-18 15:18:37 -0800316 }
Winsonf24f2162016-01-05 12:11:55 -0800317
318 // Create the animator
319 mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
Winsonf24f2162016-01-05 12:11:55 -0800320 mTransformAnimation.start();
Jorim Jaggi899327f2016-02-25 20:44:18 -0500321 mTargetAnimationTransform.copyFrom(toTransform);
Winson Chung012ef362014-07-31 18:36:25 -0700322 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800323 }
324
325 /** Resets this view's properties */
326 void resetViewProperties() {
Winsonbe8e6962016-02-01 14:27:52 -0800327 cancelTransformAnimation();
Winson1bcf3c42016-02-10 13:29:39 -0800328 setDimAlpha(0);
Winson479f7442015-11-25 15:16:27 -0800329 setVisibility(View.VISIBLE);
330 getViewBounds().reset();
Winson4c84a8c2016-01-21 18:14:12 -0800331 getHeaderView().reset();
Winson Chungdcfa7972014-07-22 12:27:13 -0700332 TaskViewTransform.reset(this);
Winsonf24f2162016-01-05 12:11:55 -0800333
334 mActionButtonView.setScaleX(1f);
335 mActionButtonView.setScaleY(1f);
Winson6ea25882016-01-13 10:59:04 -0800336 mActionButtonView.setAlpha(0f);
Winsonf24f2162016-01-05 12:11:55 -0800337 mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
Winson Chungc6a16232014-04-01 14:04:48 -0700338 }
339
Winsonf24f2162016-01-05 12:11:55 -0800340 /**
Jorim Jaggi899327f2016-02-25 20:44:18 -0500341 * @return whether we are animating towards {@param transform}
342 */
343 boolean isAnimatingTo(TaskViewTransform transform) {
344 return mTransformAnimation != null && mTransformAnimation.isStarted()
345 && mTargetAnimationTransform.isSame(transform);
346 }
347
348 /**
Winsonf24f2162016-01-05 12:11:55 -0800349 * Cancels any current transform animations.
350 */
351 public void cancelTransformAnimation() {
Winsonad66efa2016-01-08 16:40:30 -0800352 Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
Winson Chung9f49df92014-05-07 18:08:34 -0700353 }
354
Winson Chungd16c5652015-01-26 16:11:07 -0800355 /** Enables/disables handling touch on this task view. */
356 void setTouchEnabled(boolean enabled) {
357 setOnClickListener(enabled ? this : null);
358 }
359
Winson Chung969f5862014-06-16 17:08:24 -0700360 /** Animates this task view if the user does not interact with the stack after a certain time. */
Winson Chunga0e88b52014-08-11 19:25:42 -0700361 void startNoUserInteractionAnimation() {
362 mHeaderView.startNoUserInteractionAnimation();
Winson Chung969f5862014-06-16 17:08:24 -0700363 }
364
365 /** 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 -0700366 void setNoUserInteractionState() {
367 mHeaderView.setNoUserInteractionState();
368 }
369
Winson Chungb0a28ea2014-10-28 15:21:35 -0700370 /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
371 void resetNoUserInteractionState() {
372 mHeaderView.resetNoUserInteractionState();
373 }
374
Winson Chunga0e88b52014-08-11 19:25:42 -0700375 /** Dismisses this task. */
376 void dismissTask() {
377 // Animate out the view and call the callback
378 final TaskView tv = this;
Winsonef064132016-01-05 12:11:31 -0800379 DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv, mTask);
380 dismissEvent.addPostAnimationCallback(new Runnable() {
Winson Chunga0e88b52014-08-11 19:25:42 -0700381 @Override
382 public void run() {
Winsonef064132016-01-05 12:11:31 -0800383 EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv));
Winson Chunga0e88b52014-08-11 19:25:42 -0700384 }
Winsonef064132016-01-05 12:11:31 -0800385 });
386 EventBus.getDefault().send(dismissEvent);
Winson Chung303e1ff2014-03-07 15:06:19 -0800387 }
388
Winson Chung5a9b0b02014-05-20 17:32:03 -0700389 /**
390 * Returns whether this view should be clipped, or any views below should clip against this
391 * view.
392 */
393 boolean shouldClipViewInStack() {
Winson36a5a2c2015-10-29 18:04:39 -0700394 // Never clip for freeform tasks or if invisible
395 if (mTask.isFreeformTask() || getVisibility() != View.VISIBLE) {
396 return false;
397 }
398 return mClipViewInStack;
Winson Chung5a9b0b02014-05-20 17:32:03 -0700399 }
400
401 /** Sets whether this view should be clipped, or clipped against. */
402 void setClipViewInStack(boolean clip) {
403 if (clip != mClipViewInStack) {
404 mClipViewInStack = clip;
Winson Chungfe03b402014-11-17 11:47:09 -0800405 if (mCb != null) {
406 mCb.onTaskViewClipStateChanged(this);
407 }
Winson Chung93748a12014-07-13 17:43:31 -0700408 }
409 }
410
Peter Schillerb124d562015-12-11 21:31:17 -0800411 public TaskViewHeader getHeaderView() {
412 return mHeaderView;
413 }
414
Winson1bcf3c42016-02-10 13:29:39 -0800415 /**
416 * Sets the current dim.
417 */
418 public void setDimAlpha(float dimAlpha) {
Winson1bcf3c42016-02-10 13:29:39 -0800419 mDimAlpha = dimAlpha;
Winsone693aaf2016-03-01 12:05:59 -0800420 mThumbnailView.setDimAlpha(dimAlpha);
421 mHeaderView.setDimAlpha(dimAlpha);
422 }
423
424 /**
425 * Sets the current dim without updating the header's dim.
426 */
427 public void setDimAlphaWithoutHeader(float dimAlpha) {
428 mDimAlpha = dimAlpha;
429 mThumbnailView.setDimAlpha(dimAlpha);
Winson Chunga26fb782014-06-12 17:52:39 -0700430 }
431
Winson1bcf3c42016-02-10 13:29:39 -0800432 /**
433 * Returns the current dim.
434 */
435 public float getDimAlpha() {
Winson Chung353c0b92014-10-16 17:43:23 -0700436 return mDimAlpha;
Winson Chunga26fb782014-06-12 17:52:39 -0700437 }
438
Winson1bcf3c42016-02-10 13:29:39 -0800439 /**
Winson0d14d4d2015-10-26 17:05:04 -0700440 * Explicitly sets the focused state of this task.
Winson Chung1e8d71b2014-05-16 17:05:22 -0700441 */
Winsonf24f2162016-01-05 12:11:55 -0800442 public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
Winson0d14d4d2015-10-26 17:05:04 -0700443 SystemServicesProxy ssp = Recents.getSystemServices();
Winson0d14d4d2015-10-26 17:05:04 -0700444 if (isFocused) {
445 if (requestViewFocus && !isFocused()) {
446 requestFocus();
447 }
448 if (requestViewFocus && !isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) {
449 requestAccessibilityFocus();
450 }
451 } else {
452 if (isAccessibilityFocused() && ssp.isTouchExplorationEnabled()) {
453 clearAccessibilityFocus();
454 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700455 }
456 }
457
Winson Chung931c51f2015-12-17 17:08:55 -0500458 /**
459 * Shows the action button.
460 * @param fadeIn whether or not to animate the action button in.
461 * @param fadeInDuration the duration of the action button animation, only used if
462 * {@param fadeIn} is true.
463 */
464 public void showActionButton(boolean fadeIn, int fadeInDuration) {
465 mActionButtonView.setVisibility(View.VISIBLE);
466
Winson65c851e2016-01-20 12:43:35 -0800467 if (fadeIn && mActionButtonView.getAlpha() < 1f) {
468 mActionButtonView.animate()
469 .alpha(1f)
470 .scaleX(1f)
471 .scaleY(1f)
472 .setDuration(fadeInDuration)
Winsonc0d70582016-01-29 10:24:39 -0800473 .setInterpolator(Interpolators.ALPHA_IN)
Winson65c851e2016-01-20 12:43:35 -0800474 .start();
Winson Chung931c51f2015-12-17 17:08:55 -0500475 } else {
Winsonf24f2162016-01-05 12:11:55 -0800476 mActionButtonView.setScaleX(1f);
477 mActionButtonView.setScaleY(1f);
Winson Chung931c51f2015-12-17 17:08:55 -0500478 mActionButtonView.setAlpha(1f);
Winsonf24f2162016-01-05 12:11:55 -0800479 mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
Winson Chung931c51f2015-12-17 17:08:55 -0500480 }
481 }
482
483 /**
484 * Immediately hides the action button.
Winsonf24f2162016-01-05 12:11:55 -0800485 *
486 * @param fadeOut whether or not to animate the action button out.
Winson Chung931c51f2015-12-17 17:08:55 -0500487 */
Winsonf24f2162016-01-05 12:11:55 -0800488 public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown,
489 final Animator.AnimatorListener animListener) {
Winson65c851e2016-01-20 12:43:35 -0800490 if (fadeOut && mActionButtonView.getAlpha() > 0f) {
491 if (scaleDown) {
492 float toScale = 0.9f;
Winsonf24f2162016-01-05 12:11:55 -0800493 mActionButtonView.animate()
Winson65c851e2016-01-20 12:43:35 -0800494 .scaleX(toScale)
495 .scaleY(toScale);
Winsonf24f2162016-01-05 12:11:55 -0800496 }
Winson65c851e2016-01-20 12:43:35 -0800497 mActionButtonView.animate()
498 .alpha(0f)
499 .setDuration(fadeOutDuration)
Winsonc0d70582016-01-29 10:24:39 -0800500 .setInterpolator(Interpolators.ALPHA_OUT)
Winson65c851e2016-01-20 12:43:35 -0800501 .withEndAction(new Runnable() {
502 @Override
503 public void run() {
504 if (animListener != null) {
505 animListener.onAnimationEnd(null);
506 }
507 mActionButtonView.setVisibility(View.INVISIBLE);
508 }
509 })
510 .start();
Winsonf24f2162016-01-05 12:11:55 -0800511 } else {
512 mActionButtonView.setAlpha(0f);
513 mActionButtonView.setVisibility(View.INVISIBLE);
514 if (animListener != null) {
515 animListener.onAnimationEnd(null);
516 }
517 }
518 }
519
520 /**** TaskStackAnimationHelper.Callbacks Implementation ****/
521
522 @Override
523 public void onPrepareLaunchTargetForEnterAnimation() {
524 // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
Winsone693aaf2016-03-01 12:05:59 -0800525 setDimAlphaWithoutHeader(0);
Winsonf24f2162016-01-05 12:11:55 -0800526 mActionButtonView.setAlpha(0f);
527 }
528
529 @Override
Winsone693aaf2016-03-01 12:05:59 -0800530 public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
531 boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
532 // Dim the view after the app window transitions down into recents
Winsonf24f2162016-01-05 12:11:55 -0800533 postAnimationTrigger.increment();
Winsone693aaf2016-03-01 12:05:59 -0800534 AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
535 Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
536 DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
537 anim.addListener(postAnimationTrigger.decrementOnAnimationEnd());
538 anim.start();
Winsonf24f2162016-01-05 12:11:55 -0800539
540 if (screenPinningEnabled) {
541 showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
542 }
543 }
544
545 @Override
546 public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
547 ReferenceCountedTrigger postAnimationTrigger) {
Winson68088812016-02-12 16:06:04 -0800548 // Un-dim the view before/while launching the target
Winson1bcf3c42016-02-10 13:29:39 -0800549 AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
Winsone693aaf2016-03-01 12:05:59 -0800550 Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
551 DIM_ALPHA, getDimAlpha(), 0));
552 anim.start();
Winsonf24f2162016-01-05 12:11:55 -0800553
554 postAnimationTrigger.increment();
555 hideActionButton(true /* fadeOut */, duration,
556 !screenPinningRequested /* scaleDown */,
557 postAnimationTrigger.decrementOnAnimationEnd());
Winson Chung931c51f2015-12-17 17:08:55 -0500558 }
559
Winson59924fe2016-03-17 14:13:18 -0700560 @Override
561 public void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled) {
562 if (screenPinningEnabled) {
563 showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
564 }
565 }
566
Winson Chung4d7b0922014-03-13 17:14:17 -0700567 /**** TaskCallbacks Implementation ****/
568
Winson Chung04dfe0d2014-03-14 14:06:29 -0700569 public void onTaskBound(Task t) {
Winson8be16342016-02-09 11:53:27 -0800570 SystemServicesProxy ssp = Recents.getSystemServices();
Winson Chung04dfe0d2014-03-14 14:06:29 -0700571 mTask = t;
Winson Chungb169ebd2015-12-14 13:57:36 -0500572 mTask.addCallback(this);
Winson8be16342016-02-09 11:53:27 -0800573 mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
Winson Chung303e1ff2014-03-07 15:06:19 -0800574 }
575
576 @Override
Winson21700932016-03-24 17:26:23 -0700577 public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
Winsonf24f2162016-01-05 12:11:55 -0800578 // Bind each of the views to the new task data
Winson21700932016-03-24 17:26:23 -0700579 mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode);
Winson8be16342016-02-09 11:53:27 -0800580 mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700581 mTaskDataLoaded = true;
Winson Chung4d7b0922014-03-13 17:14:17 -0700582 }
583
584 @Override
Winson Chung04dfe0d2014-03-14 14:06:29 -0700585 public void onTaskDataUnloaded() {
Winsonf24f2162016-01-05 12:11:55 -0800586 // Unbind each of the views from the task data and remove the task callback
587 mTask.removeCallback(this);
588 mThumbnailView.unbindFromTask();
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000589 mHeaderView.unbindFromTask(mTouchExplorationEnabled);
Winson Chung37c8d8e2014-03-24 14:53:07 -0700590 mTaskDataLoaded = false;
Winson Chung4d7b0922014-03-13 17:14:17 -0700591 }
592
Winson Chungd16c5652015-01-26 16:11:07 -0800593 @Override
Winsone7f138c2015-10-22 16:15:21 -0700594 public void onTaskStackIdChanged() {
Winson8be16342016-02-09 11:53:27 -0800595 mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
Winson Chung1f24c7e2014-07-11 17:06:48 -0700596 }
597
Winson Chungdcfa7972014-07-22 12:27:13 -0700598 /**** View.OnClickListener Implementation ****/
599
Winson Chung4d7b0922014-03-13 17:14:17 -0700600 @Override
Winson Chung7ea62ba2014-07-22 16:30:37 -0700601 public void onClick(final View v) {
Winson8be16342016-02-09 11:53:27 -0800602 if (mIsDisabledInSafeMode) {
603 Context context = getContext();
604 String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
605 if (mDisabledAppToast != null) {
606 mDisabledAppToast.cancel();
607 }
608 mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
609 mDisabledAppToast.show();
610 return;
611 }
612
Winson Chung48f2cda2015-12-11 13:20:12 -0500613 boolean screenPinningRequested = false;
Winson2536c7e2015-10-01 15:49:31 -0700614 if (v == mActionButtonView) {
615 // Reset the translation of the action button before we animate it out
616 mActionButtonView.setTranslationZ(0f);
Winson Chung48f2cda2015-12-11 13:20:12 -0500617 screenPinningRequested = true;
Winson Chung2e7f3bd2014-09-05 13:17:22 +0200618 }
Winson Chung48f2cda2015-12-11 13:20:12 -0500619 EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID,
620 screenPinningRequested));
Winson42329522016-02-05 10:39:46 -0800621
Chris Wren7c516842016-03-01 16:44:32 -0500622 MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
Winson42329522016-02-05 10:39:46 -0800623 mTask.key.getComponent().toString());
Winson Chung6cb485f2014-05-19 10:30:43 -0700624 }
Winsonbe7607a2015-10-01 17:24:51 -0700625
626 /**** View.OnLongClickListener Implementation ****/
627
628 @Override
629 public boolean onLongClick(View v) {
Winson37fc5132015-11-30 14:53:12 -0800630 SystemServicesProxy ssp = Recents.getSystemServices();
Winson5b7dd532015-12-01 16:02:12 -0800631 // Since we are clipping the view to the bounds, manually do the hit test
632 Rect clipBounds = new Rect(mViewBounds.mClipBounds);
633 clipBounds.scale(getScaleX());
634 boolean inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
635 if (v == this && inBounds && !ssp.hasDockedTask()) {
Winsonbe7607a2015-10-01 17:24:51 -0700636 // Start listening for drag events
637 setClipViewInStack(false);
638
Winson479f7442015-11-25 15:16:27 -0800639 mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
640 mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
Winsoneca4ab62015-11-04 10:50:28 -0800641
Winsonbe7607a2015-10-01 17:24:51 -0700642 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
Winson479f7442015-11-25 15:16:27 -0800643 EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
Winsonbe7607a2015-10-01 17:24:51 -0700644 return true;
645 }
646 return false;
647 }
648
649 /**** Events ****/
650
651 public final void onBusEvent(DragEndEvent event) {
Winsoneca4ab62015-11-04 10:50:28 -0800652 if (!(event.dropTarget instanceof TaskStack.DockState)) {
Winson Chungaaeaac12015-12-16 16:49:36 -0500653 event.addPostAnimationCallback(new Runnable() {
Winsoneca4ab62015-11-04 10:50:28 -0800654 @Override
655 public void run() {
Winsoneca4ab62015-11-04 10:50:28 -0800656 // Animate the drag view back from where it is, to the view location, then after
657 // it returns, update the clip state
658 setClipViewInStack(true);
659 }
660 });
661 }
Winsonbe7607a2015-10-01 17:24:51 -0700662 EventBus.getDefault().unregister(this);
663 }
Winson Chungbbb3d3d2016-01-30 01:09:20 +0000664
665 /**
666 * Reads current system flags related to accessibility and screen pinning.
667 */
668 private void readSystemFlags() {
669 SystemServicesProxy ssp = Recents.getSystemServices();
670 mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
671 }
Winson Chung7bb18852014-05-20 23:25:41 +0000672}