Merge "Fixing memory leaks related to Tasks holding onto their callbacks."
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.recents;
|
||||
|
||||
|
||||
import android.content.ComponentCallbacks2;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
@@ -36,20 +37,20 @@ public class Console {
|
||||
|
||||
/** Logs a key */
|
||||
public static void log(String key) {
|
||||
Console.log(true, key, "", AnsiReset);
|
||||
log(true, key, "", AnsiReset);
|
||||
}
|
||||
|
||||
/** Logs a conditioned key */
|
||||
public static void log(boolean condition, String key) {
|
||||
if (condition) {
|
||||
Console.log(condition, key, "", AnsiReset);
|
||||
log(condition, key, "", AnsiReset);
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs a key in a specific color */
|
||||
public static void log(boolean condition, String key, Object data) {
|
||||
if (condition) {
|
||||
Console.log(condition, key, data, AnsiReset);
|
||||
log(condition, key, data, AnsiReset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +75,50 @@ public class Console {
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs a stack trace */
|
||||
public static void logStackTrace() {
|
||||
logStackTrace("", 99);
|
||||
}
|
||||
|
||||
/** Logs a stack trace to a certain depth */
|
||||
public static void logStackTrace(int depth) {
|
||||
logStackTrace("", depth);
|
||||
}
|
||||
|
||||
/** Logs a stack trace to a certain depth with a key */
|
||||
public static void logStackTrace(String key, int depth) {
|
||||
int offset = 0;
|
||||
StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
|
||||
String tinyStackTrace = "";
|
||||
// Skip over the known stack trace classes
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
StackTraceElement el = callStack[i];
|
||||
String className = el.getClassName();
|
||||
if (className.indexOf("dalvik.system.VMStack") == -1 &&
|
||||
className.indexOf("java.lang.Thread") == -1 &&
|
||||
className.indexOf("recents.Console") == -1) {
|
||||
break;
|
||||
} else {
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
// Build the pretty stack trace
|
||||
int start = Math.min(offset + depth, callStack.length);
|
||||
int end = offset;
|
||||
String indent = "";
|
||||
for (int i = start - 1; i >= end; i--) {
|
||||
StackTraceElement el = callStack[i];
|
||||
tinyStackTrace += indent + " -> " + el.getClassName() +
|
||||
"[" + el.getLineNumber() + "]." + el.getMethodName();
|
||||
if (i > end) {
|
||||
tinyStackTrace += "\n";
|
||||
indent += " ";
|
||||
}
|
||||
}
|
||||
log(true, key, tinyStackTrace, AnsiRed);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the stringified MotionEvent action */
|
||||
public static String motionEventActionToString(int action) {
|
||||
switch (action) {
|
||||
@@ -93,4 +138,25 @@ public class Console {
|
||||
return "" + action;
|
||||
}
|
||||
}
|
||||
|
||||
public static String trimMemoryLevelToString(int level) {
|
||||
switch (level) {
|
||||
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
|
||||
return "UI Hidden";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
|
||||
return "Running Moderate";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
|
||||
return "Background";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
|
||||
return "Running Low";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
|
||||
return "Moderate";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
|
||||
return "Critical";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
|
||||
return "Complete";
|
||||
default:
|
||||
return "" + level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,11 @@ public class Constants {
|
||||
public static final boolean EnableTaskStackClipping = false;
|
||||
public static final boolean EnableBackgroundTaskLoading = true;
|
||||
public static final boolean ForceDisableBackgroundCache = false;
|
||||
|
||||
public static final boolean TaskDataLoader = false;
|
||||
public static final boolean SystemUIHandshake = false;
|
||||
public static final boolean TimeSystemCalls = false;
|
||||
public static final boolean Memory = false;
|
||||
}
|
||||
|
||||
public static class UI {
|
||||
@@ -41,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 = true;
|
||||
public static final boolean HwLayers = false;
|
||||
}
|
||||
|
||||
public static class TaskStack {
|
||||
@@ -55,13 +57,16 @@ public class Constants {
|
||||
|
||||
public static class Values {
|
||||
public static class Window {
|
||||
// The dark background dim is set behind the empty recents view
|
||||
public static final float DarkBackgroundDim = 0.5f;
|
||||
// The background dim is set behind the card stack
|
||||
public static final float BackgroundDim = 0.35f;
|
||||
}
|
||||
|
||||
public static class RecentsTaskLoader {
|
||||
// XXX: This should be calculated on the first load
|
||||
public static final int PreloadFirstTasksCount = 5;
|
||||
// For debugging, this allows us to multiply the number of cards for each task
|
||||
public static final int TaskEntryMultiplier = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import com.android.systemui.recent.RecentTasksLoader;
|
||||
import com.android.systemui.recents.model.SpaceNode;
|
||||
import com.android.systemui.recents.model.TaskStack;
|
||||
import com.android.systemui.recents.views.RecentsView;
|
||||
@@ -167,6 +168,14 @@ public class RecentsActivity extends Activity {
|
||||
mVisible = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
if (loader != null) {
|
||||
loader.onTrimMemory(level);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!mRecentsView.unfilterFilteredStacks()) {
|
||||
|
||||
@@ -30,7 +30,6 @@ public class RecentsConfiguration {
|
||||
|
||||
DisplayMetrics mDisplayMetrics;
|
||||
|
||||
public boolean layoutVerticalStack;
|
||||
public Rect systemInsets = new Rect();
|
||||
|
||||
/** Private constructor */
|
||||
@@ -56,7 +55,6 @@ public class RecentsConfiguration {
|
||||
|
||||
boolean isPortrait = context.getResources().getConfiguration().orientation ==
|
||||
Configuration.ORIENTATION_PORTRAIT;
|
||||
layoutVerticalStack = isPortrait || Constants.LANDSCAPE_LAYOUT_VERTICAL_STACK;
|
||||
}
|
||||
|
||||
public void updateSystemInsets(Rect insets) {
|
||||
|
||||
@@ -57,8 +57,9 @@ public class RecentsService extends Service {
|
||||
tsv.computeRects(windowRect.width(), windowRect.height() - systemInsets.top);
|
||||
tsv.boundScroll();
|
||||
TaskViewTransform transform = tsv.getStackTransform(0);
|
||||
Rect taskRect = new Rect(transform.rect);
|
||||
|
||||
data.putParcelable("taskRect", transform.rect);
|
||||
data.putParcelable("taskRect", taskRect);
|
||||
Message reply = Message.obtain(null, MSG_UPDATE_RECENTS_FOR_CONFIGURATION, 0, 0);
|
||||
reply.setData(data);
|
||||
msg.replyTo.send(reply);
|
||||
@@ -100,4 +101,12 @@ public class RecentsService extends Service {
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsService|onDestroy]");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
if (loader != null) {
|
||||
loader.onTrimMemory(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.recents;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ComponentCallbacks2;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -35,6 +36,7 @@ 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;
|
||||
|
||||
|
||||
@@ -233,7 +235,7 @@ class BitmapLruCache extends LruCache<Task, Bitmap> {
|
||||
@Override
|
||||
protected int sizeOf(Task t, Bitmap bitmap) {
|
||||
// The cache size will be measured in kilobytes rather than number of items
|
||||
return bitmap.getByteCount() / 1024;
|
||||
return bitmap.getAllocationByteCount() / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,17 +249,28 @@ public class RecentsTaskLoader {
|
||||
TaskResourceLoadQueue mLoadQueue;
|
||||
TaskResourceLoader mLoader;
|
||||
|
||||
int mMaxThumbnailCacheSize;
|
||||
int mMaxIconCacheSize;
|
||||
|
||||
BitmapDrawable mDefaultIcon;
|
||||
Bitmap mDefaultThumbnail;
|
||||
|
||||
/** Private Constructor */
|
||||
private RecentsTaskLoader(Context context) {
|
||||
// Calculate the cache sizes
|
||||
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 : maxMemory / 16;
|
||||
int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 : maxMemory / 8;
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake,
|
||||
mMaxThumbnailCacheSize = maxMemory / 8;
|
||||
mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
|
||||
int iconCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
|
||||
mMaxIconCacheSize;
|
||||
int thumbnailCacheSize = Constants.DebugFlags.App.ForceDisableBackgroundCache ? 1 :
|
||||
mMaxThumbnailCacheSize;
|
||||
|
||||
Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
|
||||
"[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
|
||||
" iconCache: " + iconCacheSize);
|
||||
|
||||
// Initialize the cache and loaders
|
||||
mLoadQueue = new TaskResourceLoadQueue();
|
||||
mIconCache = new DrawableLruCache(iconCacheSize);
|
||||
mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
|
||||
@@ -293,7 +306,7 @@ public class RecentsTaskLoader {
|
||||
|
||||
/** Reload the set of recent tasks */
|
||||
SpaceNode reload(Context context, int preloadCount) {
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsTaskLoader|reload]");
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
|
||||
TaskStack stack = new TaskStack(context);
|
||||
SpaceNode root = new SpaceNode(context);
|
||||
root.setStack(stack);
|
||||
@@ -310,7 +323,7 @@ public class RecentsTaskLoader {
|
||||
Console.log(Constants.DebugFlags.App.TimeSystemCalls,
|
||||
"[RecentsTaskLoader|getRecentTasks]",
|
||||
"" + (System.currentTimeMillis() - t1) + "ms");
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake,
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|tasks]", "" + tasks.size());
|
||||
|
||||
// Remove home/recents tasks
|
||||
@@ -335,35 +348,51 @@ public class RecentsTaskLoader {
|
||||
int taskCount = tasks.size();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
ActivityManager.RecentTaskInfo t = tasks.get(i);
|
||||
|
||||
// Load the label, icon and thumbnail
|
||||
ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(),
|
||||
PackageManager.GET_META_DATA);
|
||||
String title = info.loadLabel(pm).toString();
|
||||
Drawable icon = null;
|
||||
Bitmap thumbnail = null;
|
||||
Task task;
|
||||
if (i >= (taskCount - preloadCount) || !Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake,
|
||||
// Preload the specified number of apps
|
||||
if (i >= (taskCount - preloadCount) ||
|
||||
!Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
"[RecentsTaskLoader|preloadTask]",
|
||||
"i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
|
||||
icon = info.loadIcon(pm);
|
||||
thumbnail = am.getTaskTopThumbnail(t.id);
|
||||
for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake,
|
||||
" [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
|
||||
task = new Task(t.persistentId, t.baseIntent, title, icon, thumbnail);
|
||||
|
||||
task = new Task(t.persistentId, t.baseIntent, title, null, null);
|
||||
|
||||
// Load the icon (if possible from the cache)
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
task.icon = mIconCache.get(task);
|
||||
}
|
||||
if (task.icon == null) {
|
||||
task.icon = info.loadIcon(pm);
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
if (thumbnail != null) mThumbnailCache.put(task, thumbnail);
|
||||
if (icon != null) {
|
||||
mIconCache.put(task, icon);
|
||||
}
|
||||
mIconCache.put(task, task.icon);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the thumbnail (if possible from the cache)
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
task.thumbnail = mThumbnailCache.get(task);
|
||||
}
|
||||
if (task.thumbnail == null) {
|
||||
task.thumbnail = am.getTaskTopThumbnail(t.id);
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
mThumbnailCache.put(task, task.thumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
// Create as many tasks a we want to multiply by
|
||||
for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
" [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
|
||||
stack.addTask(task);
|
||||
}
|
||||
} else {
|
||||
// Create as many tasks a we want to multiply by
|
||||
for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) {
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake,
|
||||
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);
|
||||
@@ -388,9 +417,9 @@ public class RecentsTaskLoader {
|
||||
t1 = System.currentTimeMillis();
|
||||
List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
|
||||
Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsTaskLoader|stacks]", "" + tasks.size());
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
|
||||
for (ActivityManager.StackInfo s : stackInfos) {
|
||||
Console.log(Constants.DebugFlags.App.SystemUIHandshake, " [RecentsTaskLoader|stack]", s.toString());
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString());
|
||||
if (stacks.containsKey(s.stackId)) {
|
||||
stacks.get(s.stackId).setRect(s.bounds);
|
||||
}
|
||||
@@ -403,45 +432,46 @@ public class RecentsTaskLoader {
|
||||
return root;
|
||||
}
|
||||
|
||||
/** Acquires the task resource data from the pool.
|
||||
* XXX: Move this into Task? */
|
||||
/** Acquires the task resource data from the pool. */
|
||||
public void loadTaskData(Task t) {
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
t.icon = mIconCache.get(t);
|
||||
t.thumbnail = mThumbnailCache.get(t);
|
||||
Drawable icon = mIconCache.get(t);
|
||||
Bitmap thumbnail = mThumbnailCache.get(t);
|
||||
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
|
||||
t + " icon: " + t.icon + " thumbnail: " + t.thumbnail);
|
||||
t + " icon: " + icon + " thumbnail: " + thumbnail +
|
||||
" thumbnailCacheSize: " + mThumbnailCache.size());
|
||||
|
||||
boolean requiresLoad = false;
|
||||
if (t.icon == null) {
|
||||
t.icon = mDefaultIcon;
|
||||
if (icon == null) {
|
||||
icon = mDefaultIcon;
|
||||
requiresLoad = true;
|
||||
}
|
||||
if (t.thumbnail == null) {
|
||||
t.thumbnail = mDefaultThumbnail;
|
||||
if (thumbnail == null) {
|
||||
thumbnail = mDefaultThumbnail;
|
||||
requiresLoad = true;
|
||||
}
|
||||
if (requiresLoad) {
|
||||
mLoadQueue.addTask(t);
|
||||
}
|
||||
t.notifyTaskLoaded(thumbnail, icon);
|
||||
}
|
||||
}
|
||||
|
||||
/** Releases the task resource data back into the pool.
|
||||
* XXX: Move this into Task? */
|
||||
/** 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);
|
||||
"[RecentsTaskLoader|unloadTask]", t +
|
||||
" thumbnailCacheSize: " + mThumbnailCache.size());
|
||||
mLoadQueue.removeTask(t);
|
||||
t.icon = mDefaultIcon;
|
||||
t.thumbnail = mDefaultThumbnail;
|
||||
t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
|
||||
} else {
|
||||
t.notifyTaskUnloaded(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/** Completely removes the resource data from the pool.
|
||||
* XXX: Move this into Task? */
|
||||
/** Completely removes the resource data from the pool. */
|
||||
public void deleteTaskData(Task t) {
|
||||
if (Constants.DebugFlags.App.EnableBackgroundTaskLoading) {
|
||||
Console.log(Constants.DebugFlags.App.TaskDataLoader,
|
||||
@@ -449,9 +479,10 @@ public class RecentsTaskLoader {
|
||||
mLoadQueue.removeTask(t);
|
||||
mThumbnailCache.remove(t);
|
||||
mIconCache.remove(t);
|
||||
t.notifyTaskUnloaded(mDefaultThumbnail, mDefaultIcon);
|
||||
} else {
|
||||
t.notifyTaskUnloaded(null, null);
|
||||
}
|
||||
t.icon = mDefaultIcon;
|
||||
t.thumbnail = mDefaultThumbnail;
|
||||
}
|
||||
|
||||
/** Stops the task loader */
|
||||
@@ -460,4 +491,51 @@ public class RecentsTaskLoader {
|
||||
mLoader.stop();
|
||||
mLoadQueue.clearTasks();
|
||||
}
|
||||
|
||||
void onTrimMemory(int level) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,24 @@ public class Task {
|
||||
}
|
||||
}
|
||||
|
||||
/** Notifies the callback listeners that this task has been loaded */
|
||||
public void notifyTaskLoaded(Bitmap thumbnail, Drawable icon) {
|
||||
this.icon = icon;
|
||||
this.thumbnail = thumbnail;
|
||||
if (mCb != null) {
|
||||
mCb.onTaskBound();
|
||||
}
|
||||
}
|
||||
|
||||
/** Notifies the callback listeners that this task has been unloaded */
|
||||
public void notifyTaskUnloaded(Bitmap defaultThumbnail, Drawable defaultIcon) {
|
||||
icon = defaultIcon;
|
||||
thumbnail = defaultThumbnail;
|
||||
if (mCb != null) {
|
||||
mCb.onTaskUnbound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
// If we have multiple task entries for the same task, then we do the simple object
|
||||
|
||||
@@ -20,4 +20,8 @@ package com.android.systemui.recents.model;
|
||||
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();
|
||||
}
|
||||
@@ -28,6 +28,8 @@ import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import com.android.systemui.recents.Console;
|
||||
import com.android.systemui.recents.Constants;
|
||||
|
||||
/**
|
||||
* This class facilitates swipe to dismiss. It defines an interface to be implemented by the
|
||||
@@ -176,6 +178,9 @@ public class SwipeHelper {
|
||||
}
|
||||
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
Console.log(Constants.DebugFlags.UI.TouchEvents,
|
||||
"[SwipeHelper|interceptTouchEvent]",
|
||||
Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
|
||||
final int action = ev.getAction();
|
||||
|
||||
switch (action) {
|
||||
@@ -200,7 +205,7 @@ public class SwipeHelper {
|
||||
if (Math.abs(delta) > mPagingTouchSlop) {
|
||||
mCallback.onBeginDrag(mCurrView);
|
||||
mDragging = true;
|
||||
mInitialTouchPos = getPos(ev) - getTranslation(mCurrView);
|
||||
mInitialTouchPos = pos - getTranslation(mCurrView);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -286,6 +291,10 @@ public class SwipeHelper {
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
Console.log(Constants.DebugFlags.UI.TouchEvents,
|
||||
"[SwipeHelper|touchEvent]",
|
||||
Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
|
||||
|
||||
if (!mDragging) {
|
||||
if (!onInterceptTouchEvent(ev)) {
|
||||
return mCanCurrViewBeDimissed;
|
||||
|
||||
@@ -435,19 +435,12 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
mStackRectSansPeek.top += Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height();
|
||||
|
||||
// Compute the task rect
|
||||
if (RecentsConfiguration.getInstance().layoutVerticalStack) {
|
||||
int minHeight = (int) (mStackRect.height() -
|
||||
(Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
|
||||
int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
|
||||
int centerX = mStackRect.centerX();
|
||||
mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top,
|
||||
centerX + size / 2, mStackRectSansPeek.top + size);
|
||||
} else {
|
||||
int size = Math.min(mStackRect.width(), mStackRect.height());
|
||||
int centerY = mStackRect.centerY();
|
||||
mTaskRect.set(mStackRectSansPeek.top, centerY - size / 2,
|
||||
mStackRectSansPeek.top + size, centerY + size / 2);
|
||||
}
|
||||
int minHeight = (int) (mStackRect.height() -
|
||||
(Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
|
||||
int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
|
||||
int centerX = mStackRect.centerX();
|
||||
mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top,
|
||||
centerX + size / 2, mStackRectSansPeek.top + size);
|
||||
|
||||
// Update the scroll bounds
|
||||
updateMinMaxScroll(false);
|
||||
@@ -589,7 +582,6 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
// Report that this tasks's data is no longer being used
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
loader.unloadTaskData(task);
|
||||
tv.unbindFromTask();
|
||||
|
||||
// Detach the view from the hierarchy
|
||||
detachViewFromParent(tv);
|
||||
@@ -610,7 +602,6 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
// Request that this tasks's data be filled
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
loader.loadTaskData(task);
|
||||
tv.syncToTask();
|
||||
|
||||
// Find the index where this task should be placed in the children
|
||||
int insertIndex = -1;
|
||||
@@ -678,14 +669,13 @@ public class TaskStackView extends FrameLayout implements TaskStackCallbacks, Ta
|
||||
}
|
||||
|
||||
/* Handles touch events */
|
||||
class TaskStackViewTouchHandler {
|
||||
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
static int INACTIVE_POINTER_ID = -1;
|
||||
|
||||
TaskStackView mSv;
|
||||
VelocityTracker mVelocityTracker;
|
||||
|
||||
boolean mIsScrolling;
|
||||
boolean mIsSwiping;
|
||||
|
||||
int mInitialMotionX, mInitialMotionY;
|
||||
int mLastMotionX, mLastMotionY;
|
||||
@@ -697,21 +687,24 @@ class TaskStackViewTouchHandler {
|
||||
int mMaximumVelocity;
|
||||
// The scroll touch slop is used to calculate when we start scrolling
|
||||
int mScrollTouchSlop;
|
||||
// The swipe touch slop is used to calculate when we start swiping left/right, this takes
|
||||
// precendence over the scroll touch slop in case the user makes a gesture that starts scrolling
|
||||
// but is intended to be a swipe
|
||||
int mSwipeTouchSlop;
|
||||
// After a certain amount of scrolling, we should start ignoring checks for swiping
|
||||
int mMaxScrollMotionToRejectSwipe;
|
||||
// The page touch slop is used to calculate when we start swiping
|
||||
float mPagingTouchSlop;
|
||||
|
||||
SwipeHelper mSwipeHelper;
|
||||
boolean mInterceptedBySwipeHelper;
|
||||
|
||||
public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
|
||||
ViewConfiguration configuration = ViewConfiguration.get(context);
|
||||
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
|
||||
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
|
||||
mScrollTouchSlop = configuration.getScaledTouchSlop();
|
||||
mSwipeTouchSlop = 2 * mScrollTouchSlop;
|
||||
mMaxScrollMotionToRejectSwipe = 4 * mScrollTouchSlop;
|
||||
mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
|
||||
mSv = sv;
|
||||
|
||||
|
||||
float densityScale = context.getResources().getDisplayMetrics().density;
|
||||
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
|
||||
mSwipeHelper.setMinAlpha(1f);
|
||||
}
|
||||
|
||||
/** Velocity tracker helpers */
|
||||
@@ -754,11 +747,18 @@ class TaskStackViewTouchHandler {
|
||||
"[TaskStackViewTouchHandler|interceptTouchEvent]",
|
||||
Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
|
||||
|
||||
// Return early if we have no children
|
||||
boolean hasChildren = (mSv.getChildCount() > 0);
|
||||
if (!hasChildren) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass through to swipe helper if we are swiping
|
||||
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
|
||||
if (mInterceptedBySwipeHelper) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean wasScrolling = !mSv.mScroller.isFinished() ||
|
||||
(mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
|
||||
int action = ev.getAction();
|
||||
@@ -777,7 +777,8 @@ class TaskStackViewTouchHandler {
|
||||
mVelocityTracker.addMovement(ev);
|
||||
// Check if the scroller is finished yet
|
||||
mIsScrolling = !mSv.mScroller.isFinished();
|
||||
mIsSwiping = false;
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
@@ -786,25 +787,7 @@ class TaskStackViewTouchHandler {
|
||||
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
int y = (int) ev.getY(activePointerIndex);
|
||||
int x = (int) ev.getX(activePointerIndex);
|
||||
if (mActiveTaskView != null &&
|
||||
mTotalScrollMotion < mMaxScrollMotionToRejectSwipe &&
|
||||
Math.abs(x - mInitialMotionX) > Math.abs(y - mInitialMotionY) &&
|
||||
Math.abs(x - mInitialMotionX) > mSwipeTouchSlop) {
|
||||
// Start swiping and stop scrolling
|
||||
mIsScrolling = false;
|
||||
mIsSwiping = true;
|
||||
System.out.println("SWIPING: " + mActiveTaskView);
|
||||
// Initialize the velocity tracker if necessary
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
} else if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
|
||||
if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
|
||||
// Save the touch move info
|
||||
mIsScrolling = true;
|
||||
// Initialize the velocity tracker if necessary
|
||||
@@ -815,8 +798,6 @@ class TaskStackViewTouchHandler {
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
}
|
||||
|
||||
mLastMotionX = x;
|
||||
@@ -829,16 +810,17 @@ class TaskStackViewTouchHandler {
|
||||
mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
|
||||
// Reset the drag state and the velocity tracker
|
||||
mIsScrolling = false;
|
||||
mIsSwiping = false;
|
||||
mActivePointerId = INACTIVE_POINTER_ID;
|
||||
mActiveTaskView = null;
|
||||
mTotalScrollMotion = 0;
|
||||
recycleVelocityTracker();
|
||||
// Disable HW layers
|
||||
mSv.decHwLayersRefCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return wasScrolling || mIsScrolling || mIsSwiping;
|
||||
return wasScrolling || mIsScrolling;
|
||||
}
|
||||
|
||||
/** Handles touch events once we have intercepted them */
|
||||
@@ -853,6 +835,11 @@ class TaskStackViewTouchHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass through to swipe helper if we are swiping
|
||||
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update the velocity tracker
|
||||
initVelocityTrackerIfNotExists();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
@@ -871,7 +858,6 @@ class TaskStackViewTouchHandler {
|
||||
// Initialize the velocity tracker
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
// XXX: Set mIsScrolling or mIsSwiping?
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
@@ -886,28 +872,7 @@ class TaskStackViewTouchHandler {
|
||||
int x = (int) ev.getX(activePointerIndex);
|
||||
int y = (int) ev.getY(activePointerIndex);
|
||||
int deltaY = mLastMotionY - y;
|
||||
int deltaX = x - mLastMotionX;
|
||||
if (!mIsSwiping) {
|
||||
if (mActiveTaskView != null &&
|
||||
mTotalScrollMotion < mMaxScrollMotionToRejectSwipe &&
|
||||
Math.abs(x - mInitialMotionX) > Math.abs(y - mInitialMotionY) &&
|
||||
Math.abs(x - mInitialMotionX) > mSwipeTouchSlop) {
|
||||
mIsScrolling = false;
|
||||
mIsSwiping = true;
|
||||
System.out.println("SWIPING: " + mActiveTaskView);
|
||||
// Initialize the velocity tracker if necessary
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
}
|
||||
}
|
||||
if (!mIsSwiping && !mIsScrolling) {
|
||||
if (!mIsScrolling) {
|
||||
if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
|
||||
mIsScrolling = true;
|
||||
// Initialize the velocity tracker
|
||||
@@ -927,8 +892,6 @@ class TaskStackViewTouchHandler {
|
||||
if (mSv.isScrollOutOfBounds()) {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
} else if (mIsSwiping) {
|
||||
mActiveTaskView.setTranslationX(mActiveTaskView.getTranslationX() + deltaX);
|
||||
}
|
||||
mLastMotionX = x;
|
||||
mLastMotionY = y;
|
||||
@@ -936,107 +899,33 @@ class TaskStackViewTouchHandler {
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
if (mIsScrolling || mIsSwiping) {
|
||||
final TaskView activeTv = mActiveTaskView;
|
||||
final VelocityTracker velocityTracker = mVelocityTracker;
|
||||
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
|
||||
final VelocityTracker velocityTracker = mVelocityTracker;
|
||||
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
|
||||
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
|
||||
|
||||
if (mIsSwiping) {
|
||||
int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
|
||||
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
|
||||
// Fling to dismiss
|
||||
int newScrollX = (int) (Math.signum(initialVelocity) *
|
||||
activeTv.getMeasuredWidth());
|
||||
int duration = Math.min(Constants.Values.TaskStackView.Animation.SwipeDismissDuration,
|
||||
(int) (Math.abs(newScrollX - activeTv.getScrollX()) *
|
||||
1000f / Math.abs(initialVelocity)));
|
||||
activeTv.animate()
|
||||
.translationX(newScrollX)
|
||||
.alpha(0f)
|
||||
.setDuration(duration)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
Task task = activeTv.getTask();
|
||||
Activity activity = (Activity) mSv.getContext();
|
||||
|
||||
// We have to disable the listener to ensure that we
|
||||
// don't hit this again
|
||||
activeTv.animate().setListener(null);
|
||||
|
||||
// Remove the task from the view
|
||||
mSv.mStack.removeTask(task);
|
||||
|
||||
// Remove any stored data from the loader
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
loader.deleteTaskData(task);
|
||||
|
||||
// Remove the task from activity manager
|
||||
final ActivityManager am = (ActivityManager)
|
||||
activity.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am != null) {
|
||||
am.removeTask(activeTv.getTask().id,
|
||||
ActivityManager.REMOVE_TASK_KILL_PROCESS);
|
||||
}
|
||||
|
||||
// If there are no remaining tasks, then just close the activity
|
||||
if (mSv.mStack.getTaskCount() == 0) {
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
// Disable HW layers
|
||||
mSv.decHwLayersRefCount();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
} else {
|
||||
// Animate it back into place
|
||||
// XXX: Make this animation a function of the velocity OR distance
|
||||
int duration = Constants.Values.TaskStackView.Animation.SwipeSnapBackDuration;
|
||||
activeTv.animate()
|
||||
.translationX(0)
|
||||
.setDuration(duration)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// Disable HW layers
|
||||
mSv.decHwLayersRefCount();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
}
|
||||
} else {
|
||||
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
|
||||
if ((Math.abs(velocity) > mMinimumVelocity)) {
|
||||
Console.log(Constants.DebugFlags.UI.TouchEvents,
|
||||
"[TaskStackViewTouchHandler|fling]",
|
||||
"scroll: " + mSv.getStackScroll() + " velocity: " + velocity,
|
||||
Console.AnsiGreen);
|
||||
// Enable HW layers on the stack
|
||||
mSv.addHwLayersRefCount();
|
||||
// Fling scroll
|
||||
mSv.mScroller.fling(0, mSv.getStackScroll(),
|
||||
0, -velocity,
|
||||
0, 0,
|
||||
mSv.mMinScroll, mSv.mMaxScroll,
|
||||
0, 0);
|
||||
// Invalidate to kick off computeScroll
|
||||
mSv.invalidate();
|
||||
} else if (mSv.isScrollOutOfBounds()) {
|
||||
// Animate the scroll back into bounds
|
||||
// XXX: Make this animation a function of the velocity OR distance
|
||||
mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
|
||||
}
|
||||
}
|
||||
if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
|
||||
Console.log(Constants.DebugFlags.UI.TouchEvents,
|
||||
"[TaskStackViewTouchHandler|fling]",
|
||||
"scroll: " + mSv.getStackScroll() + " velocity: " + velocity,
|
||||
Console.AnsiGreen);
|
||||
// Enable HW layers on the stack
|
||||
mSv.addHwLayersRefCount();
|
||||
// Fling scroll
|
||||
mSv.mScroller.fling(0, mSv.getStackScroll(),
|
||||
0, -velocity,
|
||||
0, 0,
|
||||
mSv.mMinScroll, mSv.mMaxScroll,
|
||||
0, 0);
|
||||
// Invalidate to kick off computeScroll
|
||||
mSv.invalidate();
|
||||
} else if (mSv.isScrollOutOfBounds()) {
|
||||
// Animate the scroll back into bounds
|
||||
// XXX: Make this animation a function of the velocity OR distance
|
||||
mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
|
||||
}
|
||||
|
||||
mActivePointerId = INACTIVE_POINTER_ID;
|
||||
mIsScrolling = false;
|
||||
mIsSwiping = false;
|
||||
mTotalScrollMotion = 0;
|
||||
recycleVelocityTracker();
|
||||
// Disable HW layers
|
||||
@@ -1044,25 +933,14 @@ class TaskStackViewTouchHandler {
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
if (mIsScrolling || mIsSwiping) {
|
||||
if (mIsSwiping) {
|
||||
// Animate it back into place
|
||||
// XXX: Make this animation a function of the velocity OR distance
|
||||
int duration = Constants.Values.TaskStackView.Animation.SwipeSnapBackDuration;
|
||||
mActiveTaskView.animate()
|
||||
.translationX(0)
|
||||
.setDuration(duration)
|
||||
.start();
|
||||
} else {
|
||||
// Animate the scroll back into bounds
|
||||
// XXX: Make this animation a function of the velocity OR distance
|
||||
mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
|
||||
}
|
||||
if (mSv.isScrollOutOfBounds()) {
|
||||
// Animate the scroll back into bounds
|
||||
// XXX: Make this animation a function of the velocity OR distance
|
||||
mSv.animateBoundScroll(Constants.Values.TaskStackView.Animation.SnapScrollBackDuration);
|
||||
}
|
||||
|
||||
mActivePointerId = INACTIVE_POINTER_ID;
|
||||
mIsScrolling = false;
|
||||
mIsSwiping = false;
|
||||
mTotalScrollMotion = 0;
|
||||
recycleVelocityTracker();
|
||||
// Disable HW layers
|
||||
@@ -1072,4 +950,72 @@ class TaskStackViewTouchHandler {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**** SwipeHelper Implementation ****/
|
||||
|
||||
@Override
|
||||
public View getChildAtPosition(MotionEvent ev) {
|
||||
return findViewAtPoint((int) ev.getX(), (int) ev.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canChildBeDismissed(View v) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeginDrag(View v) {
|
||||
// Enable HW layers
|
||||
mSv.addHwLayersRefCount();
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
parent.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDismissed(View v) {
|
||||
TaskView tv = (TaskView) v;
|
||||
Task task = tv.getTask();
|
||||
Activity activity = (Activity) mSv.getContext();
|
||||
|
||||
// We have to disable the listener to ensure that we
|
||||
// don't hit this again
|
||||
tv.animate().setListener(null);
|
||||
|
||||
// Remove the task from the view
|
||||
mSv.mStack.removeTask(task);
|
||||
|
||||
// Remove any stored data from the loader
|
||||
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
|
||||
loader.deleteTaskData(task);
|
||||
|
||||
// Remove the task from activity manager
|
||||
final ActivityManager am = (ActivityManager)
|
||||
activity.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am != null) {
|
||||
am.removeTask(tv.getTask().id,
|
||||
ActivityManager.REMOVE_TASK_KILL_PROCESS);
|
||||
}
|
||||
|
||||
// If there are no remaining tasks, then just close the activity
|
||||
if (mSv.mStack.getTaskCount() == 0) {
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
// Disable HW layers
|
||||
mSv.decHwLayersRefCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnapBackCompleted(View v) {
|
||||
// Do Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragCancelled(View v) {
|
||||
// Disable HW layers
|
||||
mSv.decHwLayersRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,13 +255,13 @@ public class TaskView extends FrameLayout implements View.OnClickListener, TaskC
|
||||
}
|
||||
|
||||
/** Actually synchronizes the model data into the views */
|
||||
void syncToTask() {
|
||||
private void syncToTask() {
|
||||
mThumbnailView.rebindToTask(mTask, false);
|
||||
mIconView.rebindToTask(mTask, false);
|
||||
}
|
||||
|
||||
/** Unset the task and callback */
|
||||
void unbindFromTask() {
|
||||
private void unbindFromTask() {
|
||||
mTask.setCallbacks(null);
|
||||
mThumbnailView.unbindFromTask();
|
||||
mIconView.unbindFromTask();
|
||||
@@ -357,16 +357,16 @@ public class TaskView extends FrameLayout implements View.OnClickListener, TaskC
|
||||
|
||||
/** Enable the hw layers on this task view */
|
||||
void enableHwLayers() {
|
||||
Console.log(Constants.DebugFlags.UI.HwLayers, "[TaskView|enableHwLayers]");
|
||||
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
|
||||
/** Disable the hw layers on this task view */
|
||||
void disableHwLayers() {
|
||||
Console.log(Constants.DebugFlags.UI.HwLayers, "[TaskView|disableHwLayers]");
|
||||
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
|
||||
/**** TaskCallbacks Implementation ****/
|
||||
|
||||
@Override
|
||||
public void onTaskDataChanged(Task task) {
|
||||
Console.log(Constants.DebugFlags.App.EnableBackgroundTaskLoading,
|
||||
@@ -379,6 +379,16 @@ public class TaskView extends FrameLayout implements View.OnClickListener, TaskC
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskBound() {
|
||||
syncToTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUnbound() {
|
||||
unbindFromTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mCb.onTaskIconClicked(this);
|
||||
|
||||
Reference in New Issue
Block a user