• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.launcher3.LauncherState.NO_OFFSET;
23 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
24 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
25 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
26 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
27 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
28 
29 import android.animation.AnimatorSet;
30 import android.animation.ValueAnimator;
31 import android.app.ActivityOptions;
32 import android.content.ComponentName;
33 import android.content.Intent;
34 import android.content.IntentSender;
35 import android.content.ServiceConnection;
36 import android.os.Bundle;
37 import android.os.CancellationSignal;
38 import android.os.IBinder;
39 import android.view.View;
40 import android.window.SplashScreen;
41 
42 import androidx.annotation.Nullable;
43 
44 import com.android.launcher3.config.FeatureFlags;
45 import com.android.launcher3.dragndrop.DragOptions;
46 import com.android.launcher3.model.WellbeingModel;
47 import com.android.launcher3.model.data.ItemInfo;
48 import com.android.launcher3.popup.SystemShortcut;
49 import com.android.launcher3.proxy.ProxyActivityStarter;
50 import com.android.launcher3.proxy.StartActivityParams;
51 import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
52 import com.android.launcher3.statehandlers.DepthController;
53 import com.android.launcher3.statemanager.StateManager.StateHandler;
54 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
55 import com.android.launcher3.taskbar.TaskbarManager;
56 import com.android.launcher3.taskbar.TaskbarStateHandler;
57 import com.android.launcher3.uioverrides.RecentsViewStateController;
58 import com.android.launcher3.util.ActivityOptionsWrapper;
59 import com.android.launcher3.util.ObjectWrapper;
60 import com.android.launcher3.util.UiThreadHelper;
61 import com.android.quickstep.OverviewCommandHelper;
62 import com.android.quickstep.RecentsModel;
63 import com.android.quickstep.SysUINavigationMode;
64 import com.android.quickstep.SysUINavigationMode.Mode;
65 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
66 import com.android.quickstep.SystemUiProxy;
67 import com.android.quickstep.TaskUtils;
68 import com.android.quickstep.TouchInteractionService;
69 import com.android.quickstep.TouchInteractionService.TISBinder;
70 import com.android.quickstep.util.RemoteAnimationProvider;
71 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
72 import com.android.quickstep.util.SplitSelectStateController;
73 import com.android.quickstep.views.OverviewActionsView;
74 import com.android.quickstep.views.RecentsView;
75 import com.android.quickstep.views.SplitPlaceholderView;
76 import com.android.systemui.shared.system.ActivityManagerWrapper;
77 import com.android.systemui.shared.system.ActivityOptionsCompat;
78 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
79 
80 import java.util.List;
81 import java.util.stream.Stream;
82 
83 /**
84  * Extension of Launcher activity to provide quickstep specific functionality
85  */
86 public abstract class BaseQuickstepLauncher extends Launcher
87         implements NavigationModeChangeListener {
88 
89     private DepthController mDepthController = new DepthController(this);
90     private QuickstepTransitionManager mAppTransitionManager;
91 
92     /**
93      * Reusable command for applying the back button alpha on the background thread.
94      */
95     public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
96             (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setNavBarButtonAlpha(
97                     Float.intBitsToFloat(arg1), arg2 != 0);
98 
99     private OverviewActionsView mActionsView;
100 
101     private @Nullable TaskbarManager mTaskbarManager;
102     private @Nullable OverviewCommandHelper mOverviewCommandHelper;
103     private @Nullable LauncherTaskbarUIController mTaskbarUIController;
104     private final ServiceConnection mTisBinderConnection = new ServiceConnection() {
105         @Override
106         public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
107             mTaskbarManager = ((TISBinder) iBinder).getTaskbarManager();
108             mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
109 
110             mOverviewCommandHelper = ((TISBinder) iBinder).getOverviewCommandHelper();
111         }
112 
113         @Override
114         public void onServiceDisconnected(ComponentName componentName) { }
115     };
116     private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
117 
118     // Will be updated when dragging from taskbar.
119     private @Nullable DragOptions mNextWorkspaceDragOptions = null;
120     private SplitPlaceholderView mSplitPlaceholderView;
121 
122     @Override
onCreate(Bundle savedInstanceState)123     protected void onCreate(Bundle savedInstanceState) {
124         super.onCreate(savedInstanceState);
125         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
126         addMultiWindowModeChangedListener(mDepthController);
127     }
128 
129     @Override
onDestroy()130     public void onDestroy() {
131         mAppTransitionManager.onActivityDestroyed();
132 
133         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
134 
135 
136         unbindService(mTisBinderConnection);
137         if (mTaskbarManager != null) {
138             mTaskbarManager.setLauncher(null);
139         }
140         super.onDestroy();
141     }
142 
143     @Override
onNewIntent(Intent intent)144     protected void onNewIntent(Intent intent) {
145         super.onNewIntent(intent);
146 
147         if (mOverviewCommandHelper != null) {
148             mOverviewCommandHelper.clearPendingCommands();
149         }
150     }
151 
getAppTransitionManager()152     public QuickstepTransitionManager getAppTransitionManager() {
153         return mAppTransitionManager;
154     }
155 
156     @Override
onNavigationModeChanged(Mode newMode)157     public void onNavigationModeChanged(Mode newMode) {
158         getDragLayer().recreateControllers();
159         if (mActionsView != null) {
160             mActionsView.updateVerticalMargin(newMode);
161         }
162     }
163 
164     @Override
onEnterAnimationComplete()165     public void onEnterAnimationComplete() {
166         super.onEnterAnimationComplete();
167         // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
168         // as a part of quickstep, so that high-res thumbnails can load the next time we enter
169         // overview
170         RecentsModel.INSTANCE.get(this).getThumbnailCache()
171                 .getHighResLoadingState().setVisible(true);
172     }
173 
174     @Override
handleGestureContract(Intent intent)175     protected void handleGestureContract(Intent intent) {
176         if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
177             super.handleGestureContract(intent);
178         }
179     }
180 
181     @Override
onTrimMemory(int level)182     public void onTrimMemory(int level) {
183         super.onTrimMemory(level);
184         RecentsModel.INSTANCE.get(this).onTrimMemory(level);
185     }
186 
187     @Override
onUiChangedWhileSleeping()188     public void onUiChangedWhileSleeping() {
189         // Remove the snapshot because the content view may have obvious changes.
190         UI_HELPER_EXECUTOR.execute(
191                 () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
192     }
193 
194     @Override
onScreenOff()195     protected void onScreenOff() {
196         super.onScreenOff();
197         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
198             RecentsView recentsView = getOverviewPanel();
199             recentsView.finishRecentsAnimation(true /* toRecents */, null);
200         }
201     }
202 
203     @Override
startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)204     public void startIntentSenderForResult(IntentSender intent, int requestCode,
205             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
206         if (requestCode != -1) {
207             mPendingActivityRequestCode = requestCode;
208             StartActivityParams params = new StartActivityParams(this, requestCode);
209             params.intentSender = intent;
210             params.fillInIntent = fillInIntent;
211             params.flagsMask = flagsMask;
212             params.flagsValues = flagsValues;
213             params.extraFlags = extraFlags;
214             params.options = options;
215             startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
216         } else {
217             super.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
218                     flagsValues, extraFlags, options);
219         }
220     }
221 
222     @Override
startActivityForResult(Intent intent, int requestCode, Bundle options)223     public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
224         if (requestCode != -1) {
225             mPendingActivityRequestCode = requestCode;
226             StartActivityParams params = new StartActivityParams(this, requestCode);
227             params.intent = intent;
228             params.options = options;
229             startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
230         } else {
231             super.startActivityForResult(intent, requestCode, options);
232         }
233     }
234 
235     @Override
onDeferredResumed()236     protected void onDeferredResumed() {
237         super.onDeferredResumed();
238         handlePendingActivityRequest();
239     }
240 
241     @Override
onStateSetEnd(LauncherState state)242     public void onStateSetEnd(LauncherState state) {
243         super.onStateSetEnd(state);
244         handlePendingActivityRequest();
245     }
246 
handlePendingActivityRequest()247     private void handlePendingActivityRequest() {
248         if (mPendingActivityRequestCode != -1 && isInState(NORMAL)
249                 && ((getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
250             // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
251             onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
252             // ProxyActivityStarter is started with clear task to reset the task after which it
253             // removes the task itself.
254             startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
255         }
256     }
257 
258     @Override
setupViews()259     protected void setupViews() {
260         super.setupViews();
261 
262         SysUINavigationMode.INSTANCE.get(this).updateMode();
263         mActionsView = findViewById(R.id.overview_actions_view);
264         mSplitPlaceholderView = findViewById(R.id.split_placeholder);
265         RecentsView overviewPanel = (RecentsView) getOverviewPanel();
266         mSplitPlaceholderView.init(
267                 new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this))
268         );
269         overviewPanel.init(mActionsView, mSplitPlaceholderView);
270         mActionsView.setDp(getDeviceProfile());
271         mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
272 
273         mAppTransitionManager = new QuickstepTransitionManager(this);
274         mAppTransitionManager.registerRemoteAnimations();
275 
276         bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
277 
278     }
279 
setTaskbarUIController(LauncherTaskbarUIController taskbarUIController)280     public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
281         mTaskbarUIController = taskbarUIController;
282     }
283 
getActionsView()284     public <T extends OverviewActionsView> T getActionsView() {
285         return (T) mActionsView;
286     }
287 
getSplitPlaceholderView()288     public SplitPlaceholderView getSplitPlaceholderView() {
289         return mSplitPlaceholderView;
290     }
291 
292     @Override
closeOpenViews(boolean animate)293     protected void closeOpenViews(boolean animate) {
294         super.closeOpenViews(animate);
295         TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
296     }
297 
298     @Override
collectStateHandlers(List<StateHandler> out)299     protected void collectStateHandlers(List<StateHandler> out) {
300         super.collectStateHandlers(out);
301         out.add(getDepthController());
302         out.add(new RecentsViewStateController(this));
303         out.add(new BackButtonAlphaHandler(this));
304         out.add(getTaskbarStateHandler());
305     }
306 
getDepthController()307     public DepthController getDepthController() {
308         return mDepthController;
309     }
310 
getTaskbarUIController()311     public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
312         return mTaskbarUIController;
313     }
314 
getTaskbarStateHandler()315     public TaskbarStateHandler getTaskbarStateHandler() {
316         return mTaskbarStateHandler;
317     }
318 
319     @Override
supportsAdaptiveIconAnimation(View clickedView)320     public boolean supportsAdaptiveIconAnimation(View clickedView) {
321         return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
322                 && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
323     }
324 
325     @Override
getDefaultWorkspaceDragOptions()326     public DragOptions getDefaultWorkspaceDragOptions() {
327         if (mNextWorkspaceDragOptions != null) {
328             DragOptions options = mNextWorkspaceDragOptions;
329             mNextWorkspaceDragOptions = null;
330             return options;
331         }
332         return super.getDefaultWorkspaceDragOptions();
333     }
334 
setNextWorkspaceDragOptions(DragOptions dragOptions)335     public void setNextWorkspaceDragOptions(DragOptions dragOptions) {
336         mNextWorkspaceDragOptions = dragOptions;
337     }
338 
339     @Override
useFadeOutAnimationForLauncherStart(CancellationSignal signal)340     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
341         QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
342         appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
343             @Override
344             public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
345                     RemoteAnimationTargetCompat[] wallpaperTargets) {
346 
347                 // On the first call clear the reference.
348                 signal.cancel();
349 
350                 ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
351                 fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
352                         wallpaperTargets));
353                 AnimatorSet anim = new AnimatorSet();
354                 anim.play(fadeAnimation);
355                 return anim;
356             }
357         }, signal);
358     }
359 
360     @Override
getNormalOverviewScaleAndOffset()361     public float[] getNormalOverviewScaleAndOffset() {
362         return SysUINavigationMode.getMode(this).hasGestures
363                 ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET};
364     }
365 
366     @Override
getNormalTaskbarScale()367     public float getNormalTaskbarScale() {
368         if (mTaskbarUIController != null) {
369             return mTaskbarUIController.getTaskbarScaleOnHome();
370         }
371         return super.getNormalTaskbarScale();
372     }
373 
374     @Override
onDragLayerHierarchyChanged()375     public void onDragLayerHierarchyChanged() {
376         onLauncherStateOrFocusChanged();
377     }
378 
379     @Override
onActivityFlagsChanged(int changeBits)380     protected void onActivityFlagsChanged(int changeBits) {
381         if ((changeBits
382                 & (ACTIVITY_STATE_WINDOW_FOCUSED | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
383             onLauncherStateOrFocusChanged();
384         }
385 
386         if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
387             mDepthController.setActivityStarted(isStarted());
388         }
389 
390         if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
391             if (mTaskbarUIController != null) {
392                 mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
393             }
394         }
395 
396         super.onActivityFlagsChanged(changeBits);
397     }
398 
shouldBackButtonBeHidden(LauncherState toState)399     public boolean shouldBackButtonBeHidden(LauncherState toState) {
400         Mode mode = SysUINavigationMode.getMode(this);
401         boolean shouldBackButtonBeHidden = mode.hasGestures
402                 && toState.hasFlag(FLAG_HIDE_BACK_BUTTON)
403                 && hasWindowFocus()
404                 && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0;
405         if (shouldBackButtonBeHidden) {
406             // Show the back button if there is a floating view visible.
407             shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(this,
408                     TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
409         }
410         return shouldBackButtonBeHidden;
411     }
412 
413     /**
414      * Sets the back button visibility based on the current state/window focus.
415      */
onLauncherStateOrFocusChanged()416     private void onLauncherStateOrFocusChanged() {
417         boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
418         if (SysUINavigationMode.getMode(this) == TWO_BUTTONS) {
419             UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
420                     shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
421         }
422         if (getDragLayer() != null) {
423             getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
424         }
425     }
426 
427     @Override
finishBindingItems(int pageBoundFirst)428     public void finishBindingItems(int pageBoundFirst) {
429         super.finishBindingItems(pageBoundFirst);
430         // Instantiate and initialize WellbeingModel now that its loading won't interfere with
431         // populating workspace.
432         // TODO: Find a better place for this
433         WellbeingModel.INSTANCE.get(this);
434     }
435 
436     @Override
getSupportedShortcuts()437     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
438         return Stream.concat(Stream.of(WellbeingModel.SHORTCUT_FACTORY),
439                 super.getSupportedShortcuts());
440     }
441 
442     @Override
getActivityLaunchOptions(View v, @Nullable ItemInfo item)443     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
444         ActivityOptionsWrapper activityOptions =
445                 mAppTransitionManager.hasControlRemoteAppTransitionPermission()
446                         ? mAppTransitionManager.getActivityLaunchOptions(v)
447                         : super.getActivityLaunchOptions(v, item);
448         if (mLastTouchUpTime > 0) {
449             ActivityOptionsCompat.setLauncherSourceInfo(
450                     activityOptions.options, mLastTouchUpTime);
451         }
452         activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
453         addLaunchCookie(item, activityOptions.options);
454         return activityOptions;
455     }
456 
457     /**
458      * Adds a new launch cookie for the activity launch of the given {@param info} if supported.
459      */
addLaunchCookie(ItemInfo info, ActivityOptions opts)460     public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
461         if (info == null) {
462             return;
463         }
464         switch (info.container) {
465             case LauncherSettings.Favorites.CONTAINER_DESKTOP:
466             case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
467                 // Fall through and continue it's on the workspace (we don't support swiping back
468                 // to other containers like all apps or the hotseat predictions (which can change)
469                 break;
470             default:
471                 if (info.container >= 0) {
472                     // Also allow swiping to folders
473                     break;
474                 }
475                 // Reset any existing launch cookies associated with the cookie
476                 opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
477                 return;
478         }
479         switch (info.itemType) {
480             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
481             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
482             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
483             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
484                 // Fall through and continue if it's an app, shortcut, or widget
485                 break;
486             default:
487                 // Reset any existing launch cookies associated with the cookie
488                 opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
489                 return;
490         }
491         opts.setLaunchCookie(ObjectWrapper.wrap(new Integer(info.id)));
492     }
493 
setHintUserWillBeActive()494     public void setHintUserWillBeActive() {
495         addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
496     }
497 }
498