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.views; 17 18 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 19 20 import static com.android.launcher3.LauncherState.ALL_APPS; 21 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON; 22 import static com.android.launcher3.LauncherState.EDIT_MODE; 23 import static com.android.launcher3.LauncherState.NORMAL; 24 import static com.android.launcher3.LauncherState.OVERVIEW; 25 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; 26 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; 27 import static com.android.launcher3.LauncherState.SPRING_LOADED; 28 29 import android.annotation.TargetApi; 30 import android.content.Context; 31 import android.os.Build; 32 import android.util.AttributeSet; 33 import android.util.Log; 34 import android.view.MotionEvent; 35 36 import androidx.annotation.Nullable; 37 38 import com.android.launcher3.AbstractFloatingView; 39 import com.android.launcher3.LauncherState; 40 import com.android.launcher3.config.FeatureFlags; 41 import com.android.launcher3.logging.StatsLogManager; 42 import com.android.launcher3.statehandlers.DepthController; 43 import com.android.launcher3.statehandlers.DesktopVisibilityController; 44 import com.android.launcher3.statemanager.StateManager; 45 import com.android.launcher3.statemanager.StateManager.StateListener; 46 import com.android.launcher3.testing.shared.TestProtocol; 47 import com.android.launcher3.uioverrides.QuickstepLauncher; 48 import com.android.launcher3.util.PendingSplitSelectInfo; 49 import com.android.launcher3.util.SplitConfigurationOptions; 50 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; 51 import com.android.quickstep.GestureState; 52 import com.android.quickstep.LauncherActivityInterface; 53 import com.android.quickstep.RotationTouchHelper; 54 import com.android.quickstep.SystemUiProxy; 55 import com.android.quickstep.util.SplitSelectStateController; 56 import com.android.systemui.shared.recents.model.Task; 57 58 /** 59 * {@link RecentsView} used in Launcher activity 60 */ 61 @TargetApi(Build.VERSION_CODES.O) 62 public class LauncherRecentsView extends RecentsView<QuickstepLauncher, LauncherState> 63 implements StateListener<LauncherState> { 64 LauncherRecentsView(Context context)65 public LauncherRecentsView(Context context) { 66 this(context, null); 67 } 68 LauncherRecentsView(Context context, @Nullable AttributeSet attrs)69 public LauncherRecentsView(Context context, @Nullable AttributeSet attrs) { 70 this(context, attrs, 0); 71 } 72 LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)73 public LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 74 super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE); 75 mActivity.getStateManager().addStateListener(this); 76 } 77 78 @Override init(OverviewActionsView actionsView, SplitSelectStateController splitPlaceholderView)79 public void init(OverviewActionsView actionsView, 80 SplitSelectStateController splitPlaceholderView) { 81 super.init(actionsView, splitPlaceholderView); 82 setContentAlpha(0); 83 } 84 85 @Override handleStartHome(boolean animated)86 protected void handleStartHome(boolean animated) { 87 StateManager stateManager = mActivity.getStateManager(); 88 animated &= stateManager.shouldAnimateStateChange(); 89 stateManager.goToState(NORMAL, animated); 90 if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { 91 mSplitSelectStateController.getSplitAnimationController() 92 .playPlaceholderDismissAnim(mActivity); 93 } 94 AbstractFloatingView.closeAllOpenViews(mActivity, animated); 95 } 96 97 @Override canStartHomeSafely()98 protected boolean canStartHomeSafely() { 99 return mActivity.canStartHomeSafely(); 100 } 101 102 @Override onTaskLaunchAnimationEnd(boolean success)103 protected void onTaskLaunchAnimationEnd(boolean success) { 104 if (success) { 105 mActivity.getStateManager().moveToRestState(); 106 } else { 107 LauncherState state = mActivity.getStateManager().getState(); 108 mActivity.getAllAppsController().setState(state); 109 } 110 super.onTaskLaunchAnimationEnd(success); 111 } 112 113 @Override onTaskIconChanged(int taskId)114 public void onTaskIconChanged(int taskId) { 115 super.onTaskIconChanged(taskId); 116 // If Launcher needs to return to split select state, do it now, after the icon has updated. 117 if (mActivity.hasPendingSplitSelectInfo()) { 118 PendingSplitSelectInfo recoveryData = mActivity.getPendingSplitSelectInfo(); 119 if (recoveryData.getStagedTaskId() == taskId) { 120 initiateSplitSelect( 121 getTaskViewByTaskId(recoveryData.getStagedTaskId()), 122 recoveryData.getStagePosition(), recoveryData.getSource() 123 ); 124 mActivity.finishSplitSelectRecovery(); 125 } 126 } 127 } 128 129 @Override reset()130 public void reset() { 131 super.reset(); 132 133 int recentsActivityRotation = getPagedViewOrientedState().getRecentsActivityRotation(); 134 setLayoutRotation(recentsActivityRotation, recentsActivityRotation); 135 } 136 137 @Override onStateTransitionStart(LauncherState toState)138 public void onStateTransitionStart(LauncherState toState) { 139 setOverviewStateEnabled(toState.overviewUi); 140 setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())); 141 setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1); 142 if (toState == OVERVIEW_MODAL_TASK) { 143 setOverviewSelectEnabled(true); 144 } 145 setFreezeViewVisibility(true); 146 } 147 148 @Override onStateTransitionComplete(LauncherState finalState)149 public void onStateTransitionComplete(LauncherState finalState) { 150 if (finalState == NORMAL || finalState == SPRING_LOADED || finalState == EDIT_MODE 151 || finalState == ALL_APPS) { 152 // Clean-up logic that occurs when recents is no longer in use/visible. 153 reset(); 154 } 155 boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK; 156 setOverlayEnabled(isOverlayEnabled); 157 setFreezeViewVisibility(false); 158 if (finalState != OVERVIEW_MODAL_TASK) { 159 setOverviewSelectEnabled(false); 160 } 161 162 if (isOverlayEnabled) { 163 runActionOnRemoteHandles(remoteTargetHandle -> 164 remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)); 165 } 166 } 167 168 @Override setOverviewStateEnabled(boolean enabled)169 public void setOverviewStateEnabled(boolean enabled) { 170 super.setOverviewStateEnabled(enabled); 171 Log.d(TestProtocol.OVERVIEW_OVER_HOME, "overview state enabled state has changed: " 172 + enabled); 173 if (enabled) { 174 LauncherState state = mActivity.getStateManager().getState(); 175 boolean hasClearAllButton = (state.getVisibleElements(mActivity) 176 & CLEAR_ALL_BUTTON) != 0; 177 setDisallowScrollToClearAll(!hasClearAllButton); 178 } 179 if (mActivity.getDesktopVisibilityController() != null) { 180 mActivity.getDesktopVisibilityController().setOverviewStateEnabled(enabled); 181 } 182 } 183 184 @Override onTouchEvent(MotionEvent ev)185 public boolean onTouchEvent(MotionEvent ev) { 186 boolean result = super.onTouchEvent(ev); 187 // Do not let touch escape to siblings below this view. 188 return result || mActivity.getStateManager().getState().overviewUi; 189 } 190 191 @Override getDepthController()192 protected DepthController getDepthController() { 193 return mActivity.getDepthController(); 194 } 195 196 @Override setModalStateEnabled(int taskId, boolean animate)197 public void setModalStateEnabled(int taskId, boolean animate) { 198 if (taskId != INVALID_TASK_ID) { 199 setSelectedTask(taskId); 200 mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK, animate); 201 } else { 202 if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) { 203 mActivity.getStateManager().goToState(LauncherState.OVERVIEW, animate); 204 resetModalVisuals(); 205 } 206 } 207 } 208 209 @Override onDismissAnimationEnds()210 protected void onDismissAnimationEnds() { 211 super.onDismissAnimationEnds(); 212 if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) { 213 // We want to keep the tasks translations in this temporary state 214 // after resetting the rest above 215 setTaskViewsPrimarySplitTranslation(mTaskViewsPrimarySplitTranslation); 216 setTaskViewsSecondarySplitTranslation(mTaskViewsSecondarySplitTranslation); 217 } 218 } 219 220 @Override initiateSplitSelect(TaskView taskView, @SplitConfigurationOptions.StagePosition int stagePosition, StatsLogManager.EventEnum splitEvent)221 public void initiateSplitSelect(TaskView taskView, 222 @SplitConfigurationOptions.StagePosition int stagePosition, 223 StatsLogManager.EventEnum splitEvent) { 224 super.initiateSplitSelect(taskView, stagePosition, splitEvent); 225 mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT); 226 } 227 228 @Override initiateSplitSelect(SplitSelectSource splitSelectSource)229 public void initiateSplitSelect(SplitSelectSource splitSelectSource) { 230 super.initiateSplitSelect(splitSelectSource); 231 mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT); 232 } 233 234 @Override canLaunchFullscreenTask()235 protected boolean canLaunchFullscreenTask() { 236 if (FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { 237 return !mSplitSelectStateController.isSplitSelectActive(); 238 } else { 239 return !mActivity.isInState(OVERVIEW_SPLIT_SELECT); 240 } 241 } 242 243 @Override onGestureAnimationStart(Task[] runningTasks, RotationTouchHelper rotationTouchHelper)244 public void onGestureAnimationStart(Task[] runningTasks, 245 RotationTouchHelper rotationTouchHelper) { 246 super.onGestureAnimationStart(runningTasks, rotationTouchHelper); 247 DesktopVisibilityController desktopVisibilityController = 248 mActivity.getDesktopVisibilityController(); 249 if (desktopVisibilityController != null) { 250 desktopVisibilityController.setRecentsGestureStart(); 251 } 252 } 253 254 @Override onGestureAnimationEnd()255 public void onGestureAnimationEnd() { 256 DesktopVisibilityController desktopVisibilityController = null; 257 boolean showDesktopApps = false; 258 GestureState.GestureEndTarget endTarget = null; 259 if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { 260 desktopVisibilityController = mActivity.getDesktopVisibilityController(); 261 endTarget = mCurrentGestureEndTarget; 262 if (endTarget == GestureState.GestureEndTarget.LAST_TASK 263 && desktopVisibilityController.areFreeformTasksVisible()) { 264 // Recents gesture was cancelled and we are returning to the previous task. 265 // After super class has handled clean up, show desktop apps on top again 266 showDesktopApps = true; 267 } 268 } 269 super.onGestureAnimationEnd(); 270 if (desktopVisibilityController != null) { 271 desktopVisibilityController.setRecentsGestureEnd(endTarget); 272 } 273 if (showDesktopApps) { 274 SystemUiProxy.INSTANCE.get(mActivity).showDesktopApps(mActivity.getDisplayId()); 275 } 276 } 277 } 278