• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.allapps;
17 
18 import static com.android.launcher3.LauncherState.ALL_APPS;
19 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
20 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
21 import static com.android.launcher3.anim.Interpolators.LINEAR;
22 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
23 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
24 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
25 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
26 
27 import android.animation.Animator;
28 import android.animation.Animator.AnimatorListener;
29 import android.animation.ObjectAnimator;
30 import android.util.FloatProperty;
31 import android.view.View;
32 import android.view.animation.Interpolator;
33 
34 import com.android.launcher3.DeviceProfile;
35 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
36 import com.android.launcher3.Launcher;
37 import com.android.launcher3.LauncherState;
38 import com.android.launcher3.Utilities;
39 import com.android.launcher3.anim.AnimatorListeners;
40 import com.android.launcher3.anim.Interpolators;
41 import com.android.launcher3.anim.PendingAnimation;
42 import com.android.launcher3.anim.PropertySetter;
43 import com.android.launcher3.config.FeatureFlags;
44 import com.android.launcher3.statemanager.StateManager.StateHandler;
45 import com.android.launcher3.states.StateAnimationConfig;
46 import com.android.launcher3.views.ScrimView;
47 
48 /**
49  * Handles AllApps view transition.
50  * 1) Slides all apps view using direct manipulation
51  * 2) When finger is released, animate to either top or bottom accordingly.
52  * <p/>
53  * Algorithm:
54  * If release velocity > THRES1, snap according to the direction of movement.
55  * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
56  * closer to top or closer to the page indicator.
57  */
58 public class AllAppsTransitionController
59         implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
60     // This constant should match the second derivative of the animator interpolator.
61     public static final float INTERP_COEFF = 1.7f;
62     private static final float CONTENT_VISIBLE_MAX_THRESHOLD = 0.5f;
63 
64     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
65             new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
66 
67                 @Override
68                 public Float get(AllAppsTransitionController controller) {
69                     return controller.mProgress;
70                 }
71 
72                 @Override
73                 public void setValue(AllAppsTransitionController controller, float progress) {
74                     controller.setProgress(progress);
75                 }
76             };
77 
78     private AllAppsContainerView mAppsView;
79 
80     private final Launcher mLauncher;
81     private boolean mIsVerticalLayout;
82 
83     // Animation in this class is controlled by a single variable {@link mProgress}.
84     // Visually, it represents top y coordinate of the all apps container if multiplied with
85     // {@link mShiftRange}.
86 
87     // When {@link mProgress} is 0, all apps container is pulled up.
88     // When {@link mProgress} is 1, all apps container is pulled down.
89     private float mShiftRange;      // changes depending on the orientation
90     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
91 
92     private float mScrollRangeDelta = 0;
93     private ScrimView mScrimView;
94 
AllAppsTransitionController(Launcher l)95     public AllAppsTransitionController(Launcher l) {
96         mLauncher = l;
97         mShiftRange = mLauncher.getDeviceProfile().heightPx;
98         mProgress = 1f;
99 
100         mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
101         mLauncher.addOnDeviceProfileChangeListener(this);
102     }
103 
getShiftRange()104     public float getShiftRange() {
105         return mShiftRange;
106     }
107 
108     @Override
onDeviceProfileChanged(DeviceProfile dp)109     public void onDeviceProfileChanged(DeviceProfile dp) {
110         mIsVerticalLayout = dp.isVerticalBarLayout();
111         setScrollRangeDelta(mScrollRangeDelta);
112 
113         if (mIsVerticalLayout) {
114             mLauncher.getHotseat().setTranslationY(0);
115             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
116         }
117     }
118 
119     /**
120      * Note this method should not be called outside this class. This is public because it is used
121      * in xml-based animations which also handle updating the appropriate UI.
122      *
123      * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
124      * @see #setState(LauncherState)
125      * @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
126      */
setProgress(float progress)127     public void setProgress(float progress) {
128         mProgress = progress;
129         mAppsView.setTranslationY(mProgress * mShiftRange);
130     }
131 
getProgress()132     public float getProgress() {
133         return mProgress;
134     }
135 
136     /**
137      * Sets the vertical transition progress to {@param state} and updates all the dependent UI
138      * accordingly.
139      */
140     @Override
setState(LauncherState state)141     public void setState(LauncherState state) {
142         setProgress(state.getVerticalProgress(mLauncher));
143         setAlphas(state, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
144         onProgressAnimationEnd();
145     }
146 
147     /**
148      * Creates an animation which updates the vertical transition progress and updates all the
149      * dependent UI using various animation events
150      */
151     @Override
setStateWithAnimation(LauncherState toState, StateAnimationConfig config, PendingAnimation builder)152     public void setStateWithAnimation(LauncherState toState,
153             StateAnimationConfig config, PendingAnimation builder) {
154         float targetProgress = toState.getVerticalProgress(mLauncher);
155         if (Float.compare(mProgress, targetProgress) == 0) {
156             setAlphas(toState, config, builder);
157             // Fail fast
158             onProgressAnimationEnd();
159             return;
160         }
161 
162         // need to decide depending on the release velocity
163         Interpolator interpolator = (config.userControlled ? LINEAR : DEACCEL_1_7);
164 
165         Animator anim = createSpringAnimation(mProgress, targetProgress);
166         anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
167         anim.addListener(getProgressAnimatorListener());
168         builder.add(anim);
169 
170         setAlphas(toState, config, builder);
171     }
172 
createSpringAnimation(float... progressValues)173     public Animator createSpringAnimation(float... progressValues) {
174         return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
175     }
176 
177     /**
178      * Updates the property for the provided state
179      */
setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter)180     public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
181         int visibleElements = state.getVisibleElements(mLauncher);
182         boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
183 
184         Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE,
185                 Interpolators.clampToProgress(LINEAR, 0, CONTENT_VISIBLE_MAX_THRESHOLD));
186         setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
187 
188         boolean shouldProtectHeader =
189                 ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
190         mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
191     }
192 
getProgressAnimatorListener()193     public AnimatorListener getProgressAnimatorListener() {
194         return AnimatorListeners.forSuccessCallback(this::onProgressAnimationEnd);
195     }
196 
197     /**
198      * see Launcher#setupViews
199      */
setupViews(ScrimView scrimView, AllAppsContainerView appsView)200     public void setupViews(ScrimView scrimView, AllAppsContainerView appsView) {
201         mScrimView = scrimView;
202         mAppsView = appsView;
203         if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) {
204             mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
205                     View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
206                             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
207         }
208         mAppsView.setScrimView(scrimView);
209     }
210 
211     /**
212      * Updates the total scroll range but does not update the UI.
213      */
setScrollRangeDelta(float delta)214     public void setScrollRangeDelta(float delta) {
215         mScrollRangeDelta = delta;
216         mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
217     }
218 
219     /**
220      * Set the final view states based on the progress.
221      * TODO: This logic should go in {@link LauncherState}
222      */
onProgressAnimationEnd()223     private void onProgressAnimationEnd() {
224         if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
225         if (Float.compare(mProgress, 1f) == 0) {
226             mAppsView.reset(false /* animate */);
227         }
228     }
229 }
230