• 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.quickstep;
17 
18 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
19 
20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
21 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
22 import static com.android.quickstep.util.LogUtils.splitFailureMessage;
23 
24 import android.app.ActivityManager;
25 import android.app.ActivityOptions;
26 import android.app.PendingIntent;
27 import android.app.PictureInPictureParams;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.ActivityInfo;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ShortcutInfo;
34 import android.graphics.Rect;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.IBinder;
38 import android.os.IBinder.DeathRecipient;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.os.UserHandle;
42 import android.util.Log;
43 import android.view.IRecentsAnimationController;
44 import android.view.IRecentsAnimationRunner;
45 import android.view.IRemoteAnimationRunner;
46 import android.view.MotionEvent;
47 import android.view.RemoteAnimationAdapter;
48 import android.view.RemoteAnimationTarget;
49 import android.view.SurfaceControl;
50 import android.window.IOnBackInvokedCallback;
51 import android.window.RemoteTransition;
52 import android.window.TaskSnapshot;
53 import android.window.TransitionFilter;
54 
55 import androidx.annotation.MainThread;
56 import androidx.annotation.Nullable;
57 import androidx.annotation.WorkerThread;
58 
59 import com.android.internal.logging.InstanceId;
60 import com.android.internal.util.ScreenshotRequest;
61 import com.android.internal.view.AppearanceRegion;
62 import com.android.launcher3.util.MainThreadInitializedObject;
63 import com.android.launcher3.util.Preconditions;
64 import com.android.launcher3.util.SplitConfigurationOptions;
65 import com.android.quickstep.util.AssistUtils;
66 import com.android.systemui.shared.recents.ISystemUiProxy;
67 import com.android.systemui.shared.recents.model.ThumbnailData;
68 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
69 import com.android.systemui.shared.system.RecentsAnimationListener;
70 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
71 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
72 import com.android.systemui.shared.system.smartspace.SmartspaceState;
73 import com.android.systemui.unfold.progress.IUnfoldAnimation;
74 import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
75 import com.android.wm.shell.back.IBackAnimation;
76 import com.android.wm.shell.bubbles.IBubbles;
77 import com.android.wm.shell.bubbles.IBubblesListener;
78 import com.android.wm.shell.desktopmode.IDesktopMode;
79 import com.android.wm.shell.desktopmode.IDesktopTaskListener;
80 import com.android.wm.shell.draganddrop.IDragAndDrop;
81 import com.android.wm.shell.onehanded.IOneHanded;
82 import com.android.wm.shell.pip.IPip;
83 import com.android.wm.shell.pip.IPipAnimationListener;
84 import com.android.wm.shell.recents.IRecentTasks;
85 import com.android.wm.shell.recents.IRecentTasksListener;
86 import com.android.wm.shell.splitscreen.ISplitScreen;
87 import com.android.wm.shell.splitscreen.ISplitScreenListener;
88 import com.android.wm.shell.splitscreen.ISplitSelectListener;
89 import com.android.wm.shell.startingsurface.IStartingWindow;
90 import com.android.wm.shell.startingsurface.IStartingWindowListener;
91 import com.android.wm.shell.transition.IShellTransitions;
92 import com.android.wm.shell.util.GroupedRecentTaskInfo;
93 
94 import java.util.ArrayList;
95 import java.util.Arrays;
96 import java.util.LinkedHashMap;
97 
98 /**
99  * Holds the reference to SystemUI.
100  */
101 public class SystemUiProxy implements ISystemUiProxy {
102     private static final String TAG = SystemUiProxy.class.getSimpleName();
103 
104     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
105             new MainThreadInitializedObject<>(SystemUiProxy::new);
106 
107     private static final int MSG_SET_SHELF_HEIGHT = 1;
108     private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2;
109 
110     private ISystemUiProxy mSystemUiProxy;
111     private IPip mPip;
112     private IBubbles mBubbles;
113     private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
114     private ISplitScreen mSplitScreen;
115     private IOneHanded mOneHanded;
116     private IShellTransitions mShellTransitions;
117     private IStartingWindow mStartingWindow;
118     private IRecentTasks mRecentTasks;
119     private IBackAnimation mBackAnimation;
120     private IDesktopMode mDesktopMode;
121     private IUnfoldAnimation mUnfoldAnimation;
122     private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
123         MAIN_EXECUTOR.execute(() -> clearProxy());
124     };
125 
126     // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
127     // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
128     // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
129     // in case SysUI needs to rebind.
130     private IPipAnimationListener mPipAnimationListener;
131     private IBubblesListener mBubblesListener;
132     private ISplitScreenListener mSplitScreenListener;
133     private ISplitSelectListener mSplitSelectListener;
134     private IStartingWindowListener mStartingWindowListener;
135     private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
136     private IRecentTasksListener mRecentTasksListener;
137     private IUnfoldTransitionListener mUnfoldAnimationListener;
138     private IDesktopTaskListener mDesktopTaskListener;
139     private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
140             new LinkedHashMap<>();
141     private IBinder mOriginalTransactionToken = null;
142     private IOnBackInvokedCallback mBackToLauncherCallback;
143     private IRemoteAnimationRunner mBackToLauncherRunner;
144     private IDragAndDrop mDragAndDrop;
145 
146     // Used to dedupe calls to SystemUI
147     private int mLastShelfHeight;
148     private boolean mLastShelfVisible;
149 
150     // Used to dedupe calls to SystemUI
151     private int mLastLauncherKeepClearAreaHeight;
152     private boolean mLastLauncherKeepClearAreaHeightVisible;
153 
154     private final Context mContext;
155     private final Handler mAsyncHandler;
156 
157     // TODO(141886704): Find a way to remove this
158     private int mLastSystemUiStateFlags;
159 
160     /**
161      * This is a singleton pending intent that is used to start recents via Shell (which is a
162      * different process). It is bare-bones, so it's expected that the component and options will
163      * be provided via fill-in intent.
164      */
165     private final PendingIntent mRecentsPendingIntent;
166 
SystemUiProxy(Context context)167     public SystemUiProxy(Context context) {
168         mContext = context;
169         mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
170         final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
171         mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent,
172                 PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
173                         | Intent.FILL_IN_COMPONENT);
174     }
175 
176     @Override
onBackPressed()177     public void onBackPressed() {
178         if (mSystemUiProxy != null) {
179             try {
180                 mSystemUiProxy.onBackPressed();
181             } catch (RemoteException e) {
182                 Log.w(TAG, "Failed call onBackPressed", e);
183             }
184         }
185     }
186 
187     @Override
onImeSwitcherPressed()188     public void onImeSwitcherPressed() {
189         if (mSystemUiProxy != null) {
190             try {
191                 mSystemUiProxy.onImeSwitcherPressed();
192             } catch (RemoteException e) {
193                 Log.w(TAG, "Failed call onImeSwitcherPressed", e);
194             }
195         }
196     }
197 
198     @Override
setHomeRotationEnabled(boolean enabled)199     public void setHomeRotationEnabled(boolean enabled) {
200         if (mSystemUiProxy != null) {
201             try {
202                 mSystemUiProxy.setHomeRotationEnabled(enabled);
203             } catch (RemoteException e) {
204                 Log.w(TAG, "Failed call onBackPressed", e);
205             }
206         }
207     }
208 
209     @Override
asBinder()210     public IBinder asBinder() {
211         // Do nothing
212         return null;
213     }
214 
215     /**
216      * Sets proxy state, including death linkage, various listeners, and other configuration objects
217      */
218     @MainThread
setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen, IOneHanded oneHanded, IShellTransitions shellTransitions, IStartingWindow startingWindow, IRecentTasks recentTasks, ISysuiUnlockAnimationController sysuiUnlockAnimationController, IBackAnimation backAnimation, IDesktopMode desktopMode, IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop)219     public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen,
220             IOneHanded oneHanded, IShellTransitions shellTransitions,
221             IStartingWindow startingWindow, IRecentTasks recentTasks,
222             ISysuiUnlockAnimationController sysuiUnlockAnimationController,
223             IBackAnimation backAnimation, IDesktopMode desktopMode,
224             IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop) {
225         Preconditions.assertUIThread();
226         unlinkToDeath();
227         mSystemUiProxy = proxy;
228         mPip = pip;
229         mBubbles = bubbles;
230         mSplitScreen = splitScreen;
231         mOneHanded = oneHanded;
232         mShellTransitions = shellTransitions;
233         mStartingWindow = startingWindow;
234         mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
235         mRecentTasks = recentTasks;
236         mBackAnimation = backAnimation;
237         mDesktopMode = desktopMode;
238         mUnfoldAnimation = unfoldAnimation;
239         mDragAndDrop = dragAndDrop;
240         linkToDeath();
241         // re-attach the listeners once missing due to setProxy has not been initialized yet.
242         setPipAnimationListener(mPipAnimationListener);
243         setBubblesListener(mBubblesListener);
244         registerSplitScreenListener(mSplitScreenListener);
245         registerSplitSelectListener(mSplitSelectListener);
246         setStartingWindowListener(mStartingWindowListener);
247         setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
248         new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
249         setupTransactionQueue();
250         registerRecentTasksListener(mRecentTasksListener);
251         setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
252         setUnfoldAnimationListener(mUnfoldAnimationListener);
253         setDesktopTaskListener(mDesktopTaskListener);
254         setAssistantOverridesRequested(
255                 AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
256     }
257 
258     /**
259      * Clear the proxy to release held resources and turn the majority of its operations into no-ops
260      */
261     @MainThread
clearProxy()262     public void clearProxy() {
263         setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null);
264     }
265 
266     // TODO(141886704): Find a way to remove this
setLastSystemUiStateFlags(int stateFlags)267     public void setLastSystemUiStateFlags(int stateFlags) {
268         mLastSystemUiStateFlags = stateFlags;
269     }
270 
271     // TODO(141886704): Find a way to remove this
getLastSystemUiStateFlags()272     public int getLastSystemUiStateFlags() {
273         return mLastSystemUiStateFlags;
274     }
275 
isActive()276     public boolean isActive() {
277         return mSystemUiProxy != null;
278     }
279 
linkToDeath()280     private void linkToDeath() {
281         if (mSystemUiProxy != null) {
282             try {
283                 mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
284             } catch (RemoteException e) {
285                 Log.e(TAG, "Failed to link sysui proxy death recipient");
286             }
287         }
288     }
289 
unlinkToDeath()290     private void unlinkToDeath() {
291         if (mSystemUiProxy != null) {
292             mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
293         }
294     }
295 
296     @Override
startScreenPinning(int taskId)297     public void startScreenPinning(int taskId) {
298         if (mSystemUiProxy != null) {
299             try {
300                 mSystemUiProxy.startScreenPinning(taskId);
301             } catch (RemoteException e) {
302                 Log.w(TAG, "Failed call startScreenPinning", e);
303             }
304         }
305     }
306 
307     @Override
onOverviewShown(boolean fromHome)308     public void onOverviewShown(boolean fromHome) {
309         onOverviewShown(fromHome, TAG);
310     }
311 
onOverviewShown(boolean fromHome, String tag)312     public void onOverviewShown(boolean fromHome, String tag) {
313         if (mSystemUiProxy != null) {
314             try {
315                 mSystemUiProxy.onOverviewShown(fromHome);
316             } catch (RemoteException e) {
317                 Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
318             }
319         }
320     }
321 
322     @MainThread
323     @Override
onStatusBarTouchEvent(MotionEvent event)324     public void onStatusBarTouchEvent(MotionEvent event) {
325         Preconditions.assertUIThread();
326         if (mSystemUiProxy != null) {
327             try {
328                 mSystemUiProxy.onStatusBarTouchEvent(event);
329             } catch (RemoteException e) {
330                 Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e);
331             }
332         }
333     }
334 
335     @Override
onStatusBarTrackpadEvent(MotionEvent event)336     public void onStatusBarTrackpadEvent(MotionEvent event) {
337         if (mSystemUiProxy != null) {
338             try {
339                 mSystemUiProxy.onStatusBarTrackpadEvent(event);
340             } catch (RemoteException e) {
341                 Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e);
342             }
343         }
344     }
345 
346     @Override
onAssistantProgress(float progress)347     public void onAssistantProgress(float progress) {
348         if (mSystemUiProxy != null) {
349             try {
350                 mSystemUiProxy.onAssistantProgress(progress);
351             } catch (RemoteException e) {
352                 Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
353             }
354         }
355     }
356 
357     @Override
onAssistantGestureCompletion(float velocity)358     public void onAssistantGestureCompletion(float velocity) {
359         if (mSystemUiProxy != null) {
360             try {
361                 mSystemUiProxy.onAssistantGestureCompletion(velocity);
362             } catch (RemoteException e) {
363                 Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
364             }
365         }
366     }
367 
368     @Override
startAssistant(Bundle args)369     public void startAssistant(Bundle args) {
370         if (mSystemUiProxy != null) {
371             try {
372                 mSystemUiProxy.startAssistant(args);
373             } catch (RemoteException e) {
374                 Log.w(TAG, "Failed call startAssistant", e);
375             }
376         }
377     }
378 
379     @Override
setAssistantOverridesRequested(int[] invocationTypes)380     public void setAssistantOverridesRequested(int[] invocationTypes) {
381         if (mSystemUiProxy != null) {
382             try {
383                 mSystemUiProxy.setAssistantOverridesRequested(invocationTypes);
384             } catch (RemoteException e) {
385                 Log.w(TAG, "Failed call setAssistantOverridesRequested", e);
386             }
387         }
388     }
389 
390     @Override
notifyAccessibilityButtonClicked(int displayId)391     public void notifyAccessibilityButtonClicked(int displayId) {
392         if (mSystemUiProxy != null) {
393             try {
394                 mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
395             } catch (RemoteException e) {
396                 Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
397             }
398         }
399     }
400 
401     @Override
notifyAccessibilityButtonLongClicked()402     public void notifyAccessibilityButtonLongClicked() {
403         if (mSystemUiProxy != null) {
404             try {
405                 mSystemUiProxy.notifyAccessibilityButtonLongClicked();
406             } catch (RemoteException e) {
407                 Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
408             }
409         }
410     }
411 
412     @Override
stopScreenPinning()413     public void stopScreenPinning() {
414         if (mSystemUiProxy != null) {
415             try {
416                 mSystemUiProxy.stopScreenPinning();
417             } catch (RemoteException e) {
418                 Log.w(TAG, "Failed call stopScreenPinning", e);
419             }
420         }
421     }
422 
423     @Override
notifyPrioritizedRotation(int rotation)424     public void notifyPrioritizedRotation(int rotation) {
425         if (mSystemUiProxy != null) {
426             try {
427                 mSystemUiProxy.notifyPrioritizedRotation(rotation);
428             } catch (RemoteException e) {
429                 Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
430             }
431         }
432     }
433 
434     @Override
notifyTaskbarStatus(boolean visible, boolean stashed)435     public void notifyTaskbarStatus(boolean visible, boolean stashed) {
436         if (mSystemUiProxy != null) {
437             try {
438                 mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
439             } catch (RemoteException e) {
440                 Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
441                         visible + ", " + stashed, e);
442             }
443         }
444     }
445 
446     /**
447      * NOTE: If called to suspend, caller MUST call this method to also un-suspend
448      * @param suspend should be true to stop auto-hide, false to resume normal behavior
449      */
450     @Override
notifyTaskbarAutohideSuspend(boolean suspend)451     public void notifyTaskbarAutohideSuspend(boolean suspend) {
452         if (mSystemUiProxy != null) {
453             try {
454                 mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
455             } catch (RemoteException e) {
456                 Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
457                         suspend, e);
458             }
459         }
460     }
461 
462     @Override
takeScreenshot(ScreenshotRequest request)463     public void takeScreenshot(ScreenshotRequest request) {
464         if (mSystemUiProxy != null) {
465             try {
466                 mSystemUiProxy.takeScreenshot(request);
467             } catch (RemoteException e) {
468                 Log.w(TAG, "Failed call takeScreenshot");
469             }
470         }
471     }
472 
473     @Override
expandNotificationPanel()474     public void expandNotificationPanel() {
475         if (mSystemUiProxy != null) {
476             try {
477                 mSystemUiProxy.expandNotificationPanel();
478             } catch (RemoteException e) {
479                 Log.w(TAG, "Failed call expandNotificationPanel", e);
480             }
481         }
482     }
483 
484     @Override
toggleNotificationPanel()485     public void toggleNotificationPanel() {
486         if (mSystemUiProxy != null) {
487             try {
488                 mSystemUiProxy.toggleNotificationPanel();
489             } catch (RemoteException e) {
490                 Log.w(TAG, "Failed call toggleNotificationPanel", e);
491             }
492         }
493     }
494 
495     //
496     // Pip
497     //
498 
499     /**
500      * Sets the shelf height.
501      */
setShelfHeight(boolean visible, int shelfHeight)502     public void setShelfHeight(boolean visible, int shelfHeight) {
503         Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
504                 visible ? 1 : 0 , shelfHeight).sendToTarget();
505     }
506 
507     @WorkerThread
setShelfHeightAsync(int visibleInt, int shelfHeight)508     private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
509         boolean visible = visibleInt != 0;
510         boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
511         IPip pip = mPip;
512         if (pip != null && changed) {
513             mLastShelfVisible = visible;
514             mLastShelfHeight = shelfHeight;
515             try {
516                 pip.setShelfHeight(visible, shelfHeight);
517             } catch (RemoteException e) {
518                 Log.w(TAG, "Failed call setShelfHeight visible: " + visible
519                         + " height: " + shelfHeight, e);
520             }
521         }
522     }
523 
524     /**
525      * Sets the height of the keep clear area that is going to be reported by
526      * the Launcher for the Hotseat.
527      */
setLauncherKeepClearAreaHeight(boolean visible, int height)528     public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
529         Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
530                 visible ? 1 : 0 , height).sendToTarget();
531     }
532 
533     @WorkerThread
setLauncherKeepClearAreaHeight(int visibleInt, int height)534     private void setLauncherKeepClearAreaHeight(int visibleInt, int height) {
535         boolean visible = visibleInt != 0;
536         boolean changed = visible != mLastLauncherKeepClearAreaHeightVisible
537                 || height != mLastLauncherKeepClearAreaHeight;
538         IPip pip = mPip;
539         if (pip != null && changed) {
540             mLastLauncherKeepClearAreaHeightVisible = visible;
541             mLastLauncherKeepClearAreaHeight = height;
542             try {
543                 pip.setLauncherKeepClearAreaHeight(visible, height);
544             } catch (RemoteException e) {
545                 Log.w(TAG, "Failed call setLauncherKeepClearAreaHeight visible: " + visible
546                         + " height: " + height, e);
547             }
548         }
549     }
550 
551     /**
552      * Sets listener to get pip animation callbacks.
553      */
setPipAnimationListener(IPipAnimationListener listener)554     public void setPipAnimationListener(IPipAnimationListener listener) {
555         if (mPip != null) {
556             try {
557                 mPip.setPipAnimationListener(listener);
558             } catch (RemoteException e) {
559                 Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
560             }
561         }
562         mPipAnimationListener = listener;
563     }
564 
565     /**
566      * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
567      */
568     @Nullable
startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, int launcherRotation, Rect hotseatKeepClearArea)569     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
570             PictureInPictureParams pictureInPictureParams, int launcherRotation,
571             Rect hotseatKeepClearArea) {
572         if (mPip != null) {
573             try {
574                 return mPip.startSwipePipToHome(componentName, activityInfo,
575                         pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
576             } catch (RemoteException e) {
577                 Log.w(TAG, "Failed call startSwipePipToHome", e);
578             }
579         }
580         return null;
581     }
582 
583     /**
584      * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
585      * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
586      * should be responsible for cleaning up the overlay.
587      */
stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, SurfaceControl overlay)588     public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
589             SurfaceControl overlay) {
590         if (mPip != null) {
591             try {
592                 mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
593             } catch (RemoteException e) {
594                 Log.w(TAG, "Failed call stopSwipePipToHome");
595             }
596         }
597     }
598 
599     /**
600      * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell
601      * can use this callback to clean up its internal states.
602      */
abortSwipePipToHome(int taskId, ComponentName componentName)603     public void abortSwipePipToHome(int taskId, ComponentName componentName) {
604         if (mPip != null) {
605             try {
606                 mPip.abortSwipePipToHome(taskId, componentName);
607             } catch (RemoteException e) {
608                 Log.w(TAG, "Failed call abortSwipePipToHome");
609             }
610         }
611     }
612 
613     /**
614      * Sets the next pip animation type to be the alpha animation.
615      */
setPipAnimationTypeToAlpha()616     public void setPipAnimationTypeToAlpha() {
617         if (mPip != null) {
618             try {
619                 mPip.setPipAnimationTypeToAlpha();
620             } catch (RemoteException e) {
621                 Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
622             }
623         }
624     }
625 
626     /**
627      * Sets the app icon size in pixel used by Launcher all apps.
628      */
setLauncherAppIconSize(int iconSizePx)629     public void setLauncherAppIconSize(int iconSizePx) {
630         if (mPip != null) {
631             try {
632                 mPip.setLauncherAppIconSize(iconSizePx);
633             } catch (RemoteException e) {
634                 Log.w(TAG, "Failed call setLauncherAppIconSize", e);
635             }
636         }
637     }
638 
639     //
640     // Bubbles
641     //
642 
643     /**
644      * Sets the listener to be notified of bubble state changes.
645      */
setBubblesListener(IBubblesListener listener)646     public void setBubblesListener(IBubblesListener listener) {
647         if (mBubbles != null) {
648             try {
649                 if (mBubblesListener != null) {
650                     // Clear out any previous listener
651                     mBubbles.unregisterBubbleListener(mBubblesListener);
652                 }
653                 if (listener != null) {
654                     mBubbles.registerBubbleListener(listener);
655                 }
656             } catch (RemoteException e) {
657                 Log.w(TAG, "Failed call registerBubblesListener");
658             }
659         }
660         mBubblesListener = listener;
661     }
662 
663     /**
664      * Tells SysUI to show the bubble with the provided key.
665      * @param key the key of the bubble to show.
666      * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X
667      *                         axis.
668      * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y
669      *                         axis.
670      */
showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY)671     public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) {
672         if (mBubbles != null) {
673             try {
674                 mBubbles.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY);
675             } catch (RemoteException e) {
676                 Log.w(TAG, "Failed call showBubble");
677             }
678         }
679     }
680 
681     /**
682      * Tells SysUI to remove the bubble with the provided key.
683      * @param key the key of the bubble to show.
684      */
removeBubble(String key)685     public void removeBubble(String key) {
686         if (mBubbles == null) return;
687         try {
688             mBubbles.removeBubble(key);
689         } catch (RemoteException e) {
690             Log.w(TAG, "Failed call removeBubble");
691         }
692     }
693 
694     /**
695      * Tells SysUI to remove all bubbles.
696      */
removeAllBubbles()697     public void removeAllBubbles() {
698         if (mBubbles == null) return;
699         try {
700             mBubbles.removeAllBubbles();
701         } catch (RemoteException e) {
702             Log.w(TAG, "Failed call removeAllBubbles");
703         }
704     }
705 
706     /**
707      * Tells SysUI to collapse the bubbles.
708      */
collapseBubbles()709     public void collapseBubbles() {
710         if (mBubbles != null) {
711             try {
712                 mBubbles.collapseBubbles();
713             } catch (RemoteException e) {
714                 Log.w(TAG, "Failed call collapseBubbles");
715             }
716         }
717     }
718 
719     /**
720      * Tells SysUI when the bubble is being dragged.
721      * Should be called only when the bubble bar is expanded.
722      * @param bubbleKey the key of the bubble to collapse/expand
723      * @param isBeingDragged whether the bubble is being dragged
724      */
onBubbleDrag(@ullable String bubbleKey, boolean isBeingDragged)725     public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) {
726         if (mBubbles == null) return;
727         try {
728             mBubbles.onBubbleDrag(bubbleKey, isBeingDragged);
729         } catch (RemoteException e) {
730             Log.w(TAG, "Failed call onBubbleDrag");
731         }
732     }
733 
734     //
735     // Splitscreen
736     //
737 
registerSplitScreenListener(ISplitScreenListener listener)738     public void registerSplitScreenListener(ISplitScreenListener listener) {
739         if (mSplitScreen != null) {
740             try {
741                 mSplitScreen.registerSplitScreenListener(listener);
742             } catch (RemoteException e) {
743                 Log.w(TAG, "Failed call registerSplitScreenListener");
744             }
745         }
746         mSplitScreenListener = listener;
747     }
748 
unregisterSplitScreenListener(ISplitScreenListener listener)749     public void unregisterSplitScreenListener(ISplitScreenListener listener) {
750         if (mSplitScreen != null) {
751             try {
752                 mSplitScreen.unregisterSplitScreenListener(listener);
753             } catch (RemoteException e) {
754                 Log.w(TAG, "Failed call unregisterSplitScreenListener");
755             }
756         }
757         mSplitScreenListener = null;
758     }
759 
registerSplitSelectListener(ISplitSelectListener listener)760     public void registerSplitSelectListener(ISplitSelectListener listener) {
761         if (mSplitScreen != null) {
762             try {
763                 mSplitScreen.registerSplitSelectListener(listener);
764             } catch (RemoteException e) {
765                 Log.w(TAG, "Failed call registerSplitSelectListener");
766             }
767         }
768         mSplitSelectListener = listener;
769     }
770 
unregisterSplitSelectListener(ISplitSelectListener listener)771     public void unregisterSplitSelectListener(ISplitSelectListener listener) {
772         if (mSplitScreen != null) {
773             try {
774                 mSplitScreen.unregisterSplitSelectListener(listener);
775             } catch (RemoteException e) {
776                 Log.w(TAG, "Failed call unregisterSplitSelectListener");
777             }
778         }
779         mSplitSelectListener = null;
780     }
781 
782     /** Start multiple tasks in split-screen simultaneously. */
startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId)783     public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
784             @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
785             RemoteTransition remoteTransition, InstanceId instanceId) {
786         if (mSystemUiProxy != null) {
787             try {
788                 mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
789                         splitRatio, remoteTransition, instanceId);
790             } catch (RemoteException e) {
791                 Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e);
792             }
793         }
794     }
795 
startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1, int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId)796     public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
797             int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
798             float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
799         if (mSystemUiProxy != null) {
800             try {
801                 mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
802                         splitPosition, splitRatio, remoteTransition, instanceId);
803             } catch (RemoteException e) {
804                 Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e);
805             }
806         }
807     }
808 
startIntents(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId)809     public void startIntents(PendingIntent pendingIntent1, int userId1,
810             @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
811             int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
812             @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
813             RemoteTransition remoteTransition, InstanceId instanceId) {
814         if (mSystemUiProxy != null) {
815             try {
816                 mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
817                         pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, splitRatio,
818                         remoteTransition, instanceId);
819             } catch (RemoteException e) {
820                 Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e);
821             }
822         }
823     }
824 
startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId)825     public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
826             Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
827             float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
828         if (mSystemUiProxy != null) {
829             try {
830                 mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
831                         splitPosition, splitRatio, remoteTransition, instanceId);
832             } catch (RemoteException e) {
833                 Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e);
834             }
835         }
836     }
837 
838     /**
839      * Start multiple tasks in split-screen simultaneously.
840      */
startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)841     public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
842             Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
843             float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
844         if (mSystemUiProxy != null) {
845             try {
846                 mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
847                         splitPosition, splitRatio, adapter, instanceId);
848             } catch (RemoteException e) {
849                 Log.w(TAG, splitFailureMessage(
850                         "startTasksWithLegacyTransition", "RemoteException"), e);
851             }
852         }
853     }
854 
startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, Bundle options1, int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)855     public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
856             Bundle options1, int taskId, Bundle options2,
857             @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
858             RemoteAnimationAdapter adapter, InstanceId instanceId) {
859         if (mSystemUiProxy != null) {
860             try {
861                 mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1,
862                         options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
863             } catch (RemoteException e) {
864                 Log.w(TAG, splitFailureMessage(
865                         "startIntentAndTaskWithLegacyTransition", "RemoteException"), e);
866             }
867         }
868     }
869 
startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1, int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)870     public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
871             int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
872             float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
873         if (mSystemUiProxy != null) {
874             try {
875                 mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
876                         taskId, options2, splitPosition, splitRatio, adapter, instanceId);
877             } catch (RemoteException e) {
878                 Log.w(TAG, splitFailureMessage(
879                         "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e);
880             }
881         }
882     }
883 
884     /**
885      * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
886      * non-null shortcut info means to start the app as a shortcut.
887      */
startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2, @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId)888     public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
889             @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
890             PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
891             @Nullable Bundle options2, @SplitConfigurationOptions.StagePosition int sidePosition,
892             float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
893         if (mSystemUiProxy != null) {
894             try {
895                 mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1,
896                         shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2,
897                         sidePosition, splitRatio, adapter, instanceId);
898             } catch (RemoteException e) {
899                 Log.w(TAG, splitFailureMessage(
900                         "startIntentsWithLegacyTransition", "RemoteException"), e);
901             }
902         }
903     }
904 
startShortcut(String packageName, String shortcutId, int position, Bundle options, UserHandle user, InstanceId instanceId)905     public void startShortcut(String packageName, String shortcutId, int position,
906             Bundle options, UserHandle user, InstanceId instanceId) {
907         if (mSplitScreen != null) {
908             try {
909                 mSplitScreen.startShortcut(packageName, shortcutId, position, options,
910                         user, instanceId);
911             } catch (RemoteException e) {
912                 Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e);
913             }
914         }
915     }
916 
startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position, Bundle options, InstanceId instanceId)917     public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
918             Bundle options, InstanceId instanceId) {
919         if (mSplitScreen != null) {
920             try {
921                 mSplitScreen.startIntent(intent, userId, fillInIntent, position, options,
922                         instanceId);
923             } catch (RemoteException e) {
924                 Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e);
925             }
926         }
927     }
928 
removeFromSideStage(int taskId)929     public void removeFromSideStage(int taskId) {
930         if (mSplitScreen != null) {
931             try {
932                 mSplitScreen.removeFromSideStage(taskId);
933             } catch (RemoteException e) {
934                 Log.w(TAG, "Failed call removeFromSideStage");
935             }
936         }
937     }
938 
939     /**
940      * Call this when going to recents so that shell can set-up and provide appropriate leashes
941      * for animation (eg. DividerBar).
942      *
943      * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
944      */
945     @Nullable
onGoingToRecentsLegacy(RemoteAnimationTarget[] apps)946     public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
947         if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) {
948             try {
949                 return mSplitScreen.onGoingToRecentsLegacy(apps);
950             } catch (RemoteException e) {
951                 Log.w(TAG, "Failed call onGoingToRecentsLegacy");
952             }
953         }
954         return null;
955     }
956 
957     @Nullable
onStartingSplitLegacy(RemoteAnimationTarget[] apps)958     public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
959         if (mSplitScreen != null) {
960             try {
961                 return mSplitScreen.onStartingSplitLegacy(apps);
962             } catch (RemoteException e) {
963                 Log.w(TAG, "Failed call onStartingSplitLegacy");
964             }
965         }
966         return null;
967     }
968 
969     //
970     // One handed
971     //
972 
startOneHandedMode()973     public void startOneHandedMode() {
974         if (mOneHanded != null) {
975             try {
976                 mOneHanded.startOneHanded();
977             } catch (RemoteException e) {
978                 Log.w(TAG, "Failed call startOneHandedMode", e);
979             }
980         }
981     }
982 
stopOneHandedMode()983     public void stopOneHandedMode() {
984         if (mOneHanded != null) {
985             try {
986                 mOneHanded.stopOneHanded();
987             } catch (RemoteException e) {
988                 Log.w(TAG, "Failed call stopOneHandedMode", e);
989             }
990         }
991     }
992 
993     //
994     // Remote transitions
995     //
996 
registerRemoteTransition( RemoteTransition remoteTransition, TransitionFilter filter)997     public void registerRemoteTransition(
998             RemoteTransition remoteTransition, TransitionFilter filter) {
999         if (mShellTransitions != null) {
1000             try {
1001                 mShellTransitions.registerRemote(filter, remoteTransition);
1002             } catch (RemoteException e) {
1003                 Log.w(TAG, "Failed call registerRemoteTransition");
1004             }
1005         }
1006         if (!mRemoteTransitions.containsKey(remoteTransition)) {
1007             mRemoteTransitions.put(remoteTransition, filter);
1008         }
1009     }
1010 
unregisterRemoteTransition(RemoteTransition remoteTransition)1011     public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
1012         if (mShellTransitions != null) {
1013             try {
1014                 mShellTransitions.unregisterRemote(remoteTransition);
1015             } catch (RemoteException e) {
1016                 Log.w(TAG, "Failed call registerRemoteTransition");
1017             }
1018         }
1019         mRemoteTransitions.remove(remoteTransition);
1020     }
1021 
1022     /**
1023      * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary
1024      * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
1025      */
shareTransactionQueue()1026     public void shareTransactionQueue() {
1027         if (mOriginalTransactionToken == null) {
1028             mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
1029         }
1030         setupTransactionQueue();
1031     }
1032 
1033     /**
1034      * Switch back to using Launcher's independent transaction queue.
1035      */
unshareTransactionQueue()1036     public void unshareTransactionQueue() {
1037         if (mOriginalTransactionToken == null) {
1038             return;
1039         }
1040         SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
1041         mOriginalTransactionToken = null;
1042     }
1043 
setupTransactionQueue()1044     private void setupTransactionQueue() {
1045         if (mOriginalTransactionToken == null) {
1046             return;
1047         }
1048         if (mShellTransitions == null) {
1049             SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
1050             return;
1051         }
1052         final IBinder shellApplyToken;
1053         try {
1054             shellApplyToken = mShellTransitions.getShellApplyToken();
1055         } catch (RemoteException e) {
1056             Log.e(TAG, "Error getting Shell's apply token", e);
1057             return;
1058         }
1059         if (shellApplyToken == null) {
1060             Log.e(TAG, "Didn't receive apply token from Shell");
1061             return;
1062         }
1063         SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken);
1064     }
1065 
1066     //
1067     // Starting window
1068     //
1069 
1070     /**
1071      * Sets listener to get callbacks when launching a task.
1072      */
setStartingWindowListener(IStartingWindowListener listener)1073     public void setStartingWindowListener(IStartingWindowListener listener) {
1074         if (mStartingWindow != null) {
1075             try {
1076                 mStartingWindow.setStartingWindowListener(listener);
1077             } catch (RemoteException e) {
1078                 Log.w(TAG, "Failed call setStartingWindowListener", e);
1079             }
1080         }
1081         mStartingWindowListener = listener;
1082     }
1083 
1084     //
1085     // SmartSpace transitions
1086     //
1087 
1088     /**
1089      * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
1090      * control the launcher side of the unlock animation. This will also cause us to dispatch the
1091      * current state of the smartspace to System UI (this will subsequently happen if the state
1092      * changes).
1093      */
setLauncherUnlockAnimationController( ILauncherUnlockAnimationController controller)1094     public void setLauncherUnlockAnimationController(
1095             ILauncherUnlockAnimationController controller) {
1096         if (mSysuiUnlockAnimationController != null) {
1097             try {
1098                 mSysuiUnlockAnimationController.setLauncherUnlockController(controller);
1099 
1100                 if (controller != null) {
1101                     controller.dispatchSmartspaceStateToSysui();
1102                 }
1103             } catch (RemoteException e) {
1104                 Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
1105             }
1106         }
1107 
1108         mLauncherUnlockAnimationController = controller;
1109     }
1110 
1111     /**
1112      * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
1113      * the unlock animation accordingly.
1114      */
notifySysuiSmartspaceStateUpdated(SmartspaceState state)1115     public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
1116         if (mSysuiUnlockAnimationController != null) {
1117             try {
1118                 mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
1119             } catch (RemoteException e) {
1120                 Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
1121                 e.printStackTrace();
1122             }
1123         }
1124     }
1125 
1126     //
1127     // Recents
1128     //
1129 
registerRecentTasksListener(IRecentTasksListener listener)1130     public void registerRecentTasksListener(IRecentTasksListener listener) {
1131         if (mRecentTasks != null) {
1132             try {
1133                 mRecentTasks.registerRecentTasksListener(listener);
1134             } catch (RemoteException e) {
1135                 Log.w(TAG, "Failed call registerRecentTasksListener", e);
1136             }
1137         }
1138         mRecentTasksListener = listener;
1139     }
1140 
unregisterRecentTasksListener(IRecentTasksListener listener)1141     public void unregisterRecentTasksListener(IRecentTasksListener listener) {
1142         if (mRecentTasks != null) {
1143             try {
1144                 mRecentTasks.unregisterRecentTasksListener(listener);
1145             } catch (RemoteException e) {
1146                 Log.w(TAG, "Failed call unregisterRecentTasksListener");
1147             }
1148         }
1149         mRecentTasksListener = null;
1150     }
1151 
1152     //
1153     // Back navigation transitions
1154     //
1155 
1156     /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
setBackToLauncherCallback(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner)1157     public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
1158             IRemoteAnimationRunner runner) {
1159         mBackToLauncherCallback = callback;
1160         mBackToLauncherRunner = runner;
1161         if (mBackAnimation == null || mBackToLauncherCallback == null) {
1162             return;
1163         }
1164         try {
1165             mBackAnimation.setBackToLauncherCallback(callback, runner);
1166         } catch (RemoteException e) {
1167             Log.e(TAG, "Failed call setBackToLauncherCallback", e);
1168         }
1169     }
1170 
1171     /** Clears the previously registered {@link IOnBackInvokedCallback}.
1172      *
1173      * @param callback The previously registered callback instance.
1174      */
clearBackToLauncherCallback(IOnBackInvokedCallback callback)1175     public void clearBackToLauncherCallback(IOnBackInvokedCallback callback) {
1176         if (mBackToLauncherCallback != callback) {
1177             return;
1178         }
1179         mBackToLauncherCallback = null;
1180         mBackToLauncherRunner = null;
1181         if (mBackAnimation == null) {
1182             return;
1183         }
1184         try {
1185             mBackAnimation.clearBackToLauncherCallback();
1186         } catch (RemoteException e) {
1187             Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
1188         }
1189     }
1190 
1191     /**
1192      * Called when the status bar color needs to be customized when back navigation.
1193      */
customizeStatusBarAppearance(AppearanceRegion appearance)1194     public void customizeStatusBarAppearance(AppearanceRegion appearance) {
1195         if (mBackAnimation == null) {
1196             return;
1197         }
1198         try {
1199             mBackAnimation.customizeStatusBarAppearance(appearance);
1200         } catch (RemoteException e) {
1201             Log.e(TAG, "Failed call useLauncherSysBarFlags", e);
1202         }
1203     }
1204 
getRecentTasks(int numTasks, int userId)1205     public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
1206         if (mRecentTasks != null) {
1207             try {
1208                 final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks,
1209                         RECENT_IGNORE_UNAVAILABLE, userId);
1210                 if (rawTasks == null) {
1211                     return new ArrayList<>();
1212                 }
1213                 return new ArrayList<>(Arrays.asList(rawTasks));
1214             } catch (RemoteException e) {
1215                 Log.w(TAG, "Failed call getRecentTasks", e);
1216             }
1217         }
1218         return new ArrayList<>();
1219     }
1220 
1221     /**
1222      * Gets the set of running tasks.
1223      */
getRunningTasks(int numTasks)1224     public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
1225         if (mRecentTasks != null
1226                 && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
1227             try {
1228                 return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
1229             } catch (RemoteException e) {
1230                 Log.w(TAG, "Failed call getRunningTasks", e);
1231             }
1232         }
1233         return new ArrayList<>();
1234     }
1235 
handleMessageAsync(Message msg)1236     private boolean handleMessageAsync(Message msg) {
1237         switch (msg.what) {
1238             case MSG_SET_SHELF_HEIGHT:
1239                 setShelfHeightAsync(msg.arg1, msg.arg2);
1240                 return true;
1241             case MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT:
1242                 setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2);
1243                 return true;
1244         }
1245 
1246         return false;
1247     }
1248 
1249     //
1250     // Desktop Mode
1251     //
1252 
1253     /** Call shell to show all apps active on the desktop */
showDesktopApps(int displayId)1254     public void showDesktopApps(int displayId) {
1255         if (mDesktopMode != null) {
1256             try {
1257                 mDesktopMode.showDesktopApps(displayId);
1258             } catch (RemoteException e) {
1259                 Log.w(TAG, "Failed call showDesktopApps", e);
1260             }
1261         }
1262     }
1263 
1264     /** Call shell to stash desktop apps */
stashDesktopApps(int displayId)1265     public void stashDesktopApps(int displayId) {
1266         if (mDesktopMode != null) {
1267             try {
1268                 mDesktopMode.stashDesktopApps(displayId);
1269             } catch (RemoteException e) {
1270                 Log.w(TAG, "Failed call stashDesktopApps", e);
1271             }
1272         }
1273     }
1274 
1275     /** Call shell to hide desktop apps that may be stashed */
hideStashedDesktopApps(int displayId)1276     public void hideStashedDesktopApps(int displayId) {
1277         if (mDesktopMode != null) {
1278             try {
1279                 mDesktopMode.hideStashedDesktopApps(displayId);
1280             } catch (RemoteException e) {
1281                 Log.w(TAG, "Failed call hideStashedDesktopApps", e);
1282             }
1283         }
1284     }
1285 
1286     /**
1287      * If task with the given id is on the desktop, bring it to front
1288      */
showDesktopApp(int taskId)1289     public void showDesktopApp(int taskId) {
1290         if (mDesktopMode != null) {
1291             try {
1292                 mDesktopMode.showDesktopApp(taskId);
1293             } catch (RemoteException e) {
1294                 Log.w(TAG, "Failed call showDesktopApp", e);
1295             }
1296         }
1297     }
1298 
1299     /** Call shell to get number of visible freeform tasks */
getVisibleDesktopTaskCount(int displayId)1300     public int getVisibleDesktopTaskCount(int displayId) {
1301         if (mDesktopMode != null) {
1302             try {
1303                 return mDesktopMode.getVisibleTaskCount(displayId);
1304             } catch (RemoteException e) {
1305                 Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e);
1306             }
1307         }
1308         return 0;
1309     }
1310 
1311     /** Set a listener on shell to get updates about desktop task state */
setDesktopTaskListener(@ullable IDesktopTaskListener listener)1312     public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
1313         mDesktopTaskListener = listener;
1314         if (mDesktopMode != null) {
1315             try {
1316                 mDesktopMode.setTaskListener(listener);
1317             } catch (RemoteException e) {
1318                 Log.w(TAG, "Failed call setDesktopTaskListener", e);
1319             }
1320         }
1321     }
1322 
1323     /** Perform cleanup transactions after animation to split select is complete */
onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo)1324     public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) {
1325         if (mDesktopMode != null) {
1326             try {
1327                 mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo);
1328             } catch (RemoteException e) {
1329                 Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e);
1330             }
1331         }
1332     }
1333 
1334     //
1335     // Unfold transition
1336     //
1337 
1338     /** Sets the unfold animation lister to sysui. */
setUnfoldAnimationListener(IUnfoldTransitionListener callback)1339     public void setUnfoldAnimationListener(IUnfoldTransitionListener callback) {
1340         mUnfoldAnimationListener = callback;
1341         if (mUnfoldAnimation == null) {
1342             return;
1343         }
1344         try {
1345             Log.d(TAG, "Registering unfold animation receiver");
1346             mUnfoldAnimation.setListener(callback);
1347         } catch (RemoteException e) {
1348             Log.e(TAG, "Failed call setUnfoldAnimationListener", e);
1349         }
1350     }
1351 
1352     //
1353     // Recents
1354     //
1355 
1356     /**
1357      * Starts the recents activity. The caller should manage the thread on which this is called.
1358      */
startRecentsActivity(Intent intent, ActivityOptions options, RecentsAnimationListener listener)1359     public boolean startRecentsActivity(Intent intent, ActivityOptions options,
1360             RecentsAnimationListener listener) {
1361         if (mRecentTasks == null) {
1362             return false;
1363         }
1364         final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
1365             @Override
1366             public void onAnimationStart(IRecentsAnimationController controller,
1367                     RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
1368                     Rect homeContentInsets, Rect minimizedHomeBounds) {
1369                 listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps,
1370                         wallpapers, homeContentInsets, minimizedHomeBounds);
1371             }
1372 
1373             @Override
1374             public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
1375                 listener.onAnimationCanceled(
1376                         ThumbnailData.wrap(taskIds, taskSnapshots));
1377             }
1378 
1379             @Override
1380             public void onTasksAppeared(RemoteAnimationTarget[] apps) {
1381                 listener.onTasksAppeared(apps);
1382             }
1383         };
1384         final Bundle optsBundle = options.toBundle();
1385         try {
1386             mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle,
1387                     mContext.getIApplicationThread(), runner);
1388             return true;
1389         } catch (RemoteException e) {
1390             Log.e(TAG, "Error starting recents via shell", e);
1391             return false;
1392         }
1393     }
1394 
1395     //
1396     // Drag and drop
1397     //
1398 
1399     /**
1400      * For testing purposes.  Returns `true` only if the shell drop target has shown and
1401      * drawn and is ready to handle drag events and the subsequent drop.
1402      */
isDragAndDropReady()1403     public boolean isDragAndDropReady() {
1404         if (mDragAndDrop == null) {
1405             return false;
1406         }
1407         try {
1408             return mDragAndDrop.isReadyToHandleDrag();
1409         } catch (RemoteException e) {
1410             Log.e(TAG, "Error querying drag state", e);
1411             return false;
1412         }
1413     }
1414 }
1415