SystemServicesProxy.java revision e1e20e11a758ee82753e8052773664823ca74746
1/*
2 * Copyright (C) 2014 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
17package com.android.systemui.recents.misc;
18
19import android.app.ActivityManager;
20import android.app.ActivityManagerNative;
21import android.app.ActivityOptions;
22import android.app.AppGlobals;
23import android.app.IActivityContainer;
24import android.app.IActivityManager;
25import android.app.ITaskStackListener;
26import android.app.SearchManager;
27import android.appwidget.AppWidgetHost;
28import android.appwidget.AppWidgetManager;
29import android.appwidget.AppWidgetProviderInfo;
30import android.content.ComponentName;
31import android.content.ContentResolver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.pm.ActivityInfo;
35import android.content.pm.IPackageManager;
36import android.content.pm.PackageManager;
37import android.content.pm.ResolveInfo;
38import android.content.res.Resources;
39import android.graphics.Bitmap;
40import android.graphics.BitmapFactory;
41import android.graphics.Canvas;
42import android.graphics.Color;
43import android.graphics.Paint;
44import android.graphics.Point;
45import android.graphics.PorterDuff;
46import android.graphics.PorterDuffXfermode;
47import android.graphics.Rect;
48import android.graphics.drawable.ColorDrawable;
49import android.graphics.drawable.Drawable;
50import android.os.Bundle;
51import android.os.ParcelFileDescriptor;
52import android.os.RemoteException;
53import android.os.SystemProperties;
54import android.os.UserHandle;
55import android.provider.Settings;
56import android.util.Log;
57import android.util.MutableBoolean;
58import android.util.Pair;
59import android.util.SparseArray;
60import android.view.Display;
61import android.view.DisplayInfo;
62import android.view.SurfaceControl;
63import android.view.WindowManager;
64import android.view.accessibility.AccessibilityManager;
65import com.android.systemui.R;
66import com.android.systemui.recents.Constants;
67import com.android.systemui.recents.Recents;
68
69import java.io.IOException;
70import java.util.ArrayList;
71import java.util.Iterator;
72import java.util.List;
73import java.util.Random;
74
75/**
76 * Acts as a shim around the real system services that we need to access data from, and provides
77 * a point of injection when testing UI.
78 */
79public class SystemServicesProxy {
80    final static String TAG = "SystemServicesProxy";
81
82    final static BitmapFactory.Options sBitmapOptions;
83
84    AccessibilityManager mAccm;
85    ActivityManager mAm;
86    IActivityManager mIam;
87    AppWidgetManager mAwm;
88    PackageManager mPm;
89    IPackageManager mIpm;
90    SearchManager mSm;
91    WindowManager mWm;
92    Display mDisplay;
93    String mRecentsPackage;
94    ComponentName mAssistComponent;
95
96    Bitmap mDummyIcon;
97    int mDummyThumbnailWidth;
98    int mDummyThumbnailHeight;
99    Paint mBgProtectionPaint;
100    Canvas mBgProtectionCanvas;
101
102    static {
103        sBitmapOptions = new BitmapFactory.Options();
104        sBitmapOptions.inMutable = true;
105    }
106
107    /** Private constructor */
108    public SystemServicesProxy(Context context) {
109        mAccm = AccessibilityManager.getInstance(context);
110        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
111        mIam = ActivityManagerNative.getDefault();
112        mAwm = AppWidgetManager.getInstance(context);
113        mPm = context.getPackageManager();
114        mIpm = AppGlobals.getPackageManager();
115        mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
116        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
117        mDisplay = mWm.getDefaultDisplay();
118        mRecentsPackage = context.getPackageName();
119
120        // Get the dummy thumbnail width/heights
121        Resources res = context.getResources();
122        int wId = com.android.internal.R.dimen.thumbnail_width;
123        int hId = com.android.internal.R.dimen.thumbnail_height;
124        mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
125        mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
126
127        // Create the protection paints
128        mBgProtectionPaint = new Paint();
129        mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
130        mBgProtectionPaint.setColor(0xFFffffff);
131        mBgProtectionCanvas = new Canvas();
132
133        // Resolve the assist intent
134        Intent assist = mSm.getAssistIntent(context, false);
135        if (assist != null) {
136            mAssistComponent = assist.getComponent();
137        }
138
139        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
140            // Create a dummy icon
141            mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
142            mDummyIcon.eraseColor(0xFF999999);
143        }
144    }
145
146    /** Returns a list of the recents tasks */
147    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
148            boolean isTopTaskHome) {
149        if (mAm == null) return null;
150
151        // If we are mocking, then create some recent tasks
152        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
153            ArrayList<ActivityManager.RecentTaskInfo> tasks =
154                    new ArrayList<ActivityManager.RecentTaskInfo>();
155            int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
156            for (int i = 0; i < count; i++) {
157                // Create a dummy component name
158                int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
159                ComponentName cn = new ComponentName("com.android.test" + packageIndex,
160                        "com.android.test" + i + ".Activity");
161                String description = "" + i + " - " +
162                        Long.toString(Math.abs(new Random().nextLong()), 36);
163                // Create the recent task info
164                ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
165                rti.id = rti.persistentId = i;
166                rti.baseIntent = new Intent();
167                rti.baseIntent.setComponent(cn);
168                rti.description = description;
169                rti.firstActiveTime = rti.lastActiveTime = i;
170                if (i % 2 == 0) {
171                    rti.taskDescription = new ActivityManager.TaskDescription(description,
172                        Bitmap.createBitmap(mDummyIcon),
173                        0xFF000000 | (0xFFFFFF & new Random().nextInt()));
174                } else {
175                    rti.taskDescription = new ActivityManager.TaskDescription();
176                }
177                tasks.add(rti);
178            }
179            return tasks;
180        }
181
182        // Remove home/recents/excluded tasks
183        int minNumTasksToQuery = 10;
184        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
185        List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
186                ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
187                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
188                ActivityManager.RECENT_INCLUDE_PROFILES |
189                ActivityManager.RECENT_WITH_EXCLUDED, userId);
190
191        // Break early if we can't get a valid set of tasks
192        if (tasks == null) {
193            return new ArrayList<>();
194        }
195
196        boolean isFirstValidTask = true;
197        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
198        while (iter.hasNext()) {
199            ActivityManager.RecentTaskInfo t = iter.next();
200
201            // NOTE: The order of these checks happens in the expected order of the traversal of the
202            // tasks
203
204            // Check the first non-recents task, include this task even if it is marked as excluded
205            // from recents if we are currently in the app.  In other words, only remove excluded
206            // tasks if it is not the first active task.
207            boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
208                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
209            if (isExcluded && (isTopTaskHome || !isFirstValidTask)) {
210                iter.remove();
211                continue;
212            }
213            isFirstValidTask = false;
214        }
215
216        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
217    }
218
219    /** Returns a list of the running tasks */
220    private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
221        if (mAm == null) return null;
222        return mAm.getRunningTasks(numTasks);
223    }
224
225    /** Returns the top task. */
226    public ActivityManager.RunningTaskInfo getTopMostTask() {
227        List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1);
228        if (tasks != null && !tasks.isEmpty()) {
229            return tasks.get(0);
230        }
231        return null;
232    }
233
234    /** Returns whether the recents is currently running */
235    public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
236            MutableBoolean isHomeTopMost) {
237        if (topTask != null) {
238            ComponentName topActivity = topTask.topActivity;
239
240            // Check if the front most activity is recents
241            if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
242                    topActivity.getClassName().equals(Recents.sRecentsActivity)) {
243                if (isHomeTopMost != null) {
244                    isHomeTopMost.value = false;
245                }
246                return true;
247            }
248
249            if (isHomeTopMost != null) {
250                isHomeTopMost.value = isInHomeStack(topTask.id);
251            }
252        }
253        return false;
254    }
255
256    /** Get the bounds of a stack / task. */
257    public Rect getTaskBounds(int stackId) {
258        ActivityManager.StackInfo info = getAllStackInfos().get(stackId);
259        if (info != null)
260          return info.bounds;
261        return new Rect();
262    }
263
264    /** Resize a given task. */
265    public void resizeTask(int taskId, Rect bounds) {
266        if (mIam == null) return;
267
268        try {
269            mIam.resizeTask(taskId, bounds);
270        } catch (RemoteException e) {
271            e.printStackTrace();
272        }
273    }
274
275    /** Returns the stack info for all stacks. */
276    public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
277        if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
278
279        try {
280            SparseArray<ActivityManager.StackInfo> stacks =
281                    new SparseArray<ActivityManager.StackInfo>();
282            List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
283            int stackCount = infos.size();
284            for (int i = 0; i < stackCount; i++) {
285                ActivityManager.StackInfo info = infos.get(i);
286                stacks.put(info.stackId, info);
287            }
288            return stacks;
289        } catch (RemoteException e) {
290            e.printStackTrace();
291            return new SparseArray<ActivityManager.StackInfo>();
292        }
293    }
294
295    /** Returns the focused stack id. */
296    public int getFocusedStack() {
297        if (mIam == null) return -1;
298
299        try {
300            return mIam.getFocusedStackId();
301        } catch (RemoteException e) {
302            e.printStackTrace();
303            return -1;
304        }
305    }
306
307    /** Returns whether the specified task is in the home stack */
308    public boolean isInHomeStack(int taskId) {
309        if (mAm == null) return false;
310
311        // If we are mocking, then just return false
312        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
313            return false;
314        }
315
316        return mAm.isInHomeStack(taskId);
317    }
318
319    /** Returns the top task thumbnail for the given task id */
320    public Bitmap getTaskThumbnail(int taskId) {
321        if (mAm == null) return null;
322
323        // If we are mocking, then just return a dummy thumbnail
324        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
325            Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
326                    Bitmap.Config.ARGB_8888);
327            thumbnail.eraseColor(0xff333333);
328            return thumbnail;
329        }
330
331        Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
332        if (thumbnail != null) {
333            thumbnail.setHasAlpha(false);
334            // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
335            // left pixel, then assume the whole thumbnail is transparent. Generally, proper
336            // screenshots are always composed onto a bitmap that has no alpha.
337            if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
338                mBgProtectionCanvas.setBitmap(thumbnail);
339                mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
340                        mBgProtectionPaint);
341                mBgProtectionCanvas.setBitmap(null);
342                Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
343            }
344        }
345        return thumbnail;
346    }
347
348    /**
349     * Returns a task thumbnail from the activity manager
350     */
351    public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
352        ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
353        if (taskThumbnail == null) return null;
354
355        Bitmap thumbnail = taskThumbnail.mainThumbnail;
356        ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
357        if (thumbnail == null && descriptor != null) {
358            thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
359                    null, sBitmapOptions);
360        }
361        if (descriptor != null) {
362            try {
363                descriptor.close();
364            } catch (IOException e) {
365            }
366        }
367        return thumbnail;
368    }
369
370    /** Moves a task to the front with the specified activity options. */
371    public void moveTaskToFront(int taskId, ActivityOptions opts) {
372        if (mAm == null) return;
373        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
374
375        if (opts != null) {
376            mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
377                    opts.toBundle());
378        } else {
379            mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME);
380        }
381    }
382
383    /** Removes the task */
384    public void removeTask(int taskId) {
385        if (mAm == null) return;
386        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
387
388        // Remove the task.
389        mAm.removeTask(taskId);
390    }
391
392    /**
393     * Returns the activity info for a given component name.
394     *
395     * @param cn The component name of the activity.
396     * @param userId The userId of the user that this is for.
397     */
398    public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
399        if (mIpm == null) return null;
400        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
401
402        try {
403            return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
404        } catch (RemoteException e) {
405            e.printStackTrace();
406            return null;
407        }
408    }
409
410    /**
411     * Returns the activity info for a given component name.
412     *
413     * @param cn The component name of the activity.
414     */
415    public ActivityInfo getActivityInfo(ComponentName cn) {
416        if (mPm == null) return null;
417        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
418
419        try {
420            return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
421        } catch (PackageManager.NameNotFoundException e) {
422            e.printStackTrace();
423            return null;
424        }
425    }
426
427    /** Returns the activity label */
428    public String getActivityLabel(ActivityInfo info) {
429        if (mPm == null) return null;
430
431        // If we are mocking, then return a mock label
432        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
433            return "Recent Task";
434        }
435
436        return info.loadLabel(mPm).toString();
437    }
438
439    /** Returns the application label */
440    public String getApplicationLabel(Intent baseIntent, int userId) {
441        if (mPm == null) return null;
442
443        // If we are mocking, then return a mock label
444        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
445            return "Recent Task";
446        }
447
448        ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId);
449        CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null;
450        return (label != null) ? label.toString() : null;
451    }
452
453    /** Returns the content description for a given task */
454    public String getContentDescription(Intent baseIntent, int userId, String activityLabel,
455            Resources res) {
456        String applicationLabel = getApplicationLabel(baseIntent, userId);
457        if (applicationLabel == null) {
458            return getBadgedLabel(activityLabel, userId);
459        }
460        String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
461        return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
462                : res.getString(R.string.accessibility_recents_task_header,
463                        badgedApplicationLabel, activityLabel);
464    }
465
466    /**
467     * Returns the activity icon for the ActivityInfo for a user, badging if
468     * necessary.
469     */
470    public Drawable getActivityIcon(ActivityInfo info, int userId) {
471        if (mPm == null) return null;
472
473        // If we are mocking, then return a mock label
474        if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
475            return new ColorDrawable(0xFF666666);
476        }
477
478        Drawable icon = info.loadIcon(mPm);
479        return getBadgedIcon(icon, userId);
480    }
481
482    /**
483     * Returns the given icon for a user, badging if necessary.
484     */
485    public Drawable getBadgedIcon(Drawable icon, int userId) {
486        if (userId != UserHandle.myUserId()) {
487            icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
488        }
489        return icon;
490    }
491
492    /**
493     * Returns the given label for a user, badging if necessary.
494     */
495    public String getBadgedLabel(String label, int userId) {
496        if (userId != UserHandle.myUserId()) {
497            label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
498        }
499        return label;
500    }
501
502    /** Returns the package name of the home activity. */
503    public String getHomeActivityPackageName() {
504        if (mPm == null) return null;
505        if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
506
507        ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
508        ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
509        if (defaultHomeActivity != null) {
510            return defaultHomeActivity.getPackageName();
511        } else if (homeActivities.size() == 1) {
512            ResolveInfo info = homeActivities.get(0);
513            if (info.activityInfo != null) {
514                return info.activityInfo.packageName;
515            }
516        }
517        return null;
518    }
519
520    /**
521     * Returns whether the foreground user is the owner.
522     */
523    public boolean isForegroundUserOwner() {
524        if (mAm == null) return false;
525
526        return mAm.getCurrentUser() == UserHandle.USER_OWNER;
527    }
528
529    /**
530     * Resolves and returns the first Recents widget from the same package as the global
531     * assist activity.
532     */
533    public AppWidgetProviderInfo resolveSearchAppWidget() {
534        if (mAwm == null) return null;
535        if (mAssistComponent == null) return null;
536
537        // Find the first Recents widget from the same package as the global assist activity
538        List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
539                AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
540        for (AppWidgetProviderInfo info : widgets) {
541            if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) {
542                return info;
543            }
544        }
545        return null;
546    }
547
548    /**
549     * Resolves and binds the search app widget that is to appear in the recents.
550     */
551    public Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host) {
552        if (mAwm == null) return null;
553        if (mAssistComponent == null) return null;
554
555        // Find the first Recents widget from the same package as the global assist activity
556        AppWidgetProviderInfo searchWidgetInfo = resolveSearchAppWidget();
557
558        // Return early if there is no search widget
559        if (searchWidgetInfo == null) return null;
560
561        // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
562        int searchWidgetId = host.allocateAppWidgetId();
563        Bundle opts = new Bundle();
564        opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
565                AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
566        if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, searchWidgetInfo.provider, opts)) {
567            host.deleteAppWidgetId(searchWidgetId);
568            return null;
569        }
570        return new Pair<Integer, AppWidgetProviderInfo>(searchWidgetId, searchWidgetInfo);
571    }
572
573    /**
574     * Returns the app widget info for the specified app widget id.
575     */
576    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
577        if (mAwm == null) return null;
578
579        return mAwm.getAppWidgetInfo(appWidgetId);
580    }
581
582    /**
583     * Destroys the specified app widget.
584     */
585    public void unbindSearchAppWidget(AppWidgetHost host, int appWidgetId) {
586        if (mAwm == null) return;
587
588        // Delete the app widget
589        host.deleteAppWidgetId(appWidgetId);
590    }
591
592    /**
593     * Returns whether touch exploration is currently enabled.
594     */
595    public boolean isTouchExplorationEnabled() {
596        if (mAccm == null) return false;
597
598        return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
599    }
600
601    /**
602     * Returns a global setting.
603     */
604    public int getGlobalSetting(Context context, String setting) {
605        ContentResolver cr = context.getContentResolver();
606        return Settings.Global.getInt(cr, setting, 0);
607    }
608
609    /**
610     * Returns a system setting.
611     */
612    public int getSystemSetting(Context context, String setting) {
613        ContentResolver cr = context.getContentResolver();
614        return Settings.System.getInt(cr, setting, 0);
615    }
616
617    /**
618     * Returns a system property.
619     */
620    public String getSystemProperty(String key) {
621        return SystemProperties.get(key);
622    }
623
624    /**
625     * Returns the window rect.
626     */
627    public Rect getWindowRect() {
628        Rect windowRect = new Rect();
629        if (mWm == null) return windowRect;
630
631        Point p = new Point();
632        mWm.getDefaultDisplay().getRealSize(p);
633        windowRect.set(0, 0, p.x, p.y);
634        return windowRect;
635    }
636
637    /**
638     * Takes a screenshot of the current surface.
639     */
640    public Bitmap takeScreenshot() {
641        DisplayInfo di = new DisplayInfo();
642        mDisplay.getDisplayInfo(di);
643        return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
644    }
645
646    /**
647     * Takes a screenshot of the current app.
648     */
649    public Bitmap takeAppScreenshot() {
650        return takeScreenshot();
651    }
652
653    /** Starts an activity from recents. */
654    public boolean startActivityFromRecents(Context context, int taskId, String taskName,
655            ActivityOptions options) {
656        if (mIam != null) {
657            try {
658                mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
659                return true;
660            } catch (Exception e) {
661                Console.logError(context,
662                        context.getString(R.string.recents_launch_error_message, taskName));
663            }
664        }
665        return false;
666    }
667
668    /** Starts an in-place animation on the front most application windows. */
669    public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
670        if (mIam == null) return;
671
672        try {
673            mIam.startInPlaceAnimationOnFrontMostApplication(opts);
674        } catch (Exception e) {
675            e.printStackTrace();
676        }
677    }
678
679    /** Registers a task stack listener with the system. */
680    public void registerTaskStackListener(ITaskStackListener listener) {
681        if (mIam == null) return;
682
683        try {
684            mIam.registerTaskStackListener(listener);
685        } catch (Exception e) {
686            e.printStackTrace();
687        }
688    }
689}
690