1 /* 2 * Copyright (C) 2017 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; 17 18 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; 19 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; 20 import static android.view.View.VISIBLE; 21 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; 22 23 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; 24 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; 25 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; 26 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; 27 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; 28 import static com.android.launcher3.anim.Interpolators.ACCEL; 29 import static com.android.launcher3.anim.Interpolators.DEACCEL; 30 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; 31 import static com.android.launcher3.anim.Interpolators.clampToProgress; 32 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; 33 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; 34 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; 35 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_STATE_ORDINAL; 36 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; 37 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; 38 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL; 39 import static com.android.launcher3.anim.Interpolators.ACCEL_2; 40 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; 41 42 import android.view.animation.Interpolator; 43 44 import com.android.launcher3.anim.AnimatorSetBuilder; 45 import com.android.launcher3.states.SpringLoadedState; 46 import com.android.launcher3.uioverrides.UiFactory; 47 import com.android.launcher3.uioverrides.states.AllAppsState; 48 import com.android.launcher3.uioverrides.states.OverviewState; 49 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; 50 51 import java.util.Arrays; 52 53 54 /** 55 * Base state for various states used for the Launcher 56 */ 57 public class LauncherState { 58 59 60 /** 61 * Set of elements indicating various workspace elements which change visibility across states 62 * Note that workspace is not included here as in that case, we animate individual pages 63 */ 64 public static final int NONE = 0; 65 public static final int HOTSEAT_ICONS = 1 << 0; 66 public static final int HOTSEAT_SEARCH_BOX = 1 << 1; 67 public static final int ALL_APPS_HEADER = 1 << 2; 68 public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions 69 public static final int ALL_APPS_CONTENT = 1 << 4; 70 public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5; 71 public static final int RECENTS_CLEAR_ALL_BUTTON = 1 << 6; 72 73 protected static final int FLAG_MULTI_PAGE = 1 << 0; 74 protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1; 75 protected static final int FLAG_DISABLE_RESTORE = 1 << 2; 76 protected static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = 1 << 3; 77 protected static final int FLAG_DISABLE_PAGE_CLIPPING = 1 << 4; 78 protected static final int FLAG_PAGE_BACKGROUNDS = 1 << 5; 79 protected static final int FLAG_DISABLE_INTERACTION = 1 << 6; 80 protected static final int FLAG_OVERVIEW_UI = 1 << 7; 81 protected static final int FLAG_HIDE_BACK_BUTTON = 1 << 8; 82 protected static final int FLAG_HAS_SYS_UI_SCRIM = 1 << 9; 83 84 protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER = 85 new PageAlphaProvider(ACCEL_2) { 86 @Override 87 public float getPageAlpha(int pageIndex) { 88 return 1; 89 } 90 }; 91 92 private static final LauncherState[] sAllStates = new LauncherState[7]; 93 94 /** 95 * TODO: Create a separate class for NORMAL state. 96 */ 97 public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL, 98 ContainerType.WORKSPACE, 0, 99 FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON | 100 FLAG_HAS_SYS_UI_SCRIM); 101 102 /** 103 * Various Launcher states arranged in the increasing order of UI layers 104 */ 105 public static final LauncherState SPRING_LOADED = new SpringLoadedState( 106 SPRING_LOADED_STATE_ORDINAL); 107 public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL); 108 109 public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL); 110 public static final LauncherState OVERVIEW_PEEK = 111 OverviewState.newPeekState(OVERVIEW_PEEK_STATE_ORDINAL); 112 public static final LauncherState QUICK_SWITCH = 113 OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL); 114 public static final LauncherState BACKGROUND_APP = 115 OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL); 116 117 public final int ordinal; 118 119 /** 120 * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher} 121 */ 122 public final int containerType; 123 124 /** 125 * True if the state can be persisted across activity restarts. 126 */ 127 public final boolean disableRestore; 128 129 /** 130 * True if workspace has multiple pages visible. 131 */ 132 public final boolean hasMultipleVisiblePages; 133 134 /** 135 * Accessibility flag for workspace and its pages. 136 * @see android.view.View#setImportantForAccessibility(int) 137 */ 138 public final int workspaceAccessibilityFlag; 139 140 /** 141 * Properties related to state transition animation 142 * 143 * @see WorkspaceStateTransitionAnimation 144 */ 145 public final boolean hasWorkspacePageBackground; 146 147 public final int transitionDuration; 148 149 /** 150 * True if the state allows workspace icons to be dragged. 151 */ 152 public final boolean workspaceIconsCanBeDragged; 153 154 /** 155 * True if the workspace pages should not be clipped relative to the workspace bounds 156 * for this state. 157 */ 158 public final boolean disablePageClipping; 159 160 /** 161 * True if launcher can not be directly interacted in this state; 162 */ 163 public final boolean disableInteraction; 164 165 /** 166 * True if the state has overview panel visible. 167 */ 168 public final boolean overviewUi; 169 170 /** 171 * True if the back button should be hidden when in this state (assuming no floating views are 172 * open, launcher has window focus, etc). 173 */ 174 public final boolean hideBackButton; 175 176 public final boolean hasSysUiScrim; 177 LauncherState(int id, int containerType, int transitionDuration, int flags)178 public LauncherState(int id, int containerType, int transitionDuration, int flags) { 179 this.containerType = containerType; 180 this.transitionDuration = transitionDuration; 181 182 this.hasWorkspacePageBackground = (flags & FLAG_PAGE_BACKGROUNDS) != 0; 183 this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0; 184 this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0 185 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 186 : IMPORTANT_FOR_ACCESSIBILITY_AUTO; 187 this.disableRestore = (flags & FLAG_DISABLE_RESTORE) != 0; 188 this.workspaceIconsCanBeDragged = (flags & FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED) != 0; 189 this.disablePageClipping = (flags & FLAG_DISABLE_PAGE_CLIPPING) != 0; 190 this.disableInteraction = (flags & FLAG_DISABLE_INTERACTION) != 0; 191 this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0; 192 this.hideBackButton = (flags & FLAG_HIDE_BACK_BUTTON) != 0; 193 this.hasSysUiScrim = (flags & FLAG_HAS_SYS_UI_SCRIM) != 0; 194 195 this.ordinal = id; 196 sAllStates[id] = this; 197 } 198 values()199 public static LauncherState[] values() { 200 return Arrays.copyOf(sAllStates, sAllStates.length); 201 } 202 getWorkspaceScaleAndTranslation(Launcher launcher)203 public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) { 204 return new ScaleAndTranslation(1, 0, 0); 205 } 206 getHotseatScaleAndTranslation(Launcher launcher)207 public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) { 208 // For most states, treat the hotseat as if it were part of the workspace. 209 return getWorkspaceScaleAndTranslation(launcher); 210 } 211 getOverviewScaleAndTranslation(Launcher launcher)212 public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) { 213 return UiFactory.getOverviewScaleAndTranslationForNormalState(launcher); 214 } 215 getOverviewFullscreenProgress()216 public float getOverviewFullscreenProgress() { 217 return 0; 218 } 219 onStateEnabled(Launcher launcher)220 public void onStateEnabled(Launcher launcher) { 221 dispatchWindowStateChanged(launcher); 222 } 223 onStateDisabled(Launcher launcher)224 public void onStateDisabled(Launcher launcher) { } 225 getVisibleElements(Launcher launcher)226 public int getVisibleElements(Launcher launcher) { 227 if (launcher.getDeviceProfile().isVerticalBarLayout()) { 228 return HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR; 229 } 230 return HOTSEAT_ICONS | HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR; 231 } 232 233 /** 234 * Fraction shift in the vertical translation UI and related properties 235 * 236 * @see com.android.launcher3.allapps.AllAppsTransitionController 237 */ getVerticalProgress(Launcher launcher)238 public float getVerticalProgress(Launcher launcher) { 239 return 1f; 240 } 241 getWorkspaceScrimAlpha(Launcher launcher)242 public float getWorkspaceScrimAlpha(Launcher launcher) { 243 return 0; 244 } 245 getDescription(Launcher launcher)246 public String getDescription(Launcher launcher) { 247 return launcher.getWorkspace().getCurrentPageDescription(); 248 } 249 getWorkspacePageAlphaProvider(Launcher launcher)250 public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { 251 if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) { 252 return DEFAULT_ALPHA_PROVIDER; 253 } 254 final int centerPage = launcher.getWorkspace().getNextPage(); 255 return new PageAlphaProvider(ACCEL_2) { 256 @Override 257 public float getPageAlpha(int pageIndex) { 258 return pageIndex != centerPage ? 0 : 1f; 259 } 260 }; 261 } 262 263 public LauncherState getHistoryForState(LauncherState previousState) { 264 // No history is supported 265 return NORMAL; 266 } 267 268 /** 269 * Called when the start transition ends and the user settles on this particular state. 270 */ 271 public void onStateTransitionEnd(Launcher launcher) { 272 if (this == NORMAL) { 273 // Clear any rotation locks when going to normal state 274 launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE); 275 } 276 } 277 278 public void onBackPressed(Launcher launcher) { 279 if (this != NORMAL) { 280 LauncherStateManager lsm = launcher.getStateManager(); 281 LauncherState lastState = lsm.getLastState(); 282 lsm.goToState(lastState); 283 } 284 } 285 286 /** 287 * Prepares for a non-user controlled animation from fromState to this state. Preparations 288 * include: 289 * - Setting interpolators for various animations included in the state transition. 290 * - Setting some start values (e.g. scale) for views that are hidden but about to be shown. 291 */ 292 public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, 293 AnimatorSetBuilder builder) { 294 if (this == NORMAL && fromState == OVERVIEW) { 295 builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); 296 builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); 297 builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f)); 298 builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL); 299 builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); 300 Workspace workspace = launcher.getWorkspace(); 301 302 // Start from a higher workspace scale, but only if we're invisible so we don't jump. 303 boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE; 304 if (isWorkspaceVisible) { 305 CellLayout currentChild = (CellLayout) workspace.getChildAt( 306 workspace.getCurrentPage()); 307 isWorkspaceVisible = currentChild.getVisibility() == VISIBLE 308 && currentChild.getShortcutsAndWidgets().getAlpha() > 0; 309 } 310 if (!isWorkspaceVisible) { 311 workspace.setScaleX(0.92f); 312 workspace.setScaleY(0.92f); 313 } 314 Hotseat hotseat = launcher.getHotseat(); 315 boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0; 316 if (!isHotseatVisible) { 317 hotseat.setScaleX(0.92f); 318 hotseat.setScaleY(0.92f); 319 } 320 } else if (this == NORMAL && fromState == OVERVIEW_PEEK) { 321 // Keep fully visible until the very end (when overview is offscreen) to make invisible. 322 builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1); 323 } 324 } 325 326 protected static void dispatchWindowStateChanged(Launcher launcher) { 327 launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED); 328 } 329 330 public static abstract class PageAlphaProvider { 331 332 public final Interpolator interpolator; 333 334 public PageAlphaProvider(Interpolator interpolator) { 335 this.interpolator = interpolator; 336 } 337 338 public abstract float getPageAlpha(int pageIndex); 339 } 340 341 public static class ScaleAndTranslation { 342 public float scale; 343 public float translationX; 344 public float translationY; 345 346 public ScaleAndTranslation(float scale, float translationX, float translationY) { 347 this.scale = scale; 348 this.translationX = translationX; 349 this.translationY = translationY; 350 } 351 } 352 } 353