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

Commit b67e105e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Improve caching behavior of thumbnails" into oc-dev

parents 1df5ca59 cdef591e
Loading
Loading
Loading
Loading
+40 −16
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskGrouping;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskStackViewScroller;
import com.android.systemui.recents.views.TaskViewHeader;
@@ -126,11 +127,22 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
                if (runningTaskInfo != null) {
                    launchOpts.runningTaskId = runningTaskInfo.id;
                }
                launchOpts.numVisibleTasks = 2;
                launchOpts.numVisibleTaskThumbnails = 2;
                mDummyStackView.setTasks(plan.getTaskStack(), false /* allowNotify */);
                updateDummyStackViewLayout(plan.getTaskStack(),
                        getWindowRect(null /* windowRectOverride */));

                // Launched from app is always the worst case (in terms of how many thumbnails/tasks
                // visible)
                RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
                launchState.launchedFromApp = true;
                mDummyStackView.updateLayoutAlgorithm(true /* boundScroll */, launchState);

                VisibilityReport visibilityReport = mDummyStackView.computeStackVisibilityReport();
                launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
                launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
                launchOpts.onlyLoadForCache = true;
                launchOpts.onlyLoadPausedActivities = true;
                launchOpts.loadThumbnails = !ActivityManager.ENABLE_TASK_SNAPSHOTS;
                launchOpts.loadThumbnails = true;
                loader.loadTasks(mContext, plan, launchOpts);
            }
        }
@@ -605,23 +617,12 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
        mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
    }

    /**
     * Prepares the header bar layout for the next transition, if the task view bounds has changed
     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
     *
     * @param stack the stack to initialize the stack layout with
     * @param windowRectOverride the rectangle to use when calculating the stack state which can
     *                           be different from the current window rect if recents is resizing
     *                           while being launched
     */
    private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
    private void updateDummyStackViewLayout(TaskStack stack, Rect windowRect) {
        SystemServicesProxy ssp = Recents.getSystemServices();
        Rect displayRect = ssp.getDisplayRect();
        Rect systemInsets = new Rect();
        ssp.getStableInsets(systemInsets);
        Rect windowRect = windowRectOverride != null
                ? new Rect(windowRectOverride)
                : ssp.getWindowRect();

        // When docked, the nav bar insets are consumed and the activity is measured without insets.
        // However, the window bounds include the insets, so we need to subtract them here to make
        // them identical.
@@ -642,6 +643,29 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
            stackLayout.reset();
            stackLayout.initialize(displayRect, windowRect, mTaskStackBounds,
                    TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
        }
    }

    private Rect getWindowRect(Rect windowRectOverride) {
       return windowRectOverride != null
                ? new Rect(windowRectOverride)
                : Recents.getSystemServices().getWindowRect();
    }

    /**
     * Prepares the header bar layout for the next transition, if the task view bounds has changed
     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
     *
     * @param stack the stack to initialize the stack layout with
     * @param windowRectOverride the rectangle to use when calculating the stack state which can
     *                           be different from the current window rect if recents is resizing
     *                           while being launched
     */
    private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
        Rect windowRect = getWindowRect(windowRectOverride);
        updateDummyStackViewLayout(stack, windowRect);
        if (stack != null) {
            TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
            mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
            // Get the width of a task view so that we know how wide to draw the header bar.
            int taskViewWidth = 0;
+1 −2
Original line number Diff line number Diff line
@@ -671,8 +671,7 @@ public class SystemServicesProxy {
        if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
            ActivityManager.TaskSnapshot snapshot = null;
            try {
                snapshot = ActivityManager.getService().getTaskSnapshot(taskId,
                        false /* reducedResolution */);
                snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to retrieve snapshot", e);
            }
+6 −14
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public class RecentsTaskLoadPlan {
    public static class Options {
        public int runningTaskId = -1;
        public boolean loadIcons = true;
        public boolean loadThumbnails = true;
        public boolean loadThumbnails = false;
        public boolean onlyLoadForCache = false;
        public boolean onlyLoadPausedActivities = false;
        public int numVisibleTasks = 0;
@@ -189,7 +189,7 @@ public class RecentsTaskLoadPlan {
                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                    : null;
            ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                    false /* loadIfNotCached */);
                    false /* loadIfNotCached */, false /* storeInCache */);
            int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
            int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
            boolean isSystemApp = (info != null) &&
@@ -223,9 +223,7 @@ public class RecentsTaskLoadPlan {
    /**
     * Called to apply the actual loading based on the specified conditions.
     */
    public synchronized void executePlan(Options opts, RecentsTaskLoader loader,
            TaskResourceLoadQueue loadQueue) {
        RecentsConfiguration config = Recents.getConfiguration();
    public synchronized void executePlan(Options opts, RecentsTaskLoader loader) {
        Resources res = mContext.getResources();

        // Iterate through each of the tasks and load them according to the load conditions.
@@ -250,15 +248,9 @@ public class RecentsTaskLoadPlan {
                            true);
                }
            }
            if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
                if (task.thumbnail == null || isRunningTask) {
                    if (config.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
            if (opts.loadThumbnails && isVisibleThumbnail) {
                task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
                                true /* loadIfNotCached */);
                    } else if (config.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
                        loadQueue.addTask(task);
                    }
                }
                        true /* loadIfNotCached */, true /* storeInCache */);
            }
        }
    }
+34 −9
Original line number Diff line number Diff line
@@ -37,8 +37,6 @@ import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task.TaskKey;

