Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a91c293b authored by Winson Chung's avatar Winson Chung
Browse files

Preload only visible thumbnails and task icons. (Bug 17672056, Bug 18291345)

This change ensures that only the number of visible thumbnails and task icons are
loaded to minimize the delay required when initializing the stack without the 
cache.  In addition, this change reduces the number of times that the task stack
is recomposed when launching recents (in addition to the number of calls to 
getRecentTasks()).

There is also a fix to a regression where the exit trigger is not run when the
task stack view is empty.

Change-Id: I75834ff3c57c0e5dad6252b982f71c6e740071f2
parent a11bb742
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -776,7 +776,7 @@
         This needs to match the constants in
         policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
    -->
    <integer name="config_longPressOnHomeBehavior">1</integer>
    <integer name="config_longPressOnHomeBehavior">0</integer>

    <!-- Control the behavior when the user double-taps the home button.
            0 - Nothing
+69 −38
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
@@ -63,6 +64,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
    final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
    final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey";
    final public static String EXTRA_REUSE_TASK_STACK_VIEWS = "recents.reuseTaskStackViews";
    final public static String EXTRA_NUM_VISIBLE_TASKS = "recents.numVisibleTasks";
    final public static String EXTRA_NUM_VISIBLE_THUMBNAILS = "recents.numVisibleThumbnails";

    final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
    final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
@@ -75,6 +78,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
    final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";

    static RecentsComponent.Callbacks sRecentsComponentCallbacks;
    static RecentsTaskLoadPlan sInstanceLoadPlan;

    Context mContext;
    LayoutInflater mInflater;
