1 /*
2  * Copyright (C) 2019 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.launcher3.AbstractFloatingView.TYPE_ALL;
19 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
20 import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
21 import static com.android.launcher3.LauncherState.NORMAL;
22 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
23 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
24 
25 import android.animation.AnimatorSet;
26 import android.animation.ValueAnimator;
27 import android.content.Intent;
28 import android.content.IntentSender;
29 import android.content.SharedPreferences;
30 import android.os.Bundle;
31 import android.os.CancellationSignal;
32 
33 import com.android.launcher3.config.FeatureFlags;
34 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
35 import com.android.launcher3.model.WellbeingModel;
36 import com.android.launcher3.popup.SystemShortcut;
37 import com.android.launcher3.proxy.ProxyActivityStarter;
38 import com.android.launcher3.proxy.StartActivityParams;
39 import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
40 import com.android.launcher3.statehandlers.DepthController;
41 import com.android.launcher3.statemanager.StateManager.StateHandler;
42 import com.android.launcher3.uioverrides.RecentsViewStateController;
43 import com.android.launcher3.util.OnboardingPrefs;
44 import com.android.launcher3.util.UiThreadHelper;
45 import com.android.quickstep.RecentsModel;
46 import com.android.quickstep.SysUINavigationMode;
47 import com.android.quickstep.SysUINavigationMode.Mode;
48 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
49 import com.android.quickstep.SystemUiProxy;
50 import com.android.quickstep.util.QuickstepOnboardingPrefs;
51 import com.android.quickstep.util.RemoteAnimationProvider;
52 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
53 import com.android.quickstep.util.ShelfPeekAnim;
54 import com.android.quickstep.views.OverviewActionsView;
55 import com.android.quickstep.views.RecentsView;
56 import com.android.systemui.shared.system.ActivityManagerWrapper;
57 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
58 
59 import java.util.stream.Stream;
60 
61 /**
62  * Extension of Launcher activity to provide quickstep specific functionality
63  */
64 public abstract class BaseQuickstepLauncher extends Launcher
65         implements NavigationModeChangeListener {
66 
67     private DepthController mDepthController = new DepthController(this);
68 
69     /**
70      * Reusable command for applying the back button alpha on the background thread.
71      */
72     public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
73             (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
74                     Float.intBitsToFloat(arg1), arg2 != 0);
75 
76     private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
77 
78     private OverviewActionsView mActionsView;
79     protected HotseatPredictionController mHotseatPredictionController;
80 
81     @Override
onCreate(Bundle savedInstanceState)82     protected void onCreate(Bundle savedInstanceState) {
83         super.onCreate(savedInstanceState);
84 
85         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
86         addMultiWindowModeChangedListener(mDepthController);
87     }
88 
89     @Override
onDestroy()90     public void onDestroy() {
91         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
92         super.onDestroy();
93     }
94 
95     @Override
onNavigationModeChanged(Mode newMode)96     public void onNavigationModeChanged(Mode newMode) {
97         getDragLayer().recreateControllers();
98         if (mActionsView != null && isOverviewActionsEnabled()) {
99             mActionsView.updateVerticalMargin(newMode);
100         }
101     }
102 
103     @Override
onEnterAnimationComplete()104     public void onEnterAnimationComplete() {
105         super.onEnterAnimationComplete();
106         // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
107         // as a part of quickstep, so that high-res thumbnails can load the next time we enter
108         // overview
109         RecentsModel.INSTANCE.get(this).getThumbnailCache()
110                 .getHighResLoadingState().setVisible(true);
111     }
112 
113     @Override
onTrimMemory(int level)114     public void onTrimMemory(int level) {
115         super.onTrimMemory(level);
116         RecentsModel.INSTANCE.get(this).onTrimMemory(level);
117     }
118 
119     @Override
onUiChangedWhileSleeping()120     protected void onUiChangedWhileSleeping() {
121         // Remove the snapshot because the content view may have obvious changes.
122         ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this);
123     }
124 
125     @Override
startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)126     public void startIntentSenderForResult(IntentSender intent, int requestCode,
127             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
128         if (requestCode != -1) {
129             mPendingActivityRequestCode = requestCode;
130             StartActivityParams params = new StartActivityParams(this, requestCode);
131             params.intentSender = intent;
132             params.fillInIntent = fillInIntent;
133             params.flagsMask = flagsMask;
134             params.flagsValues = flagsValues;
135             params.extraFlags = extraFlags;
136             params.options = options;
137             startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
138         } else {
139             super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
140                     flagsValues, extraFlags, options);
141         }
142     }
143 
144     @Override
startActivityForResult(Intent intent, int requestCode, Bundle options)145     public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
146         if (requestCode != -1) {
147             mPendingActivityRequestCode = requestCode;
148             StartActivityParams params = new StartActivityParams(this, requestCode);
149             params.intent = intent;
150             params.options = options;
151             startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
152         } else {
153             super.startActivityForResult(intent, requestCode, options);
154         }
155     }
156 
157     @Override
onDeferredResumed()158     protected void onDeferredResumed() {
159         super.onDeferredResumed();
160         if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
161             // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
162             onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
163             // ProxyActivityStarter is started with clear task to reset the task after which it
164             // removes the task itself.
165             startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
166         }
167     }
168 
169     @Override
setupViews()170     protected void setupViews() {
171         super.setupViews();
172 
173         SysUINavigationMode.INSTANCE.get(this).updateMode();
174         mActionsView = findViewById(R.id.overview_actions_view);
175         ((RecentsView) getOverviewPanel()).init(mActionsView);
176 
177         if (isOverviewActionsEnabled()) {
178             // Overview is above all other launcher elements, including qsb, so move it to the top.
179             getOverviewPanel().bringToFront();
180             mActionsView.bringToFront();
181             mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
182         }
183     }
184 
isOverviewActionsEnabled()185     private boolean isOverviewActionsEnabled() {
186         return FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this);
187     }
188 
getActionsView()189     public <T extends OverviewActionsView> T getActionsView() {
190         return (T) mActionsView;
191     }
192 
193     @Override
closeOpenViews(boolean animate)194     protected void closeOpenViews(boolean animate) {
195         super.closeOpenViews(animate);
196         ActivityManagerWrapper.getInstance()
197                 .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
198     }
199 
200     @Override
createStateHandlers()201     protected StateHandler<LauncherState>[] createStateHandlers() {
202         return new StateHandler[] {
203                 getAllAppsController(),
204                 getWorkspace(),
205                 getDepthController(),
206                 new RecentsViewStateController(this),
207                 new BackButtonAlphaHandler(this)};
208     }
209 
getDepthController()210     public DepthController getDepthController() {
211         return mDepthController;
212     }
213 
214     @Override
createOnboardingPrefs(SharedPreferences sharedPrefs)215     protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
216         return new QuickstepOnboardingPrefs(this, sharedPrefs);
217     }
218 
219     @Override
useFadeOutAnimationForLauncherStart(CancellationSignal signal)220     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
221         QuickstepAppTransitionManagerImpl appTransitionManager =
222                 (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
223         appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
224             @Override
225             public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
226                     RemoteAnimationTargetCompat[] wallpaperTargets) {
227 
228                 // On the first call clear the reference.
229                 signal.cancel();
230 
231                 ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
232                 fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
233                         wallpaperTargets));
234                 AnimatorSet anim = new AnimatorSet();
235                 anim.play(fadeAnimation);
236                 return anim;
237             }
238         }, signal);
239     }
240 
241     @Override
getNormalOverviewScaleAndOffset()242     public float[] getNormalOverviewScaleAndOffset() {
243         return SysUINavigationMode.getMode(this) == Mode.NO_BUTTON
244                 ? new float[] {1, 1} : new float[] {1.1f, 0};
245     }
246 
247     @Override
onDragLayerHierarchyChanged()248     public void onDragLayerHierarchyChanged() {
249         onLauncherStateOrFocusChanged();
250     }
251 
252     @Override
onActivityFlagsChanged(int changeBits)253     protected void onActivityFlagsChanged(int changeBits) {
254         if ((changeBits
255                 & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
256             onLauncherStateOrFocusChanged();
257         }
258 
259         if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
260             mDepthController.setActivityStarted(isStarted());
261         }
262 
263         super.onActivityFlagsChanged(changeBits);
264     }
265 
shouldBackButtonBeHidden(LauncherState toState)266     public boolean shouldBackButtonBeHidden(LauncherState toState) {
267         Mode mode = SysUINavigationMode.getMode(this);
268         boolean shouldBackButtonBeHidden = mode.hasGestures
269                 && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
270                 && hasWindowFocus()
271                 && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
272         if (shouldBackButtonBeHidden) {
273             // Show the back button if there is a floating view visible.
274             shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
275                     TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
276         }
277         return shouldBackButtonBeHidden;
278     }
279 
280     /**
281      * Sets the back button visibility based on the current state/window focus.
282      */
onLauncherStateOrFocusChanged()283     private void onLauncherStateOrFocusChanged() {
284         boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
285         UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
286                 shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
287         if (getDragLayer() != null) {
288             getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
289         }
290     }
291 
292     @Override
finishBindingItems(int pageBoundFirst)293     public void finishBindingItems(int pageBoundFirst) {
294         super.finishBindingItems(pageBoundFirst);
295         // Instantiate and initialize WellbeingModel now that its loading won't interfere with
296         // populating workspace.
297         // TODO: Find a better place for this
298         WellbeingModel.INSTANCE.get(this);
299     }
300 
301     @Override
getSupportedShortcuts()302     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
303         return Stream.concat(super.getSupportedShortcuts(),
304                 Stream.of(WellbeingModel.SHORTCUT_FACTORY));
305     }
306 
getShelfPeekAnim()307     public ShelfPeekAnim getShelfPeekAnim() {
308         return mShelfPeekAnim;
309     }
310 
311     /**
312      * Returns Prediction controller for hybrid hotseat
313      */
getHotseatPredictionController()314     public HotseatPredictionController getHotseatPredictionController() {
315         return mHotseatPredictionController;
316     }
317 
setHintUserWillBeActive()318     public void setHintUserWillBeActive() {
319         addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
320     }
321 }
322