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