@@ -133,8 +137,15 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
            }
        }

        // When we start, preload the metadata associated with the previous tasks
        RecentsTaskLoader.getInstance().preload(mContext, RecentsTaskLoader.ALL_TASKS);
        // When we start, preload the metadata and icons associated with the recent tasks.
        // We can use a new plan since the caches will be the same.
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
        loader.preloadTasks(plan, true /* isTopTaskHome */);
        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
        launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
        launchOpts.loadThumbnails = false;
        loader.loadTasks(mContext, plan, launchOpts);
    }

    public void onBootCompleted() {
@@ -182,9 +193,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
    }

    public void onPreloadRecents() {
        // When we start, preload the metadata associated with the previous tasks
        RecentsTaskLoader.getInstance().preload(mContext,
                Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
        // Preload only the raw task list into a new load plan (which will be consumed by the
        // RecentsActivity)
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        sInstanceLoadPlan = loader.createLoadPlan(mContext);
        sInstanceLoadPlan.preloadRawTasks(true);
    }

    public void onCancelPreloadingRecents() {
@@ -193,8 +206,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta

    void showRelativeAffiliatedTask(boolean showNextTask) {
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
                -1, -1, RecentsTaskLoader.ALL_TASKS, false, true, null, null);
        RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
        loader.preloadTasks(plan, true /* isTopTaskHome */);
        TaskStack stack = plan.getTaskStack();

        // Return early if there are no tasks
        if (stack.getTaskCount() == 0) return;

@@ -402,11 +417,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
     * Creates the activity options for an app->recents transition.
     */
    ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
            boolean isTopTaskHome) {
            TaskStack stack, TaskStackView stackView) {
        // Update the destination rect
        Task toTask = new Task();
        TaskViewTransform toTransform = getThumbnailTransitionTransform(topTask.id, isTopTaskHome,
                toTask);
        TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
                topTask.id, toTask);
        if (toTransform != null && toTask.key != null) {
            Rect toTaskRect = toTransform.rect;
            int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
@@ -434,16 +449,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
    }

    /** Returns the transition rect for the given task id. */
    TaskViewTransform getThumbnailTransitionTransform(int runningTaskId, boolean isTopTaskHome,
                                                      Task runningTaskOut) {
        // Get the stack of tasks that we are animating into
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
                runningTaskId, -1, RecentsTaskLoader.ALL_TASKS, false, isTopTaskHome, null, null);
        if (stack.getTaskCount() == 0) {
            return null;
        }

    TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
            int runningTaskId, Task runningTaskOut) {
        // Find the running task in the TaskStack
        Task task = null;
        ArrayList<Task> tasks = stack.getTasks();
@@ -465,30 +472,42 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
        }

        // Get the transform for the running task
        mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
        mDummyStackView.getScroller().setStackScrollToInitialState();
        mTmpTransform = mDummyStackView.getStackAlgorithm().getStackTransform(task,
                mDummyStackView.getScroller().getStackScroll(), mTmpTransform, null);
        stackView.getScroller().setStackScrollToInitialState();
        mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
                stackView.getScroller().getStackScroll(), mTmpTransform, null);
        return mTmpTransform;
    }

    /** Starts the recents activity */
    void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
        // If Recents is not the front-most activity and we should animate into it.  If
        // the activity at the root of the top task stack in the home stack, then we just do a
        // simple transition.  Otherwise, we animate to the rects defined by the Recents service,
        // which can differ depending on the number of items in the list.
        SystemServicesProxy ssp = mSystemServicesProxy;
        List<ActivityManager.RecentTaskInfo> recentTasks =
                ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
        boolean useThumbnailTransition = !isTopTaskHome;
        boolean hasRecentTasks = !recentTasks.isEmpty();
        if (sInstanceLoadPlan == null) {
            // Create a new load plan if onPreloadRecents() was never triggered
            RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
            sInstanceLoadPlan = loader.createLoadPlan(mContext);
        }
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
        TaskStack stack = sInstanceLoadPlan.getTaskStack();

        // Prepare the dummy stack for the transition
        mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
        TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
                mDummyStackView.computeStackVisibilityReport();
        boolean hasRecentTasks = stack.getTaskCount() > 0;
        boolean useThumbnailTransition = !isTopTaskHome && hasRecentTasks;

        if (useThumbnailTransition) {
            // Ensure that we load the running task's icon
            RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
            launchOpts.runningTaskId = topTask.id;
            launchOpts.loadThumbnails = false;
            loader.loadTasks(mContext, sInstanceLoadPlan, launchOpts);

            // Try starting with a thumbnail transition
            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, isTopTaskHome);
            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
                    mDummyStackView);
            if (opts != null) {
                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL);
                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL, stackVr);
            } else {
                // Fall through below to the non-thumbnail transition
                useThumbnailTransition = false;
@@ -522,11 +541,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta

                ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
                startAlternateRecentsActivity(topTask, opts,
                        fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME);
                        fromSearchHome ? EXTRA_FROM_SEARCH_HOME : EXTRA_FROM_HOME, stackVr);
            } else {
                // Otherwise we do the normal fade from an unknown source
                ActivityOptions opts = getUnknownTransitionActivityOptions();
                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME);
                startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr);
            }
        }
        mLastToggleTime = System.currentTimeMillis();
@@ -534,7 +553,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta

    /** Starts the recents activity */
    void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
                                       ActivityOptions opts, String extraFlag) {
            ActivityOptions opts, String extraFlag,
            TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
        Intent intent = new Intent(sToggleRecentsAction);
        intent.setClassName(sRecentsPackage, sRecentsActivity);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -546,6 +566,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
        intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
        intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
        intent.putExtra(EXTRA_REUSE_TASK_STACK_VIEWS, mCanReuseTaskStackViews);
        intent.putExtra(EXTRA_NUM_VISIBLE_TASKS, vr.numVisibleTasks);
        intent.putExtra(EXTRA_NUM_VISIBLE_THUMBNAILS, vr.numVisibleThumbnails);
        if (opts != null) {
            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
        } else {
@@ -566,6 +588,15 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
        }
    }

    /**
     * Returns the preloaded load plan and invalidates it.
     */
    public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
        RecentsTaskLoadPlan plan = sInstanceLoadPlan;
        sInstanceLoadPlan = null;
        return plan;
    }

    /**** OnAnimationStartedListener Implementation ****/

    @Override
