Refactoring to support groups.
- Removing RecentService, determining animations just in time
- Fixing a few issues with animations of newly picked up tasks from the pool
- Moving helper classes into sub package
Change-Id: Ie10385d1f9ca79eea918b16932f56b60e2802304
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 1b215c9..3e2ef94 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -40,7 +40,8 @@
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
@@ -200,7 +201,7 @@
final ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
final PackageManager pm = mContext.getPackageManager();
- final Bitmap thumbnail = Utilities.getThumbnail(am, td.persistentTaskId);
+ final Bitmap thumbnail = SystemServicesProxy.getThumbnail(am, td.persistentTaskId);
Drawable icon = getFullResIcon(td.resolveInfo, pm);
if (td.userId != UserHandle.myUserId()) {
// Need to badge the icon
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 52ccb59..3c4f030 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -22,126 +22,43 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.view.View;
-import android.view.WindowManager;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewTransform;
-import java.lang.ref.WeakReference;
-import java.util.Iterator;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/** A proxy implementation for the recents component */
public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
- /** A handler for messages from the recents implementation */
- class RecentsMessageHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_UPDATE_FOR_CONFIGURATION) {
- Resources res = mContext.getResources();
- float statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- Bundle replyData = msg.getData().getParcelable(KEY_CONFIGURATION_DATA);
- mSingleCountFirstTaskRect = replyData.getParcelable(KEY_SINGLE_TASK_STACK_RECT);
- mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight);
- mTwoCountFirstTaskRect = replyData.getParcelable(KEY_TWO_TASK_STACK_RECT);
- mTwoCountFirstTaskRect.offset(0, (int) statusBarHeight);
- mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT);
- mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight);
- if (Console.Enabled) {
- Console.log(Constants.Log.App.RecentsComponent,
- "[RecentsComponent|RecentsMessageHandler|handleMessage]",
- "singleTaskRect: " + mSingleCountFirstTaskRect +
- " twoTaskRect: " + mTwoCountFirstTaskRect +
- " multipleTaskRect: " + mMultipleCountFirstTaskRect);
- }
-
- // If we had the update the animation rects as a result of onServiceConnected, then
- // we check for whether we need to toggle the recents here.
- if (mToggleRecentsUponServiceBound) {
- startRecentsActivity();
- mToggleRecentsUponServiceBound = false;
- }
- }
- }
- }
-
- /** A service connection to the recents implementation */
- class RecentsServiceConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.RecentsComponent,
- "[RecentsComponent|ServiceConnection|onServiceConnected]",
- "toggleRecents: " + mToggleRecentsUponServiceBound);
- }
- mService = new Messenger(service);
- mServiceIsBound = true;
-
- if (hasValidTaskRects()) {
- // Start recents if this new service connection was triggered by hitting recents
- if (mToggleRecentsUponServiceBound) {
- startRecentsActivity();
- mToggleRecentsUponServiceBound = false;
- }
- } else {
- // Otherwise, update the animation rects before starting the recents if requested
- updateAnimationRects();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.RecentsComponent,
- "[RecentsComponent|ServiceConnection|onServiceDisconnected]");
- }
- mService = null;
- mServiceIsBound = false;
- }
- }
-
- final public static int MSG_UPDATE_FOR_CONFIGURATION = 0;
- final public static int MSG_UPDATE_TASK_THUMBNAIL = 1;
- final public static int MSG_PRELOAD_TASKS = 2;
- final public static int MSG_CANCEL_PRELOAD_TASKS = 3;
- final public static int MSG_SHOW_RECENTS = 4;
- final public static int MSG_HIDE_RECENTS = 5;
- final public static int MSG_TOGGLE_RECENTS = 6;
- final public static int MSG_START_ENTER_ANIMATION = 7;
-
final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
- final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
- final public static String KEY_WINDOW_RECT = "recents.windowRect";
- final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
- final public static String KEY_SINGLE_TASK_STACK_RECT = "recents.singleCountTaskRect";
- final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
- final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
+ final public static String EXTRA_TRIGGERED_FROM_TASK_ID = "recents.activeTaskId";
final static int sMinToggleDelay = 425;
final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
final static String sRecentsPackage = "com.android.systemui";
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
- final static String sRecentsService = "com.android.systemui.recents.RecentsService";
static Bitmap sLastScreenshot;
static RecentsComponent.Callbacks sRecentsComponentCallbacks;
@@ -150,37 +67,37 @@
SystemServicesProxy mSystemServicesProxy;
// Recents service binding
- Messenger mService = null;
- Messenger mMessenger;
- RecentsMessageHandler mHandler;
+ Handler mHandler;
boolean mBootCompleted = false;
- boolean mServiceIsBound = false;
- boolean mToggleRecentsUponServiceBound;
- RecentsServiceConnection mConnection = new RecentsServiceConnection();
+
+ // Task launching
+ RecentsConfiguration mConfig;
+ Rect mWindowRect;
+ Rect mTaskStackBounds;
+ TaskViewTransform mTmpTransform = new TaskViewTransform();
+ int mStatusBarHeight;
// Variables to keep track of if we need to start recents after binding
View mStatusBarView;
boolean mTriggeredFromAltTab;
-
- Rect mSingleCountFirstTaskRect = new Rect();
- Rect mTwoCountFirstTaskRect = new Rect();
- Rect mMultipleCountFirstTaskRect = new Rect();
long mLastToggleTime;
public AlternateRecentsComponent(Context context) {
mContext = context;
mSystemServicesProxy = new SystemServicesProxy(context);
- mHandler = new RecentsMessageHandler();
- mMessenger = new Messenger(mHandler);
+ mHandler = new Handler();
+ mConfig = RecentsConfiguration.reinitialize(context);
+ mWindowRect = mSystemServicesProxy.getWindowRect();
+ mTaskStackBounds = new Rect();
+ mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+ mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
}
public void onStart() {
if (Console.Enabled) {
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]");
}
-
- // Try to create a long-running connection to the recents service
- bindToRecentsService(false);
}
public void onBootCompleted() {
@@ -194,12 +111,6 @@
}
mStatusBarView = statusBarView;
mTriggeredFromAltTab = triggeredFromAltTab;
- if (!mServiceIsBound) {
- // Try to create a long-running connection to the recents service before toggling
- // recents
- bindToRecentsService(true);
- return;
- }
try {
startRecentsActivity();
@@ -214,18 +125,14 @@
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
}
- if (mServiceIsBound && mBootCompleted) {
- if (isRecentsTopMost(null)) {
- // Notify recents to close it
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
- triggeredFromAltTab ? 1 : 0, 0);
- msg.setData(data);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
+ if (mBootCompleted) {
+ if (isRecentsTopMost(getTopMostTask(), null)) {
+ // Notify recents to hide itself
+ Intent intent = new Intent(RecentsActivity.ACTION_HIDE_RECENTS_ACTIVITY);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(RecentsActivity.EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+ mContext.sendBroadcast(intent);
}
}
}
@@ -237,17 +144,10 @@
Constants.Log.App.TimeRecentsStartupKey);
Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
Constants.Log.App.TimeRecentsLaunchKey);
- Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]",
- "serviceIsBound: " + mServiceIsBound);
+ Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]", "");
}
mStatusBarView = statusBarView;
mTriggeredFromAltTab = false;
- if (!mServiceIsBound) {
- // Try to create a long-running connection to the recents service before toggling
- // recents
- bindToRecentsService(true);
- return;
- }
try {
toggleRecentsActivity();
@@ -265,94 +165,26 @@
}
public void onConfigurationChanged(Configuration newConfig) {
- updateAnimationRects();
+ mConfig = RecentsConfiguration.reinitialize(mContext);
+ mWindowRect = mSystemServicesProxy.getWindowRect();
+ mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mTaskStackBounds);
+ sLastScreenshot = null;
}
- /** Binds to the recents implementation */
- private void bindToRecentsService(boolean toggleRecentsUponConnection) {
- mToggleRecentsUponServiceBound = toggleRecentsUponConnection;
- Intent intent = new Intent();
- intent.setClassName(sRecentsPackage, sRecentsService);
- mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- }
-
- /** Returns whether we have valid task rects to animate to. */
- boolean hasValidTaskRects() {
- return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 &&
- mSingleCountFirstTaskRect.height() > 0 && mTwoCountFirstTaskRect != null &&
- mTwoCountFirstTaskRect.width() > 0 && mTwoCountFirstTaskRect.height() > 0 &&
- mMultipleCountFirstTaskRect != null && mMultipleCountFirstTaskRect.width() > 0 &&
- mMultipleCountFirstTaskRect.height() > 0;
- }
-
- /** Updates each of the task animation rects. */
- void updateAnimationRects() {
- if (mServiceIsBound) {
- Resources res = mContext.getResources();
- int statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- int navBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height);
- Rect rect = new Rect();
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getRectSize(rect);
-
- // Try and update the recents configuration
- try {
- Bundle data = new Bundle();
- data.putParcelable(KEY_WINDOW_RECT, rect);
- data.putParcelable(KEY_SYSTEM_INSETS, new Rect(0, statusBarHeight, 0, 0));
- Message msg = Message.obtain(null, MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
- msg.setData(data);
- msg.replyTo = mMessenger;
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
- }
- }
-
- /** Loads the first task thumbnail */
- Bitmap loadFirstTaskThumbnail() {
+ /** Gets the top task. */
+ ActivityManager.RunningTaskInfo getTopMostTask() {
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
-
- for (ActivityManager.RunningTaskInfo t : tasks) {
- return ssp.getTaskThumbnail(t.id);
+ if (!tasks.isEmpty()) {
+ return tasks.get(0);
}
return null;
}
- /** Returns the proper rect to use for the animation, given the number of tasks. */
- Rect getAnimationTaskRect(List<ActivityManager.RecentTaskInfo> tasks) {
- // NOTE: Currently there's no method to get the number of non-home tasks, so we have to
- // compute this ourselves
- SystemServicesProxy ssp = mSystemServicesProxy;
- Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
- while (iter.hasNext()) {
- ActivityManager.RecentTaskInfo t = iter.next();
-
- // Skip tasks in the home stack
- if (ssp.isInHomeStack(t.persistentId)) {
- iter.remove();
- continue;
- }
- }
- if (tasks.size() <= 1) {
- return mSingleCountFirstTaskRect;
- } else if (tasks.size() <= 2) {
- return mTwoCountFirstTaskRect;
- } else {
- return mMultipleCountFirstTaskRect;
- }
- }
-
/** Returns whether the recents is currently running */
- boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
+ boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask, AtomicBoolean isHomeTopMost) {
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
- if (!tasks.isEmpty()) {
- ActivityManager.RunningTaskInfo topTask = tasks.get(0);
+ if (topTask != null) {
ComponentName topActivity = topTask.topActivity;
// Check if the front most activity is recents
@@ -382,39 +214,37 @@
// If Recents is the front most activity, then we should just communicate with it directly
// to launch the first task or dismiss itself
+ ActivityManager.RunningTaskInfo topTask = getTopMostTask();
AtomicBoolean isTopTaskHome = new AtomicBoolean();
- if (isRecentsTopMost(isTopTaskHome)) {
- // Notify recents to close itself
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_TOGGLE_RECENTS, 0, 0);
- msg.setData(data);
- mService.send(msg);
+ if (isRecentsTopMost(topTask, isTopTaskHome)) {
+ // Notify recents to toggle itself
+ Intent intent = new Intent(RecentsActivity.ACTION_TOGGLE_RECENTS_ACTIVITY);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
- // Time this path
- if (Console.Enabled) {
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
- }
- } catch (RemoteException re) {
- re.printStackTrace();
+ // Time this path
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
}
mLastToggleTime = System.currentTimeMillis();
return;
} else {
// Otherwise, start the recents activity
- startRecentsActivity(isTopTaskHome.get());
+ startRecentsActivity(topTask, isTopTaskHome.get());
}
}
/** Starts the recents activity if it is not already running */
void startRecentsActivity() {
// Check if the top task is in the home stack, and start the recents activity
+ ActivityManager.RunningTaskInfo topTask = getTopMostTask();
AtomicBoolean isTopTaskHome = new AtomicBoolean();
- if (!isRecentsTopMost(isTopTaskHome)) {
- startRecentsActivity(isTopTaskHome.get());
+ if (!isRecentsTopMost(topTask, isTopTaskHome)) {
+ startRecentsActivity(topTask, isTopTaskHome.get());
}
}
@@ -422,8 +252,6 @@
* Creates the activity options for a unknown state->recents transition.
*/
ActivityOptions getUnknownTransitionActivityOptions() {
- // Reset the last screenshot
- consumeLastScreenshot();
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit, mHandler, this);
@@ -433,8 +261,6 @@
* Creates the activity options for a home->recents transition.
*/
ActivityOptions getHomeTransitionActivityOptions() {
- // Reset the last screenshot
- consumeLastScreenshot();
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
R.anim.recents_from_launcher_exit, mHandler, this);
@@ -444,13 +270,13 @@
* Creates the activity options for an app->recents transition. If this method sets the static
* screenshot, then we will use that for the transition.
*/
- ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+ ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask) {
// Recycle the last screenshot
consumeLastScreenshot();
// Take the full screenshot
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
- sLastScreenshot = mSystemServicesProxy.takeScreenshot();
+ sLastScreenshot = mSystemServicesProxy.takeAppScreenshot();
if (sLastScreenshot != null) {
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_app_enter,
@@ -459,8 +285,10 @@
}
// If the screenshot fails, then load the first task thumbnail and use that
- Bitmap firstThumbnail = loadFirstTaskThumbnail();
+ Bitmap firstThumbnail = mSystemServicesProxy.getTaskThumbnail(topTask.id);
if (firstThumbnail != null) {
+ Rect taskRect = getThumbnailTransitionRect(topTask.id);
+
// Create the new thumbnail for the animation down
// XXX: We should find a way to optimize this so we don't need to create a new bitmap
Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
@@ -480,8 +308,46 @@
return getUnknownTransitionActivityOptions();
}
+ /** Returns the transition rect for the given task id. */
+ Rect getThumbnailTransitionRect(int runningTaskId) {
+ // Get the stack of tasks that we are animating into
+ TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy);
+ TaskStackView tsv = new TaskStackView(mContext, stack);
+ TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
+ tsv.computeRects(mTaskStackBounds.width(), mTaskStackBounds.height() - mStatusBarHeight, 0, 0);
+ tsv.setStackScrollToInitialState();
+
+ // Find the running task in the TaskStack
+ Task task = null;
+ ArrayList<Task> tasks = stack.getTasks();
+ if (runningTaskId != -1) {
+ // Otherwise, try and find the task with the
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.key.id == runningTaskId) {
+ task = t;
+ break;
+ }
+ }
+ }
+ if (task == null) {
+ // If no task is specified or we can not find the task just use the front most one
+ task = tasks.get(tasks.size() - 1);
+ }
+
+ // Get the transform for the running task
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+ stack.getGroupIndexForTask(task, groupTaskIndex);
+ mTmpTransform = algo.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+ tsv.getStackScroll(), mTmpTransform);
+ mTmpTransform.rect.offset(mTaskStackBounds.left, mTaskStackBounds.top);
+ mTmpTransform.rect.offset(0, mStatusBarHeight);
+ return new Rect(mTmpTransform.rect);
+ }
+
/** Starts the recents activity */
- void startRecentsActivity(boolean isTopTaskHome) {
+ void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
// If Recents is not the front-most activity and we should animate into it. If
// the activity at the root of the top task stack in the home stack, then we just do a
// simple transition. Otherwise, we animate to the rects defined by the Recents service,
@@ -489,34 +355,34 @@
SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> recentTasks =
ssp.getRecentTasks(3, UserHandle.CURRENT.getIdentifier());
- Rect taskRect = getAnimationTaskRect(recentTasks);
- boolean useThumbnailTransition = !isTopTaskHome &&
- hasValidTaskRects();
+ boolean useThumbnailTransition = !isTopTaskHome;
boolean hasRecentTasks = !recentTasks.isEmpty();
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask);
if (opts != null) {
if (sLastScreenshot != null) {
- startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
} else {
- startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_APP_THUMBNAIL);
}
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
}
- } else {
+ }
+
+ if (!useThumbnailTransition) {
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition and do the animation from home
if (hasRecentTasks) {
ActivityOptions opts = getHomeTransitionActivityOptions();
- startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
+ startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME);
} else {
// Otherwise we do the normal fade from an unknown source
ActivityOptions opts = getUnknownTransitionActivityOptions();
- startAlternateRecentsActivity(opts, null);
+ startAlternateRecentsActivity(topTask, opts, null);
}
}
@@ -528,7 +394,8 @@
}
/** Starts the recents activity */
- void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
+ void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ ActivityOptions opts, String extraFlag) {
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -538,6 +405,7 @@
intent.putExtra(extraFlag, true);
}
intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
+ intent.putExtra(EXTRA_TRIGGERED_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
UserHandle.USER_CURRENT));
@@ -576,11 +444,9 @@
@Override
public void onAnimationStarted() {
// Notify recents to start the enter animation
- try {
- Message msg = Message.obtain(null, MSG_START_ENTER_ANIMATION, 0, 0);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
+ Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 7a49a04..8a80b76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,10 +25,12 @@
public static final boolean Verbose = false;
public static class App {
+ // Enables the simulated task affiliations
+ public static final boolean EnableSimulatedTaskGroups = false;
// Enables the screenshot app->Recents transition
public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
- public static final boolean EnableTaskFiltering = true;
+ public static final boolean EnableTaskFiltering = false;
// Enables clipping of tasks against each other
public static final boolean EnableTaskStackClipping = true;
// Enables tapping on the TaskBar to launch the task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 19a38c7..bf894a7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -35,6 +35,11 @@
import android.view.ViewStub;
import android.widget.Toast;
import com.android.systemui.R;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DebugTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.TaskStack;
@@ -51,6 +56,11 @@
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
+ final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
+ final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
+ final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+ final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+
RecentsView mRecentsView;
SystemBarScrimViews mScrimViews;
ViewStub mEmptyViewStub;
@@ -123,8 +133,8 @@
Console.log(Constants.Log.App.SystemUIHandshake,
"[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
}
- if (action.equals(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY)) {
- if (intent.getBooleanExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
+ if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) {
+ if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
// Dismiss recents, launching the focused task
dismissRecentsIfVisible();
} else {
@@ -138,13 +148,13 @@
new ViewAnimation.TaskViewExitContext(exitTrigger));
}
}
- } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+ } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// Try and unfilter and filtered stacks
if (!mRecentsView.unfilterFilteredStacks()) {
// If there are no filtered stacks, dismiss recents and launch the first task
dismissRecentsIfVisible();
}
- } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
+ } else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
@@ -438,9 +448,9 @@
// Register the broadcast receiver to handle messages from our service
IntentFilter filter = new IntentFilter();
- filter.addAction(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
- filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
- filter.addAction(RecentsService.ACTION_START_ENTER_ANIMATION);
+ filter.addAction(ACTION_HIDE_RECENTS_ACTIVITY);
+ filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY);
+ filter.addAction(ACTION_START_ENTER_ANIMATION);
registerReceiver(mServiceBroadcastReceiver, filter);
// Start listening for widget package changes if there is one bound
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 3754340..43d7a54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -19,6 +19,7 @@
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
/** Our special app widget host for the Search widget */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 3041a3c..a2407eb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -28,6 +28,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.recents.misc.Console;
/** A static Recents configuration for the current context
@@ -75,7 +76,6 @@
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
public int taskViewTranslationZIncrementPx;
- public int taskViewShadowOutlineBottomInsetPx;
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
@@ -202,8 +202,6 @@
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
taskViewTranslationZIncrementPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
- taskViewShadowOutlineBottomInsetPx =
- res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
// Task bar colors
taskBarViewDefaultBackgroundColor =
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
deleted file mode 100644
index 49149a6..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ /dev/null
@@ -1,212 +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;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.lang.ref.WeakReference;
-
-
-/** The message handler to process Recents SysUI messages */
-class SystemUIMessageHandler extends Handler {
- WeakReference<Context> mContext;
-
- SystemUIMessageHandler(Context context) {
- // Keep a weak ref to the context instead of a strong ref
- mContext = new WeakReference<Context>(context);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake,
- "[RecentsService|handleMessage]", msg);
- }
-
- Context context = mContext.get();
- if (context == null) return;
-
- if (msg.what == AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION) {
- RecentsTaskLoader.initialize(context);
- RecentsConfiguration config = RecentsConfiguration.reinitialize(context);
- config.updateOnConfigurationChange();
-
- try {
- Bundle data = msg.getData();
- Rect windowRect = data.getParcelable(AlternateRecentsComponent.KEY_WINDOW_RECT);
- Rect systemInsets = data.getParcelable(AlternateRecentsComponent.KEY_SYSTEM_INSETS);
-
- // NOTE: None of the rects computed below need to be offset for the status bar,
- // since that is done when we compute the animation itself in the Recents component
-
- // Create a dummy task stack & compute the rect for the thumbnail to animate to
- TaskStack stack = new TaskStack();
- TaskStackView tsv = new TaskStackView(context, stack);
- TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
- Bundle replyData = new Bundle();
- TaskViewTransform transform;
-
- // Get the task stack and search bar bounds
- Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(windowRect.width(), windowRect.height(), taskStackBounds);
-
- // Calculate the target task rect for when there is one task.
-
- // NOTE: Since the nav bar height is already accounted for in the windowRect, don't
- // pass in a left or bottom inset
- stack.addTask(new Task());
- tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
- systemInsets.top - systemInsets.bottom, 0, 0);
- tsv.setStackScrollToInitialState();
- transform = algo.getStackTransform(0, tsv.getStackScroll());
- transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- replyData.putParcelable(AlternateRecentsComponent.KEY_SINGLE_TASK_STACK_RECT,
- new Rect(transform.rect));
-
- // Also calculate the target task rect when there are two tasks.
- stack.addTask(new Task());
- tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
- systemInsets.top - systemInsets.bottom, 0, 0);
- tsv.setStackScrollToInitialState();
- transform = algo.getStackTransform(1, tsv.getStackScroll());
- transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- replyData.putParcelable(AlternateRecentsComponent.KEY_TWO_TASK_STACK_RECT,
- new Rect(transform.rect));
-
- // Also calculate the target task rect when there are two tasks.
- stack.addTask(new Task());
- tsv.computeRects(taskStackBounds.width(), taskStackBounds.height() -
- systemInsets.top - systemInsets.bottom, 0, 0);
- tsv.setStackScrollToInitialState();
- transform = algo.getStackTransform(2, tsv.getStackScroll());
- transform.rect.offset(taskStackBounds.left, taskStackBounds.top);
- replyData.putParcelable(AlternateRecentsComponent.KEY_MULTIPLE_TASK_STACK_RECT,
- new Rect(transform.rect));
-
- data.putParcelable(AlternateRecentsComponent.KEY_CONFIGURATION_DATA, replyData);
- Message reply = Message.obtain(null,
- AlternateRecentsComponent.MSG_UPDATE_FOR_CONFIGURATION, 0, 0);
- reply.setData(data);
- msg.replyTo.send(reply);
- } catch (RemoteException re) {
- re.printStackTrace();
- }
- } else if (msg.what == AlternateRecentsComponent.MSG_HIDE_RECENTS) {
- // Send a broadcast to hide recents
- Intent intent = new Intent(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (msg.arg1 != 0) {
- intent.putExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, true);
- }
- context.sendBroadcast(intent);
- } else if (msg.what == AlternateRecentsComponent.MSG_TOGGLE_RECENTS) {
- // Send a broadcast to toggle recents
- Intent intent = new Intent(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- context.sendBroadcast(intent);
-
- // Time this path
- if (Console.Enabled) {
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
- }
- } else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
- // Send a broadcast to start the enter animation
- Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- context.sendBroadcast(intent);
- }
- }
-}
-
-/* Service */
-public class RecentsService extends Service {
- final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
- final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
- final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
- final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
-
- Messenger mSystemUIMessenger = new Messenger(new SystemUIMessageHandler(this));
-
- @Override
- public void onCreate() {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onCreate]");
- }
- super.onCreate();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onBind]");
- }
- return mSystemUIMessenger.getBinder();
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onUnbind]");
- }
- return super.onUnbind(intent);
- }
-
- @Override
- public void onRebind(Intent intent) {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onRebind]");
- }
- super.onRebind(intent);
- }
-
- @Override
- public void onDestroy() {
- if (Console.Enabled) {
- Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsService|onDestroy]");
- }
- super.onDestroy();
- }
-
- @Override
- public void onTrimMemory(int level) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- if (loader != null) {
- loader.onTrimMemory(level);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Console.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/recents/Console.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
index 0cb74b9..28ac9d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Console.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Console.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.content.ComponentCallbacks2;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
index d90e2be..d000985 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DebugTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DebugTrigger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.os.Handler;
import android.os.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index f0b2cb6..4456066 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.os.Handler;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java b/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java
new file mode 100644
index 0000000..ec3c39c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/NamedCounter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.misc;
+
+/**
+ * Used to generate successive incremented names.
+ */
+public class NamedCounter {
+
+ int mCount;
+ String mPrefix = "";
+ String mSuffix = "";
+
+ public NamedCounter(String prefix, String suffix) {
+ mPrefix = prefix;
+ mSuffix = suffix;
+ }
+
+ /** Returns the next name. */
+ public String nextName() {
+ String name = mPrefix + mCount + mSuffix;
+ mCount++;
+ return name;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index d525546..31825af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 3765d1c..05c0f58 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -30,14 +30,17 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -47,7 +50,9 @@
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.systemui.recents.Constants;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -128,6 +133,7 @@
rti.baseIntent = new Intent();
rti.baseIntent.setComponent(cn);
rti.description = description;
+ rti.firstActiveTime = rti.lastActiveTime = i;
if (i % 2 == 0) {
rti.taskDescription = new ActivityManager.TaskDescription(description,
Bitmap.createBitmap(mDummyIcon),
@@ -208,7 +214,7 @@
return thumbnail;
}
- Bitmap thumbnail = Utilities.getThumbnail(mAm, taskId);
+ Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
if (thumbnail != null) {
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
// left pixel, then assume the whole thumbnail is transparent. Generally, proper
@@ -224,6 +230,25 @@
return thumbnail;
}
+ /**
+ * Returns a task thumbnail from the activity manager
+ */
+ public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
+ ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
+ Bitmap thumbnail = taskThumbnail.mainThumbnail;
+ ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
+ if (thumbnail == null && descriptor != null) {
+ thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
+ }
+ if (descriptor != null) {
+ try {
+ descriptor.close();
+ } catch (IOException e) {
+ }
+ }
+ return thumbnail;
+ }
+
/** Moves a task to the front with the specified activity options */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
@@ -371,6 +396,17 @@
}
/**
+ * Returns the window rect.
+ */
+ public Rect getWindowRect() {
+ Rect windowRect = new Rect();
+ if (mWm == null) return windowRect;
+
+ mWm.getDefaultDisplay().getRectSize(windowRect);
+ return windowRect;
+ }
+
+ /**
* Takes a screenshot of the current surface.
*/
public Bitmap takeScreenshot() {
@@ -378,4 +414,11 @@
mDisplay.getDisplayInfo(di);
return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
}
+
+ /**
+ * Takes a screenshot of the current app.
+ */
+ public Bitmap takeAppScreenshot() {
+ return takeScreenshot();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/recents/Utilities.java
rename to packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 6a7cfcc..bda195b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.recents;
+package com.android.systemui.recents.misc;
import android.app.ActivityManager;
import android.graphics.Bitmap;
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.ParcelFileDescriptor;
+import com.android.systemui.recents.RecentsConfiguration;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -95,21 +96,4 @@
throws IllegalAccessException, InvocationTargetException {
sPropertyMethod.invoke(null, property, value);
}
-
- /** Retrieves a task thumbnail from the activity manager */
- public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
- ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
- Bitmap thumbnail = taskThumbnail.mainThumbnail;
- final ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
- if (thumbnail == null && descriptor != null) {
- thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor());
- }
- if (descriptor != null) {
- try {
- descriptor.close();
- } catch (IOException e) {
- }
- }
- return thumbnail;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index cbe39e3..2d50659 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -21,7 +21,7 @@
import android.content.Context;
import android.os.Looper;
import com.android.internal.content.PackageMonitor;
-import com.android.systemui.recents.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.HashSet;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 9d8d746..29f1440 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -28,10 +28,10 @@
import android.os.HandlerThread;
import android.os.UserHandle;
import android.util.Pair;
-import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.SystemServicesProxy;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.ArrayList;
import java.util.Collections;
@@ -368,10 +368,9 @@
return mSystemServicesProxy;
}
- private List<ActivityManager.RecentTaskInfo> getRecentTasks() {
+ private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
long t1 = System.currentTimeMillis();
- SystemServicesProxy ssp = mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> tasks =
ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
@@ -402,7 +401,7 @@
// Get the recent tasks
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks();
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
// Add each task to the task stack
t1 = System.currentTimeMillis();
@@ -430,7 +429,7 @@
// Create a new task
Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel,
- activityIcon, activityColor, t.userId, t.lastActiveTime);
+ activityIcon, activityColor, t.userId, t.firstActiveTime, t.lastActiveTime);
// Preload the specified number of apps
if (i >= (taskCount - preloadCount)) {
@@ -495,6 +494,9 @@
"" + (System.currentTimeMillis() - t1) + "ms");
}
+ // Simulate the groupings that we describe
+ stack.createSimulatedAffiliatedGroupings();
+
// Start the task loader
mLoader.start(context);
@@ -509,6 +511,24 @@
return root;
}
+ /** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
+ public static TaskStack getShallowTaskStack(SystemServicesProxy ssp) {
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
+ TaskStack stack = new TaskStack();
+
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ ActivityManager.RecentTaskInfo t = tasks.get(i);
+ ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent(), t.userId);
+ if (info == null) continue;
+
+ stack.addTask(new Task(t.persistentId, true, t.baseIntent, null, null, 0, 0,
+ t.firstActiveTime, t.lastActiveTime));
+ }
+ stack.createSimulatedAffiliatedGroupings();
+ return stack;
+ }
+
/** Acquires the task resource data directly from the pool. */
public void loadTaskData(Task t) {
Drawable applicationIcon = mApplicationIconCache.get(t.key);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index abfb221..002395f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -19,7 +19,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
/**
@@ -39,12 +39,14 @@
public final int id;
public final Intent baseIntent;
public final int userId;
+ public long firstActiveTime;
public long lastActiveTime;
- public TaskKey(int id, Intent intent, int userId, long lastActiveTime) {
+ public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
this.id = id;
this.baseIntent = intent;
this.userId = userId;
+ this.firstActiveTime = firstActiveTime;
this.lastActiveTime = lastActiveTime;
}
@@ -76,6 +78,7 @@
}
public TaskKey key;
+ public TaskGrouping group;
public Drawable applicationIcon;
public Drawable activityIcon;
public String activityLabel;
@@ -92,8 +95,9 @@
}
public Task(int id, boolean isActive, Intent intent, String activityTitle,
- Drawable activityIcon, int colorPrimary, int userId, long lastActiveTime) {
- this.key = new TaskKey(id, intent, userId, lastActiveTime);
+ Drawable activityIcon, int colorPrimary, int userId, long firstActiveTime,
+ long lastActiveTime) {
+ this.key = new TaskKey(id, intent, userId, firstActiveTime, lastActiveTime);
this.activityLabel = activityTitle;
this.activityIcon = activityIcon;
this.colorPrimary = colorPrimary;
@@ -107,6 +111,14 @@
mCb = cb;
}
+ /** Set the grouping */
+ public void setGroup(TaskGrouping group) {
+ if (group != null && this.group != null) {
+ throw new RuntimeException("This task is already assigned to a group.");
+ }
+ this.group = group;
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
this.applicationIcon = applicationIcon;
@@ -134,6 +146,11 @@
@Override
public String toString() {
- return "Task: " + key.baseIntent.getComponent().getPackageName() + " [" + super.toString() + "]";
+ String groupAffiliation = "no group";
+ if (group != null) {
+ groupAffiliation = group.affiliation;
+ }
+ return "Task (" + groupAffiliation + "): " + key.baseIntent.getComponent().getPackageName() +
+ " [" + super.toString() + "]";
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
new file mode 100644
index 0000000..3a18bce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -0,0 +1,53 @@
+package com.android.systemui.recents.model;
+
+import java.util.ArrayList;
+
+/** Represents a grouping of tasks witihin a stack. */
+public class TaskGrouping {
+
+ String affiliation;
+ long latestActiveTimeInGroup;
+
+ ArrayList<Task.TaskKey> mTasks = new ArrayList<Task.TaskKey>();
+
+ /** Creates a group with a specified affiliation. */
+ public TaskGrouping(String affiliation) {
+ this.affiliation = affiliation;
+ }
+
+ /** Adds a new task to this group. */
+ void addTask(Task t) {
+ mTasks.add(t.key);
+ if (t.key.lastActiveTime > latestActiveTimeInGroup) {
+ latestActiveTimeInGroup = t.key.lastActiveTime;
+ }
+ t.setGroup(this);
+ }
+
+ /** Removes a task from this group. */
+ void removeTask(Task t) {
+ mTasks.remove(t.key);
+ latestActiveTimeInGroup = 0;
+ int taskCount = mTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ long lastActiveTime = mTasks.get(i).lastActiveTime;
+ if (lastActiveTime > latestActiveTimeInGroup) {
+ latestActiveTimeInGroup = lastActiveTime;
+ }
+ }
+ t.setGroup(null);
+ }
+
+ /** Gets the front task */
+ public boolean isFrontMostTask(Task t) {
+ return t.key.equals(mTasks.get(mTasks.size() - 1));
+ }
+
+ /** Finds the index of a given task in a group. */
+ public int indexOf(Task t) {
+ return mTasks.indexOf(t.key);
+ }
+
+ /** Returns the number of tasks in this group. */
+ public int getTaskCount() { return mTasks.size(); }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 24e01bd..ffd2857 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,9 +16,13 @@
package com.android.systemui.recents.model;
-import android.content.Context;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.misc.NamedCounter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
@@ -36,6 +40,7 @@
class FilteredTaskList {
ArrayList<Task> mTasks = new ArrayList<Task>();
ArrayList<Task> mFilteredTasks = new ArrayList<Task>();
+ HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<Task.TaskKey, Integer>();
TaskFilter mFilter;
/** Sets the task filter, saving the current touch state */
@@ -83,7 +88,7 @@
/** Returns the index of this task in the list of filtered tasks */
int indexOf(Task t) {
- return mFilteredTasks.indexOf(t);
+ return mTaskIndices.get(t.key);
}
/** Returns the size of the list of filtered tasks */
@@ -93,7 +98,7 @@
/** Returns whether the filtered list contains this task */
boolean contains(Task t) {
- return mFilteredTasks.contains(t);
+ return mTaskIndices.containsKey(t.key);
}
/** Updates the list of filtered tasks whenever the base task list changes */
@@ -110,6 +115,17 @@
} else {
mFilteredTasks.addAll(mTasks);
}
+ updateFilteredTaskIndices();
+ }
+
+ /** Updates the mapping of tasks to indices. */
+ private void updateFilteredTaskIndices() {
+ mTaskIndices.clear();
+ int taskCount = mFilteredTasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = mFilteredTasks.get(i);
+ mTaskIndices.put(t.key, i);
+ }
}
/** Returns whether this task list is filtered */
@@ -127,7 +143,8 @@
* The task stack contains a list of multiple tasks.
*/
public class TaskStack {
- /* Task stack callbacks */
+
+ /** Task stack callbacks */
public interface TaskStackCallbacks {
/* Notifies when a task has been added to the stack */
public void onStackTaskAdded(TaskStack stack, Task t);
@@ -139,11 +156,25 @@
public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
}
+ /** A pair of indices representing the group and task positions in the stack and group. */
+ public static class GroupTaskIndex {
+ public int groupIndex; // Index in the stack
+ public int taskIndex; // Index in the group
+
+ public GroupTaskIndex() {}
+
+ public GroupTaskIndex(int gi, int ti) {
+ groupIndex = gi;
+ taskIndex = ti;
+ }
+ }
+
FilteredTaskList mTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
- public TaskStack() {
- }
+ ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
+ HashMap<String, TaskGrouping> mAffinitiesGroups = new HashMap<String, TaskGrouping>();
+ HashMap<TaskGrouping, Integer> mGroupsIndices = new HashMap<TaskGrouping, Integer>();
/** Sets the callbacks for this task stack */
public void setCallbacks(TaskStackCallbacks cb) {
@@ -161,8 +192,16 @@
/** Removes a task */
public void removeTask(Task t) {
if (mTaskList.contains(t)) {
+ // Remove the task from the list
mTaskList.remove(t);
+ // Remove it from the group as well, and if it is empty, remove the group
+ TaskGrouping group = t.group;
+ group.removeTask(t);
+ if (group.getTaskCount() == 0) {
+ removeGroup(group);
+ }
if (mCb != null) {
+ // Notify that a task has been removed
mCb.onStackTaskRemoved(this, t);
}
}
@@ -170,10 +209,20 @@
/** Sets a few tasks in one go */
public void setTasks(List<Task> tasks) {
- int taskCount = mTaskList.getTasks().size();
+ ArrayList<Task> taskList = mTaskList.getTasks();
+ int taskCount = taskList.size();
for (int i = 0; i < taskCount; i++) {
- Task t = mTaskList.getTasks().get(i);
+ Task t = taskList.get(i);
+ // Remove the task from the list
+ mTaskList.remove(t);
+ // Remove it from the group as well, and if it is empty, remove the group
+ TaskGrouping group = t.group;
+ group.removeTask(t);
+ if (group.getTaskCount() == 0) {
+ removeGroup(group);
+ }
if (mCb != null) {
+ // Notify that a task has been removed
mCb.onStackTaskRemoved(this, t);
}
}
@@ -185,6 +234,11 @@
}
}
+ /** Gets the front task */
+ public Task getFrontMostTask() {
+ return mTaskList.getTasks().get(mTaskList.size() - 1);
+ }
+
/** Gets the tasks */
public ArrayList<Task> getTasks() {
return mTaskList.getTasks();
@@ -200,10 +254,7 @@
return mTaskList.indexOf(t);
}
- /** Tests whether a task is in this current task stack */
- public boolean containsTask(Task t) {
- return mTaskList.contains(t);
- }
+ /******** Filtering ********/
/** Filters the stack into tasks similar to the one specified */
public void filterTasks(final Task t) {
@@ -238,6 +289,135 @@
return mTaskList.hasFilter();
}
+ /******** Grouping ********/
+
+ /** Adds a group to the set */
+ public void addGroup(TaskGrouping group) {
+ mGroups.add(group);
+ mAffinitiesGroups.put(group.affiliation, group);
+ updateTaskGroupingIndices();
+ }
+
+ public void removeGroup(TaskGrouping group) {
+ // XXX: Ensure that there are no more tasks in this group
+ mGroups.remove(group);
+ mAffinitiesGroups.remove(group.affiliation);
+ mGroupsIndices.remove(group);
+ updateTaskGroupingIndices();
+ }
+
+ /** Adds a mapping from a task to a group. */
+ public void addTaskToGroup(TaskGrouping group, Task task) {
+ if (!mAffinitiesGroups.containsKey(group.affiliation)) {
+ throw new RuntimeException("Unexpected group");
+ }
+ group.addTask(task);
+ }
+
+ /** Returns the group with the specified affiliation. */
+ public TaskGrouping getGroupWithAffiliation(String affiliation) {
+ return mAffinitiesGroups.get(affiliation);
+ }
+
+ /** Returns the number of groups. */
+ public int getGroupingCount() {
+ return mGroups.size();
+ }
+
+ /** Returns the group and task indices for a given task. */
+ public void getGroupIndexForTask(Task task, GroupTaskIndex indices) {
+ indices.groupIndex = mGroupsIndices.get(task.group);
+ indices.taskIndex = task.group.indexOf(task);
+ }
+
+ /**
+ * Temporary: This method will simulate affiliation groups by
+ */
+ public void createSimulatedAffiliatedGroupings() {
+ if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
+ HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
+ // Sort all tasks by increasing firstActiveTime of the task
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ Collections.sort(tasks, new Comparator<Task>() {
+ @Override
+ public int compare(Task task, Task task2) {
+ return (int) (task.key.firstActiveTime - task2.key.firstActiveTime);
+ }
+ });
+ // Create groups when sequential packages are the same
+ NamedCounter counter = new NamedCounter("task-group", "");
+ int taskCount = tasks.size();
+ String prevPackage = "";
+ String prevAffiliation = "";
+ for (int i = 0; i < taskCount; i++) {
+ Task t = tasks.get(i);
+ String packageName = t.key.baseIntent.getComponent().getPackageName();
+ TaskGrouping group;
+ if (packageName.equals(prevPackage)) {
+ group = getGroupWithAffiliation(prevAffiliation);
+ } else {
+ String affiliation = counter.nextName();
+ group = new TaskGrouping(affiliation);
+ addGroup(group);
+ prevAffiliation = affiliation;
+ prevPackage = packageName;
+ }
+ group.addTask(t);
+ taskMap.put(t.key, t);
+ }
+ // Sort groups by increasing latestActiveTime of the group
+ Collections.sort(mGroups, new Comparator<TaskGrouping>() {
+ @Override
+ public int compare(TaskGrouping taskGrouping, TaskGrouping taskGrouping2) {
+ return (int) (taskGrouping.latestActiveTimeInGroup -
+ taskGrouping2.latestActiveTimeInGroup);
+ }
+ });
+ updateTaskGroupingIndices();
+ // Sort group tasks by increasing firstActiveTime of the task, and also build a new list of
+ // tasks
+ int taskIndex = 0;
+ int groupCount = mGroups.size();
+ for (int i = 0; i < groupCount; i++) {
+ TaskGrouping group = mGroups.get(i);
+ Collections.sort(group.mTasks, new Comparator<Task.TaskKey>() {
+ @Override
+ public int compare(Task.TaskKey taskKey, Task.TaskKey taskKey2) {
+ return (int) (taskKey.firstActiveTime - taskKey2.firstActiveTime);
+ }
+ });
+ ArrayList<Task.TaskKey> groupTasks = group.mTasks;
+ int groupTaskCount = groupTasks.size();
+ for (int j = 0; j < groupTaskCount; j++) {
+ tasks.set(taskIndex, taskMap.get(groupTasks.get(j)));
+ taskIndex++;
+ }
+ }
+ mTaskList.set(tasks);
+ } else {
+ // Create a group per task
+ NamedCounter counter = new NamedCounter("task-group", "");
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task t = tasks.get(i);
+ TaskGrouping group = new TaskGrouping(counter.nextName());
+ addGroup(group);
+ group.addTask(t);
+ }
+ }
+ }
+
+ /** Updates the mapping of tasks to indices. */
+ private void updateTaskGroupingIndices() {
+ mGroupsIndices.clear();
+ int groupsCount = mGroups.size();
+ for (int i = 0; i < groupsCount; i++) {
+ TaskGrouping g = mGroups.get(i);
+ mGroupsIndices.put(g, i);
+ }
+ }
+
@Override
public String toString() {
String str = "Tasks:\n";
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
index 6568b1a..49a7ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -31,7 +30,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index f203d3e..87d45ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,7 +34,7 @@
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.RecentsPackageMonitor;
@@ -438,23 +438,25 @@
}
// Upfront the processing of the thumbnail
- TaskViewTransform transform;
+ TaskViewTransform transform = new TaskViewTransform();
View sourceView = tv;
int offsetX = 0;
int offsetY = 0;
int stackScroll = stackView.getStackScroll();
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+ stack.getGroupIndexForTask(task, groupTaskIndex);
if (tv == null) {
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
sourceView = stackView;
- transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
- stackScroll);
+ transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+ groupTaskIndex.taskIndex, stackScroll, transform);
offsetX = transform.rect.left;
offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
- transform = stackView.getStackAlgorithm().getStackTransform(stack.indexOfTask(task),
- stackScroll);
+ transform = stackView.getStackAlgorithm().getStackTransform(groupTaskIndex.groupIndex,
+ groupTaskIndex.taskIndex, stackScroll, transform);
}
// Compute the thumbnail to scale up from
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index cae6bd7..8409227a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -28,7 +28,7 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 9c60603..4ed3b59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -34,7 +34,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index dd47fddf..4a1faee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -31,18 +31,19 @@
import android.widget.FrameLayout;
import android.widget.OverScroller;
import com.android.systemui.R;
-import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.DozeTrigger;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.ReferenceCountedTrigger;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.DozeTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Set;
@@ -180,7 +181,18 @@
mStackViewsDirty = true;
}
- /** Finds the child view given a specific task */
+ /** Returns a mapping of child view to Task. */
+ HashMap<Task, TaskView> getTaskChildViewMap() {
+ HashMap<Task, TaskView> taskViewMap = new HashMap<Task, TaskView>();
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ taskViewMap.put(tv.getTask(), tv);
+ }
+ return taskViewMap;
+ }
+
+ /** Finds the child view given a specific task. */
TaskView getChildViewForTask(Task t) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -225,9 +237,11 @@
}
// Update the stack transforms
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
for (int i = taskCount - 1; i >= 0; i--) {
- TaskViewTransform transform = mStackAlgorithm.getStackTransform(i, stackScroll,
- taskTransforms.get(i));
+ mStack.getGroupIndexForTask(tasks.get(i), groupTaskIndex);
+ TaskViewTransform transform = mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex,
+ groupTaskIndex.taskIndex, stackScroll, taskTransforms.get(i));
if (transform.visible) {
if (frontMostVisibleIndex < 0) {
frontMostVisibleIndex = i;
@@ -253,6 +267,11 @@
if (visibleRangeOut != null) {
visibleRangeOut[0] = frontMostVisibleIndex;
visibleRangeOut[1] = backMostVisibleIndex;
+ if (Console.Enabled) {
+ Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
+ "[TaskStackView|updateStackTransforms]",
+ "Back: " + backMostVisibleIndex + " Front: " + frontMostVisibleIndex);
+ }
}
}
@@ -278,58 +297,57 @@
"mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
}
if (mStackViewsDirty) {
- // XXX: Consider using TaskViewTransform pool to prevent allocations
- // XXX: Iterate children views, update transforms and remove all that are not visible
- // For all remaining tasks, update transforms and if visible add the view
-
// Get all the task transforms
- int[] visibleRange = mTmpVisibleRange;
- int stackScroll = getStackScroll();
ArrayList<Task> tasks = mStack.getTasks();
+ int stackScroll = getStackScroll();
+ int[] visibleRange = mTmpVisibleRange;
updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
+ TaskViewTransform tmpTransform = new TaskViewTransform();
+ TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex();
- // Update the visible state of all the tasks
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- TaskViewTransform transform = mCurrentTaskTransforms.get(i);
- TaskView tv = getChildViewForTask(task);
-
- if (transform.visible) {
- if (tv == null) {
- tv = mViewPool.pickUpViewFromPool(task, task);
- // When we are picking up a new view from the view pool, prepare it for any
- // following animation by putting it in a reasonable place
- if (mStackViewsAnimationDuration > 0 && i != 0) {
- int fromIndex = (transform.t < 0) ?
- Math.max(0, (visibleRange[1] - 1)) :
- Math.min(taskCount - 1, (visibleRange[0] + 1));
- tv.updateViewPropertiesToTaskTransform(
- mStackAlgorithm.getStackTransform(fromIndex, stackScroll), 0);
- }
- }
- } else {
- if (tv != null) {
- mViewPool.returnViewToPool(tv);
- }
- }
- }
-
- // Update all the remaining view children
- // NOTE: We have to iterate in reverse where because we are removing views directly
+ // Return all the invisible children to the pool
+ HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (taskIndex < 0 || !mCurrentTaskTransforms.get(taskIndex).visible) {
+ if (taskIndex < visibleRange[1] || taskIndex > visibleRange[0]) {
+ taskChildViewMap.remove(task);
mViewPool.returnViewToPool(tv);
- } else {
- tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
- mStackViewsAnimationDuration);
}
}
+ // Pick up all the newly visible children and update all the existing children
+ boolean isValidVisibleRange = visibleRange[0] != -1 && visibleRange[1] != -1;
+ for (int i = visibleRange[0]; isValidVisibleRange && i >= visibleRange[1]; i--) {
+ Task task = tasks.get(i);
+ TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+ TaskView tv = taskChildViewMap.get(task);
+ int taskIndex = mStack.indexOfTask(task);
+
+ if (tv == null) {
+ tv = mViewPool.pickUpViewFromPool(task, task);
+ if (mStackViewsAnimationDuration > 0) {
+ // For items in the list, put them in start animating them from the
+ // approriate ends of the list where they are expected to appear
+ Task fromTask = (transform.t < 0) ?
+ tasks.get(visibleRange[1]) :
+ tasks.get(visibleRange[0]);
+ mStack.getGroupIndexForTask(fromTask, gti);
+ tmpTransform = mStackAlgorithm.getStackTransform(
+ (transform.t < 0) ? gti.groupIndex - 1 : gti.groupIndex + 1,
+ (transform.t < 0) ? gti.taskIndex - 1 : gti.taskIndex + 1,
+ stackScroll, tmpTransform);
+ tv.updateViewPropertiesToTaskTransform(tmpTransform, 0);
+ }
+ }
+
+ // Update and animate the task into place
+ tv.updateViewPropertiesToTaskTransform(mCurrentTaskTransforms.get(taskIndex),
+ mStackViewsAnimationDuration);
+ }
+
if (Console.Enabled) {
Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
" [TaskStackView|viewChildren]", "" + getChildCount());
@@ -357,7 +375,7 @@
}
/** Computes the initial stack scroll for the stack. */
int getInitialStackScroll() {
- if (mStack.getTaskCount() > 2) {
+ if (mStack.getGroupingCount() > 2) {
return mMaxScroll - mStackAlgorithm.mTaskRect.height() / 2;
}
return mMaxScroll;
@@ -477,7 +495,7 @@
/** Updates the min and max virtual scroll bounds */
void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
- mStackAlgorithm.computeMinMaxScroll(mStack.getTaskCount());
+ mStackAlgorithm.computeMinMaxScroll(mStack.getGroupingCount());
mMinScroll = mStackAlgorithm.mMinScroll;
mMaxScroll = mStackAlgorithm.mMaxScroll;
@@ -794,30 +812,37 @@
return;
}
- // Animate all the task views into view
- TaskViewTransform transform = mStackAlgorithm.getStackTransform(mStack.getTaskCount() - 1,
- getInitialStackScroll());
- ctx.taskRect = transform.rect;
- ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- TaskView tv = (TaskView) getChildAt(i);
- ctx.stackViewIndex = i;
- ctx.stackViewCount = childCount;
- ctx.isFrontMost = (i == (getChildCount() - 1));
- ctx.transform = mStackAlgorithm.getStackTransform(
- mStack.indexOfTask(tv.getTask()), getStackScroll());
- tv.startEnterRecentsAnimation(ctx);
- }
-
- // Add a runnable to the post animation ref counter to clear all the views
- ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- // Start dozing
- mUIDozeTrigger.startDozing();
+ if (mStack.getTaskCount() > 0) {
+ // Animate all the task views into view
+ TaskViewTransform transform = new TaskViewTransform();
+ TaskStack.GroupTaskIndex groupTaskIndex = new TaskStack.GroupTaskIndex();
+ mStack.getGroupIndexForTask(mStack.getFrontMostTask(), groupTaskIndex);
+ mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+ getInitialStackScroll(), transform);
+ ctx.taskRect = transform.rect;
+ ctx.stackRectSansPeek = mStackAlgorithm.mStackRectSansPeek;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ ctx.stackViewIndex = i;
+ ctx.stackViewCount = childCount;
+ ctx.isFrontMost = (i == (getChildCount() - 1));
+ ctx.transform = new TaskViewTransform();
+ mStack.getGroupIndexForTask(tv.getTask(), groupTaskIndex);
+ mStackAlgorithm.getStackTransform(groupTaskIndex.groupIndex, groupTaskIndex.taskIndex,
+ getStackScroll(), ctx.transform);
+ tv.startEnterRecentsAnimation(ctx);
}
- });
+
+ // Add a runnable to the post animation ref counter to clear all the views
+ ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // Start dozing
+ mUIDozeTrigger.startDozing();
+ }
+ });
+ }
}
/** Requests this task stacks to start it's exit-recents animation. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index daa18bc..ab47757 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -19,7 +19,7 @@
import android.graphics.Rect;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.Utilities;
+import com.android.systemui.recents.misc.Utilities;
/* The layout logic for a TaskStackView */
public class TaskStackViewLayoutAlgorithm {
@@ -94,17 +94,11 @@
}
}
- /** Update/get the transform (creates a new TaskViewTransform) */
- public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
- TaskViewTransform transform = new TaskViewTransform();
- return getStackTransform(indexInStack, stackScroll, transform);
- }
-
/** Update/get the transform */
- public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
- TaskViewTransform transformOut) {
+ public TaskViewTransform getStackTransform(int groupIndexInStack, int taskIndexInGroup,
+ int stackScroll, TaskViewTransform transformOut) {
// Return early if we have an invalid index
- if (indexInStack < 0) {
+ if (groupIndexInStack < 0) {
transformOut.reset();
return transformOut;
}
@@ -113,7 +107,7 @@
int numPeekCards = StackPeekNumCards;
float overlapHeight = StackOverlapPct * mTaskRect.height();
float peekHeight = StackPeekHeightPct * mStackRect.height();
- float t = ((indexInStack * overlapHeight) - stackScroll) / overlapHeight;
+ float t = ((groupIndexInStack * overlapHeight) - stackScroll) / overlapHeight;
float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position
@@ -133,6 +127,7 @@
} else {
transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
}
+ transformOut.translationY += 100 * taskIndexInGroup;
// Set the z translation
int minZ = mConfig.taskViewTranslationZMinPx;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 304d45c..e186e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -22,7 +22,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
/* Handles touch events for a TaskStackView. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 6b06945..0d0fccc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -25,18 +25,14 @@
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.Console;
+import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -61,6 +57,7 @@
Task mTask;
boolean mTaskDataLoaded;
boolean mIsFocused;
+ boolean mIsStub;
boolean mClipViewInStack;
Rect mTmpRect = new Rect();
Paint mLayerPaint = new Paint();
@@ -133,8 +130,8 @@
// Update the outline
Outline o = new Outline();
- o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
- mConfig.taskViewShadowOutlineBottomInsetPx, mConfig.taskViewRoundedCornerRadiusPx);
+ o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+ mConfig.taskViewRoundedCornerRadiusPx);
setOutline(o);
}
@@ -469,6 +466,21 @@
mBarView.disableHwLayers();
}
+ /** Sets the stubbed state of this task view. */
+ void setStubState(boolean isStub) {
+ if (!mIsStub && isStub) {
+ // This is now a stub task view, so clip to the bar height, hide the thumbnail
+ setClipBounds(new Rect(0, 0, getMeasuredWidth(), mBarView.getMeasuredHeight()));
+ mThumbnailView.setVisibility(View.INVISIBLE);
+ // Temporary
+ mBarView.mActivityDescription.setText("Stub");
+ } else if (mIsStub && !isStub) {
+ setClipBounds(null);
+ mThumbnailView.setVisibility(View.VISIBLE);
+ }
+ mIsStub = isStub;
+ }
+
/**
* Returns whether this view should be clipped, or any views below should clip against this
* view.
@@ -573,7 +585,9 @@
mThumbnailView.rebindToTask(mTask);
mBarView.rebindToTask(mTask);
// Rebind any listeners
- mBarView.mApplicationIcon.setOnClickListener(this);
+ if (Constants.DebugFlags.App.EnableTaskFiltering) {
+ mBarView.mApplicationIcon.setOnClickListener(this);
+ }
mBarView.mDismissButton.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
if (mConfig.developerOptionsEnabled) {
@@ -592,7 +606,9 @@
mThumbnailView.unbindFromTask();
mBarView.unbindFromTask();
// Unbind any listeners
- mBarView.mApplicationIcon.setOnClickListener(null);
+ if (Constants.DebugFlags.App.EnableTaskFiltering) {
+ mBarView.mApplicationIcon.setOnClickListener(null);
+ }
mBarView.mDismissButton.setOnClickListener(null);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mBarView.mApplicationIcon.setOnLongClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index fa97d2c..ec24198 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -17,7 +17,7 @@
package com.android.systemui.recents.views;
import android.graphics.Rect;
-import com.android.systemui.recents.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
/* Common code related to view animations */
public class ViewAnimation {