Merge "Simplifying memory management, use Task Keys as resource cache keys."
This commit is contained in:
@@ -28,10 +28,10 @@ public class Constants {
|
||||
public static class App {
|
||||
public static final boolean EnableTaskFiltering = false;
|
||||
public static final boolean EnableTaskStackClipping = false;
|
||||
public static final boolean EnableBackgroundTaskLoading = true;
|
||||
public static final boolean ForceDisableBackgroundCache = false;
|
||||
// This disables the bitmap and icon caches to
|
||||
public static final boolean DisableBackgroundCache = false;
|
||||
|
||||
public static final boolean TaskDataLoader = false;
|
||||
public static final boolean TaskDataLoader = true;
|
||||
public static final boolean SystemUIHandshake = false;
|
||||
public static final boolean TimeSystemCalls = false;
|
||||
public static final boolean Memory = false;
|
||||
@@ -43,7 +43,7 @@ public class Constants {
|
||||
public static final boolean TouchEvents = false;
|
||||
public static final boolean MeasureAndLayout = false;
|
||||
public static final boolean Clipping = false;
|
||||
public static final boolean HwLayers = false;
|
||||
public static final boolean HwLayers = true;
|
||||
}
|
||||
|
||||
public static class TaskStack {
|
||||
@@ -74,8 +74,6 @@ public class Constants {
|
||||
public static class Animation {
|
||||
public static final int TaskRemovedReshuffleDuration = 200;
|
||||
public static final int SnapScrollBackDuration = 650;
|
||||
public static final int SwipeDismissDuration = 350;
|
||||
public static final int SwipeSnapBackDuration = 350;
|
||||
}
|
||||
|
||||
// The padding will be applied to the smallest dimension, and then applied to all sides
|
||||
|
||||
@@ -45,7 +45,6 @@ public class RecentsActivity extends Activity {
|
||||
SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
|
||||
ArrayList<TaskStack> stacks = root.getStacks();
|
||||
if (!stacks.isEmpty()) {
|
||||
// XXX: We just replace the root every time for now, we will change this in the future
|
||||
mRecentsView.setBSP(root);
|
||||
}
|
||||
|
||||
@@ -155,7 +154,7 @@ public class RecentsActivity extends Activity {
|
||||
Console.AnsiRed);
|
||||
super.onPause();
|
||||
|
||||
// Stop the loader when we leave Recents
|
||||
// Stop the loader immediately when we leave Recents
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
loader.stopLoader();
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.android.systemui.recents.model.TaskStack;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
|
||||
@@ -84,7 +83,9 @@ class TaskResourceLoader implements Runnable {
|
||||
TaskResourceLoadQueue mLoadQueue;
|
||||
DrawableLruCache mIconCache;
|
||||
BitmapLruCache mThumbnailCache;
|
||||
|
||||
boolean mCancelled;
|
||||
boolean mWaitingOnLoadQueue;
|
||||
|
||||
/** Constructor, creates a new loading thread that loads task resources in the background */
|
||||
public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache,
|
||||
@@ -116,6 +117,11 @@ class TaskResourceLoader implements Runnable {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]");
|
||||
// Mark as cancelled for the thread to pick up
|
||||
mCancelled = true;
|
||||
// If we are waiting for the load queue for more tasks, then we can just reset the
|
||||
// Context now, since nothing is using it
|
||||
if (mWaitingOnLoadQueue) {
|
||||
mContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,6 +130,8 @@ class TaskResourceLoader implements Runnable {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
|
||||
if (mCancelled) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
|
||||
// We have to unset the context here, since the background thread may be using it
|
||||
// when we call stop()
|
||||
mContext = null;
|
||||
@@ -142,50 +150,52 @@ class TaskResourceLoader implements Runnable {
|
||||
final Task t = mLoadQueue.nextTask();
|
||||
if (t != null) {
|
||||
try {
|
||||
Drawable cachedIcon = mIconCache.get(t);
|
||||
Bitmap cachedThumbnail = mThumbnailCache.get(t);
|
||||
Drawable loadIcon = mIconCache.get(t.key);
|
||||
Bitmap loadThumbnail = mThumbnailCache.get(t.key);
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
" [TaskResourceLoader|load]",
|
||||
t + " icon: " + cachedIcon + " thumbnail: " + cachedThumbnail);
|
||||
t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail);
|
||||
// Load the icon
|
||||
if (cachedIcon == null) {
|
||||
if (loadIcon == null) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ActivityInfo info = pm.getActivityInfo(t.intent.getComponent(),
|
||||
ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
|
||||
PackageManager.GET_META_DATA);
|
||||
Drawable icon = info.loadIcon(pm);
|
||||
if (!mCancelled) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
" [TaskResourceLoader|loadIcon]",
|
||||
icon);
|
||||
t.icon = icon;
|
||||
mIconCache.put(t, icon);
|
||||
loadIcon = icon;
|
||||
mIconCache.put(t.key, icon);
|
||||
}
|
||||
}
|
||||
// Load the thumbnail
|
||||
if (cachedThumbnail == null) {
|
||||
if (loadThumbnail == null) {
|
||||
ActivityManager am = (ActivityManager)
|
||||
mContext.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
Bitmap thumbnail = am.getTaskTopThumbnail(t.id);
|
||||
Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
|
||||
if (!mCancelled) {
|
||||
if (thumbnail != null) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
" [TaskResourceLoader|loadThumbnail]",
|
||||
thumbnail);
|
||||
t.thumbnail = thumbnail;
|
||||
mThumbnailCache.put(t, thumbnail);
|
||||
loadThumbnail = thumbnail;
|
||||
mThumbnailCache.put(t.key, thumbnail);
|
||||
} else {
|
||||
Console.logError(mContext,
|
||||
"Failed to load task top thumbnail for: " +
|
||||
t.intent.getComponent().getPackageName());
|
||||
t.key.intent.getComponent().getPackageName());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mCancelled) {
|
||||
// Notify that the task data has changed
|
||||
final Drawable newIcon = loadIcon;
|
||||
final Bitmap newThumbnail = loadThumbnail;
|
||||
mMainThreadHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
t.notifyTaskDataChanged();
|
||||
t.notifyTaskDataLoaded(newThumbnail, newIcon);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -200,7 +210,9 @@ class TaskResourceLoader implements Runnable {
|
||||
try {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[TaskResourceLoader|waitOnLoadQueue]");
|
||||
mWaitingOnLoadQueue = true;
|
||||
mLoadQueue.wait();
|
||||
mWaitingOnLoadQueue = false;
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
@@ -211,14 +223,17 @@ class TaskResourceLoader implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/** The drawable cache */
|
||||
class DrawableLruCache extends LruCache<Task, Drawable> {
|
||||
/**
|
||||
* The drawable cache. By using the Task's key, we can prevent holding onto a reference to the Task
|
||||
* resource data, while keeping the cache data in memory where necessary.
|
||||
*/
|
||||
class DrawableLruCache extends LruCache<Task.TaskKey, Drawable> {
|
||||
public DrawableLruCache(int cacheSize) {
|
||||
super(cacheSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int sizeOf(Task t, Drawable d) {
|
||||
protected int sizeOf(Task.TaskKey t, Drawable d) {
|
||||
// The cache size will be measured in kilobytes rather than number of items
|
||||
// NOTE: this isn't actually correct, as the icon may be smaller
|
||||
int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
|
||||
@@ -226,14 +241,17 @@ class DrawableLruCache extends LruCache<Task, Drawable> {
|
||||
}
|
||||
}
|
||||
|
||||
/** The bitmap cache */
|
||||
class BitmapLruCache extends LruCache<Task, Bitmap> {
|
||||
/**
|
||||
* The bitmap cache. By using the Task's key, we can prevent holding onto a reference to the Task
|
||||
* resource data, while keeping the cache data in memory where necessary.
|
||||
*/
|
||||
class BitmapLruCache extends LruCache<Task.TaskKey, Bitmap> {
|
||||
public BitmapLruCache(int cacheSize) {
|
||||
super(cacheSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int sizeOf(Task t, Bitmap bitmap) {
|
||||
protected int sizeOf(Task.TaskKey t, Bitmap bitmap) {
|
||||
// The cache size will be measured in kilobytes rather than number of items
|
||||
return bitmap.getAllocationByteCount() / 1024;
|
||||
}
|
||||
@@ -257,16 +275,18 @@ public class RecentsTaskLoader {
|
||||
|
||||
/** Private Constructor */
|
||||
private RecentsTaskLoader(Context context) {
|
||||
// Calculate the cache sizes
|
||||
// Calculate the cache sizes, we just use a reasonable number here similar to those
|
||||
// suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
|
||||
// for icons.
|
||||
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
mMaxThumbnailCacheSize = maxMemory / 8;
|
||||
mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
|
||||
int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
|
||||
int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
|
||||
mMaxIconCacheSize;
|
||||
int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
|
||||
int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
|
||||
mMaxThumbnailCacheSize;
|
||||
|
||||
Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
|
||||
" iconCache: " + iconCacheSize);
|
||||
|
||||
@@ -351,36 +371,34 @@ public class RecentsTaskLoader {
|
||||
ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
|
||||
PackageManager.GET_META_DATA);
|
||||
String title = info.loadLabel(pm).toString();
|
||||
Task task;
|
||||
boolean isForemostTask = (i == (taskCount - 1));
|
||||
|
||||
// Preload the specified number of apps
|
||||
if (i >= (taskCount - preloadCount) ||
|
||||
!Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
if (i >= (taskCount - preloadCount)) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|preloadTask]",
|
||||
"i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
|
||||
|
||||
task = new Task(t.persistentId, t.baseIntent, title, null, null);
|
||||
Task task = new Task(t.persistentId, t.baseIntent, title);
|
||||
|
||||
// Load the icon (if possible from the cache)
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
task.icon = mIconCache.get(task);
|
||||
// Load the icon (if possible and not the foremost task, from the cache)
|
||||
if (!isForemostTask) {
|
||||
task.icon = mIconCache.get(task.key);
|
||||
}
|
||||
if (task.icon == null) {
|
||||
task.icon = info.loadIcon(pm);
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
mIconCache.put(task, task.icon);
|
||||
}
|
||||
mIconCache.put(task.key, task.icon);
|
||||
}
|
||||
|
||||
// Load the thumbnail (if possible from the cache)
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
task.thumbnail = mThumbnailCache.get(task);
|
||||
// Load the thumbnail (if possible and not the foremost task, from the cache)
|
||||
if (!isForemostTask) {
|
||||
task.thumbnail = mThumbnailCache.get(task.key);
|
||||
}
|
||||
if (task.thumbnail == null) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|loadingTaskThumbnail]");
|
||||
task.thumbnail = am.getTaskTopThumbnail(t.id);
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
mThumbnailCache.put(task, task.thumbnail);
|
||||
}
|
||||
mThumbnailCache.put(task.key, task.thumbnail);
|
||||
}
|
||||
|
||||
// Create as many tasks a we want to multiply by
|
||||
@@ -394,19 +412,9 @@ public class RecentsTaskLoader {
|
||||
for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
" [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
|
||||
task = new Task(t.persistentId, t.baseIntent, title, null, null);
|
||||
stack.addTask(task);
|
||||
stack.addTask(new Task(t.persistentId, t.baseIntent, title));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (stacks.containsKey(t.stackId)) {
|
||||
builder = stacks.get(t.stackId);
|
||||
} else {
|
||||
builder = new TaskStackBuilder();
|
||||
stacks.put(t.stackId, builder);
|
||||
}
|
||||
*/
|
||||
}
|
||||
Console.log(Constants.DebugFlags.App.TimeSystemCalls,
|
||||
"[RecentsTaskLoader|getAllTaskTopThumbnail]",
|
||||
@@ -428,64 +436,59 @@ public class RecentsTaskLoader {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Start the task loader
|
||||
mLoader.start(context);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/** Acquires the task resource data from the pool. */
|
||||
public void loadTaskData(Task t) {
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
Drawable icon = mIconCache.get(t);
|
||||
Bitmap thumbnail = mThumbnailCache.get(t);
|
||||
Drawable icon = mIconCache.get(t.key);
|
||||
Bitmap thumbnail = mThumbnailCache.get(t.key);
|
||||
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
|
||||
t + " icon: " + icon + " thumbnail: " + thumbnail +
|
||||
" thumbnailCacheSize: " + mThumbnailCache.size());
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
|
||||
t + " icon: " + icon + " thumbnail: " + thumbnail +
|
||||
" thumbnailCacheSize: " + mThumbnailCache.size());
|
||||
|
||||
boolean requiresLoad = false;
|
||||
if (icon == null) {
|
||||
icon = mDefaultIcon;
|
||||
requiresLoad = true;
|
||||
}
|
||||
if (thumbnail == null) {
|
||||
thumbnail = mDefaultThumbnail;
|
||||
requiresLoad = true;
|
||||
}
|
||||
if (requiresLoad) {
|
||||
mLoadQueue.addTask(t);
|
||||
}
|
||||
t.notifyTaskLoaded(thumbnail, icon);
|
||||
boolean requiresLoad = false;
|
||||
if (icon == null) {
|
||||
icon = mDefaultIcon;
|
||||
requiresLoad = true;
|
||||
}
|
||||
if (thumbnail == null) {
|
||||
thumbnail = mDefaultThumbnail;
|
||||
requiresLoad = true;
|
||||
}
|
||||
if (requiresLoad) {
|
||||
mLoadQueue.addTask(t);
|
||||
}
|
||||
t.notifyTaskDataLoaded(thumbnail, icon);
|
||||
}
|
||||
|
||||
/** Releases the task resource data back into the pool. */
|
||||
public void unloadTaskData(Task t) {
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|unloadTask]", t +
|
||||
" thumbnailCacheSize: " + mThumbnailCache.size());
|
||||
mLoadQueue.removeTask(t);
|
||||
t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
|
||||
} else {
|
||||
t.notifyTaskUnloaded(null, null);
|
||||
}
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|unloadTask]", t +
|
||||
" thumbnailCacheSize: " + mThumbnailCache.size());
|
||||
|
||||
mLoadQueue.removeTask(t);
|
||||
t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
|
||||
}
|
||||
|
||||
/** Completely removes the resource data from the pool. */
|
||||
public void deleteTaskData(Task t) {
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|deleteTask]", t);
|
||||
mLoadQueue.removeTask(t);
|
||||
mThumbnailCache.remove(t);
|
||||
mIconCache.remove(t);
|
||||
t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
|
||||
} else {
|
||||
t.notifyTaskUnloaded(null, null);
|
||||
}
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|deleteTask]", t);
|
||||
|
||||
mLoadQueue.removeTask(t);
|
||||
mThumbnailCache.remove(t.key);
|
||||
mIconCache.remove(t.key);
|
||||
t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon);
|
||||
}
|
||||
|
||||
/** Stops the task loader */
|
||||
/** Stops the task loader and clears all pending tasks */
|
||||
void stopLoader() {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
|
||||
mLoader.stop();
|
||||
@@ -496,46 +499,30 @@ public class RecentsTaskLoader {
|
||||
Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
|
||||
Console.trimMemoryLevelToString(level));
|
||||
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
// If we are hidden, then we should unload each of the task keys
|
||||
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
|
||||
Console.log(Constants.DebugFlags.App.Memory, "[RecentsTaskLoader|unloadTasks]"
|
||||
);
|
||||
// Unload each of the keys in the thumbnail cache
|
||||
Map<Task, Bitmap> thumbnailCache = mThumbnailCache.snapshot();
|
||||
for (Task t : thumbnailCache.keySet()) {
|
||||
unloadTaskData(t);
|
||||
}
|
||||
// As well as the keys in the icon cache
|
||||
Map<Task, Drawable> iconCache = mIconCache.snapshot();
|
||||
for (Task t : iconCache.keySet()) {
|
||||
unloadTaskData(t);
|
||||
}
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
|
||||
// We are leaving recents, so trim the data a bit
|
||||
mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
|
||||
mIconCache.trimToSize(mMaxIconCacheSize / 2);
|
||||
break;
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
|
||||
// We are going to be low on memory
|
||||
mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
|
||||
mIconCache.trimToSize(mMaxIconCacheSize / 4);
|
||||
break;
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
|
||||
// We are low on memory, so release everything
|
||||
mThumbnailCache.evictAll();
|
||||
mIconCache.evictAll();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (level) {
|
||||
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
|
||||
// Do nothing
|
||||
break;
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
|
||||
// We are leaving recents, so trim the data a bit
|
||||
mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2);
|
||||
mIconCache.trimToSize(mMaxIconCacheSize / 2);
|
||||
break;
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
|
||||
// We are going to be low on memory
|
||||
mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4);
|
||||
mIconCache.trimToSize(mMaxIconCacheSize / 4);
|
||||
break;
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
|
||||
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
|
||||
// We are low on memory, so release everything
|
||||
mThumbnailCache.evictAll();
|
||||
mIconCache.evictAll();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.recents.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -26,6 +27,14 @@ import java.util.ArrayList;
|
||||
* stacks should be placed.
|
||||
*/
|
||||
public class SpaceNode {
|
||||
/* BSP node callbacks */
|
||||
public interface SpaceNodeCallbacks {
|
||||
/** Notifies when a node is added */
|
||||
public void onSpaceNodeAdded(SpaceNode node);
|
||||
/** Notifies when a node is measured */
|
||||
public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
|
||||
}
|
||||
|
||||
Context mContext;
|
||||
|
||||
SpaceNode mStartNode;
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.graphics.Rect;
|
||||
|
||||
|
||||
/* BSP node callbacks */
|
||||
public interface SpaceNodeCallbacks {
|
||||
/** Notifies when a node is added */
|
||||
public void onSpaceNodeAdded(SpaceNode node);
|
||||
/** Notifies when a node is measured */
|
||||
public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
|
||||
}
|
||||
@@ -26,17 +26,53 @@ import com.android.systemui.recents.Constants;
|
||||
* A task represents the top most task in the system's task stack.
|
||||
*/
|
||||
public class Task {
|
||||
public final int id;
|
||||
public final Intent intent;
|
||||
/* Task callbacks */
|
||||
public interface TaskCallbacks {
|
||||
/* Notifies when a task has been bound */
|
||||
public void onTaskDataLoaded();
|
||||
/* Notifies when a task has been unbound */
|
||||
public void onTaskDataUnloaded();
|
||||
}
|
||||
|
||||
/* The Task Key represents the unique primary key for the task */
|
||||
public static class TaskKey {
|
||||
public final int id;
|
||||
public final Intent intent;
|
||||
|
||||
public TaskKey(int id, Intent intent) {
|
||||
this.id = id;
|
||||
this.intent = intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return hashCode() == o.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Task.Key: " + id + ", " + intent.getComponent().getPackageName();
|
||||
}
|
||||
}
|
||||
|
||||
public TaskKey key;
|
||||
public String title;
|
||||
public Drawable icon;
|
||||
public Bitmap thumbnail;
|
||||
|
||||
TaskCallbacks mCb;
|
||||
|
||||
public Task(int id, Intent intent, String activityTitle) {
|
||||
this(id, intent, activityTitle, null, null);
|
||||
}
|
||||
|
||||
public Task(int id, Intent intent, String activityTitle, Drawable icon, Bitmap thumbnail) {
|
||||
this.id = id;
|
||||
this.intent = intent;
|
||||
this.key = new TaskKey(id, intent);
|
||||
this.title = activityTitle;
|
||||
this.icon = icon;
|
||||
this.thumbnail = thumbnail;
|
||||
@@ -47,28 +83,21 @@ public class Task {
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
/** Notifies the callback listeners that this task's data has changed */
|
||||
public void notifyTaskDataChanged() {
|
||||
if (mCb != null) {
|
||||
mCb.onTaskDataChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** Notifies the callback listeners that this task has been loaded */
|
||||
public void notifyTaskLoaded(Bitmap thumbnail, Drawable icon) {
|
||||
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon) {
|
||||
this.icon = icon;
|
||||
this.thumbnail = thumbnail;
|
||||
if (mCb != null) {
|
||||
mCb.onTaskBound();
|
||||
mCb.onTaskDataLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
/** Notifies the callback listeners that this task has been unloaded */
|
||||
public void notifyTaskUnloaded(Bitmap defaultThumbnail, Drawable defaultIcon) {
|
||||
public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultIcon) {
|
||||
icon = defaultIcon;
|
||||
thumbnail = defaultThumbnail;
|
||||
if (mCb != null) {
|
||||
mCb.onTaskUnbound();
|
||||
mCb.onTaskDataUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +112,11 @@ public class Task {
|
||||
// Otherwise, check that the id and intent match (the other fields can be asynchronously
|
||||
// loaded and is unsuitable to testing the identity of this Task)
|
||||
Task t = (Task) o;
|
||||
return (id == t.id) &&
|
||||
(intent.equals(t.intent));
|
||||
return key.equals(t.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Task: " + intent.getComponent().getPackageName() + " [" + super.toString() + "]";
|
||||
return "Task: " + key.intent.getComponent().getPackageName() + " [" + super.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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;
|
||||
|
||||
/* Task callbacks */
|
||||
public interface TaskCallbacks {
|
||||
/* Notifies when a task's data has been updated */
|
||||
public void onTaskDataChanged(Task task);
|
||||
/* Notifies when a task has been bound */
|
||||
public void onTaskBound();
|
||||
/* Notifies when a task has been unbound */
|
||||
public void onTaskUnbound();
|
||||
}
|
||||
@@ -119,6 +119,18 @@ class FilteredTaskList {
|
||||
* The task stack contains a list of multiple tasks.
|
||||
*/
|
||||
public class TaskStack {
|
||||
/* Task stack callbacks */
|
||||
public interface TaskStackCallbacks {
|
||||
/* Notifies when a task has been added to the stack */
|
||||
public void onStackTaskAdded(TaskStack stack, Task t);
|
||||
/* Notifies when a task has been removed from the stack */
|
||||
public void onStackTaskRemoved(TaskStack stack, Task t);
|
||||
/** Notifies when the stack was filtered */
|
||||
public void onStackFiltered(TaskStack stack);
|
||||
/** Notifies when the stack was un-filtered */
|
||||
public void onStackUnfiltered(TaskStack stack);
|
||||
}
|
||||
|
||||
Context mContext;
|
||||
|
||||
FilteredTaskList mTaskList = new FilteredTaskList();
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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;
|
||||
|
||||
/* Task stack callbacks */
|
||||
public interface TaskStackCallbacks {
|
||||
/* Notifies when a task has been added to the stack */
|
||||
public void onStackTaskAdded(TaskStack stack, Task t);
|
||||
/* Notifies when a task has been removed from the stack */
|
||||
public void onStackTaskRemoved(TaskStack stack, Task t);
|
||||
/** Notifies when the stack was filtered */
|
||||
public void onStackFiltered(TaskStack stack);
|
||||
/** Notifies when the stack was un-filtered */
|
||||
public void onStackUnfiltered(TaskStack stack);
|
||||
}
|
||||
@@ -39,7 +39,7 @@ import java.util.ArrayList;
|
||||
* This view is the the top level layout that contains TaskStacks (which are laid out according
|
||||
* to their SpaceNode bounds.
|
||||
*/
|
||||
public class RecentsView extends FrameLayout implements TaskStackViewCallbacks {
|
||||
public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
|
||||
// The space partitioning root of this container
|
||||
SpaceNode mBSP;
|
||||
|
||||
@@ -52,8 +52,7 @@ public class RecentsView extends FrameLayout implements TaskStackViewCallbacks {
|
||||
public void setBSP(SpaceNode n) {
|
||||
mBSP = n;
|
||||
|
||||
// XXX: We shouldn't be recereating new stacks every time, but for now, that is OK
|
||||
// Add all the stacks for this partition
|
||||
// Create and add all the stacks for this partition of space.
|
||||
removeAllViews();
|
||||
ArrayList<TaskStack> stacks = mBSP.getStacks();
|
||||
for (TaskStack stack : stacks) {
|
||||
@@ -206,7 +205,7 @@ public class RecentsView extends FrameLayout implements TaskStackViewCallbacks {
|
||||
}
|
||||
|
||||
// Launch the activity with the desired animation
|
||||
Intent i = new Intent(task.intent);
|
||||
Intent i = new Intent(task.key.intent);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
||||
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
|
||||
| Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
@@ -33,7 +33,6 @@ import android.view.ViewConfiguration;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.OverScroller;
|
||||
import android.widget.Toast;
|
||||
import com.android.systemui.recents.Console;
|
||||
import com.android.systemui.recents.Constants;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
@@ -41,18 +40,20 @@ import com.android.systemui.recents.RecentsTaskLoader;
|
||||
import com.android.systemui.recents.Utilities;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
import com.android.systemui.recents.model.TaskStack;
|
||||
import com.android.systemui.recents.model.TaskStackCallbacks;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** The TaskView callbacks */
|
||||
interface TaskStackViewCallbacks {
|
||||
public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t);
|
||||
}
|
||||
|
||||
/* The visual representation of a task stack view */
|
||||
public class TaskStackView extends FrameLayout implements TaskStackCallbacks, TaskViewCallbacks,
|
||||
ViewPoolConsumer<TaskView, Task>, View.OnClickListener {
|
||||
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
|
||||
TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
|
||||
View.OnClickListener {
|
||||
|
||||
/** The TaskView callbacks */
|
||||
interface TaskStackViewCallbacks {
|
||||
public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t);
|
||||
}
|
||||
|
||||
TaskStack mStack;
|
||||
TaskStackViewTouchHandler mTouchHandler;
|
||||
TaskStackViewCallbacks mCb;
|
||||
@@ -348,7 +349,8 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
tv.disableHwLayers();
|
||||
}
|
||||
} else if (mHwLayersRefCount < 0) {
|
||||
throw new RuntimeException("Invalid hw layers ref count");
|
||||
new Throwable("Invalid hw layers ref count").printStackTrace();
|
||||
Console.logError(getContext(), "Invalid HW layers ref count");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +600,7 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
// Setup and attach the view to the window
|
||||
Task task = prepareData;
|
||||
// We try and rebind the task (this MUST be done before the task filled)
|
||||
tv.bindToTask(task, this);
|
||||
tv.onTaskBound(task);
|
||||
// Request that this tasks's data be filled
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
loader.loadTaskData(task);
|
||||
@@ -619,7 +621,10 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
"" + insertIndex);
|
||||
if (isNewView) {
|
||||
addView(tv, insertIndex);
|
||||
|
||||
// Set the callbacks and listeners for this new view
|
||||
tv.setOnClickListener(this);
|
||||
tv.setCallbacks(this);
|
||||
} else {
|
||||
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
|
||||
}
|
||||
@@ -649,7 +654,7 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
mStack.filterTasks(tv.getTask());
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Task Filtering TBD", Toast.LENGTH_SHORT).show();
|
||||
Console.logError(getContext(), "Task Filtering TBD");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -995,7 +1000,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
final ActivityManager am = (ActivityManager)
|
||||
activity.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am != null) {
|
||||
am.removeTask(tv.getTask().id,
|
||||
am.removeTask(tv.getTask().key.id,
|
||||
ActivityManager.REMOVE_TASK_KILL_PROCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,13 +39,7 @@ import com.android.systemui.recents.Console;
|
||||
import com.android.systemui.recents.Constants;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
import com.android.systemui.recents.model.TaskCallbacks;
|
||||
|
||||
/** The TaskView callbacks */
|
||||
interface TaskViewCallbacks {
|
||||
public void onTaskIconClicked(TaskView tv);
|
||||
// public void onTaskViewReboundToTask(TaskView tv, Task t);
|
||||
}
|
||||
|
||||
/** The task thumbnail view */
|
||||
class TaskThumbnailView extends ImageView {
|
||||
@@ -66,7 +60,7 @@ class TaskThumbnailView extends ImageView {
|
||||
// Update the bar color
|
||||
if (Constants.Values.TaskView.DrawColoredTaskBars) {
|
||||
int[] colors = {0xFFCC0C39, 0xFFE6781E, 0xFFC8CF02, 0xFF1693A7};
|
||||
mBarColor = colors[mTask.intent.getComponent().getPackageName().length() % colors.length];
|
||||
mBarColor = colors[mTask.key.intent.getComponent().getPackageName().length() % colors.length];
|
||||
}
|
||||
|
||||
setImageBitmap(t.thumbnail);
|
||||
@@ -213,7 +207,13 @@ class TaskIconView extends ImageView {
|
||||
}
|
||||
|
||||
/* A task view */
|
||||
public class TaskView extends FrameLayout implements View.OnClickListener, TaskCallbacks {
|
||||
public class TaskView extends FrameLayout implements View.OnClickListener, Task.TaskCallbacks {
|
||||
/** The TaskView callbacks */
|
||||
interface TaskViewCallbacks {
|
||||
public void onTaskIconClicked(TaskView tv);
|
||||
// public void onTaskViewReboundToTask(TaskView tv, Task t);
|
||||
}
|
||||
|
||||
Task mTask;
|
||||
TaskThumbnailView mThumbnailView;
|
||||
TaskIconView mIconView;
|
||||
@@ -247,26 +247,11 @@ public class TaskView extends FrameLayout implements View.OnClickListener, TaskC
|
||||
((LayoutParams) mIconView.getLayoutParams()).rightMargin = offset;
|
||||
}
|
||||
|
||||
/** Set the task and callback */
|
||||
void bindToTask(Task t, TaskViewCallbacks cb) {
|
||||
mTask = t;
|
||||
mTask.setCallbacks(this);
|
||||
/** Set callback */
|
||||
void setCallbacks(TaskViewCallbacks cb) {
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
/** Actually synchronizes the model data into the views */
|
||||
private void syncToTask() {
|
||||
mThumbnailView.rebindToTask(mTask, false);
|
||||
mIconView.rebindToTask(mTask, false);
|
||||
}
|
||||
|
||||
/** Unset the task and callback */
|
||||
private void unbindFromTask() {
|
||||
mTask.setCallbacks(null);
|
||||
mThumbnailView.unbindFromTask();
|
||||
mIconView.unbindFromTask();
|
||||
}
|
||||
|
||||
/** Gets the task */
|
||||
Task getTask() {
|
||||
return mTask;
|
||||
@@ -367,26 +352,25 @@ public class TaskView extends FrameLayout implements View.OnClickListener, TaskC
|
||||
|
||||
/**** TaskCallbacks Implementation ****/
|
||||
|
||||
@Override
|
||||
public void onTaskDataChanged(Task task) {
|
||||
Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
|
||||
"[TaskView|onTaskDataChanged]", task);
|
||||
|
||||
// Only update this task view if the changed task is the same as the task for this view
|
||||
if (mTask == task) {
|
||||
mThumbnailView.rebindToTask(mTask, true);
|
||||
mIconView.rebindToTask(mTask, true);
|
||||
}
|
||||
/** Binds this task view to the task */
|
||||
public void onTaskBound(Task t) {
|
||||
mTask = t;
|
||||
mTask.setCallbacks(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskBound() {
|
||||
syncToTask();
|
||||
public void onTaskDataLoaded() {
|
||||
// Bind each of the views to the new task data
|
||||
mThumbnailView.rebindToTask(mTask, false);
|
||||
mIconView.rebindToTask(mTask, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUnbound() {
|
||||
unbindFromTask();
|
||||
public void onTaskDataUnloaded() {
|
||||
// Unbind each of the views from the task data and remove the task callback
|
||||
mTask.setCallbacks(null);
|
||||
mThumbnailView.unbindFromTask();
|
||||
mIconView.unbindFromTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,15 @@ import java.util.LinkedList;
|
||||
|
||||
/* A view pool to manage more views than we can visibly handle */
|
||||
public class ViewPool<V, T> {
|
||||
|
||||
/* An interface to the consumer of a view pool */
|
||||
public interface ViewPoolConsumer<V, T> {
|
||||
public V createView(Context context);
|
||||
public void prepareViewToEnterPool(V v);
|
||||
public void prepareViewToLeavePool(V v, T prepareData, boolean isNewView);
|
||||
public boolean hasPreferredData(V v, T preferredData);
|
||||
}
|
||||
|
||||
Context mContext;
|
||||
ViewPoolConsumer<V, T> mViewCreator;
|
||||
LinkedList<V> mPool = new LinkedList<V>();
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.views;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
||||
/* An interface to the consumer of a view pool */
|
||||
public interface ViewPoolConsumer<V, T> {
|
||||
public V createView(Context context);
|
||||
public void prepareViewToEnterPool(V v);
|
||||
public void prepareViewToLeavePool(V v, T prepareData, boolean isNewView);
|
||||
public boolean hasPreferredData(V v, T preferredData);
|
||||
}
|
||||
Reference in New Issue
Block a user