+0 −5
Original line number Diff line number Diff line
@@ -64,11 +64,6 @@ public class Constants {
            public static String DebugModeVersion = "A";
        }

        public static class RecentsTaskLoader {
            // XXX: This should be calculated on the first load
            public static final int PreloadFirstTasksCount = 6;
        }

        public static class TaskStackView {
            public static final int TaskStackOverscrollRange = 150;
            public static final int FilterStartDelay = 25;
+37 −15
Original line number Diff line number Diff line
@@ -27,10 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.util.Pair;
import android.view.KeyEvent;
@@ -42,6 +39,7 @@ import com.android.systemui.recents.misc.DebugTrigger;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
@@ -163,9 +161,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                // When the screen turns off, dismiss Recents to Home
                dismissRecentsToHome(false);
                // Start preloading some tasks in the background
                RecentsTaskLoader.getInstance().preload(RecentsActivity.this,
                        Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
                // Preload the metadata for all tasks in the background
                RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
                RecentsTaskLoadPlan plan = loader.createLoadPlan(context);
                loader.preloadTasks(plan, true /* isTopTaskHome */);
            } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
                // When the search activity changes, update the Search widget
                refreshSearchWidget();
@@ -188,6 +187,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
        // Update the configuration based on the launch intent
        boolean fromSearchHome = launchIntent.getBooleanExtra(
                AlternateRecentsComponent.EXTRA_FROM_SEARCH_HOME, false);
        int numVisibleTasks = launchIntent.getIntExtra(
                AlternateRecentsComponent.EXTRA_NUM_VISIBLE_TASKS, 0);
        int numVisibleThumbnails = launchIntent.getIntExtra(
                AlternateRecentsComponent.EXTRA_NUM_VISIBLE_THUMBNAILS, 0);
        mConfig.launchedFromHome = fromSearchHome || launchIntent.getBooleanExtra(
                AlternateRecentsComponent.EXTRA_FROM_HOME, false);
        mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
@@ -199,16 +202,29 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
        mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra(
                AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false);

        // Load all the tasks
        // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
        // reconstructing the task stack
        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
        SpaceNode root = loader.reload(this,
                Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount,
                mConfig.launchedFromHome);
        RecentsTaskLoadPlan plan = AlternateRecentsComponent.consumeInstanceLoadPlan();
        if (plan == null) {
            plan = loader.createLoadPlan(this);
            loader.preloadTasks(plan, mConfig.launchedFromHome);
        }

        // Start loading tasks according to the load plan
        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
        loadOpts.runningTaskId = mConfig.launchedToTaskId;
        loadOpts.numVisibleTasks = numVisibleTasks;
        loadOpts.numVisibleTaskThumbnails = numVisibleThumbnails;
        loader.loadTasks(this, plan, loadOpts);

        SpaceNode root = plan.getSpaceNode();
        ArrayList<TaskStack> stacks = root.getStacks();
        if (!stacks.isEmpty()) {
            mRecentsView.setTaskStacks(root.getStacks());
        boolean hasTasks = root.hasTasks();
        if (hasTasks) {
            mRecentsView.setTaskStacks(stacks);
        }
        mConfig.launchedWithNoRecentTasks = !root.hasTasks();
        mConfig.launchedWithNoRecentTasks = !hasTasks;

        // Create the home intent runnable
        Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
@@ -434,6 +450,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView

    /** Inflates the debug overlay if debug mode is enabled. */
    void inflateDebugOverlay() {
        if (!Constants.DebugFlags.App.EnableDebugMode) return;

        if (mConfig.debugModeEnabled && mDebugOverlay == null) {
            // Inflate the overlay and seek bars
            mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
@@ -592,14 +610,18 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
                settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply();
                mConfig.debugModeEnabled = false;
                inflateDebugOverlay();
                if (mDebugOverlay != null) {
                    mDebugOverlay.disable();
                }
            } else {
                // Enable the debug mode
                settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply();
                mConfig.debugModeEnabled = true;
                inflateDebugOverlay();
                if (mDebugOverlay != null) {
                    mDebugOverlay.enable();
                }
            }
            Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
                (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
                Toast.LENGTH_SHORT).show();
+1 −0
Original line number Diff line number Diff line
@@ -242,6 +242,7 @@ public class SystemServicesProxy {

        Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
        if (thumbnail != null) {
            thumbnail.setHasAlpha(false);
            // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
            // left pixel, then assume the whole thumbnail is transparent. Generally, proper
            // screenshots are always composed onto a bitmap that has no alpha.
Loading