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.quickstep;
17 
18 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
19 
20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
21 
22 import android.app.PendingIntent;
23 import android.app.PictureInPictureParams;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ActivityInfo;
28 import android.graphics.Bitmap;
29 import android.graphics.Insets;
30 import android.graphics.Rect;
31 import android.os.Bundle;
32 import android.os.IBinder;
33 import android.os.IBinder.DeathRecipient;
34 import android.os.RemoteException;
35 import android.os.UserHandle;
36 import android.util.Log;
37 import android.view.MotionEvent;
38 import android.view.RemoteAnimationAdapter;
39 import android.view.RemoteAnimationTarget;
40 import android.view.SurfaceControl;
41 
42 import com.android.launcher3.util.MainThreadInitializedObject;
43 import com.android.launcher3.util.SplitConfigurationOptions;
44 import com.android.systemui.shared.recents.ISystemUiProxy;
45 import com.android.systemui.shared.recents.model.Task;
46 import com.android.systemui.shared.system.RemoteTransitionCompat;
47 import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
48 import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
49 import com.android.wm.shell.onehanded.IOneHanded;
50 import com.android.wm.shell.pip.IPip;
51 import com.android.wm.shell.pip.IPipAnimationListener;
52 import com.android.wm.shell.recents.IRecentTasks;
53 import com.android.wm.shell.recents.IRecentTasksListener;
54 import com.android.wm.shell.splitscreen.ISplitScreen;
55 import com.android.wm.shell.splitscreen.ISplitScreenListener;
56 import com.android.wm.shell.startingsurface.IStartingWindow;
57 import com.android.wm.shell.startingsurface.IStartingWindowListener;
58 import com.android.wm.shell.transition.IShellTransitions;
59 import com.android.wm.shell.util.GroupedRecentTaskInfo;
60 
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 
64 /**
65  * Holds the reference to SystemUI.
66  */
67 public class SystemUiProxy implements ISystemUiProxy,
68         SysUINavigationMode.NavigationModeChangeListener {
69     private static final String TAG = SystemUiProxy.class.getSimpleName();
70 
71     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
72             new MainThreadInitializedObject<>(SystemUiProxy::new);
73 
74     private ISystemUiProxy mSystemUiProxy;
75     private IPip mPip;
76     private ISmartspaceTransitionController mSmartspaceTransitionController;
77     private ISplitScreen mSplitScreen;
78     private IOneHanded mOneHanded;
79     private IShellTransitions mShellTransitions;
80     private IStartingWindow mStartingWindow;
81     private IRecentTasks mRecentTasks;
82     private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
83         MAIN_EXECUTOR.execute(() -> clearProxy());
84     };
85 
86     // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
87     // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
88     // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
89     // in case SysUI needs to rebind.
90     private IPipAnimationListener mPipAnimationListener;
91     private ISplitScreenListener mSplitScreenListener;
92     private IStartingWindowListener mStartingWindowListener;
93     private ISmartspaceCallback mSmartspaceCallback;
94     private IRecentTasksListener mRecentTasksListener;
95     private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
96 
97     // Used to dedupe calls to SystemUI
98     private int mLastShelfHeight;
99     private boolean mLastShelfVisible;
100     private float mLastNavButtonAlpha;
101     private boolean mLastNavButtonAnimate;
102     private boolean mHasNavButtonAlphaBeenSet = false;
103     private Runnable mPendingSetNavButtonAlpha = null;
104 
105     // TODO(141886704): Find a way to remove this
106     private int mLastSystemUiStateFlags;
107 
SystemUiProxy(Context context)108     public SystemUiProxy(Context context) {
109         SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
110     }
111 
112     @Override
onNavigationModeChanged(SysUINavigationMode.Mode newMode)113     public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
114         // Whenever the nav mode changes, force reset the nav button alpha
115         setNavBarButtonAlpha(1f, false);
116     }
117 
118     @Override
onBackPressed()119     public void onBackPressed() {
120         if (mSystemUiProxy != null) {
121             try {
122                 mSystemUiProxy.onBackPressed();
123             } catch (RemoteException e) {
124                 Log.w(TAG, "Failed call onBackPressed", e);
125             }
126         }
127     }
128 
129     @Override
onImeSwitcherPressed()130     public void onImeSwitcherPressed() {
131         if (mSystemUiProxy != null) {
132             try {
133                 mSystemUiProxy.onImeSwitcherPressed();
134             } catch (RemoteException e) {
135                 Log.w(TAG, "Failed call onImeSwitcherPressed", e);
136             }
137         }
138     }
139 
140     @Override
setHomeRotationEnabled(boolean enabled)141     public void setHomeRotationEnabled(boolean enabled) {
142         if (mSystemUiProxy != null) {
143             try {
144                 mSystemUiProxy.setHomeRotationEnabled(enabled);
145             } catch (RemoteException e) {
146                 Log.w(TAG, "Failed call onBackPressed", e);
147             }
148         }
149     }
150 
151     @Override
asBinder()152     public IBinder asBinder() {
153         // Do nothing
154         return null;
155     }
156 
setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen, IOneHanded oneHanded, IShellTransitions shellTransitions, IStartingWindow startingWindow, IRecentTasks recentTasks, ISmartspaceTransitionController smartSpaceTransitionController)157     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
158             IOneHanded oneHanded, IShellTransitions shellTransitions,
159             IStartingWindow startingWindow, IRecentTasks recentTasks,
160             ISmartspaceTransitionController smartSpaceTransitionController) {
161         unlinkToDeath();
162         mSystemUiProxy = proxy;
163         mPip = pip;
164         mSplitScreen = splitScreen;
165         mOneHanded = oneHanded;
166         mShellTransitions = shellTransitions;
167         mStartingWindow = startingWindow;
168         mSmartspaceTransitionController = smartSpaceTransitionController;
169         mRecentTasks = recentTasks;
170         linkToDeath();
171         // re-attach the listeners once missing due to setProxy has not been initialized yet.
172         if (mPipAnimationListener != null && mPip != null) {
173             setPinnedStackAnimationListener(mPipAnimationListener);
174         }
175         if (mSplitScreenListener != null && mSplitScreen != null) {
176             registerSplitScreenListener(mSplitScreenListener);
177         }
178         if (mStartingWindowListener != null && mStartingWindow != null) {
179             setStartingWindowListener(mStartingWindowListener);
180         }
181         if (mSmartspaceCallback != null && mSmartspaceTransitionController != null) {
182             setSmartspaceCallback(mSmartspaceCallback);
183         }
184         for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
185             registerRemoteTransition(mRemoteTransitions.get(i));
186         }
187         if (mRecentTasksListener != null && mRecentTasks != null) {
188             registerRecentTasksListener(mRecentTasksListener);
189         }
190 
191         if (mPendingSetNavButtonAlpha != null) {
192             mPendingSetNavButtonAlpha.run();
193             mPendingSetNavButtonAlpha = null;
194         }
195     }
196 
clearProxy()197     public void clearProxy() {
198         setProxy(null, null, null, null, null, null, null, null);
199     }
200 
201     // TODO(141886704): Find a way to remove this
setLastSystemUiStateFlags(int stateFlags)202     public void setLastSystemUiStateFlags(int stateFlags) {
203         mLastSystemUiStateFlags = stateFlags;
204     }
205 
206     // TODO(141886704): Find a way to remove this
getLastSystemUiStateFlags()207     public int getLastSystemUiStateFlags() {
208         return mLastSystemUiStateFlags;
209     }
210 
isActive()211     public boolean isActive() {
212         return mSystemUiProxy != null;
213     }
214 
linkToDeath()215     private void linkToDeath() {
216         if (mSystemUiProxy != null) {
217             try {
218                 mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
219             } catch (RemoteException e) {
220                 Log.e(TAG, "Failed to link sysui proxy death recipient");
221             }
222         }
223     }
224 
unlinkToDeath()225     private void unlinkToDeath() {
226         if (mSystemUiProxy != null) {
227             mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
228         }
229     }
230 
231     @Override
startScreenPinning(int taskId)232     public void startScreenPinning(int taskId) {
233         if (mSystemUiProxy != null) {
234             try {
235                 mSystemUiProxy.startScreenPinning(taskId);
236             } catch (RemoteException e) {
237                 Log.w(TAG, "Failed call startScreenPinning", e);
238             }
239         }
240     }
241 
242     @Override
onOverviewShown(boolean fromHome)243     public void onOverviewShown(boolean fromHome) {
244         onOverviewShown(fromHome, TAG);
245     }
246 
onOverviewShown(boolean fromHome, String tag)247     public void onOverviewShown(boolean fromHome, String tag) {
248         if (mSystemUiProxy != null) {
249             try {
250                 mSystemUiProxy.onOverviewShown(fromHome);
251             } catch (RemoteException e) {
252                 Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
253             }
254         }
255     }
256 
257     @Override
getNonMinimizedSplitScreenSecondaryBounds()258     public Rect getNonMinimizedSplitScreenSecondaryBounds() {
259         if (mSystemUiProxy != null) {
260             try {
261                 return mSystemUiProxy.getNonMinimizedSplitScreenSecondaryBounds();
262             } catch (RemoteException e) {
263                 Log.w(TAG, "Failed call getNonMinimizedSplitScreenSecondaryBounds", e);
264             }
265         }
266         return null;
267     }
268 
getLastNavButtonAlpha()269     public float getLastNavButtonAlpha() {
270         return mLastNavButtonAlpha;
271     }
272 
273     @Override
setNavBarButtonAlpha(float alpha, boolean animate)274     public void setNavBarButtonAlpha(float alpha, boolean animate) {
275         boolean changed = Float.compare(alpha, mLastNavButtonAlpha) != 0
276                 || animate != mLastNavButtonAnimate
277                 || !mHasNavButtonAlphaBeenSet;
278         if (changed) {
279             if (mSystemUiProxy == null) {
280                 mPendingSetNavButtonAlpha = () -> setNavBarButtonAlpha(alpha, animate);
281             } else {
282                 mLastNavButtonAlpha = alpha;
283                 mLastNavButtonAnimate = animate;
284                 mHasNavButtonAlphaBeenSet = true;
285                 try {
286                     mSystemUiProxy.setNavBarButtonAlpha(alpha, animate);
287                 } catch (RemoteException e) {
288                     Log.w(TAG, "Failed call setNavBarButtonAlpha", e);
289                 }
290             }
291         }
292     }
293 
294     @Override
onStatusBarMotionEvent(MotionEvent event)295     public void onStatusBarMotionEvent(MotionEvent event) {
296         if (mSystemUiProxy != null) {
297             try {
298                 mSystemUiProxy.onStatusBarMotionEvent(event);
299             } catch (RemoteException e) {
300                 Log.w(TAG, "Failed call onStatusBarMotionEvent", e);
301             }
302         }
303     }
304 
305     @Override
onAssistantProgress(float progress)306     public void onAssistantProgress(float progress) {
307         if (mSystemUiProxy != null) {
308             try {
309                 mSystemUiProxy.onAssistantProgress(progress);
310             } catch (RemoteException e) {
311                 Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
312             }
313         }
314     }
315 
316     @Override
onAssistantGestureCompletion(float velocity)317     public void onAssistantGestureCompletion(float velocity) {
318         if (mSystemUiProxy != null) {
319             try {
320                 mSystemUiProxy.onAssistantGestureCompletion(velocity);
321             } catch (RemoteException e) {
322                 Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
323             }
324         }
325     }
326 
327     @Override
startAssistant(Bundle args)328     public void startAssistant(Bundle args) {
329         if (mSystemUiProxy != null) {
330             try {
331                 mSystemUiProxy.startAssistant(args);
332             } catch (RemoteException e) {
333                 Log.w(TAG, "Failed call startAssistant", e);
334             }
335         }
336     }
337 
338     @Override
notifyAccessibilityButtonClicked(int displayId)339     public void notifyAccessibilityButtonClicked(int displayId) {
340         if (mSystemUiProxy != null) {
341             try {
342                 mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
343             } catch (RemoteException e) {
344                 Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
345             }
346         }
347     }
348 
349     @Override
notifyAccessibilityButtonLongClicked()350     public void notifyAccessibilityButtonLongClicked() {
351         if (mSystemUiProxy != null) {
352             try {
353                 mSystemUiProxy.notifyAccessibilityButtonLongClicked();
354             } catch (RemoteException e) {
355                 Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
356             }
357         }
358     }
359 
360     @Override
stopScreenPinning()361     public void stopScreenPinning() {
362         if (mSystemUiProxy != null) {
363             try {
364                 mSystemUiProxy.stopScreenPinning();
365             } catch (RemoteException e) {
366                 Log.w(TAG, "Failed call stopScreenPinning", e);
367             }
368         }
369     }
370 
371     @Override
handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i)372     public void handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i) {
373         if (mSystemUiProxy != null) {
374             try {
375                 mSystemUiProxy.handleImageAsScreenshot(bitmap, rect, insets, i);
376             } catch (RemoteException e) {
377                 Log.w(TAG, "Failed call handleImageAsScreenshot", e);
378             }
379         }
380     }
381 
382     @Override
setSplitScreenMinimized(boolean minimized)383     public void setSplitScreenMinimized(boolean minimized) {
384         if (mSystemUiProxy != null) {
385             try {
386                 mSystemUiProxy.setSplitScreenMinimized(minimized);
387             } catch (RemoteException e) {
388                 Log.w(TAG, "Failed call setSplitScreenMinimized", e);
389             }
390         }
391     }
392 
393     @Override
notifySwipeUpGestureStarted()394     public void notifySwipeUpGestureStarted() {
395         if (mSystemUiProxy != null) {
396             try {
397                 mSystemUiProxy.notifySwipeUpGestureStarted();
398             } catch (RemoteException e) {
399                 Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e);
400             }
401         }
402     }
403 
404     /**
405      * Notifies that swipe-to-home action is finished.
406      */
407     @Override
notifySwipeToHomeFinished()408     public void notifySwipeToHomeFinished() {
409         if (mSystemUiProxy != null) {
410             try {
411                 mSystemUiProxy.notifySwipeToHomeFinished();
412             } catch (RemoteException e) {
413                 Log.w(TAG, "Failed call notifySwipeToHomeFinished", e);
414             }
415         }
416     }
417 
418     @Override
notifyPrioritizedRotation(int rotation)419     public void notifyPrioritizedRotation(int rotation) {
420         if (mSystemUiProxy != null) {
421             try {
422                 mSystemUiProxy.notifyPrioritizedRotation(rotation);
423             } catch (RemoteException e) {
424                 Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
425             }
426         }
427     }
428 
429     @Override
notifyTaskbarStatus(boolean visible, boolean stashed)430     public void notifyTaskbarStatus(boolean visible, boolean stashed) {
431         if (mSystemUiProxy != null) {
432             try {
433                 mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
434             } catch (RemoteException e) {
435                 Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
436                         visible + ", " + stashed, e);
437             }
438         }
439     }
440 
441     /**
442      * NOTE: If called to suspend, caller MUST call this method to also un-suspend
443      * @param suspend should be true to stop auto-hide, false to resume normal behavior
444      */
445     @Override
notifyTaskbarAutohideSuspend(boolean suspend)446     public void notifyTaskbarAutohideSuspend(boolean suspend) {
447         if (mSystemUiProxy != null) {
448             try {
449                 mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
450             } catch (RemoteException e) {
451                 Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
452                         suspend, e);
453             }
454         }
455     }
456 
457     @Override
handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, Insets visibleInsets, Task.TaskKey task)458     public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
459             Insets visibleInsets, Task.TaskKey task) {
460         if (mSystemUiProxy != null) {
461             try {
462                 mSystemUiProxy.handleImageBundleAsScreenshot(screenImageBundle, locationInScreen,
463                     visibleInsets, task);
464             } catch (RemoteException e) {
465                 Log.w(TAG, "Failed call handleImageBundleAsScreenshot");
466             }
467         }
468     }
469 
470     @Override
expandNotificationPanel()471     public void expandNotificationPanel() {
472         if (mSystemUiProxy != null) {
473             try {
474                 mSystemUiProxy.expandNotificationPanel();
475             } catch (RemoteException e) {
476                 Log.w(TAG, "Failed call expandNotificationPanel", e);
477             }
478         }
479     }
480 
481     //
482     // Pip
483     //
484 
485     /**
486      * Sets the shelf height.
487      */
setShelfHeight(boolean visible, int shelfHeight)488     public void setShelfHeight(boolean visible, int shelfHeight) {
489         boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
490         if (mPip != null && changed) {
491             mLastShelfVisible = visible;
492             mLastShelfHeight = shelfHeight;
493             try {
494                 mPip.setShelfHeight(visible, shelfHeight);
495             } catch (RemoteException e) {
496                 Log.w(TAG, "Failed call setShelfHeight visible: " + visible
497                         + " height: " + shelfHeight, e);
498             }
499         }
500     }
501 
502     /**
503      * Sets listener to get pinned stack animation callbacks.
504      */
setPinnedStackAnimationListener(IPipAnimationListener listener)505     public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
506         if (mPip != null) {
507             try {
508                 mPip.setPinnedStackAnimationListener(listener);
509             } catch (RemoteException e) {
510                 Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
511             }
512         }
513         mPipAnimationListener = listener;
514     }
515 
startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight)516     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
517             PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
518         if (mPip != null) {
519             try {
520                 return mPip.startSwipePipToHome(componentName, activityInfo,
521                         pictureInPictureParams, launcherRotation, shelfHeight);
522             } catch (RemoteException e) {
523                 Log.w(TAG, "Failed call startSwipePipToHome", e);
524             }
525         }
526         return null;
527     }
528 
stopSwipePipToHome(ComponentName componentName, Rect destinationBounds, SurfaceControl overlay)529     public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
530             SurfaceControl overlay) {
531         if (mPip != null) {
532             try {
533                 mPip.stopSwipePipToHome(componentName, destinationBounds, overlay);
534             } catch (RemoteException e) {
535                 Log.w(TAG, "Failed call stopSwipePipToHome");
536             }
537         }
538     }
539 
540     //
541     // Splitscreen
542     //
543 
registerSplitScreenListener(ISplitScreenListener listener)544     public void registerSplitScreenListener(ISplitScreenListener listener) {
545         if (mSplitScreen != null) {
546             try {
547                 mSplitScreen.registerSplitScreenListener(listener);
548             } catch (RemoteException e) {
549                 Log.w(TAG, "Failed call registerSplitScreenListener");
550             }
551         }
552         mSplitScreenListener = listener;
553     }
554 
unregisterSplitScreenListener(ISplitScreenListener listener)555     public void unregisterSplitScreenListener(ISplitScreenListener listener) {
556         if (mSplitScreen != null) {
557             try {
558                 mSplitScreen.unregisterSplitScreenListener(listener);
559             } catch (RemoteException e) {
560                 Log.w(TAG, "Failed call unregisterSplitScreenListener");
561             }
562         }
563         mSplitScreenListener = null;
564     }
565 
566     /** Start multiple tasks in split-screen simultaneously. */
startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, RemoteTransitionCompat remoteTransition)567     public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
568             @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
569             RemoteTransitionCompat remoteTransition) {
570         if (mSystemUiProxy != null) {
571             try {
572                 mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
573                         sidePosition, splitRatio, remoteTransition.getTransition());
574             } catch (RemoteException e) {
575                 Log.w(TAG, "Failed call startTask");
576             }
577         }
578     }
579 
580     /**
581      * Start multiple tasks in split-screen simultaneously.
582      */
startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter)583     public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
584             Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
585             float splitRatio, RemoteAnimationAdapter adapter) {
586         if (mSystemUiProxy != null) {
587             try {
588                 mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
589                         sideOptions, sidePosition, splitRatio, adapter);
590             } catch (RemoteException e) {
591                 Log.w(TAG, "Failed call startTasksWithLegacyTransition");
592             }
593         }
594     }
595 
startShortcut(String packageName, String shortcutId, int position, Bundle options, UserHandle user)596     public void startShortcut(String packageName, String shortcutId, int position,
597             Bundle options, UserHandle user) {
598         if (mSplitScreen != null) {
599             try {
600                 mSplitScreen.startShortcut(packageName, shortcutId, position, options,
601                         user);
602             } catch (RemoteException e) {
603                 Log.w(TAG, "Failed call startShortcut");
604             }
605         }
606     }
607 
startIntent(PendingIntent intent, Intent fillInIntent, int position, Bundle options)608     public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
609             Bundle options) {
610         if (mSplitScreen != null) {
611             try {
612                 mSplitScreen.startIntent(intent, fillInIntent, position, options);
613             } catch (RemoteException e) {
614                 Log.w(TAG, "Failed call startIntent");
615             }
616         }
617     }
618 
removeFromSideStage(int taskId)619     public void removeFromSideStage(int taskId) {
620         if (mSplitScreen != null) {
621             try {
622                 mSplitScreen.removeFromSideStage(taskId);
623             } catch (RemoteException e) {
624                 Log.w(TAG, "Failed call removeFromSideStage");
625             }
626         }
627     }
628 
629     /**
630      * Call this when going to recents so that shell can set-up and provide appropriate leashes
631      * for animation (eg. DividerBar).
632      *
633      * @param cancel true if recents starting is being cancelled.
634      * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
635      */
onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps)636     public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
637             RemoteAnimationTarget[] apps) {
638         if (mSplitScreen != null) {
639             try {
640                 return mSplitScreen.onGoingToRecentsLegacy(cancel, apps);
641             } catch (RemoteException e) {
642                 Log.w(TAG, "Failed call onGoingToRecentsLegacy");
643             }
644         }
645         return null;
646     }
647 
648     //
649     // One handed
650     //
651 
startOneHandedMode()652     public void startOneHandedMode() {
653         if (mOneHanded != null) {
654             try {
655                 mOneHanded.startOneHanded();
656             } catch (RemoteException e) {
657                 Log.w(TAG, "Failed call startOneHandedMode", e);
658             }
659         }
660     }
661 
stopOneHandedMode()662     public void stopOneHandedMode() {
663         if (mOneHanded != null) {
664             try {
665                 mOneHanded.stopOneHanded();
666             } catch (RemoteException e) {
667                 Log.w(TAG, "Failed call stopOneHandedMode", e);
668             }
669         }
670     }
671 
672     //
673     // Remote transitions
674     //
675 
registerRemoteTransition(RemoteTransitionCompat remoteTransition)676     public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
677         if (mShellTransitions != null) {
678             try {
679                 mShellTransitions.registerRemote(remoteTransition.getFilter(),
680                         remoteTransition.getTransition());
681             } catch (RemoteException e) {
682                 Log.w(TAG, "Failed call registerRemoteTransition");
683             }
684         }
685         if (!mRemoteTransitions.contains(remoteTransition)) {
686             mRemoteTransitions.add(remoteTransition);
687         }
688     }
689 
unregisterRemoteTransition(RemoteTransitionCompat remoteTransition)690     public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
691         if (mShellTransitions != null) {
692             try {
693                 mShellTransitions.unregisterRemote(remoteTransition.getTransition());
694             } catch (RemoteException e) {
695                 Log.w(TAG, "Failed call registerRemoteTransition");
696             }
697         }
698         mRemoteTransitions.remove(remoteTransition);
699     }
700 
701     //
702     // Starting window
703     //
704 
705     /**
706      * Sets listener to get callbacks when launching a task.
707      */
setStartingWindowListener(IStartingWindowListener listener)708     public void setStartingWindowListener(IStartingWindowListener listener) {
709         if (mStartingWindow != null) {
710             try {
711                 mStartingWindow.setStartingWindowListener(listener);
712             } catch (RemoteException e) {
713                 Log.w(TAG, "Failed call setStartingWindowListener", e);
714             }
715         }
716         mStartingWindowListener = listener;
717     }
718 
719     //
720     // SmartSpace transitions
721     //
722 
setSmartspaceCallback(ISmartspaceCallback callback)723     public void setSmartspaceCallback(ISmartspaceCallback callback) {
724         if (mSmartspaceTransitionController != null) {
725             try {
726                 mSmartspaceTransitionController.setSmartspace(callback);
727             } catch (RemoteException e) {
728                 Log.w(TAG, "Failed call setStartingWindowListener", e);
729             }
730         }
731         mSmartspaceCallback = callback;
732     }
733 
734     //
735     // Recents
736     //
737 
registerRecentTasksListener(IRecentTasksListener listener)738     public void registerRecentTasksListener(IRecentTasksListener listener) {
739         if (mRecentTasks != null) {
740             try {
741                 mRecentTasks.registerRecentTasksListener(listener);
742             } catch (RemoteException e) {
743                 Log.w(TAG, "Failed call registerRecentTasksListener", e);
744             }
745         }
746         mRecentTasksListener = listener;
747     }
748 
unregisterRecentTasksListener(IRecentTasksListener listener)749     public void unregisterRecentTasksListener(IRecentTasksListener listener) {
750         if (mRecentTasks != null) {
751             try {
752                 mRecentTasks.unregisterRecentTasksListener(listener);
753             } catch (RemoteException e) {
754                 Log.w(TAG, "Failed call unregisterRecentTasksListener");
755             }
756         }
757         mRecentTasksListener = null;
758     }
759 
getRecentTasks(int numTasks, int userId)760     public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
761         if (mRecentTasks != null) {
762             try {
763                 return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
764                         RECENT_IGNORE_UNAVAILABLE, userId)));
765             } catch (RemoteException e) {
766                 Log.w(TAG, "Failed call getRecentTasks", e);
767             }
768         }
769         return new ArrayList<>();
770     }
771 }
772