1 /* 2 * Copyright (C) 2019 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.launcher3.uioverrides.touchcontrollers; 17 18 import static com.android.app.animation.Interpolators.ACCELERATE_2; 19 import static com.android.app.animation.Interpolators.DECELERATE_2; 20 import static com.android.app.animation.Interpolators.INSTANT; 21 import static com.android.app.animation.Interpolators.LINEAR; 22 import static com.android.launcher3.LauncherState.NORMAL; 23 import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; 24 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; 25 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; 26 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; 27 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; 28 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; 29 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; 30 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; 31 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; 32 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK; 33 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; 34 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; 35 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; 36 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; 37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 38 39 import android.view.MotionEvent; 40 41 import com.android.launcher3.Launcher; 42 import com.android.launcher3.LauncherState; 43 import com.android.launcher3.Utilities; 44 import com.android.launcher3.states.StateAnimationConfig; 45 import com.android.launcher3.touch.AbstractStateChangeTouchController; 46 import com.android.launcher3.touch.SingleAxisSwipeDetector; 47 import com.android.launcher3.util.DisplayController; 48 import com.android.launcher3.util.NavigationMode; 49 import com.android.quickstep.SystemUiProxy; 50 import com.android.quickstep.TaskUtils; 51 import com.android.quickstep.views.DesktopTaskView; 52 import com.android.quickstep.views.RecentsView; 53 import com.android.quickstep.views.TaskView; 54 55 /** 56 * Handles quick switching to a recent task from the home screen. 57 */ 58 public class QuickSwitchTouchController extends AbstractStateChangeTouchController { 59 60 protected final RecentsView mOverviewPanel; 61 QuickSwitchTouchController(Launcher launcher)62 public QuickSwitchTouchController(Launcher launcher) { 63 this(launcher, SingleAxisSwipeDetector.HORIZONTAL); 64 } 65 QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir)66 protected QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) { 67 super(l, dir); 68 mOverviewPanel = l.getOverviewPanel(); 69 } 70 71 @Override canInterceptTouch(MotionEvent ev)72 protected boolean canInterceptTouch(MotionEvent ev) { 73 if (mCurrentAnimation != null) { 74 return true; 75 } 76 if (!mLauncher.isInState(LauncherState.NORMAL)) { 77 return false; 78 } 79 if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) { 80 return false; 81 } 82 if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { 83 // TODO(b/268075592): add support for quickswitch to/from desktop 84 return false; 85 } 86 return true; 87 } 88 89 @Override getTargetState(LauncherState fromState, boolean isDragTowardPositive)90 protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { 91 int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags(); 92 if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) { 93 return NORMAL; 94 } 95 return isDragTowardPositive ? QUICK_SWITCH_FROM_HOME : NORMAL; 96 } 97 98 @Override onDragStart(boolean start, float startDisplacement)99 public void onDragStart(boolean start, float startDisplacement) { 100 super.onDragStart(start, startDisplacement); 101 mStartContainerType = LAUNCHER_STATE_BACKGROUND; 102 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); 103 } 104 105 @Override onSwipeInteractionCompleted(LauncherState targetState)106 protected void onSwipeInteractionCompleted(LauncherState targetState) { 107 super.onSwipeInteractionCompleted(targetState); 108 } 109 110 @Override initCurrentAnimation()111 protected float initCurrentAnimation() { 112 StateAnimationConfig config = new StateAnimationConfig(); 113 setupInterpolators(config); 114 config.duration = (long) (getShiftRange() * 2); 115 116 // Set RecentView's initial properties for coming in from the side. 117 RECENTS_SCALE_PROPERTY.set(mOverviewPanel, 118 QUICK_SWITCH_FROM_HOME.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f); 119 ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f); 120 mOverviewPanel.setContentAlpha(1); 121 122 mCurrentAnimation = mLauncher.getStateManager() 123 .createAnimationToNewWorkspace(mToState, config); 124 mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener); 125 mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> 126 updateFullscreenProgress((Float) valueAnimator.getAnimatedValue())); 127 return 1 / getShiftRange(); 128 } 129 setupInterpolators(StateAnimationConfig stateAnimationConfig)130 private void setupInterpolators(StateAnimationConfig stateAnimationConfig) { 131 stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DECELERATE_2); 132 stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DECELERATE_2); 133 if (DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) { 134 // Overview lives to the left of workspace, so translate down later than over 135 stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCELERATE_2); 136 stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCELERATE_2); 137 stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCELERATE_2); 138 stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCELERATE_2); 139 stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); 140 } else { 141 stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR); 142 stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR); 143 } 144 } 145 146 @Override updateProgress(float progress)147 protected void updateProgress(float progress) { 148 super.updateProgress(progress); 149 updateFullscreenProgress(Utilities.boundToRange(progress, 0, 1)); 150 } 151 updateFullscreenProgress(float progress)152 private void updateFullscreenProgress(float progress) { 153 mOverviewPanel.setFullscreenProgress(progress); 154 if (progress > UPDATE_SYSUI_FLAGS_THRESHOLD) { 155 int sysuiFlags = 0; 156 TaskView tv = mOverviewPanel.getTaskViewAt(0); 157 if (tv != null) { 158 sysuiFlags = tv.getThumbnail().getSysUiStatusNavFlags(); 159 } 160 mLauncher.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, sysuiFlags); 161 } else { 162 mLauncher.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0); 163 } 164 } 165 166 @Override getShiftRange()167 protected float getShiftRange() { 168 return mLauncher.getDeviceProfile().widthPx / 2f; 169 } 170 } 171