blob: 71f06cbf6e64bca3200bc08eead2ea550d03669b [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
Winson231bc9c2016-02-09 12:31:00 -080019import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
20
Winsonf24f2162016-01-05 12:11:55 -080021import android.animation.Animator;
Winson49df4202016-01-25 17:33:34 -080022import android.animation.ObjectAnimator;
Winson Chungd45aac42017-08-17 17:15:31 -070023import android.animation.ValueAnimator;
24import android.animation.ValueAnimator.AnimatorUpdateListener;
Jorim Jaggi192086e2016-03-11 17:17:03 +010025import android.app.ActivityOptions.OnAnimationStartedListener;
Winson Chung303e1ff2014-03-07 15:06:19 -080026import android.content.Context;
Lucas Dupine17ce522017-07-17 15:45:06 -070027import android.content.res.ColorStateList;
Winson Chung303e1ff2014-03-07 15:06:19 -080028import android.graphics.Canvas;
Winson49df4202016-01-25 17:33:34 -080029import android.graphics.Color;
Winson Chungd45aac42017-08-17 17:15:31 -070030import android.graphics.Point;
Lucas Dupine17ce522017-07-17 15:45:06 -070031import android.graphics.PointF;
Winson Chung303e1ff2014-03-07 15:06:19 -080032import android.graphics.Rect;
Winson Chungd45aac42017-08-17 17:15:31 -070033import android.graphics.drawable.ColorDrawable;
Winson4165d3362015-10-10 14:40:35 -070034import android.graphics.drawable.Drawable;
Winson882072b2015-10-12 11:26:33 -070035import android.util.ArraySet;
Winson Chung8e548f72014-06-24 14:40:53 -070036import android.util.AttributeSet;
Lucas Dupinae90ba82017-06-16 16:45:59 -070037import android.util.MathUtils;
Jorim Jaggic69bd222016-03-15 14:38:37 +010038import android.view.AppTransitionAnimationSpec;
Winson Chungecd9b302014-04-16 17:07:18 -070039import android.view.LayoutInflater;
Winsonbe7607a2015-10-01 17:24:51 -070040import android.view.MotionEvent;
Winsonc29ff002015-11-20 16:00:45 -080041import android.view.View;
Winson231bc9c2016-02-09 12:31:00 -080042import android.view.ViewDebug;
Jorim Jaggi870ab5a2015-12-02 18:37:54 -080043import android.view.ViewPropertyAnimator;
Winson Chungd45aac42017-08-17 17:15:31 -070044import android.view.Window;
Winson Chung653f70c22014-05-19 14:49:42 -070045import android.view.WindowInsets;
Winson Chung303e1ff2014-03-07 15:06:19 -080046import android.widget.FrameLayout;
Winson Chungde750de2015-12-11 10:26:06 -050047import android.widget.TextView;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050048
Lucas Dupin7224c1e2017-07-06 14:35:30 -070049import com.android.internal.colorextraction.ColorExtractor;
Lucas Dupine17ce522017-07-17 15:45:06 -070050import com.android.internal.colorextraction.drawable.GradientDrawable;
Winsondc8de842016-01-06 15:21:41 -080051import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010052import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Lucas Dupine17ce522017-07-17 15:45:06 -070053import com.android.settingslib.Utils;
Winsonc0d70582016-01-29 10:24:39 -080054import com.android.systemui.Interpolators;
Winson Chungd16c5652015-01-26 16:11:07 -080055import com.android.systemui.R;
Winsone7f138c2015-10-22 16:15:21 -070056import com.android.systemui.recents.Recents;
Winsonbe7607a2015-10-01 17:24:51 -070057import com.android.systemui.recents.RecentsActivity;
Winsonb1e71d02015-11-23 12:40:23 -080058import com.android.systemui.recents.RecentsActivityLaunchState;
Winson Chung303e1ff2014-03-07 15:06:19 -080059import com.android.systemui.recents.RecentsConfiguration;
Winsonc29ff002015-11-20 16:00:45 -080060import com.android.systemui.recents.RecentsDebugFlags;
Winson2536c7e2015-10-01 15:49:31 -070061import com.android.systemui.recents.events.EventBus;
Winson412e1802015-10-20 16:57:57 -070062import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
Jorim Jaggi192086e2016-03-11 17:17:03 +010063import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
Winson49df4202016-01-25 17:33:34 -080064import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
Winson8f6ee482016-03-18 17:51:48 -070065import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
Winson Chung48f2cda2015-12-11 13:20:12 -050066import com.android.systemui.recents.events.activity.LaunchTaskEvent;
Winsond2a03062016-04-15 11:19:07 -070067import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
Winson Chungb5026902017-05-03 12:45:13 -070068import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
Winson8f6ee482016-03-18 17:51:48 -070069import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
Winson Chungb5026902017-05-03 12:45:13 -070070import com.android.systemui.recents.events.component.ExpandPipEvent;
Winson3b6ba1a2016-03-22 15:37:54 -070071import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
Winson3b6ba1a2016-03-22 15:37:54 -070072import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
Jorim Jaggidd98d412015-11-18 15:57:38 -080073import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
74import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
Winsoneca4ab62015-11-04 10:50:28 -080075import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
Winson27c28f82016-05-05 16:16:50 -070076import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
Winsonbe7607a2015-10-01 17:24:51 -070077import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
78import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
Winson Chung9a742902015-12-11 10:25:40 -050079import com.android.systemui.recents.misc.ReferenceCountedTrigger;
Winson Chung1f24c7e2014-07-11 17:06:48 -070080import com.android.systemui.recents.misc.SystemServicesProxy;
Winsonc5fd3502016-01-18 15:18:37 -080081import com.android.systemui.recents.misc.Utilities;
Winson Chung303e1ff2014-03-07 15:06:19 -080082import com.android.systemui.recents.model.Task;
83import com.android.systemui.recents.model.TaskStack;
Jorim Jaggic69bd222016-03-15 14:38:37 +010084import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
Jorim Jaggicf4411d2017-05-12 17:27:46 +020085import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
Jorim Jaggi870ab5a2015-12-02 18:37:54 -080086import com.android.systemui.stackdivider.WindowManagerProxy;
87import com.android.systemui.statusbar.FlingAnimationUtils;
Lucas Dupinae90ba82017-06-16 16:45:59 -070088import com.android.systemui.statusbar.phone.ScrimController;
Winson Chung303e1ff2014-03-07 15:06:19 -080089
Winsond72c3152016-04-05 15:33:35 -070090import java.io.PrintWriter;
Winson Chung303e1ff2014-03-07 15:06:19 -080091import java.util.ArrayList;
Winson Chung6ac8bd62015-01-07 16:38:35 -080092import java.util.List;
Winson Chung303e1ff2014-03-07 15:06:19 -080093
Winson Chung303e1ff2014-03-07 15:06:19 -080094/**
95 * This view is the the top level layout that contains TaskStacks (which are laid out according
96 * to their SpaceNode bounds.
97 */
Lucas Dupine17ce522017-07-17 15:45:06 -070098public class RecentsView extends FrameLayout {
Winson Chung47c4c692014-03-17 10:17:11 -070099
Winsond72c3152016-04-05 15:33:35 -0700100 private static final String TAG = "RecentsView";
101
Winson49df4202016-01-25 17:33:34 -0800102 private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
Jorim Jaggi6e18e002015-06-02 17:07:39 -0700103
Winsonb677bcb2016-04-18 16:58:33 -0700104 private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
Winson3b6ba1a2016-03-22 15:37:54 -0700105 private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
106
Lucas Dupinae90ba82017-06-16 16:45:59 -0700107 private static final int BUSY_RECENTS_TASK_COUNT = 3;
108
Winson Chung9a742902015-12-11 10:25:40 -0500109 private TaskStackView mTaskStackView;
Winson8f6ee482016-03-18 17:51:48 -0700110 private TextView mStackActionButton;
Winson196e8612016-03-15 15:20:17 -0700111 private TextView mEmptyView;
Lucas Dupine17ce522017-07-17 15:45:06 -0700112 private final float mStackButtonShadowRadius;
113 private final PointF mStackButtonShadowDistance;
114 private final int mStackButtonShadowColor;
Winson49df4202016-01-25 17:33:34 -0800115
Winson Chung9a742902015-12-11 10:25:40 -0500116 private boolean mAwaitingFirstLayout = true;
117 private boolean mLastTaskLaunchedWasFreeform;
Winson231bc9c2016-02-09 12:31:00 -0800118
119 @ViewDebug.ExportedProperty(category="recents")
Winson08deff02016-08-05 13:58:31 -0700120 Rect mSystemInsets = new Rect();
Winson3e874742016-01-07 10:08:17 -0800121 private int mDividerSize;
Winsonbe7607a2015-10-01 17:24:51 -0700122
Lucas Dupinae90ba82017-06-16 16:45:59 -0700123 private float mBusynessFactor;
Lucas Dupin7aaa3532017-05-28 08:51:07 -0700124 private GradientDrawable mBackgroundScrim;
Winson Chungd45aac42017-08-17 17:15:31 -0700125 private ColorDrawable mMultiWindowBackgroundScrim;
126 private ValueAnimator mBackgroundScrimAnimator;
127 private Point mTmpDisplaySize = new Point();
128
129 private final AnimatorUpdateListener mUpdateBackgroundScrimAlpha = (animation) -> {
130 int alpha = (Integer) animation.getAnimatedValue();
131 mBackgroundScrim.setAlpha(alpha);
132 mMultiWindowBackgroundScrim.setAlpha(alpha);
133 };
Winson49df4202016-01-25 17:33:34 -0800134
Winson Chung9a742902015-12-11 10:25:40 -0500135 private RecentsTransitionHelper mTransitionHelper;
Winson231bc9c2016-02-09 12:31:00 -0800136 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
Winson Chung9a742902015-12-11 10:25:40 -0500137 private RecentsViewTouchHandler mTouchHandler;
Winson Chung9a742902015-12-11 10:25:40 -0500138 private final FlingAnimationUtils mFlingAnimationUtils;
Jorim Jaggi870ab5a2015-12-02 18:37:54 -0800139
Winson Chung303e1ff2014-03-07 15:06:19 -0800140 public RecentsView(Context context) {
Jorim Jaggi870ab5a2015-12-02 18:37:54 -0800141 this(context, null);
Winson Chung8e548f72014-06-24 14:40:53 -0700142 }
143
144 public RecentsView(Context context, AttributeSet attrs) {
145 this(context, attrs, 0);
146 }
147
148 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
149 this(context, attrs, defStyleAttr, 0);
150 }
151
152 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
153 super(context, attrs, defStyleAttr, defStyleRes);
Winsonbe7607a2015-10-01 17:24:51 -0700154 setWillNotDraw(false);
Winson3e874742016-01-07 10:08:17 -0800155
156 SystemServicesProxy ssp = Recents.getSystemServices();
Winsona1ededd2016-03-25 12:23:12 -0700157 mTransitionHelper = new RecentsTransitionHelper(getContext());
Winson3e874742016-01-07 10:08:17 -0800158 mDividerSize = ssp.getDockedDividerSize(context);
Winsonbe7607a2015-10-01 17:24:51 -0700159 mTouchHandler = new RecentsViewTouchHandler(this);
Jorim Jaggi870ab5a2015-12-02 18:37:54 -0800160 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
Lucas Dupin6672fb42017-05-01 15:59:41 -0700161 mBackgroundScrim = new GradientDrawable(context);
Winson Chungd45aac42017-08-17 17:15:31 -0700162 mMultiWindowBackgroundScrim = new ColorDrawable();
Winsonc29ff002015-11-20 16:00:45 -0800163
164 LayoutInflater inflater = LayoutInflater.from(context);
Lucas Dupin3ceaa0a2017-06-27 18:17:40 -0700165 mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
166 addView(mEmptyView);
167
Winson8f6ee482016-03-18 17:51:48 -0700168 if (RecentsDebugFlags.Static.EnableStackActionButton) {
Lucas Dupine17ce522017-07-17 15:45:06 -0700169 if (mStackActionButton != null) {
170 removeView(mStackActionButton);
171 }
Matthew Ng43db6d22017-06-27 15:29:39 -0700172 mStackActionButton = (TextView) inflater.inflate(Recents.getConfiguration()
173 .isLowRamDevice
174 ? R.layout.recents_low_ram_stack_action_button
175 : R.layout.recents_stack_action_button,
Winsona1ededd2016-03-25 12:23:12 -0700176 this, false);
Lucas Dupine17ce522017-07-17 15:45:06 -0700177 mStackActionButton.setOnClickListener(
178 v -> EventBus.getDefault().send(new DismissAllTaskViewsEvent()));
179
180 mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
181 mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
182 mStackActionButton.getShadowDy());
183 mStackButtonShadowColor = mStackActionButton.getShadowColor();
184 addView(mStackActionButton);
185 }
186
187 reevaluateStyles();
188 }
189
190 public void reevaluateStyles() {
191 int textColor = Utils.getColorAttr(mContext, R.attr.wallpaperTextColor);
192 boolean usingDarkText = Color.luminance(textColor) < 0.5f;
193
194 mEmptyView.setTextColor(textColor);
195 mEmptyView.setCompoundDrawableTintList(new ColorStateList(new int[][]{
196 {android.R.attr.state_enabled}}, new int[]{textColor}));
197
198 if (mStackActionButton != null) {
199 mStackActionButton.setTextColor(textColor);
200 // Enable/disable shadow if text color is already dark.
Lucas Dupin3ceaa0a2017-06-27 18:17:40 -0700201 if (usingDarkText) {
202 mStackActionButton.setShadowLayer(0, 0, 0, 0);
Lucas Dupine17ce522017-07-17 15:45:06 -0700203 } else {
204 mStackActionButton.setShadowLayer(mStackButtonShadowRadius,
205 mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
206 mStackButtonShadowColor);
Lucas Dupin3ceaa0a2017-06-27 18:17:40 -0700207 }
Winson91b225d2016-02-16 15:18:07 -0800208 }
Lucas Dupin3ceaa0a2017-06-27 18:17:40 -0700209
210 // Let's also require dark status and nav bars if the text is dark
211 int systemBarsStyle = usingDarkText ? View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
212 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0;
213
214 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
215 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
216 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
217 systemBarsStyle);
Winson Chungd16c5652015-01-26 16:11:07 -0800218 }
219
Winsona1ededd2016-03-25 12:23:12 -0700220 /**
221 * Called from RecentsActivity when it is relaunched.
222 */
Lucas Dupinae90ba82017-06-16 16:45:59 -0700223 public void onReload(TaskStack stack, boolean isResumingFromVisible) {
224 final RecentsConfiguration config = Recents.getConfiguration();
225 final RecentsActivityLaunchState launchState = config.getLaunchState();
226 final boolean isTaskStackEmpty = stack.getTaskCount() == 0;
Winson88737542016-02-17 13:27:33 -0800227
Winsona1ededd2016-03-25 12:23:12 -0700228 if (mTaskStackView == null) {
Winson88737542016-02-17 13:27:33 -0800229 isResumingFromVisible = false;
Winson88737542016-02-17 13:27:33 -0800230 mTaskStackView = new TaskStackView(getContext());
Winson59924fe2016-03-17 14:13:18 -0700231 mTaskStackView.setSystemInsets(mSystemInsets);
Winson147ecaf2015-09-16 16:49:55 -0700232 addView(mTaskStackView);
Winson Chung303e1ff2014-03-07 15:06:19 -0800233 }
Winson Chung9a742902015-12-11 10:25:40 -0500234
Winson88737542016-02-17 13:27:33 -0800235 // Reset the state
236 mAwaitingFirstLayout = !isResumingFromVisible;
237 mLastTaskLaunchedWasFreeform = false;
238
239 // Update the stack
Winsona1ededd2016-03-25 12:23:12 -0700240 mTaskStackView.onReload(isResumingFromVisible);
Lucas Dupinae90ba82017-06-16 16:45:59 -0700241 updateStack(stack, true /* setStackViewTasks */);
242 updateBusyness();
Winson88737542016-02-17 13:27:33 -0800243
244 if (isResumingFromVisible) {
245 // If we are already visible, then restore the background scrim
Lucas Dupinae90ba82017-06-16 16:45:59 -0700246 animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
Winson49df4202016-01-25 17:33:34 -0800247 } else {
Winson88737542016-02-17 13:27:33 -0800248 // If we are already occluded by the app, then set the final background scrim alpha now.
249 // Otherwise, defer until the enter animation completes to animate the scrim alpha with
250 // the tasks for the home animation.
Winsonc69249f2016-03-28 13:38:39 -0700251 if (launchState.launchedViaDockGesture || launchState.launchedFromApp
Winsona1ededd2016-03-25 12:23:12 -0700252 || isTaskStackEmpty) {
Lucas Dupinae90ba82017-06-16 16:45:59 -0700253 mBackgroundScrim.setAlpha((int) (getOpaqueScrimAlpha() * 255));
Winson88737542016-02-17 13:27:33 -0800254 } else {
255 mBackgroundScrim.setAlpha(0);
256 }
Winson Chungd45aac42017-08-17 17:15:31 -0700257 mMultiWindowBackgroundScrim.setAlpha(mBackgroundScrim.getAlpha());
Winson49df4202016-01-25 17:33:34 -0800258 }
Winsona1ededd2016-03-25 12:23:12 -0700259 }
260
261 /**
262 * Called from RecentsActivity when the task stack is updated.
263 */
Winsond2a03062016-04-15 11:19:07 -0700264 public void updateStack(TaskStack stack, boolean setStackViewTasks) {
Winsond2a03062016-04-15 11:19:07 -0700265 if (setStackViewTasks) {
266 mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
267 }
Winson49df4202016-01-25 17:33:34 -0800268
Winson Chung9a742902015-12-11 10:25:40 -0500269 // Update the top level view's visibilities
Winson4b057c62016-01-12 15:01:52 -0800270 if (stack.getTaskCount() > 0) {
Winson Chung9a742902015-12-11 10:25:40 -0500271 hideEmptyView();
Winsonc29ff002015-11-20 16:00:45 -0800272 } else {
Winson196e8612016-03-15 15:20:17 -0700273 showEmptyView(R.string.recents_empty_message);
Winsonc29ff002015-11-20 16:00:45 -0800274 }
Winson Chung19fc1172014-07-31 18:40:03 -0700275 }
276
Winsonb1e71d02015-11-23 12:40:23 -0800277 /**
Lucas Dupinae90ba82017-06-16 16:45:59 -0700278 * Animates the scrim opacity based on how many tasks are visible.
279 * Called from {@link RecentsActivity} when tasks are dismissed.
280 */
281 public void updateScrimOpacity() {
282 if (updateBusyness()) {
283 animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
284 }
285 }
286
287 /**
288 * Updates the busyness factor.
289 *
290 * @return True if it changed.
291 */
292 private boolean updateBusyness() {
293 final int taskCount = mTaskStackView.getStack().getStackTaskCount();
294 final float busyness = Math.min(taskCount, BUSY_RECENTS_TASK_COUNT)
295 / (float) BUSY_RECENTS_TASK_COUNT;
296 if (mBusynessFactor == busyness) {
297 return false;
298 } else {
299 mBusynessFactor = busyness;
300 return true;
301 }
302 }
303
304 /**
Winsona1ededd2016-03-25 12:23:12 -0700305 * Returns the current TaskStack.
306 */
307 public TaskStack getStack() {
Matthew Ngb7164cd2016-10-25 17:06:26 -0700308 return mTaskStackView.getStack();
Winsona1ededd2016-03-25 12:23:12 -0700309 }
310
Lucas Dupinae90ba82017-06-16 16:45:59 -0700311 /**
Winsone8a4eff2016-03-28 16:20:35 -0700312 * Returns the window background scrim.
313 */
Winson Chungd45aac42017-08-17 17:15:31 -0700314 public void updateBackgroundScrim(Window window, boolean isInMultiWindow) {
315 if (isInMultiWindow) {
316 mBackgroundScrim.setCallback(null);
317 window.setBackgroundDrawable(mMultiWindowBackgroundScrim);
318 } else {
319 mMultiWindowBackgroundScrim.setCallback(null);
320 window.setBackgroundDrawable(mBackgroundScrim);
321 }
Winsone8a4eff2016-03-28 16:20:35 -0700322 }
323
Winsona1ededd2016-03-25 12:23:12 -0700324 /**
Winsonb1e71d02015-11-23 12:40:23 -0800325 * Returns whether the last task launched was in the freeform stack or not.
326 */
327 public boolean isLastTaskLaunchedFreeform() {
328 return mLastTaskLaunchedWasFreeform;
329 }
330
Winson Chung1e8d71b2014-05-16 17:05:22 -0700331 /** Launches the focused task from the first stack if possible */
Winson42329522016-02-05 10:39:46 -0800332 public boolean launchFocusedTask(int logEvent) {
Winson147ecaf2015-09-16 16:49:55 -0700333 if (mTaskStackView != null) {
Winson142af422015-11-09 10:39:57 -0800334 Task task = mTaskStackView.getFocusedTask();
335 if (task != null) {
336 TaskView taskView = mTaskStackView.getChildViewForTask(task);
Winson Chung48f2cda2015-12-11 13:20:12 -0500337 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
338 INVALID_STACK_ID, false));
Winson42329522016-02-05 10:39:46 -0800339
340 if (logEvent != 0) {
341 MetricsLogger.action(getContext(), logEvent,
342 task.key.getComponent().toString());
343 }
Winson142af422015-11-09 10:39:57 -0800344 return true;
Winson Chung1e8d71b2014-05-16 17:05:22 -0700345 }
346 }
Winson Chung1e8d71b2014-05-16 17:05:22 -0700347 return false;
348 }
349
Winsona0731a12015-12-02 15:10:14 -0800350 /** Launches the task that recents was launched from if possible */
351 public boolean launchPreviousTask() {
Winson Chungb5026902017-05-03 12:45:13 -0700352 if (Recents.getConfiguration().getLaunchState().launchedFromPipApp) {
353 // If the app auto-entered PiP on the way to Recents, then just re-expand it
354 EventBus.getDefault().send(new ExpandPipEvent());
355 return true;
356 }
357
Winsona0731a12015-12-02 15:10:14 -0800358 if (mTaskStackView != null) {
Matthew Ngb7164cd2016-10-25 17:06:26 -0700359 Task task = getStack().getLaunchTarget();
Winsona0731a12015-12-02 15:10:14 -0800360 if (task != null) {
361 TaskView taskView = mTaskStackView.getChildViewForTask(task);
Winson Chung48f2cda2015-12-11 13:20:12 -0500362 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
363 INVALID_STACK_ID, false));
Winsona0731a12015-12-02 15:10:14 -0800364 return true;
365 }
366 }
367 return false;
368 }
369
Skuhne8aa7d162015-03-20 13:40:53 -0700370 /** Launches a given task. */
Filip Gruszczynskif48b5852015-10-13 10:28:22 -0700371 public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
Winson147ecaf2015-09-16 16:49:55 -0700372 if (mTaskStackView != null) {
Skuhne8aa7d162015-03-20 13:40:53 -0700373 // Iterate the stack views and try and find the given task.
Winson147ecaf2015-09-16 16:49:55 -0700374 List<TaskView> taskViews = mTaskStackView.getTaskViews();
Skuhne8aa7d162015-03-20 13:40:53 -0700375 int taskViewCount = taskViews.size();
376 for (int j = 0; j < taskViewCount; j++) {
377 TaskView tv = taskViews.get(j);
378 if (tv.getTask() == task) {
Winson Chung48f2cda2015-12-11 13:20:12 -0500379 EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds,
380 destinationStack, false));
Skuhne8aa7d162015-03-20 13:40:53 -0700381 return true;
382 }
383 }
384 }
385 return false;
386 }
387
Winson Chung9a742902015-12-11 10:25:40 -0500388 /**
389 * Hides the task stack and shows the empty view.
390 */
Winson196e8612016-03-15 15:20:17 -0700391 public void showEmptyView(int msgResId) {
Winson Chung9a742902015-12-11 10:25:40 -0500392 mTaskStackView.setVisibility(View.INVISIBLE);
Winson196e8612016-03-15 15:20:17 -0700393 mEmptyView.setText(msgResId);
Winson Chung9a742902015-12-11 10:25:40 -0500394 mEmptyView.setVisibility(View.VISIBLE);
395 mEmptyView.bringToFront();
Winson8f6ee482016-03-18 17:51:48 -0700396 if (RecentsDebugFlags.Static.EnableStackActionButton) {
397 mStackActionButton.bringToFront();
Winson91b225d2016-02-16 15:18:07 -0800398 }
Winson Chung9a742902015-12-11 10:25:40 -0500399 }
400
401 /**
402 * Shows the task stack and hides the empty view.
403 */
404 public void hideEmptyView() {
405 mEmptyView.setVisibility(View.INVISIBLE);
406 mTaskStackView.setVisibility(View.VISIBLE);
Winson Chung9a742902015-12-11 10:25:40 -0500407 mTaskStackView.bringToFront();
Winson8f6ee482016-03-18 17:51:48 -0700408 if (RecentsDebugFlags.Static.EnableStackActionButton) {
409 mStackActionButton.bringToFront();
Winson91b225d2016-02-16 15:18:07 -0800410 }
Winson Chung772b6b12014-07-03 15:54:02 -0700411 }
412
Lucas Dupine17ce522017-07-17 15:45:06 -0700413 /**
414 * Set the color of the scrim.
415 *
416 * @param scrimColors Colors to use.
417 * @param animated Interpolate colors if true.
418 */
419 public void setScrimColors(ColorExtractor.GradientColors scrimColors, boolean animated) {
420 mBackgroundScrim.setColors(scrimColors, animated);
Winson Chungd45aac42017-08-17 17:15:31 -0700421 int alpha = mMultiWindowBackgroundScrim.getAlpha();
422 mMultiWindowBackgroundScrim.setColor(scrimColors.getMainColor());
423 mMultiWindowBackgroundScrim.setAlpha(alpha);
Lucas Dupine17ce522017-07-17 15:45:06 -0700424 }
425
Winsonbe7607a2015-10-01 17:24:51 -0700426 @Override
427 protected void onAttachedToWindow() {
428 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
Winsonc8a4cdc2016-01-19 11:30:51 -0800429 EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2);
Winsonbe7607a2015-10-01 17:24:51 -0700430 super.onAttachedToWindow();
431 }
432
433 @Override
434 protected void onDetachedFromWindow() {
435 super.onDetachedFromWindow();
436 EventBus.getDefault().unregister(this);
437 EventBus.getDefault().unregister(mTouchHandler);
438 }
439
Winson Chungf7bca432014-04-30 17:11:13 -0700440 /**
441 * This is called with the full size of the window since we are handling our own insets.
442 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800443 @Override
444 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
445 int width = MeasureSpec.getSize(widthMeasureSpec);
446 int height = MeasureSpec.getSize(heightMeasureSpec);
Winson Chungbd912972014-03-18 14:36:35 -0700447
Winson59924fe2016-03-17 14:13:18 -0700448 if (mTaskStackView.getVisibility() != GONE) {
Winson147ecaf2015-09-16 16:49:55 -0700449 mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
Winson Chung303e1ff2014-03-07 15:06:19 -0800450 }
451
Winson49df4202016-01-25 17:33:34 -0800452 // Measure the empty view to the full size of the screen
453 if (mEmptyView.getVisibility() != GONE) {
Winson196e8612016-03-15 15:20:17 -0700454 measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
455 MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
Winson49df4202016-01-25 17:33:34 -0800456 }
Winson Chung9a742902015-12-11 10:25:40 -0500457
Winson8f6ee482016-03-18 17:51:48 -0700458 if (RecentsDebugFlags.Static.EnableStackActionButton) {
459 // Measure the stack action button within the constraints of the space above the stack
Jiaquan He26f637b2016-12-27 14:44:14 -0800460 Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect();
Winson8f6ee482016-03-18 17:51:48 -0700461 measureChild(mStackActionButton,
Winson19852372016-03-30 18:26:49 -0700462 MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
463 MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
Winson214f0f02016-01-26 13:37:37 -0800464 }
Winson Chung9a742902015-12-11 10:25:40 -0500465
Winson Chung303e1ff2014-03-07 15:06:19 -0800466 setMeasuredDimension(width, height);
467 }
468
Winson Chungf7bca432014-04-30 17:11:13 -0700469 /**
470 * This is called with the full size of the window since we are handling our own insets.
471 */
Winson Chung303e1ff2014-03-07 15:06:19 -0800472 @Override
473 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winson59924fe2016-03-17 14:13:18 -0700474 if (mTaskStackView.getVisibility() != GONE) {
Winson147ecaf2015-09-16 16:49:55 -0700475 mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
Winson Chung303e1ff2014-03-07 15:06:19 -0800476 }
Winsonbe7607a2015-10-01 17:24:51 -0700477
Winson Chung9a742902015-12-11 10:25:40 -0500478 // Layout the empty view
Winson49df4202016-01-25 17:33:34 -0800479 if (mEmptyView.getVisibility() != GONE) {
Winson196e8612016-03-15 15:20:17 -0700480 int leftRightInsets = mSystemInsets.left + mSystemInsets.right;
481 int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom;
482 int childWidth = mEmptyView.getMeasuredWidth();
483 int childHeight = mEmptyView.getMeasuredHeight();
Winson24251b62016-04-04 18:45:24 -0700484 int childLeft = left + mSystemInsets.left +
485 Math.max(0, (right - left - leftRightInsets - childWidth)) / 2;
486 int childTop = top + mSystemInsets.top +
487 Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2;
Winson196e8612016-03-15 15:20:17 -0700488 mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
Winson49df4202016-01-25 17:33:34 -0800489 }
Winson Chung9a742902015-12-11 10:25:40 -0500490
Lucas Dupin6672fb42017-05-01 15:59:41 -0700491 // Needs to know the screen size since the gradient never scales up or down
492 // even when bounds change.
Winson Chungd45aac42017-08-17 17:15:31 -0700493 mContext.getDisplay().getRealSize(mTmpDisplaySize);
494 mBackgroundScrim.setScreenSize(mTmpDisplaySize.x, mTmpDisplaySize.y);
Lucas Dupin6672fb42017-05-01 15:59:41 -0700495 mBackgroundScrim.setBounds(left, top, right, bottom);
Winson Chungd45aac42017-08-17 17:15:31 -0700496 mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
Lucas Dupin6672fb42017-05-01 15:59:41 -0700497
Winson8f6ee482016-03-18 17:51:48 -0700498 if (RecentsDebugFlags.Static.EnableStackActionButton) {
499 // Layout the stack action button such that its drawable is start-aligned with the
500 // stack, vertically centered in the available space above the stack
Winson19852372016-03-30 18:26:49 -0700501 Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
502 mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
503 buttonBounds.bottom);
Winson214f0f02016-01-26 13:37:37 -0800504 }
505
Winsonb1e71d02015-11-23 12:40:23 -0800506 if (mAwaitingFirstLayout) {
507 mAwaitingFirstLayout = false;
Winsonb1e71d02015-11-23 12:40:23 -0800508 // If launched via dragging from the nav bar, then we should translate the whole view
509 // down offscreen
510 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
511 if (launchState.launchedViaDragGesture) {
512 setTranslationY(getMeasuredHeight());
Jorim Jaggi0ca88262015-12-07 17:15:16 -0800513 } else {
514 setTranslationY(0f);
Winsonb1e71d02015-11-23 12:40:23 -0800515 }
Matthew Ng43db6d22017-06-27 15:29:39 -0700516
517 if (Recents.getConfiguration().isLowRamDevice
518 && mEmptyView.getVisibility() == View.VISIBLE) {
519 animateEmptyView(true /* show */, null /* postAnimationTrigger */);
520 }
Winsonb1e71d02015-11-23 12:40:23 -0800521 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800522 }
523
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700524 @Override
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700525 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Winson35f30502015-09-28 11:24:36 -0700526 mSystemInsets.set(insets.getSystemWindowInsets());
Winson59924fe2016-03-17 14:13:18 -0700527 mTaskStackView.setSystemInsets(mSystemInsets);
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700528 requestLayout();
Winsonc29ff002015-11-20 16:00:45 -0800529 return insets;
Winson Chung8eaeb7d2014-06-25 15:10:59 -0700530 }
531
Winsonbe7607a2015-10-01 17:24:51 -0700532 @Override
533 public boolean onInterceptTouchEvent(MotionEvent ev) {
534 return mTouchHandler.onInterceptTouchEvent(ev);
535 }
536
537 @Override
538 public boolean onTouchEvent(MotionEvent ev) {
539 return mTouchHandler.onTouchEvent(ev);
540 }
541
542 @Override
Winsonbe8e6962016-02-01 14:27:52 -0800543 public void onDrawForeground(Canvas canvas) {
544 super.onDrawForeground(canvas);
545
Winson036693c2016-01-14 17:16:06 -0800546 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
547 for (int i = visDockStates.size() - 1; i >= 0; i--) {
Winson99ef4582016-04-18 16:57:27 -0700548 visDockStates.get(i).viewState.draw(canvas);
Winsonbe7607a2015-10-01 17:24:51 -0700549 }
550 }
551
Winson4165d3362015-10-10 14:40:35 -0700552 @Override
553 protected boolean verifyDrawable(Drawable who) {
Winson036693c2016-01-14 17:16:06 -0800554 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
555 for (int i = visDockStates.size() - 1; i >= 0; i--) {
556 Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
Winson882072b2015-10-12 11:26:33 -0700557 if (d == who) {
558 return true;
559 }
560 }
561 return super.verifyDrawable(who);
Winson4165d3362015-10-10 14:40:35 -0700562 }
563
Winsonbe7607a2015-10-01 17:24:51 -0700564 /**** EventBus Events ****/
565
Winson Chung48f2cda2015-12-11 13:20:12 -0500566 public final void onBusEvent(LaunchTaskEvent event) {
567 mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
Matthew Ngb7164cd2016-10-25 17:06:26 -0700568 mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView,
Jorim Jaggicf4411d2017-05-12 17:27:46 +0200569 event.taskView, event.screenPinningRequested, event.targetTaskStack);
Matthew Ng43db6d22017-06-27 15:29:39 -0700570 if (Recents.getConfiguration().isLowRamDevice) {
571 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, false /* translate */);
572 }
Winson Chung48f2cda2015-12-11 13:20:12 -0500573 }
574
Winsonef064132016-01-05 12:11:31 -0800575 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
Winson50448632016-02-01 18:04:59 -0800576 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
Winson8f6ee482016-03-18 17:51:48 -0700577 if (RecentsDebugFlags.Static.EnableStackActionButton) {
578 // Hide the stack action button
579 hideStackActionButton(taskViewExitToHomeDuration, false /* translate */);
Winson91b225d2016-02-16 15:18:07 -0800580 }
Winson49df4202016-01-25 17:33:34 -0800581 animateBackgroundScrim(0f, taskViewExitToHomeDuration);
Matthew Ng43db6d22017-06-27 15:29:39 -0700582
583 if (Recents.getConfiguration().isLowRamDevice) {
584 animateEmptyView(false /* show */, event.getAnimationTrigger());
585 }
Winsonef064132016-01-05 12:11:31 -0800586 }
587
Winsonbe7607a2015-10-01 17:24:51 -0700588 public final void onBusEvent(DragStartEvent event) {
Matthew Ngb7035f32017-01-19 11:04:50 -0800589 updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(),
Winson3e874742016-01-07 10:08:17 -0800590 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
Winson99ef4582016-04-18 16:57:27 -0700591 TaskStack.DockState.NONE.viewState.hintTextAlpha,
Winson3e874742016-01-07 10:08:17 -0800592 true /* animateAlpha */, false /* animateBounds */);
Winsonb677bcb2016-04-18 16:58:33 -0700593
594 // Temporarily hide the stack action button without changing visibility
595 if (mStackActionButton != null) {
596 mStackActionButton.animate()
597 .alpha(0f)
598 .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
599 .setInterpolator(Interpolators.ALPHA_OUT)
600 .start();
601 }
Winsonbe7607a2015-10-01 17:24:51 -0700602 }
603
Winsoneca4ab62015-11-04 10:50:28 -0800604 public final void onBusEvent(DragDropTargetChangedEvent event) {
605 if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
Matthew Ngb7035f32017-01-19 11:04:50 -0800606 updateVisibleDockRegions(
607 Recents.getConfiguration().getDockStatesForCurrentOrientation(),
Winson3e874742016-01-07 10:08:17 -0800608 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
Winson99ef4582016-04-18 16:57:27 -0700609 TaskStack.DockState.NONE.viewState.hintTextAlpha,
Winson3e874742016-01-07 10:08:17 -0800610 true /* animateAlpha */, true /* animateBounds */);
Winson882072b2015-10-12 11:26:33 -0700611 } else {
Winsoneca4ab62015-11-04 10:50:28 -0800612 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
Winson3e874742016-01-07 10:08:17 -0800613 updateVisibleDockRegions(new TaskStack.DockState[] {dockState},
Winson99ef4582016-04-18 16:57:27 -0700614 false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
Winson3e874742016-01-07 10:08:17 -0800615 true /* animateBounds */);
Winson882072b2015-10-12 11:26:33 -0700616 }
Winson19852372016-03-30 18:26:49 -0700617 if (mStackActionButton != null) {
618 event.addPostAnimationCallback(new Runnable() {
619 @Override
620 public void run() {
621 // Move the clear all button to its new position
622 Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
623 mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top,
624 buttonBounds.right, buttonBounds.bottom);
625 }
626 });
627 }
Winsonbe7607a2015-10-01 17:24:51 -0700628 }
629
630 public final void onBusEvent(final DragEndEvent event) {
Winson479f7442015-11-25 15:16:27 -0800631 // Handle the case where we drop onto a dock region
632 if (event.dropTarget instanceof TaskStack.DockState) {
Winson3e874742016-01-07 10:08:17 -0800633 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
634
635 // Hide the dock region
Winson99ef4582016-04-18 16:57:27 -0700636 updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
Winson3e874742016-01-07 10:08:17 -0800637 false /* animateAlpha */, false /* animateBounds */);
638
Winson3e874742016-01-07 10:08:17 -0800639 // We translated the view but we need to animate it back from the current layout-space
640 // rect to its final layout-space rect
Winson27c28f82016-05-05 16:16:50 -0700641 Utilities.setViewFrameFromTranslation(event.taskView);
Jorim Jaggic69bd222016-03-15 14:38:37 +0100642
643 // Dock the task and launch it
644 SystemServicesProxy ssp = Recents.getSystemServices();
Winson27c28f82016-05-05 16:16:50 -0700645 if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) {
646 final OnAnimationStartedListener startedListener =
647 new OnAnimationStartedListener() {
648 @Override
649 public void onAnimationStarted() {
650 EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
651 // Remove the task and don't bother relaying out, as all the tasks will be
652 // relaid out when the stack changes on the multiwindow change event
Matthew Ngb7164cd2016-10-25 17:06:26 -0700653 getStack().removeTask(event.task, null, true /* fromDockGesture */);
Winson27c28f82016-05-05 16:16:50 -0700654 }
655 };
Winsona0a8ae42015-12-01 10:43:02 -0800656
Winson27c28f82016-05-05 16:16:50 -0700657 final Rect taskRect = getTaskRect(event.taskView);
Jorim Jaggicf4411d2017-05-12 17:27:46 +0200658 AppTransitionAnimationSpecsFuture future =
Winson27c28f82016-05-05 16:16:50 -0700659 mTransitionHelper.getAppTransitionFuture(
660 new AnimationSpecComposer() {
661 @Override
662 public List<AppTransitionAnimationSpec> composeSpecs() {
663 return mTransitionHelper.composeDockAnimationSpec(
664 event.taskView, taskRect);
665 }
666 });
Jorim Jaggicf4411d2017-05-12 17:27:46 +0200667 ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(),
Winson27c28f82016-05-05 16:16:50 -0700668 mTransitionHelper.wrapStartedListener(startedListener),
669 true /* scaleUp */);
670
671 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
672 event.task.getTopComponent().flattenToShortString());
673 } else {
Matthew Ngb7164cd2016-10-25 17:06:26 -0700674 EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
Winson27c28f82016-05-05 16:16:50 -0700675 event.taskView));
676 }
Winson3e874742016-01-07 10:08:17 -0800677 } else {
678 // Animate the overlay alpha back to 0
Winson99ef4582016-04-18 16:57:27 -0700679 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
Winson3e874742016-01-07 10:08:17 -0800680 true /* animateAlpha */, false /* animateBounds */);
Winsonbe7607a2015-10-01 17:24:51 -0700681 }
Winsonb677bcb2016-04-18 16:58:33 -0700682
683 // Show the stack action button again without changing visibility
684 if (mStackActionButton != null) {
685 mStackActionButton.animate()
686 .alpha(1f)
687 .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION)
688 .setInterpolator(Interpolators.ALPHA_IN)
689 .start();
690 }
Winsonbe7607a2015-10-01 17:24:51 -0700691 }
Winson4165d3362015-10-10 14:40:35 -0700692
Winson27c28f82016-05-05 16:16:50 -0700693 public final void onBusEvent(final DragEndCancelledEvent event) {
694 // Animate the overlay alpha back to 0
695 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
696 true /* animateAlpha */, false /* animateBounds */);
697 }
698
Jorim Jaggic69bd222016-03-15 14:38:37 +0100699 private Rect getTaskRect(TaskView taskView) {
700 int[] location = taskView.getLocationOnScreen();
701 int viewX = location[0];
702 int viewY = location[1];
703 return new Rect(viewX, viewY,
704 (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
705 (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
706 }
707
Jorim Jaggidd98d412015-11-18 15:57:38 -0800708 public final void onBusEvent(DraggingInRecentsEvent event) {
Jorim Jaggi54b1a6d2015-11-24 17:48:26 -0800709 if (mTaskStackView.getTaskViews().size() > 0) {
710 setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
711 }
Jorim Jaggidd98d412015-11-18 15:57:38 -0800712 }
713
714 public final void onBusEvent(DraggingInRecentsEndedEvent event) {
Jorim Jaggi870ab5a2015-12-02 18:37:54 -0800715 ViewPropertyAnimator animator = animate();
Jorim Jaggi8fccfe22015-12-03 16:58:08 -0800716 if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
Jorim Jaggi870ab5a2015-12-02 18:37:54 -0800717 animator.translationY(getHeight());
718 animator.withEndAction(new Runnable() {
719 @Override
720 public void run() {
721 WindowManagerProxy.getInstance().maximizeDockedStack();
722 }
723 });
724 mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity);
725 } else {
726 animator.translationY(0f);
727 animator.setListener(null);
728 mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity);
729 }
730 animator.start();
Jorim Jaggidd98d412015-11-18 15:57:38 -0800731 }
732
Winson49df4202016-01-25 17:33:34 -0800733 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
734 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
Winsonc69249f2016-03-28 13:38:39 -0700735 if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
Matthew Ngb7164cd2016-10-25 17:06:26 -0700736 && getStack().getTaskCount() > 0) {
Lucas Dupinae90ba82017-06-16 16:45:59 -0700737 animateBackgroundScrim(getOpaqueScrimAlpha(),
Winson50448632016-02-01 18:04:59 -0800738 TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
Winson49df4202016-01-25 17:33:34 -0800739 }
Matthew Ng43db6d22017-06-27 15:29:39 -0700740 if (Recents.getConfiguration().isLowRamDevice && mEmptyView.getVisibility() != View.VISIBLE) {
741 showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, false /* translate */);
742 }
Winson49df4202016-01-25 17:33:34 -0800743 }
744
Winson3b6ba1a2016-03-22 15:37:54 -0700745 public final void onBusEvent(AllTaskViewsDismissedEvent event) {
746 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
747 }
748
749 public final void onBusEvent(DismissAllTaskViewsEvent event) {
750 SystemServicesProxy ssp = Recents.getSystemServices();
751 if (!ssp.hasDockedTask()) {
752 // Animate the background away only if we are dismissing Recents to home
753 animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION);
754 }
755 }
756
Winson8f6ee482016-03-18 17:51:48 -0700757 public final void onBusEvent(ShowStackActionButtonEvent event) {
758 if (!RecentsDebugFlags.Static.EnableStackActionButton) {
Winson91b225d2016-02-16 15:18:07 -0800759 return;
760 }
761
Winson3b6ba1a2016-03-22 15:37:54 -0700762 showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
Winson49df4202016-01-25 17:33:34 -0800763 }
764
Winson8f6ee482016-03-18 17:51:48 -0700765 public final void onBusEvent(HideStackActionButtonEvent event) {
766 if (!RecentsDebugFlags.Static.EnableStackActionButton) {
Winson91b225d2016-02-16 15:18:07 -0800767 return;
768 }
769
Winson3b6ba1a2016-03-22 15:37:54 -0700770 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
Winson49df4202016-01-25 17:33:34 -0800771 }
772
Winsond2a03062016-04-15 11:19:07 -0700773 public final void onBusEvent(MultiWindowStateChangedEvent event) {
774 updateStack(event.stack, false /* setStackViewTasks */);
775 }
776
Winson Chungb5026902017-05-03 12:45:13 -0700777 public final void onBusEvent(ShowEmptyViewEvent event) {
778 showEmptyView(R.string.recents_empty_message);
779 }
780
Winsonc29ff002015-11-20 16:00:45 -0800781 /**
Winson8f6ee482016-03-18 17:51:48 -0700782 * Shows the stack action button.
Winsonc29ff002015-11-20 16:00:45 -0800783 */
Winson8f6ee482016-03-18 17:51:48 -0700784 private void showStackActionButton(final int duration, final boolean translate) {
785 if (!RecentsDebugFlags.Static.EnableStackActionButton) {
Winson91b225d2016-02-16 15:18:07 -0800786 return;
787 }
788
Winson49df4202016-01-25 17:33:34 -0800789 final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
Winson8f6ee482016-03-18 17:51:48 -0700790 if (mStackActionButton.getVisibility() == View.INVISIBLE) {
791 mStackActionButton.setVisibility(View.VISIBLE);
792 mStackActionButton.setAlpha(0f);
Winson49df4202016-01-25 17:33:34 -0800793 if (translate) {
Winson8f6ee482016-03-18 17:51:48 -0700794 mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
Winson214f0f02016-01-26 13:37:37 -0800795 } else {
Winson8f6ee482016-03-18 17:51:48 -0700796 mStackActionButton.setTranslationY(0f);
Winson49df4202016-01-25 17:33:34 -0800797 }
798 postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
Winsonf24f2162016-01-05 12:11:55 -0800799 @Override
800 public void run() {
Winson49df4202016-01-25 17:33:34 -0800801 if (translate) {
Winson8f6ee482016-03-18 17:51:48 -0700802 mStackActionButton.animate()
Winson49df4202016-01-25 17:33:34 -0800803 .translationY(0f);
804 }
Winson8f6ee482016-03-18 17:51:48 -0700805 mStackActionButton.animate()
Winsonf24f2162016-01-05 12:11:55 -0800806 .alpha(1f)
807 .setDuration(duration)
Winsonc0d70582016-01-29 10:24:39 -0800808 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Winsonf24f2162016-01-05 12:11:55 -0800809 .start();
810 }
811 });
812 }
Winson49df4202016-01-25 17:33:34 -0800813 postAnimationTrigger.flushLastDecrementRunnables();
Winsonc29ff002015-11-20 16:00:45 -0800814 }
815
816 /**
Winson8f6ee482016-03-18 17:51:48 -0700817 * Hides the stack action button.
Winsonc29ff002015-11-20 16:00:45 -0800818 */
Winson8f6ee482016-03-18 17:51:48 -0700819 private void hideStackActionButton(int duration, boolean translate) {
820 if (!RecentsDebugFlags.Static.EnableStackActionButton) {
Winson91b225d2016-02-16 15:18:07 -0800821 return;
822 }
823
Winson49df4202016-01-25 17:33:34 -0800824 final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
Winson8f6ee482016-03-18 17:51:48 -0700825 hideStackActionButton(duration, translate, postAnimationTrigger);
Winson Chungaaeaac12015-12-16 16:49:36 -0500826 postAnimationTrigger.flushLastDecrementRunnables();
Winson Chung9a742902015-12-11 10:25:40 -0500827 }
828
Winson49df4202016-01-25 17:33:34 -0800829 /**
Winson8f6ee482016-03-18 17:51:48 -0700830 * Hides the stack action button.
Winson49df4202016-01-25 17:33:34 -0800831 */
Winson8f6ee482016-03-18 17:51:48 -0700832 private void hideStackActionButton(int duration, boolean translate,
833 final ReferenceCountedTrigger postAnimationTrigger) {
834 if (!RecentsDebugFlags.Static.EnableStackActionButton) {
Winson91b225d2016-02-16 15:18:07 -0800835 return;
836 }
837
Winson8f6ee482016-03-18 17:51:48 -0700838 if (mStackActionButton.getVisibility() == View.VISIBLE) {
Winson49df4202016-01-25 17:33:34 -0800839 if (translate) {
Winson8f6ee482016-03-18 17:51:48 -0700840 mStackActionButton.animate()
841 .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f);
Winson49df4202016-01-25 17:33:34 -0800842 }
Winson8f6ee482016-03-18 17:51:48 -0700843 mStackActionButton.animate()
Winsonf24f2162016-01-05 12:11:55 -0800844 .alpha(0f)
845 .setDuration(duration)
Winsonc0d70582016-01-29 10:24:39 -0800846 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
Winsonf24f2162016-01-05 12:11:55 -0800847 .withEndAction(new Runnable() {
848 @Override
849 public void run() {
Winson8f6ee482016-03-18 17:51:48 -0700850 mStackActionButton.setVisibility(View.INVISIBLE);
Winson49df4202016-01-25 17:33:34 -0800851 postAnimationTrigger.decrement();
Winsonf24f2162016-01-05 12:11:55 -0800852 }
853 })
Winsonf24f2162016-01-05 12:11:55 -0800854 .start();
Winson49df4202016-01-25 17:33:34 -0800855 postAnimationTrigger.increment();
Winsonf24f2162016-01-05 12:11:55 -0800856 }
Winsonc29ff002015-11-20 16:00:45 -0800857 }
858
Winson4165d3362015-10-10 14:40:35 -0700859 /**
Matthew Ng43db6d22017-06-27 15:29:39 -0700860 * Animates a translation in the Y direction and fades in/out for empty view to show or hide it.
861 * @param show whether to translate up and fade in the empty view to the center of the screen
862 * @param postAnimationTrigger to keep track of the animation
863 */
864 private void animateEmptyView(boolean show, ReferenceCountedTrigger postAnimationTrigger) {
865 float start = mTaskStackView.getStackAlgorithm().getTaskRect().height() / 4;
866 mEmptyView.setTranslationY(show ? start : 0);
867 mEmptyView.setAlpha(show ? 0f : 1f);
868 ViewPropertyAnimator animator = mEmptyView.animate()
869 .setDuration(150)
870 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
871 .translationY(show ? 0 : start)
872 .alpha(show ? 1f : 0f);
873
874 if (postAnimationTrigger != null) {
875 animator.setListener(postAnimationTrigger.decrementOnAnimationEnd());
876 postAnimationTrigger.increment();
877 }
878 animator.start();
879 }
880
881 /**
Winson4165d3362015-10-10 14:40:35 -0700882 * Updates the dock region to match the specified dock state.
883 */
Winson3e874742016-01-07 10:08:17 -0800884 private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
Winson99ef4582016-04-18 16:57:27 -0700885 boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
886 boolean animateAlpha, boolean animateBounds) {
Winsonc5fd3502016-01-18 15:18:37 -0800887 ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
888 new ArraySet<TaskStack.DockState>());
Winson036693c2016-01-14 17:16:06 -0800889 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
890 for (int i = visDockStates.size() - 1; i >= 0; i--) {
891 TaskStack.DockState dockState = visDockStates.get(i);
Winson882072b2015-10-12 11:26:33 -0700892 TaskStack.DockState.ViewState viewState = dockState.viewState;
893 if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
894 // This is no longer visible, so hide it
Winson3f32e7e2016-04-20 17:18:08 -0700895 viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION,
896 Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds);
Winson882072b2015-10-12 11:26:33 -0700897 } else {
898 // This state is now visible, update the bounds and show it
Winson99ef4582016-04-18 16:57:27 -0700899 int areaAlpha = overrideAreaAlpha != -1
900 ? overrideAreaAlpha
901 : viewState.dockAreaAlpha;
902 int hintAlpha = overrideHintAlpha != -1
903 ? overrideHintAlpha
904 : viewState.hintTextAlpha;
Winson3e874742016-01-07 10:08:17 -0800905 Rect bounds = isDefaultDockState
Winson08deff02016-08-05 13:58:31 -0700906 ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
907 mSystemInsets)
Winson3e874742016-01-07 10:08:17 -0800908 : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
Winson08deff02016-08-05 13:58:31 -0700909 mDividerSize, mSystemInsets, getResources());
Winson3e874742016-01-07 10:08:17 -0800910 if (viewState.dockAreaOverlay.getCallback() != this) {
911 viewState.dockAreaOverlay.setCallback(this);
912 viewState.dockAreaOverlay.setBounds(bounds);
913 }
Winsonb677bcb2016-04-18 16:58:33 -0700914 viewState.startAnimation(bounds, areaAlpha, hintAlpha,
Winson3f32e7e2016-04-20 17:18:08 -0700915 TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
Winsonb677bcb2016-04-18 16:58:33 -0700916 animateAlpha, animateBounds);
Winson882072b2015-10-12 11:26:33 -0700917 }
Winson4165d3362015-10-10 14:40:35 -0700918 }
Winson4165d3362015-10-10 14:40:35 -0700919 }
Filip Gruszczynski14b4e572015-11-03 15:53:55 -0800920
Winson49df4202016-01-25 17:33:34 -0800921 /**
Lucas Dupinae90ba82017-06-16 16:45:59 -0700922 * Scrim alpha based on how busy recents is:
923 * Scrim will be {@link ScrimController#GRADIENT_SCRIM_ALPHA} when the stack is empty,
924 * and {@link ScrimController#GRADIENT_SCRIM_ALPHA_BUSY} when it's full.
925 *
926 * @return Alpha from 0 to 1.
927 */
928 private float getOpaqueScrimAlpha() {
929 return MathUtils.map(0, 1, ScrimController.GRADIENT_SCRIM_ALPHA,
930 ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mBusynessFactor);
931 }
932
933 /**
Winson49df4202016-01-25 17:33:34 -0800934 * Animates the background scrim to the given {@param alpha}.
935 */
936 private void animateBackgroundScrim(float alpha, int duration) {
937 Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
Winson47c78f92016-03-31 16:02:30 -0700938 // Calculate the absolute alpha to animate from
Lucas Dupinae90ba82017-06-16 16:45:59 -0700939 final int fromAlpha = mBackgroundScrim.getAlpha();
940 final int toAlpha = (int) (alpha * 255);
Winson Chungd45aac42017-08-17 17:15:31 -0700941 mBackgroundScrimAnimator = ValueAnimator.ofInt(fromAlpha, toAlpha);
Winson49df4202016-01-25 17:33:34 -0800942 mBackgroundScrimAnimator.setDuration(duration);
Winson47c78f92016-03-31 16:02:30 -0700943 mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha
944 ? Interpolators.ALPHA_IN
945 : Interpolators.ALPHA_OUT);
Winson Chungd45aac42017-08-17 17:15:31 -0700946 mBackgroundScrimAnimator.addUpdateListener(mUpdateBackgroundScrimAlpha);
Winson49df4202016-01-25 17:33:34 -0800947 mBackgroundScrimAnimator.start();
Filip Gruszczynski14b4e572015-11-03 15:53:55 -0800948 }
Winson19852372016-03-30 18:26:49 -0700949
950 /**
951 * @return the bounds of the stack action button.
952 */
953 private Rect getStackActionButtonBoundsFromStackLayout() {
Jiaquan He26f637b2016-12-27 14:44:14 -0800954 Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
Matthew Ng43db6d22017-06-27 15:29:39 -0700955 int left, top;
956 if (Recents.getConfiguration().isLowRamDevice) {
957 Rect windowRect = Recents.getSystemServices().getWindowRect();
958 left = (windowRect.width() - mSystemInsets.left - mSystemInsets.right
959 - mStackActionButton.getMeasuredWidth()) / 2;
960 top = windowRect.height() - (mStackActionButton.getMeasuredHeight()
961 + mSystemInsets.bottom + mStackActionButton.getPaddingBottom() / 2);
962 } else {
963 left = isLayoutRtl()
Winson19852372016-03-30 18:26:49 -0700964 ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
965 : actionButtonRect.right + mStackActionButton.getPaddingRight()
Winson47c78f92016-03-31 16:02:30 -0700966 - mStackActionButton.getMeasuredWidth();
Matthew Ng43db6d22017-06-27 15:29:39 -0700967 top = actionButtonRect.top +
Winson19852372016-03-30 18:26:49 -0700968 (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
Matthew Ng43db6d22017-06-27 15:29:39 -0700969 }
Winson47c78f92016-03-31 16:02:30 -0700970 actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
971 top + mStackActionButton.getMeasuredHeight());
Winson19852372016-03-30 18:26:49 -0700972 return actionButtonRect;
973 }
Winsond72c3152016-04-05 15:33:35 -0700974
975 public void dump(String prefix, PrintWriter writer) {
976 String innerPrefix = prefix + " ";
977 String id = Integer.toHexString(System.identityHashCode(this));
978
979 writer.print(prefix); writer.print(TAG);
980 writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
981 writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets));
982 writer.print(" [0x"); writer.print(id); writer.print("]");
983 writer.println();
984
Matthew Ngb7164cd2016-10-25 17:06:26 -0700985 if (getStack() != null) {
986 getStack().dump(innerPrefix, writer);
Winsond72c3152016-04-05 15:33:35 -0700987 }
988 if (mTaskStackView != null) {
989 mTaskStackView.dump(innerPrefix, writer);
990 }
991 }
Winson Chung303e1ff2014-03-07 15:06:19 -0800992}