import java.io.PrintWriter;
import java.util.Map;
@@ -245,11 +243,11 @@ public class RecentsTaskLoader {
    private final TaskResourceLoadQueue mLoadQueue;
    private final BackgroundTaskLoader mLoader;
    private final HighResThumbnailLoader mHighResThumbnailLoader;

    private final TaskKeyStrongCache<ThumbnailData> mThumbnailCache = new TaskKeyStrongCache<>();
    private final TaskKeyStrongCache<ThumbnailData> mTempCache = new TaskKeyStrongCache<>();
    private final int mMaxThumbnailCacheSize;
    private final int mMaxIconCacheSize;
    private int mNumVisibleTasksLoaded;
    private int mNumVisibleThumbnailsLoaded;

    int mDefaultTaskBarBackgroundColor;
    int mDefaultTaskViewBackgroundColor;
@@ -332,10 +330,19 @@ public class RecentsTaskLoader {
        if (opts == null) {
            throw new RuntimeException("Requires load options");
        }
        plan.executePlan(opts, this, mLoadQueue);
        if (opts.onlyLoadForCache && opts.loadThumbnails) {

            // If we are loading for the cache, we'd like to have the real cache only include the
            // visible thumbnails. However, we also don't want to reload already cached thumbnails.
            // Thus, we copy over the current entries into a second cache, and clear the real cache,
            // such that the real cache only contains visible thumbnails.
            mTempCache.copyEntries(mThumbnailCache);
            mThumbnailCache.evictAll();
        }
        plan.executePlan(opts, this);
        mTempCache.evictAll();
        if (!opts.onlyLoadForCache) {
            mNumVisibleTasksLoaded = opts.numVisibleTasks;
            mNumVisibleThumbnailsLoaded = opts.numVisibleTaskThumbnails;

            // Start the loader
            mLoader.start(context);
@@ -349,7 +356,7 @@ public class RecentsTaskLoader {
        Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
        icon = icon != null ? icon : mDefaultIcon;
        mLoadQueue.addTask(t);
        t.notifyTaskDataLoaded(null, icon);
        t.notifyTaskDataLoaded(t.thumbnail, icon);
    }

    /** Releases the task resource data back into the pool. */
@@ -404,6 +411,7 @@ public class RecentsTaskLoader {
                // The cache is small, only clear the label cache when we are critical
                mActivityLabelCache.evictAll();
                mContentDescriptionCache.evictAll();
                mThumbnailCache.evictAll();
                break;
            default:
                break;
@@ -500,15 +508,31 @@ public class RecentsTaskLoader {
    /**
     * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
     */
    ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached) {
    ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached,
            boolean storeInCache) {
        SystemServicesProxy ssp = Recents.getSystemServices();

        ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
        if (cached != null) {
            return cached;
        }

        cached = mTempCache.getAndInvalidateIfModified(taskKey);
        if (cached != null) {
            mThumbnailCache.put(taskKey, cached);
            return cached;
        }

        if (loadIfNotCached) {
            RecentsConfiguration config = Recents.getConfiguration();
            if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
                // Load the thumbnail from the system
                ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id, true /* reducedResolution */);
                ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id,
                        true /* reducedResolution */);
                if (thumbnailData.thumbnail != null) {
                    if (storeInCache) {
                        mThumbnailCache.put(taskKey, thumbnailData);
                    }
                    return thumbnailData;
                }
            }
@@ -590,5 +614,6 @@ public class RecentsTaskLoader {
        writer.print(prefix); writer.println(TAG);
        writer.print(prefix); writer.println("Icon Cache");
        mIconCache.dump(innerPrefix, writer);
        mThumbnailCache.dump(innerPrefix, writer);
    }
}
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.recents.model;

import android.util.Log;
import android.util.SparseArray;

import com.android.systemui.recents.model.Task.TaskKey;

/**
 * Base class for both strong and LRU task key cache.
 */
public abstract class TaskKeyCache<V> {

    protected static final String TAG = "TaskKeyCache";

    protected final SparseArray<TaskKey> mKeys = new SparseArray<>();

    /**
     * Gets a specific entry in the cache with the specified key, regardless of whether the cached
     * value is valid or not.
     */
    final V get(Task.TaskKey key) {
        return getCacheEntry(key.id);
    }

    /**
     * Returns the value only if the key is valid (has not been updated since the last time it was
     * in the cache)
     */
    final V getAndInvalidateIfModified(Task.TaskKey key) {
        Task.TaskKey lastKey = mKeys.get(key.id);
        if (lastKey != null) {
            if ((lastKey.stackId != key.stackId) ||
                    (lastKey.lastActiveTime != key.lastActiveTime)) {
                // The task has updated (been made active since the last time it was put into the
                // LRU cache) or the stack id for the task has changed, invalidate that cache item
                remove(key);
                return null;
            }
        }
        // Either the task does not exist in the cache, or the last active time is the same as
        // the key specified, so return what is in the cache
        return getCacheEntry(key.id);
    }

    /** Puts an entry in the cache for a specific key. */
    final void put(Task.TaskKey key, V value) {
        if (key == null || value == null) {
            Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
            return;
        }
        mKeys.put(key.id, key);
        putCacheEntry(key.id, value);
    }


    /** Removes a cache entry for a specific key. */
    final void remove(Task.TaskKey key) {
        // Remove the key after the cache value because we need it to make the callback
        removeCacheEntry(key.id);
        mKeys.remove(key.id);
    }

    /** Removes all the entries in the cache. */
    final void evictAll() {
        evictAllCache();
        mKeys.clear();
    }

    protected abstract V getCacheEntry(int id);
    protected abstract void putCacheEntry(int id, V value);
    protected abstract void removeCacheEntry(int id);
    protected abstract void evictAllCache();
}
Loading