Merge "Simplifying memory management, use Task Keys as resource cache keys."

This commit is contained in:
Winson Chung
2014-03-17 01:54:04 +00:00
committed by Android (Google) Code Review
14 changed files with 241 additions and 323 deletions

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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() + "]";
}
}

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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>();

View File

@@ -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);
}