• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.quickstep.fallback;
17 
18 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
19 
20 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
21 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
22 import static com.android.quickstep.fallback.RecentsState.HOME;
23 import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
24 import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
25 
26 import android.animation.AnimatorSet;
27 import android.annotation.TargetApi;
28 import android.content.Context;
29 import android.os.Build;
30 import android.util.AttributeSet;
31 import android.view.MotionEvent;
32 
33 import androidx.annotation.Nullable;
34 
35 import com.android.launcher3.AbstractFloatingView;
36 import com.android.launcher3.anim.AnimatorPlaybackController;
37 import com.android.launcher3.anim.PendingAnimation;
38 import com.android.launcher3.logging.StatsLogManager;
39 import com.android.launcher3.statemanager.StateManager.StateListener;
40 import com.android.launcher3.util.SplitConfigurationOptions;
41 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
42 import com.android.quickstep.FallbackActivityInterface;
43 import com.android.quickstep.GestureState;
44 import com.android.quickstep.RecentsActivity;
45 import com.android.quickstep.RotationTouchHelper;
46 import com.android.quickstep.util.GroupTask;
47 import com.android.quickstep.util.SplitSelectStateController;
48 import com.android.quickstep.util.TaskViewSimulator;
49 import com.android.quickstep.views.OverviewActionsView;
50 import com.android.quickstep.views.RecentsView;
51 import com.android.quickstep.views.TaskView;
52 import com.android.systemui.shared.recents.model.Task;
53 
54 import java.util.ArrayList;
55 
56 @TargetApi(Build.VERSION_CODES.R)
57 public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
58         implements StateListener<RecentsState> {
59 
60     private static final int TASK_DISMISS_DURATION = 150;
61 
62     @Nullable
63     private Task mHomeTask;
64 
FallbackRecentsView(Context context, AttributeSet attrs)65     public FallbackRecentsView(Context context, AttributeSet attrs) {
66         this(context, attrs, 0);
67     }
68 
FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr)69     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
70         super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
71         mActivity.getStateManager().addStateListener(this);
72     }
73 
74     @Override
init(OverviewActionsView actionsView, SplitSelectStateController splitController)75     public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
76         super.init(actionsView, splitController);
77         setOverviewStateEnabled(true);
78         setOverlayEnabled(true);
79     }
80 
81     @Override
startHome()82     public void startHome() {
83         mActivity.startHome();
84         AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
85     }
86 
87     /**
88      * When starting gesture interaction from home, we add a temporary invisible tile corresponding
89      * to the home task. This allows us to handle quick-switch similarly to a quick-switching
90      * from a foreground task.
91      */
onGestureAnimationStartOnHome(Task[] homeTask, RotationTouchHelper rotationTouchHelper)92     public void onGestureAnimationStartOnHome(Task[] homeTask,
93             RotationTouchHelper rotationTouchHelper) {
94         // TODO(b/195607777) General fallback love, but this might be correct
95         //  Home task should be defined as the front-most task info I think?
96         mHomeTask = homeTask.length > 0 ? homeTask[0] : null;
97         onGestureAnimationStart(homeTask, rotationTouchHelper);
98     }
99 
100     /**
101      * When the gesture ends and we're going to recents view, we also remove the temporary
102      * invisible tile added for the home task. This also pushes the remaining tiles back
103      * to the center.
104      */
105     @Override
onPrepareGestureEndAnimation( @ullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget, TaskViewSimulator[] taskViewSimulators)106     public void onPrepareGestureEndAnimation(
107             @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget,
108             TaskViewSimulator[] taskViewSimulators) {
109         super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulators);
110         if (mHomeTask != null && endTarget == RECENTS && animatorSet != null) {
111             TaskView tv = getTaskViewByTaskId(mHomeTask.key.id);
112             if (tv != null) {
113                 PendingAnimation pa = new PendingAnimation(TASK_DISMISS_DURATION);
114                 createTaskDismissAnimation(pa, tv, true, false,
115                         TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/);
116                 pa.addEndListener(e -> setCurrentTask(-1));
117                 AnimatorPlaybackController controller = pa.createPlaybackController();
118                 controller.dispatchOnStart();
119                 animatorSet.play(controller.getAnimationPlayer());
120             }
121         }
122     }
123 
124     @Override
onGestureAnimationEnd()125     public void onGestureAnimationEnd() {
126         if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.HOME) {
127             // Clean-up logic that occurs when recents is no longer in use/visible.
128             reset();
129         }
130         super.onGestureAnimationEnd();
131     }
132 
133     @Override
setCurrentTask(int runningTaskViewId)134     public void setCurrentTask(int runningTaskViewId) {
135         super.setCurrentTask(runningTaskViewId);
136         int runningTaskId = getTaskIdsForRunningTaskView()[0];
137         if (mHomeTask != null && mHomeTask.key.id != runningTaskId) {
138             mHomeTask = null;
139             setRunningTaskHidden(false);
140         }
141     }
142 
143     @Nullable
144     @Override
getHomeTaskView()145     protected TaskView getHomeTaskView() {
146         return mHomeTask != null ? getTaskViewByTaskId(mHomeTask.key.id) : null;
147     }
148 
149     @Override
shouldAddStubTaskView(Task[] runningTasks)150     protected boolean shouldAddStubTaskView(Task[] runningTasks) {
151         if (runningTasks.length > 1) {
152             // can't be in split screen w/ home task
153             return super.shouldAddStubTaskView(runningTasks);
154         }
155 
156         Task runningTask = runningTasks[0];
157         if (mHomeTask != null && runningTask != null
158                 && mHomeTask.key.id == runningTask.key.id
159                 && getTaskViewCount() == 0 && mLoadPlanEverApplied) {
160             // Do not add a stub task if we are running over home with empty recents, so that we
161             // show the empty recents message instead of showing a stub task and later removing it.
162             // Ignore empty task signal if applyLoadPlan has never run.
163             return false;
164         }
165         return super.shouldAddStubTaskView(runningTasks);
166     }
167 
168     @Override
applyLoadPlan(ArrayList<GroupTask> taskGroups)169     protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
170         // When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
171         // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
172         // track the index of the next task appropriately, as if we are switching on any other app.
173         // TODO(b/195607777) Confirm home task info is front-most task and not mixed in with others
174         int runningTaskId = getTaskIdsForRunningTaskView()[0];
175         if (mHomeTask != null && mHomeTask.key.id == runningTaskId
176                 && !taskGroups.isEmpty()) {
177             // Check if the task list has running task
178             boolean found = false;
179             for (GroupTask group : taskGroups) {
180                 if (group.containsTask(runningTaskId)) {
181                     found = true;
182                     break;
183                 }
184             }
185             if (!found) {
186                 ArrayList<GroupTask> newList = new ArrayList<>(taskGroups.size() + 1);
187                 newList.addAll(taskGroups);
188                 newList.add(new GroupTask(mHomeTask, null, null));
189                 taskGroups = newList;
190             }
191         }
192         super.applyLoadPlan(taskGroups);
193     }
194 
195     @Override
setRunningTaskHidden(boolean isHidden)196     public void setRunningTaskHidden(boolean isHidden) {
197         if (mHomeTask != null) {
198             // Always keep the home task hidden
199             isHidden = true;
200         }
201         super.setRunningTaskHidden(isHidden);
202     }
203 
204     @Override
setModalStateEnabled(int taskId, boolean animate)205     public void setModalStateEnabled(int taskId, boolean animate) {
206         if (taskId != INVALID_TASK_ID) {
207             setSelectedTask(taskId);
208             mActivity.getStateManager().goToState(RecentsState.MODAL_TASK, animate);
209         } else {
210             if (mActivity.isInState(RecentsState.MODAL_TASK)) {
211                 mActivity.getStateManager().goToState(DEFAULT, animate);
212                 resetModalVisuals();
213             }
214         }
215     }
216 
217     @Override
initiateSplitSelect(TaskView taskView, @SplitConfigurationOptions.StagePosition int stagePosition, StatsLogManager.EventEnum splitEvent)218     public void initiateSplitSelect(TaskView taskView,
219             @SplitConfigurationOptions.StagePosition int stagePosition,
220             StatsLogManager.EventEnum splitEvent) {
221         super.initiateSplitSelect(taskView, stagePosition, splitEvent);
222         mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
223     }
224 
225     @Override
onStateTransitionStart(RecentsState toState)226     public void onStateTransitionStart(RecentsState toState) {
227         setOverviewStateEnabled(true);
228         setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
229         setOverviewFullscreenEnabled(toState.isFullScreen());
230         if (toState == MODAL_TASK) {
231             setOverviewSelectEnabled(true);
232         }
233         setFreezeViewVisibility(true);
234     }
235 
236     @Override
onStateTransitionComplete(RecentsState finalState)237     public void onStateTransitionComplete(RecentsState finalState) {
238         if (finalState == HOME) {
239             // Clean-up logic that occurs when recents is no longer in use/visible.
240             reset();
241         }
242         boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
243         setOverlayEnabled(isOverlayEnabled);
244         setFreezeViewVisibility(false);
245         if (finalState != MODAL_TASK) {
246             setOverviewSelectEnabled(false);
247         }
248         if (finalState != OVERVIEW_SPLIT_SELECT) {
249             resetFromSplitSelectionState();
250         }
251 
252         if (isOverlayEnabled) {
253             runActionOnRemoteHandles(remoteTargetHandle ->
254                     remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
255         }
256     }
257 
258     @Override
setOverviewStateEnabled(boolean enabled)259     public void setOverviewStateEnabled(boolean enabled) {
260         super.setOverviewStateEnabled(enabled);
261         if (enabled) {
262             RecentsState state = mActivity.getStateManager().getState();
263             setDisallowScrollToClearAll(!state.hasClearAllButton());
264         }
265     }
266 
267     @Override
onTouchEvent(MotionEvent ev)268     public boolean onTouchEvent(MotionEvent ev) {
269         boolean result = super.onTouchEvent(ev);
270         // Do not let touch escape to siblings below this view.
271         return result || mActivity.getStateManager().getState().overviewUi();
272     }
273 
274     @Override
initiateSplitSelect(SplitSelectSource splitSelectSource)275     public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
276         super.initiateSplitSelect(splitSelectSource);
277         mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
278     }
279 
280     @Override
canLaunchFullscreenTask()281     protected boolean canLaunchFullscreenTask() {
282         return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
283     }
284 }
285