• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.app.animation.Interpolators.ACCELERATE_2;
19 import static com.android.app.animation.Interpolators.DECELERATE_2;
20 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
21 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
22 import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
23 import static com.android.launcher3.testing.shared.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
24 import static com.android.launcher3.testing.shared.TestProtocol.EDIT_MODE_STATE_ORDINAL;
25 import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_ORDINAL;
26 import static com.android.launcher3.testing.shared.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
27 import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
28 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
29 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
30 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
31 import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
32 import static com.android.launcher3.testing.shared.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
33 
34 import android.content.Context;
35 import android.graphics.Color;
36 import android.view.View;
37 import android.view.animation.Interpolator;
38 
39 import androidx.annotation.FloatRange;
40 
41 import com.android.launcher3.statemanager.BaseState;
42 import com.android.launcher3.statemanager.StateManager;
43 import com.android.launcher3.states.EditModeState;
44 import com.android.launcher3.states.HintState;
45 import com.android.launcher3.states.SpringLoadedState;
46 import com.android.launcher3.testing.shared.TestProtocol;
47 import com.android.launcher3.uioverrides.states.AllAppsState;
48 import com.android.launcher3.uioverrides.states.OverviewState;
49 import com.android.launcher3.views.ActivityContext;
50 
51 import java.util.Arrays;
52 
53 /**
54  * Base state for various states used for the Launcher
55  */
56 public abstract class LauncherState implements BaseState<LauncherState> {
57 
58     /**
59      * Set of elements indicating various workspace elements which change visibility across states
60      * Note that workspace is not included here as in that case, we animate individual pages
61      */
62     public static final int NONE = 0;
63     public static final int HOTSEAT_ICONS = 1 << 0;
64     public static final int ALL_APPS_CONTENT = 1 << 1;
65     public static final int VERTICAL_SWIPE_INDICATOR = 1 << 2;
66     public static final int OVERVIEW_ACTIONS = 1 << 3;
67     public static final int CLEAR_ALL_BUTTON = 1 << 4;
68     public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
69     public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
70     public static final int FLOATING_SEARCH_BAR = 1 << 7;
71 
72     // Flag indicating workspace has multiple pages visible.
73     public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
74     // Flag indicating that workspace and its contents are not accessible
75     public static final int FLAG_WORKSPACE_INACCESSIBLE = BaseState.getFlag(1);
76 
77     // Flag indicating the state allows workspace icons to be dragged.
78     public static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = BaseState.getFlag(2);
79     // Flag to indicate that workspace should draw page background
80     public static final int FLAG_WORKSPACE_HAS_BACKGROUNDS = BaseState.getFlag(3);
81     // Flag to indicate if the state would have scrim over sysui region: statu sbar and nav bar
82     public static final int FLAG_HAS_SYS_UI_SCRIM = BaseState.getFlag(4);
83     // Flag to inticate that all popups should be closed when this state is enabled.
84     public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(5);
85     public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(6);
86 
87     // Flag indicating that hotseat and its contents are not accessible.
88     public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(7);
89 
90 
91     public static final float NO_OFFSET = 0;
92     public static final float NO_SCALE = 1;
93 
94     protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER =
95             new PageAlphaProvider(ACCELERATE_2) {
96                 @Override
97                 public float getPageAlpha(int pageIndex) {
98                     return 1;
99                 }
100             };
101 
102     protected static final PageTranslationProvider DEFAULT_PAGE_TRANSLATION_PROVIDER =
103             new PageTranslationProvider(DECELERATE_2) {
104                 @Override
105                 public float getPageTranslation(int pageIndex) {
106                     return 0;
107                 }
108             };
109 
110     private static final LauncherState[] sAllStates = new LauncherState[11];
111 
112     /**
113      * TODO: Create a separate class for NORMAL state.
114      */
115     public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
116             LAUNCHER_STATE_HOME,
117             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HAS_SYS_UI_SCRIM) {
118         @Override
119         public int getTransitionDuration(Context context, boolean isToState) {
120             // Arbitrary duration, when going to NORMAL we use the state we're coming from instead.
121             return 0;
122         }
123     };
124 
125     /**
126      * Various Launcher states arranged in the increasing order of UI layers
127      */
128     public static final LauncherState SPRING_LOADED = new SpringLoadedState(
129             SPRING_LOADED_STATE_ORDINAL);
130     public static final LauncherState EDIT_MODE = new EditModeState(EDIT_MODE_STATE_ORDINAL);
131     public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
132     public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL);
133     public static final LauncherState HINT_STATE_TWO_BUTTON = new HintState(
134             HINT_STATE_TWO_BUTTON_ORDINAL, LAUNCHER_STATE_OVERVIEW);
135 
136     public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
137     public static final LauncherState OVERVIEW_MODAL_TASK = OverviewState.newModalTaskState(
138             OVERVIEW_MODAL_TASK_STATE_ORDINAL);
139     /**
140      * State when user performs a quickswitch gesture from home/workspace to the most recent
141      * app
142      */
143     public static final LauncherState QUICK_SWITCH_FROM_HOME =
144             OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL);
145     public static final LauncherState BACKGROUND_APP =
146             OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL);
147     public static final LauncherState OVERVIEW_SPLIT_SELECT =
148             OverviewState.newSplitSelectState(OVERVIEW_SPLIT_SELECT_ORDINAL);
149 
150     public final int ordinal;
151 
152     /**
153      * Used for {@link com.android.launcher3.logging.StatsLogManager}
154      */
155     public final int statsLogOrdinal;
156 
157     /**
158      * True if the state has overview panel visible.
159      */
160     public final boolean overviewUi;
161 
162     private final int mFlags;
163 
LauncherState(int id, int statsLogOrdinal, int flags)164     public LauncherState(int id, int statsLogOrdinal, int flags) {
165         this.statsLogOrdinal = statsLogOrdinal;
166         this.mFlags = flags;
167         this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
168         this.ordinal = id;
169         sAllStates[id] = this;
170     }
171 
172     /**
173      * Returns if the state has the provided flag
174      */
175     @Override
hasFlag(int mask)176     public final boolean hasFlag(int mask) {
177         return (mFlags & mask) != 0;
178     }
179 
values()180     public static LauncherState[] values() {
181         return Arrays.copyOf(sAllStates, sAllStates.length);
182     }
183 
getWorkspaceScaleAndTranslation(Launcher launcher)184     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
185         return new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
186     }
187 
getHotseatScaleAndTranslation(Launcher launcher)188     public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
189         // For most states, treat the hotseat as if it were part of the workspace.
190         return getWorkspaceScaleAndTranslation(launcher);
191     }
192 
193     /**
194      * Returns an array of two elements.
195      * The first specifies the scale for the overview
196      * The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
197      * should be shifted horizontally.
198      */
getOverviewScaleAndOffset(Launcher launcher)199     public float[] getOverviewScaleAndOffset(Launcher launcher) {
200         return launcher.getNormalOverviewScaleAndOffset();
201     }
202 
getOverviewFullscreenProgress()203     public float getOverviewFullscreenProgress() {
204         return 0;
205     }
206 
207     /**
208      * How far from the bottom of the screen the <em>floating</em> search bar should rest in this
209      * state when the IME is not present.
210      * <p>
211      * To hide offscreen, use a negative value.
212      * <p>
213      * Note: if the provided value is non-negative but less than the current bottom insets, the
214      * insets will be applied. As such, you can use 0 to default to this.
215      */
getFloatingSearchBarRestingMarginBottom(Launcher launcher)216     public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
217         DeviceProfile dp = launcher.getDeviceProfile();
218         return areElementsVisible(launcher, FLOATING_SEARCH_BAR) ? dp.getQsbOffsetY()
219                 : -dp.hotseatQsbHeight;
220     }
221 
222     /**
223      * How far from the start of the screen the <em>floating</em> search bar should rest.
224      * <p>
225      * To use original margin, return a negative value.
226      */
getFloatingSearchBarRestingMarginStart(Launcher launcher)227     public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
228         boolean isRtl = Utilities.isRtl(launcher.getResources());
229         View qsb = launcher.getHotseat().getQsb();
230         return isRtl ? launcher.getHotseat().getRight() - qsb.getRight() : qsb.getLeft();
231     }
232 
233     /**
234      * How far from the end of the screen the <em>floating</em> search bar should rest.
235      * <p>
236      * To use original margin, return a negative value.
237      */
getFloatingSearchBarRestingMarginEnd(Launcher launcher)238     public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
239         DeviceProfile dp = launcher.getDeviceProfile();
240         if (dp.isQsbInline) {
241             int marginStart = getFloatingSearchBarRestingMarginStart(launcher);
242             return dp.widthPx - marginStart - dp.hotseatQsbWidth;
243         }
244 
245         boolean isRtl = Utilities.isRtl(launcher.getResources());
246         View qsb = launcher.getHotseat().getQsb();
247         return isRtl ? qsb.getLeft() : launcher.getHotseat().getRight() - qsb.getRight();
248     }
249 
250     /** Whether the <em>floating</em> search bar should use the pill UI when not focused. */
shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher)251     public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
252         return false;
253     }
254 
getVisibleElements(Launcher launcher)255     public int getVisibleElements(Launcher launcher) {
256         int elements = HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR;
257         // Floating search bar is visible in normal state except in landscape on phones.
258         if (!(launcher.getDeviceProfile().isPhone && launcher.getDeviceProfile().isLandscape)) {
259             elements |= FLOATING_SEARCH_BAR;
260         }
261         return elements;
262     }
263 
264     /**
265      * A shorthand for checking getVisibleElements() & elements == elements.
266      * @return Whether all of the given elements are visible.
267      */
areElementsVisible(Launcher launcher, int elements)268     public boolean areElementsVisible(Launcher launcher, int elements) {
269         return (getVisibleElements(launcher) & elements) == elements;
270     }
271 
272     /**
273      * Returns whether taskbar is stashed and thus should either:
274      * 1) replace hotseat or taskbar icons with a handle in gesture navigation mode or
275      * 2) fade out the hotseat or taskbar icons in 3-button navigation mode.
276      */
isTaskbarStashed(Launcher launcher)277     public boolean isTaskbarStashed(Launcher launcher) {
278         return false;
279     }
280 
281     /** Returns whether taskbar is aligned with the hotseat vs position inside apps */
isTaskbarAlignedWithHotseat(Launcher launcher)282     public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
283         return true;
284     }
285 
286     /**
287      * Returns whether taskbar global drag is disallowed in this state.
288      */
disallowTaskbarGlobalDrag()289     public boolean disallowTaskbarGlobalDrag() {
290         return false;
291     }
292 
293     /**
294      * Returns whether the taskbar shortcut should trigger split selection mode.
295      */
allowTaskbarInitialSplitSelection()296     public boolean allowTaskbarInitialSplitSelection() {
297         return false;
298     }
299 
300     /**
301      * Fraction shift in the vertical translation UI and related properties
302      *
303      * @see com.android.launcher3.allapps.AllAppsTransitionController
304      */
getVerticalProgress(Launcher launcher)305     public float getVerticalProgress(Launcher launcher) {
306         return 1f;
307     }
308 
getWorkspaceBackgroundAlpha(Launcher launcher)309     public float getWorkspaceBackgroundAlpha(Launcher launcher) {
310         return 0;
311     }
312 
313     /**
314      * What color should the workspace scrim be in when at rest in this state.
315      * Return {@link Color#TRANSPARENT} for no scrim.
316      */
getWorkspaceScrimColor(Launcher launcher)317     public int getWorkspaceScrimColor(Launcher launcher) {
318         return Color.TRANSPARENT;
319     }
320 
321     /**
322      * For this state, how modal should over view been shown. 0 modalness means all tasks drawn,
323      * 1 modalness means the current task is show on its own.
324      */
getOverviewModalness()325     public float getOverviewModalness() {
326         return 0;
327     }
328 
329     /**
330      * For this state, how much additional translation there should be for each of the
331      * child TaskViews. Note that the translation can be its primary or secondary dimension.
332      */
getSplitSelectTranslation(Launcher launcher)333     public float getSplitSelectTranslation(Launcher launcher) {
334         return 0;
335     }
336 
337     /**
338      * The amount of blur and wallpaper zoom to apply to the background of either the app
339      * or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
340      *
341      * 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
342      */
343     public final  <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
getDepth(DEVICE_PROFILE_CONTEXT context)344             float getDepth(DEVICE_PROFILE_CONTEXT context) {
345         return getDepth(context,
346                 BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
347     }
348 
349     /**
350      * Returns the amount of blur and wallpaper zoom for this state with {@param isMultiWindowMode}.
351      *
352      * @see #getDepth(Context).
353      */
354     public final <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
getDepth(DEVICE_PROFILE_CONTEXT context, boolean isMultiWindowMode)355             float getDepth(DEVICE_PROFILE_CONTEXT context, boolean isMultiWindowMode) {
356         if (isMultiWindowMode) {
357             return 0;
358         }
359         return getDepthUnchecked(context);
360     }
361 
362     protected <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
getDepthUnchecked(DEVICE_PROFILE_CONTEXT context)363             float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
364         return 0f;
365     }
366 
getDescription(Launcher launcher)367     public String getDescription(Launcher launcher) {
368         return launcher.getWorkspace().getCurrentPageDescription();
369     }
370 
getWorkspacePageAlphaProvider(Launcher launcher)371     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
372         if ((this != NORMAL && this != HINT_STATE)
373                 || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
374             return DEFAULT_ALPHA_PROVIDER;
375         }
376         final int centerPage = launcher.getWorkspace().getNextPage();
377         return new PageAlphaProvider(ACCELERATE_2) {
378             @Override
379             public float getPageAlpha(int pageIndex) {
380                 return pageIndex != centerPage ? 0 : 1f;
381             }
382         };
383     }
384 
385     /**
386      * Gets the translation provider for workspace pages.
387      */
388     public PageTranslationProvider getWorkspacePageTranslationProvider(Launcher launcher) {
389         if (!(this == SPRING_LOADED || this == EDIT_MODE)
390                 || !launcher.getDeviceProfile().isTwoPanels) {
391             return DEFAULT_PAGE_TRANSLATION_PROVIDER;
392         }
393         final float quarterPageSpacing = launcher.getWorkspace().getPageSpacing() / 4f;
394         return new PageTranslationProvider(DECELERATE_2) {
395             @Override
396             public float getPageTranslation(int pageIndex) {
397                 boolean isRtl = launcher.getWorkspace().mIsRtl;
398                 boolean isFirstPage = pageIndex % 2 == 0;
399                 return ((isFirstPage && !isRtl) || (!isFirstPage && isRtl)) ? -quarterPageSpacing
400                         : quarterPageSpacing;
401             }
402         };
403     }
404 
405     /**
406      * Called when leaving this LauncherState
407      * @param launcher - Launcher instance
408      * @param toState - New LauncherState that is being entered
409      */
410     public void onLeavingState(Launcher launcher, LauncherState toState) {
411         // no-op
412         // override to handle when leaving current LauncherState
413     }
414 
415     @Override
416     public LauncherState getHistoryForState(LauncherState previousState) {
417         // No history is supported
418         return NORMAL;
419     }
420 
421     @Override
422     public String toString() {
423         return TestProtocol.stateOrdinalToString(ordinal);
424     }
425 
426     public void onBackPressed(Launcher launcher) {
427         if (this != NORMAL) {
428             StateManager<LauncherState> lsm = launcher.getStateManager();
429             LauncherState lastState = lsm.getLastState();
430             lsm.goToState(lastState);
431         }
432     }
433 
434     /**
435      * Find {@link StateManager} and target {@link LauncherState} to handle back progress in
436      * predictive back gesture.
437      */
438     public void onBackProgressed(
439             Launcher launcher, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
440         StateManager<LauncherState> lsm = launcher.getStateManager();
441         LauncherState toState = lsm.getLastState();
442         lsm.onBackProgressed(toState, backProgress);
443     }
444 
445     /**
446      * Find {@link StateManager} and target {@link LauncherState} to handle backProgress in
447      * predictive back gesture.
448      */
449     public void onBackCancelled(Launcher launcher) {
450         StateManager<LauncherState> lsm = launcher.getStateManager();
451         LauncherState toState = lsm.getLastState();
452         lsm.onBackCancelled(toState);
453     }
454 
455     public static abstract class PageAlphaProvider {
456 
457         public final Interpolator interpolator;
458 
459         public PageAlphaProvider(Interpolator interpolator) {
460             this.interpolator = interpolator;
461         }
462 
463         public abstract float getPageAlpha(int pageIndex);
464     }
465 
466     /**
467      * Provider for the translation and animation interpolation of workspace pages.
468      */
469     public abstract static class PageTranslationProvider {
470 
471         public final Interpolator interpolator;
472 
473         public PageTranslationProvider(Interpolator interpolator) {
474             this.interpolator = interpolator;
475         }
476 
477         /**
478          * Gets the translation of the workspace page at the provided page index.
479          */
480         public abstract float getPageTranslation(int pageIndex);
481     }
482 
483     public static class ScaleAndTranslation {
484         public float scale;
485         public float translationX;
486         public float translationY;
487 
488         public ScaleAndTranslation(float scale, float translationX, float translationY) {
489             this.scale = scale;
490             this.translationX = translationX;
491             this.translationY = translationY;
492         }
493     }
494 }
495