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.uioverrides.states;
17 
18 import static com.android.app.animation.Interpolators.DECELERATE_2;
19 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
20 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
21 
22 import android.content.Context;
23 
24 import com.android.internal.jank.Cuj;
25 import com.android.launcher3.DeviceProfile;
26 import com.android.launcher3.Launcher;
27 import com.android.launcher3.LauncherState;
28 import com.android.launcher3.R;
29 import com.android.launcher3.config.FeatureFlags;
30 import com.android.launcher3.util.Themes;
31 import com.android.launcher3.views.ActivityContext;
32 import com.android.quickstep.util.BaseDepthController;
33 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
34 
35 import java.util.concurrent.TimeUnit;
36 
37 /**
38  * Definition for AllApps state
39  */
40 public class AllAppsState extends LauncherState {
41 
42     private static final int STATE_FLAGS =
43             FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE;
44     private static final long BACK_CUJ_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
45 
46 
AllAppsState(int id)47     public AllAppsState(int id) {
48         super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
49     }
50 
51     @Override
52     public <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState)53     int getTransitionDuration(DEVICE_PROFILE_CONTEXT context, boolean isToState) {
54         return isToState
55                 ? context.getDeviceProfile().allAppsOpenDuration
56                 : context.getDeviceProfile().allAppsCloseDuration;
57     }
58 
59     @Override
onBackStarted(Launcher launcher)60     public void onBackStarted(Launcher launcher) {
61         // Because the back gesture can take longer time depending on when user release the finger,
62         // we pass BACK_CUJ_TIMEOUT_MS as timeout to the jank monitor.
63         InteractionJankMonitorWrapper.begin(launcher.getAppsView(),
64                 Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK, BACK_CUJ_TIMEOUT_MS);
65         super.onBackStarted(launcher);
66     }
67 
68     @Override
onBackInvoked(Launcher launcher)69     public void onBackInvoked(Launcher launcher) {
70         // In predictive back swipe, onBackInvoked() will be called after onBackStarted().
71         // In 3 button mode, onBackStarted() is not called but onBackInvoked() will be called.
72         // Thus In onBackInvoked(), we should only begin instrumenting if we didn't call
73         // onBackStarted() to start instrumenting CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK.
74         if (!InteractionJankMonitorWrapper.isInstrumenting(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK)) {
75             InteractionJankMonitorWrapper.begin(
76                     launcher.getAppsView(), Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
77         }
78         super.onBackInvoked(launcher);
79     }
80 
81     /** Called when predictive back swipe is cancelled. */
82     @Override
onBackCancelled(Launcher launcher)83     public void onBackCancelled(Launcher launcher) {
84         super.onBackCancelled(launcher);
85         InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
86     }
87 
88     @Override
onBackAnimationCompleted(boolean success)89     protected void onBackAnimationCompleted(boolean success) {
90         if (success) {
91             // Animation was successful.
92             InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
93         } else {
94             // Animation was canceled.
95             InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK);
96         }
97     }
98 
99     @Override
getDescription(Launcher launcher)100     public String getDescription(Launcher launcher) {
101         return launcher.getAppsView().getDescription();
102     }
103 
104     @Override
getTitle()105     public int getTitle() {
106         return R.string.all_apps_label;
107     }
108 
109     @Override
getVerticalProgress(Launcher launcher)110     public float getVerticalProgress(Launcher launcher) {
111         return 0f;
112     }
113 
114     @Override
getWorkspaceScaleAndTranslation(Launcher launcher)115     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
116         return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET,
117                 NO_OFFSET);
118     }
119 
120     @Override
getHotseatScaleAndTranslation(Launcher launcher)121     public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
122         if (launcher.getDeviceProfile().isTablet) {
123             return getWorkspaceScaleAndTranslation(launcher);
124         } else {
125             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
126                     .getWorkspaceScaleAndTranslation(launcher);
127             return new ScaleAndTranslation(
128                     launcher.getDeviceProfile().workspaceContentScale,
129                     overviewScaleAndTranslation.translationX,
130                     overviewScaleAndTranslation.translationY);
131         }
132     }
133 
134     @Override
135     protected <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
getDepthUnchecked(DEVICE_PROFILE_CONTEXT context)136             float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
137         if (context.getDeviceProfile().isTablet) {
138             return context.getDeviceProfile().bottomSheetDepth;
139         } else {
140             // The scrim fades in at approximately 50% of the swipe gesture.
141             if (enableScalingRevealHomeAnimation()) {
142                 // This means that the depth should be twice of what we want, in order to fully zoom
143                 // out during the visible portion of the animation.
144                 return BaseDepthController.DEPTH_60_PERCENT;
145             } else {
146                 // This means that the depth should be greater than 1, in order to fully zoom out.
147                 return 2f;
148             }
149         }
150     }
151 
152     @Override
getWorkspacePageAlphaProvider(Launcher launcher)153     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
154         PageAlphaProvider superPageAlphaProvider = super.getWorkspacePageAlphaProvider(launcher);
155         return new PageAlphaProvider(DECELERATE_2) {
156             @Override
157             public float getPageAlpha(int pageIndex) {
158                 return launcher.getDeviceProfile().isTablet
159                         ? superPageAlphaProvider.getPageAlpha(pageIndex)
160                         : 0;
161             }
162         };
163     }
164 
165     @Override
166     public int getVisibleElements(Launcher launcher) {
167         int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
168         // Only add HOTSEAT_ICONS for tablets in ALL_APPS state.
169         if (launcher.getDeviceProfile().isTablet) {
170             elements |= HOTSEAT_ICONS;
171         }
172         return elements;
173     }
174 
175     @Override
176     public int getFloatingSearchBarRestingMarginBottom(Launcher launcher) {
177         return 0;
178     }
179 
180     @Override
181     public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
182         DeviceProfile dp = launcher.getDeviceProfile();
183         return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(launcher);
184     }
185 
186     @Override
187     public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
188         DeviceProfile dp = launcher.getDeviceProfile();
189         return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(launcher);
190     }
191 
192     @Override
193     public boolean shouldFloatingSearchBarUsePillWhenUnfocused(Launcher launcher) {
194         DeviceProfile dp = launcher.getDeviceProfile();
195         return dp.isPhone && !dp.isLandscape;
196     }
197 
198     @Override
199     public LauncherState getHistoryForState(LauncherState previousState) {
200         return previousState == BACKGROUND_APP ? QUICK_SWITCH_FROM_HOME
201                 : previousState == OVERVIEW ? OVERVIEW : NORMAL;
202     }
203 
204     @Override
205     public float[] getOverviewScaleAndOffset(Launcher launcher) {
206         if (!FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
207             return super.getOverviewScaleAndOffset(launcher);
208         }
209         // This handles the case of returning to the previous app from Overview -> All Apps gesture.
210         // This is the start scale/offset of overview that will be used for that transition.
211         // TODO (b/283336332): Translate in Y direction (ideally with overview resistance).
212         return new float[] {0.5f /* scale */, NO_OFFSET};
213     }
214 
215     @Override
216     public int getWorkspaceScrimColor(Launcher launcher) {
217         return launcher.getDeviceProfile().isTablet
218                 ? launcher.getResources().getColor(R.color.widgets_picker_scrim)
219                 : Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
220     }
221 }
222