1 /* 2 * Copyright (C) 2015 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.touch; 17 18 import static com.android.app.animation.Interpolators.DECELERATED_EASE; 19 import static com.android.app.animation.Interpolators.EMPHASIZED; 20 import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE; 21 import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE; 22 import static com.android.app.animation.Interpolators.FINAL_FRAME; 23 import static com.android.app.animation.Interpolators.INSTANT; 24 import static com.android.app.animation.Interpolators.LINEAR; 25 import static com.android.app.animation.Interpolators.clampToProgress; 26 import static com.android.launcher3.LauncherState.ALL_APPS; 27 import static com.android.launcher3.LauncherState.NORMAL; 28 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; 29 import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; 30 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE; 31 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; 32 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; 33 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; 34 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; 35 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; 36 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE; 37 38 import android.view.MotionEvent; 39 import android.view.animation.Interpolator; 40 41 import com.android.app.animation.Interpolators; 42 import com.android.launcher3.AbstractFloatingView; 43 import com.android.launcher3.Flags; 44 import com.android.launcher3.Launcher; 45 import com.android.launcher3.LauncherState; 46 import com.android.launcher3.states.StateAnimationConfig; 47 48 /** 49 * TouchController to switch between NORMAL and ALL_APPS state. 50 */ 51 public class AllAppsSwipeController extends AbstractStateChangeTouchController { 52 53 private static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f; 54 private static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f; 55 private static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = 0.1f; 56 private static final float ALL_APPS_STAGGERED_FADE_THRESHOLD = 0.5f; 57 58 private static final Interpolator ALL_APPS_SCRIM_RESPONDER = 59 Interpolators.clampToProgress( 60 LINEAR, ALL_APPS_SCRIM_VISIBLE_THRESHOLD, ALL_APPS_STAGGERED_FADE_THRESHOLD); 61 private static final Interpolator ALL_APPS_CLAMPING_RESPONDER = 62 Interpolators.clampToProgress( 63 LINEAR, 64 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, 65 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD); 66 67 // ---- Custom interpolators for NORMAL -> ALL_APPS on phones only. ---- 68 69 public static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.3333f; 70 public static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f; 71 private static final float ALL_APPS_FADE_END_ATOMIC = 0.8333f; 72 private static final float ALL_APPS_FADE_END_MANUAL = 0.8f; 73 private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f; 74 private static final float SCRIM_FADE_START_ATOMIC = 0.2642f; 75 private static final float SCRIM_FADE_START_MANUAL = 0.117f; 76 private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f; 77 78 private static final Interpolator LINEAR_EARLY_MANUAL = 79 Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); 80 private static final Interpolator STEP_TRANSITION_ATOMIC = 81 Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION_ATOMIC); 82 private static final Interpolator STEP_TRANSITION_MANUAL = 83 Interpolators.clampToProgress(FINAL_FRAME, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); 84 85 // The blur to All Apps is set to be complete when the interpolator is at 0.5. 86 private static final Interpolator BLUR_ADJUSTED = 87 Interpolators.mapToProgress(LINEAR, 0f, ALL_APPS_FULL_DEPTH_PROGRESS); 88 public static final Interpolator BLUR_ATOMIC = 89 Interpolators.clampToProgress( 90 BLUR_ADJUSTED, WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); 91 public static final Interpolator BLUR_MANUAL = 92 Interpolators.clampToProgress(BLUR_ADJUSTED, 0f, ALL_APPS_STATE_TRANSITION_MANUAL); 93 94 public static final Interpolator WORKSPACE_FADE_ATOMIC = STEP_TRANSITION_ATOMIC; 95 public static final Interpolator WORKSPACE_FADE_MANUAL = STEP_TRANSITION_MANUAL; 96 97 public static final Interpolator WORKSPACE_SCALE_ATOMIC = 98 Interpolators.clampToProgress( 99 EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC, 100 ALL_APPS_STATE_TRANSITION_ATOMIC); 101 public static final Interpolator WORKSPACE_SCALE_MANUAL = LINEAR_EARLY_MANUAL; 102 103 public static final Interpolator HOTSEAT_FADE_ATOMIC = STEP_TRANSITION_ATOMIC; 104 public static final Interpolator HOTSEAT_FADE_MANUAL = STEP_TRANSITION_MANUAL; 105 106 public static final Interpolator HOTSEAT_SCALE_ATOMIC = 107 Interpolators.clampToProgress( 108 EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC, 109 ALL_APPS_STATE_TRANSITION_ATOMIC); 110 public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL; 111 112 public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC = STEP_TRANSITION_ATOMIC; 113 public static final Interpolator HOTSEAT_TRANSLATE_MANUAL = STEP_TRANSITION_MANUAL; 114 115 public static final Interpolator SCRIM_FADE_ATOMIC = 116 Interpolators.clampToProgress( 117 Interpolators.mapToProgress(LINEAR, 0f, 0.8f), 118 SCRIM_FADE_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC); 119 public static final Interpolator SCRIM_FADE_MANUAL = 120 Interpolators.clampToProgress( 121 LINEAR, SCRIM_FADE_START_MANUAL, ALL_APPS_STATE_TRANSITION_MANUAL); 122 123 public static final Interpolator ALL_APPS_FADE_ATOMIC = 124 Interpolators.clampToProgress( 125 Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.2f, 1f), 126 ALL_APPS_STATE_TRANSITION_ATOMIC, ALL_APPS_FADE_END_ATOMIC); 127 public static final Interpolator ALL_APPS_FADE_MANUAL = 128 Interpolators.clampToProgress( 129 LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, ALL_APPS_FADE_END_MANUAL); 130 131 public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC = 132 Interpolators.clampToProgress( 133 Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.4f, 1f), 134 ALL_APPS_STATE_TRANSITION_ATOMIC, 1f); 135 public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_MANUAL = LINEAR; 136 137 // -------- 138 AllAppsSwipeController(Launcher l)139 public AllAppsSwipeController(Launcher l) { 140 super(l, SingleAxisSwipeDetector.VERTICAL); 141 } 142 143 @Override canInterceptTouch(MotionEvent ev)144 protected boolean canInterceptTouch(MotionEvent ev) { 145 if (mCurrentAnimation != null) { 146 // If we are already animating from a previous state, we can intercept. 147 return true; 148 } 149 if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { 150 return false; 151 } 152 if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) { 153 // Don't listen for the swipe gesture if we are already in some other state. 154 return false; 155 } 156 if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) { 157 return false; 158 } 159 return true; 160 } 161 162 @Override getTargetState(LauncherState fromState, boolean isDragTowardPositive)163 protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { 164 if (fromState == NORMAL && shouldOpenAllApps(isDragTowardPositive)) { 165 return ALL_APPS; 166 } else if (fromState == ALL_APPS && !isDragTowardPositive) { 167 return NORMAL; 168 } 169 return fromState; 170 } 171 172 @Override initCurrentAnimation()173 protected float initCurrentAnimation() { 174 float range = getShiftRange(); 175 StateAnimationConfig config = getConfigForStates(mFromState, mToState); 176 config.duration = (long) (2 * range); 177 178 mCurrentAnimation = mLauncher.getStateManager() 179 .createAnimationToNewWorkspace(mToState, config); 180 float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; 181 float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; 182 float totalShift = endVerticalShift - startVerticalShift; 183 return 1 / totalShift; 184 } 185 186 @Override getConfigForStates(LauncherState fromState, LauncherState toState)187 protected StateAnimationConfig getConfigForStates(LauncherState fromState, 188 LauncherState toState) { 189 StateAnimationConfig config = super.getConfigForStates(fromState, toState); 190 config.animProps |= StateAnimationConfig.USER_CONTROLLED; 191 if (fromState == NORMAL && toState == ALL_APPS) { 192 applyNormalToAllAppsAnimConfig(mLauncher, config); 193 } else if (fromState == ALL_APPS && toState == NORMAL) { 194 applyAllAppsToNormalConfig(mLauncher, config); 195 } 196 return config; 197 } 198 199 /** 200 * Applies Animation config values for transition from all apps to home. 201 */ applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config)202 public static void applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config) { 203 if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) { 204 config.setInterpolator(ANIM_SCRIM_FADE, 205 Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER)); 206 config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME); 207 if (!config.isUserControlled()) { 208 config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED); 209 } 210 config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE); 211 config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE); 212 if (Flags.allAppsBlur()) { 213 if (!config.isUserControlled()) { 214 config.setInterpolator(ANIM_DEPTH, EMPHASIZED_DECELERATE); 215 } 216 config.setInterpolator(ANIM_WORKSPACE_FADE, 217 clampToProgress(LINEAR, 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, 1)); 218 config.setInterpolator(ANIM_HOTSEAT_FADE, 219 clampToProgress(LINEAR, 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, 1)); 220 } else if (launcher.getDeviceProfile().isPhone) { 221 // On phones without blur, reveal the workspace and hotseat when leaving All Apps. 222 config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT); 223 config.setInterpolator(ANIM_HOTSEAT_FADE, INSTANT); 224 config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER; 225 } 226 } else { 227 if (config.isUserControlled()) { 228 config.setInterpolator(ANIM_DEPTH, Interpolators.reverse(BLUR_MANUAL)); 229 config.setInterpolator(ANIM_WORKSPACE_FADE, 230 Interpolators.reverse(WORKSPACE_FADE_MANUAL)); 231 config.setInterpolator(ANIM_WORKSPACE_SCALE, 232 Interpolators.reverse(WORKSPACE_SCALE_MANUAL)); 233 config.setInterpolator(ANIM_HOTSEAT_FADE, 234 Interpolators.reverse(HOTSEAT_FADE_MANUAL)); 235 config.setInterpolator(ANIM_HOTSEAT_SCALE, 236 Interpolators.reverse(HOTSEAT_SCALE_MANUAL)); 237 config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, 238 Interpolators.reverse(HOTSEAT_TRANSLATE_MANUAL)); 239 config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.reverse(SCRIM_FADE_MANUAL)); 240 config.setInterpolator(ANIM_ALL_APPS_FADE, 241 Interpolators.reverse(ALL_APPS_FADE_MANUAL)); 242 config.setInterpolator(ANIM_VERTICAL_PROGRESS, 243 Interpolators.reverse(ALL_APPS_VERTICAL_PROGRESS_MANUAL)); 244 } else { 245 config.setInterpolator(ANIM_SCRIM_FADE, 246 Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER)); 247 config.setInterpolator(ANIM_ALL_APPS_FADE, ALL_APPS_CLAMPING_RESPONDER); 248 config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT); 249 config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED_ACCELERATE); 250 } 251 } 252 } 253 254 /** 255 * Applies Animation config values for transition from home to all apps. 256 */ applyNormalToAllAppsAnimConfig( Launcher launcher, StateAnimationConfig config)257 public static void applyNormalToAllAppsAnimConfig( 258 Launcher launcher, StateAnimationConfig config) { 259 if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) { 260 config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT); 261 config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER); 262 if (!config.isUserControlled()) { 263 config.setInterpolator(ANIM_VERTICAL_PROGRESS, EMPHASIZED); 264 } 265 config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE); 266 config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE); 267 if (Flags.allAppsBlur()) { 268 config.setInterpolator(ANIM_DEPTH, LINEAR); 269 config.setInterpolator(ANIM_WORKSPACE_FADE, 270 clampToProgress(LINEAR, 0, ALL_APPS_SCRIM_VISIBLE_THRESHOLD)); 271 config.setInterpolator(ANIM_HOTSEAT_FADE, 272 clampToProgress(LINEAR, 0, ALL_APPS_SCRIM_VISIBLE_THRESHOLD)); 273 } else if (launcher.getDeviceProfile().isPhone) { 274 // On phones without blur, hide the workspace and hotseat when entering All Apps. 275 config.setInterpolator(ANIM_WORKSPACE_FADE, FINAL_FRAME); 276 config.setInterpolator(ANIM_HOTSEAT_FADE, FINAL_FRAME); 277 config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER; 278 } 279 } else { 280 config.setInterpolator(ANIM_DEPTH, 281 config.isUserControlled() ? BLUR_MANUAL : BLUR_ATOMIC); 282 config.setInterpolator(ANIM_WORKSPACE_FADE, 283 config.isUserControlled() ? WORKSPACE_FADE_MANUAL : WORKSPACE_FADE_ATOMIC); 284 config.setInterpolator(ANIM_WORKSPACE_SCALE, 285 config.isUserControlled() ? WORKSPACE_SCALE_MANUAL : WORKSPACE_SCALE_ATOMIC); 286 config.setInterpolator(ANIM_HOTSEAT_FADE, 287 config.isUserControlled() ? HOTSEAT_FADE_MANUAL : HOTSEAT_FADE_ATOMIC); 288 config.setInterpolator(ANIM_HOTSEAT_SCALE, 289 config.isUserControlled() ? HOTSEAT_SCALE_MANUAL : HOTSEAT_SCALE_ATOMIC); 290 config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, 291 config.isUserControlled() 292 ? HOTSEAT_TRANSLATE_MANUAL 293 : HOTSEAT_TRANSLATE_ATOMIC); 294 config.setInterpolator(ANIM_SCRIM_FADE, 295 config.isUserControlled() ? SCRIM_FADE_MANUAL : SCRIM_FADE_ATOMIC); 296 config.setInterpolator(ANIM_ALL_APPS_FADE, 297 config.isUserControlled() ? ALL_APPS_FADE_MANUAL : ALL_APPS_FADE_ATOMIC); 298 config.setInterpolator(ANIM_VERTICAL_PROGRESS, 299 config.isUserControlled() 300 ? ALL_APPS_VERTICAL_PROGRESS_MANUAL 301 : ALL_APPS_VERTICAL_PROGRESS_ATOMIC); 302 } 303 } 304 305 /** Creates an interpolator that is 0 until the threshold, then follows given interpolator. */ thresholdInterpolator(float threshold, Interpolator interpolator)306 private static Interpolator thresholdInterpolator(float threshold, Interpolator interpolator) { 307 return progress -> progress <= threshold ? 0 : interpolator.getInterpolation(progress); 308 } 309 } 310