summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2011-04-08 18:14:09 -0700
committerDianne Hackborn <hackbod@google.com>2011-04-08 18:16:21 -0700
commitf26fd99a7c2f554b0297760bb66336473c7db61f (patch)
tree292bc65e6d068857bda10f2f45727a7c88601b64
parentbdf7b013f81b0b56a18cc9dd2fb987b56d595650 (diff)
downloadframeworks_base-f26fd99a7c2f554b0297760bb66336473c7db61f.zip
frameworks_base-f26fd99a7c2f554b0297760bb66336473c7db61f.tar.gz
frameworks_base-f26fd99a7c2f554b0297760bb66336473c7db61f.tar.bz2
Rework thumbnails in activity manager.
We now only keep a thumbnail for the task, not for each activity. However if you use FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, we will make a new secondary thumbnail for that series of activities. There is a new API for the app to get these secondary thumbnails. Also set a default thumbnail size for non-xlarge screens so we have thumbnails on phones. (We need some smarter code in the platform for computing the actual thumbnail dimensions of the current device). And add a test app to show recent tasks + thumbnails. Change-Id: Ic36759f6635522118a2cb7f156662229a610c492
-rw-r--r--Android.mk1
-rw-r--r--core/java/android/app/ActivityManager.java67
-rw-r--r--core/java/android/app/ActivityManagerNative.java12
-rw-r--r--core/java/android/app/IActivityManager.java4
-rw-r--r--core/java/android/app/IThumbnailRetriever.aidl24
-rw-r--r--core/res/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java4
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java96
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java77
-rw-r--r--services/java/com/android/server/am/ActivityStack.java42
-rw-r--r--services/java/com/android/server/am/TaskRecord.java4
-rw-r--r--services/java/com/android/server/am/ThumbnailHolder.java24
-rw-r--r--tests/ActivityTests/Android.mk11
-rw-r--r--tests/ActivityTests/AndroidManifest.xml29
-rw-r--r--tests/ActivityTests/res/values/strings.xml19
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java99
16 files changed, 442 insertions, 75 deletions
diff --git a/Android.mk b/Android.mk
index 3f52948..317668a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -87,6 +87,7 @@ LOCAL_SRC_FILES += \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IThumbnailReceiver.aidl \
+ core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
core/java/android/app/IUiModeManager.aidl \
core/java/android/app/IWallpaperManager.aidl \
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index b6581e9..a9c9d9c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -38,6 +38,7 @@ import android.util.Log;
import com.android.internal.app.IUsageStats;
import com.android.internal.os.PkgUsageStats;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -396,9 +397,71 @@ public class ActivityManager {
}
/** @hide */
- public Bitmap getTaskThumbnail(int id) throws SecurityException {
+ public static class TaskThumbnails implements Parcelable {
+ public Bitmap mainThumbnail;
+
+ public int numSubThumbbails;
+
+ /** @hide */
+ public IThumbnailRetriever retriever;
+
+ /** @hide Magic for ActivityManagerService. Not marshalled */
+ public ArrayList<Bitmap> otherThumbnails;
+
+ public TaskThumbnails() {
+ }
+
+ public Bitmap getSubThumbnail(int index) {
+ try {
+ return retriever.getThumbnail(index);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mainThumbnail != null) {
+ dest.writeInt(1);
+ mainThumbnail.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(numSubThumbbails);
+ dest.writeStrongInterface(retriever);
+ }
+
+ public void readFromParcel(Parcel source) {
+ if (source.readInt() != 0) {
+ mainThumbnail = Bitmap.CREATOR.createFromParcel(source);
+ } else {
+ mainThumbnail = null;
+ }
+ numSubThumbbails = source.readInt();
+ retriever = IThumbnailRetriever.Stub.asInterface(source.readStrongBinder());
+ }
+
+ public static final Creator<TaskThumbnails> CREATOR = new Creator<TaskThumbnails>() {
+ public TaskThumbnails createFromParcel(Parcel source) {
+ return new TaskThumbnails(source);
+ }
+ public TaskThumbnails[] newArray(int size) {
+ return new TaskThumbnails[size];
+ }
+ };
+
+ private TaskThumbnails(Parcel source) {
+ readFromParcel(source);
+ }
+ }
+
+ /** @hide */
+ public TaskThumbnails getTaskThumbnails(int id) throws SecurityException {
try {
- return ActivityManagerNative.getDefault().getTaskThumbnail(id);
+ return ActivityManagerNative.getDefault().getTaskThumbnails(id);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return null;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6426635..f51e4d0 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -442,10 +442,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
- case GET_TASK_THUMBNAIL_TRANSACTION: {
+ case GET_TASK_THUMBNAILS_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int id = data.readInt();
- Bitmap bm = getTaskThumbnail(id);
+ ActivityManager.TaskThumbnails bm = getTaskThumbnails(id);
reply.writeNoException();
if (bm != null) {
reply.writeInt(1);
@@ -1831,16 +1831,16 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
- public Bitmap getTaskThumbnail(int id) throws RemoteException {
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int id) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(id);
- mRemote.transact(GET_TASK_THUMBNAIL_TRANSACTION, data, reply, 0);
+ mRemote.transact(GET_TASK_THUMBNAILS_TRANSACTION, data, reply, 0);
reply.readException();
- Bitmap bm = null;
+ ActivityManager.TaskThumbnails bm = null;
if (reply.readInt() != 0) {
- bm = Bitmap.CREATOR.createFromParcel(reply);
+ bm = ActivityManager.TaskThumbnails.CREATOR.createFromParcel(reply);
}
data.recycle();
reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 61e6fc8..cd26a62 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -133,7 +133,7 @@ public interface IActivityManager extends IInterface {
IThumbnailReceiver receiver) throws RemoteException;
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags) throws RemoteException;
- public Bitmap getTaskThumbnail(int taskId) throws RemoteException;
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException;
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
@@ -515,7 +515,7 @@ public interface IActivityManager extends IInterface {
int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78;
int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79;
int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80;
- int GET_TASK_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
+ int GET_TASK_THUMBNAILS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81;
int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82;
int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83;
int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84;
diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl
new file mode 100644
index 0000000..2a6737d
--- /dev/null
+++ b/core/java/android/app/IThumbnailRetriever.aidl
@@ -0,0 +1,24 @@
+/* Copyright 2011, 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 android.app;
+
+import android.graphics.Bitmap;
+
+/**
+ * System private API for retrieving thumbnails
+ */
+interface IThumbnailRetriever {
+ Bitmap getThumbnail(int index);
+}
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 968d99c..da1c157 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -19,9 +19,9 @@
-->
<resources>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">0dp</dimen>
+ <dimen name="thumbnail_width">64dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">0dp</dimen>
+ <dimen name="thumbnail_height">100dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index c5a7df2..a39bef8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -465,8 +465,10 @@ public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, O
int id = recentTasks.get(i).id;
if (title != null && title.length() > 0 && icon != null) {
if (DEBUG) Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
+ ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(
+ recentInfo.persistentId);
ActivityDescription item = new ActivityDescription(
- am.getTaskThumbnail(recentInfo.persistentId),
+ thumbs != null ? thumbs.mainThumbnail : null,
icon, title, recentInfo.description, intent, id,
index, info.packageName);
activityDescriptions.add(item);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 2e72068..ef509b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -44,6 +44,7 @@ import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
+import android.app.IThumbnailRetriever;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
@@ -2603,8 +2604,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " finishing=" + r.finishing);
r.makeFinishing();
mMainStack.mHistory.remove(i);
-
- r.inHistory = false;
+ r.takeFromHistory();
mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
mWindowManager.validateAppTokens(mMainStack.mHistory);
@@ -3808,16 +3808,7 @@ public final class ActivityManagerService extends ActivityManagerNative
r = (ActivityRecord)mMainStack.mHistory.get(index);
r.icicle = icicle;
r.haveState = true;
- if (thumbnail != null) {
- r.thumbnail = thumbnail;
- if (r.task != null) {
- r.task.lastThumbnail = r.thumbnail;
- }
- }
- r.description = description;
- if (r.task != null) {
- r.task.lastDescription = r.description;
- }
+ r.updateThumbnail(thumbnail, description);
r.stopped = true;
r.state = ActivityState.STOPPED;
if (!r.finishing) {
@@ -4851,7 +4842,6 @@ public final class ActivityManagerService extends ActivityManagerNative
ActivityRecord next =
pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null;
ActivityRecord top = null;
- CharSequence topDescription = null;
TaskRecord curTask = null;
int numActivities = 0;
int numRunning = 0;
@@ -4865,7 +4855,6 @@ public final class ActivityManagerService extends ActivityManagerNative
(top.state == ActivityState.INITIALIZING
&& top.task == r.task)) {
top = r;
- topDescription = r.description;
curTask = r.task;
numActivities = numRunning = 0;
}
@@ -4875,9 +4864,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (r.app != null && r.app.thread != null) {
numRunning++;
}
- if (topDescription == null) {
- topDescription = r.description;
- }
if (localLOGV) Slog.v(
TAG, r.intent.getComponent().flattenToShortString()
@@ -4892,13 +4878,15 @@ public final class ActivityManagerService extends ActivityManagerNative
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
if (canReadFb) {
- if (top.thumbnail != null) {
- ci.thumbnail = top.thumbnail;
- } else if (top.state == ActivityState.RESUMED) {
+ if (top.state == ActivityState.RESUMED) {
ci.thumbnail = top.stack.screenshotActivities(top);
+ } else if (top.thumbHolder != null) {
+ ci.thumbnail = top.thumbHolder.lastThumbnail;
}
}
- ci.description = topDescription;
+ if (top.thumbHolder != null) {
+ ci.description = top.thumbHolder.lastDescription;
+ }
ci.numActivities = numActivities;
ci.numRunning = numRunning;
//System.out.println(
@@ -5015,7 +5003,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- public Bitmap getTaskThumbnail(int id) {
+ public ActivityManager.TaskThumbnails getTaskThumbnails(int id) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskThumbnail()");
@@ -5024,11 +5012,61 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (tr.taskId == id) {
- if (resumed != null && resumed.task == tr) {
- return resumed.stack.screenshotActivities(resumed);
+ final ActivityManager.TaskThumbnails thumbs
+ = new ActivityManager.TaskThumbnails();
+ if (resumed != null && resumed.thumbHolder == tr) {
+ thumbs.mainThumbnail = resumed.stack.screenshotActivities(resumed);
} else {
- return tr.lastThumbnail;
+ thumbs.mainThumbnail = tr.lastThumbnail;
+ }
+ // How many different sub-thumbnails?
+ final int NA = mMainStack.mHistory.size();
+ int j = 0;
+ ThumbnailHolder holder = null;
+ while (j < NA) {
+ ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
+ j++;
+ if (ar.task == tr) {
+ holder = ar.thumbHolder;
+ break;
+ }
+ }
+ ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
+ thumbs.otherThumbnails = bitmaps;
+ ActivityRecord lastActivity = null;
+ while (j < NA) {
+ ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j);
+ j++;
+ if (ar.task != tr) {
+ break;
+ }
+ lastActivity = ar;
+ if (ar.thumbHolder != holder && holder != null) {
+ thumbs.numSubThumbbails++;
+ holder = ar.thumbHolder;
+ bitmaps.add(holder.lastThumbnail);
+ }
+ }
+ if (lastActivity != null && bitmaps.size() > 0) {
+ if (resumed == lastActivity) {
+ Bitmap bm = lastActivity.stack.screenshotActivities(lastActivity);
+ if (bm != null) {
+ bitmaps.remove(bitmaps.size()-1);
+ bitmaps.add(bm);
+ }
+ }
+ }
+ if (thumbs.numSubThumbbails > 0) {
+ thumbs.retriever = new IThumbnailRetriever.Stub() {
+ public Bitmap getThumbnail(int index) {
+ if (index < 0 || index >= thumbs.otherThumbnails.size()) {
+ return null;
+ }
+ return thumbs.otherThumbnails.get(index);
+ }
+ };
}
+ return thumbs;
}
}
}
@@ -5254,9 +5292,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r = (ActivityRecord)mMainStack.mHistory.get(index);
}
- if (thumbnail == null) {
- thumbnail = r.thumbnail;
- description = r.description;
+ if (thumbnail == null && r.thumbHolder != null) {
+ thumbnail = r.thumbHolder.lastThumbnail;
+ description = r.thumbHolder.lastDescription;
}
if (thumbnail == null && !always) {
// If there is no thumbnail, and this entry is not actually
@@ -8440,7 +8478,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final String[] args = new String[0];
for (int i=list.size()-1; i>=0; i--) {
final ActivityRecord r = (ActivityRecord)list.get(i);
- final boolean full = !brief && (complete || !r.inHistory);
+ final boolean full = !brief && (complete || !r.isInHistory());
if (needNL) {
pw.println(" ");
needNL = false;
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 8d8f303..2703481 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -47,7 +47,7 @@ import java.util.HashSet;
/**
* An entry in the history stack, representing an activity.
*/
-class ActivityRecord extends IApplicationToken.Stub {
+final class ActivityRecord extends IApplicationToken.Stub {
final ActivityManagerService service; // owner
final ActivityStack stack; // owner
final ActivityInfo info; // all about me
@@ -74,6 +74,7 @@ class ActivityRecord extends IApplicationToken.Stub {
int realTheme; // actual theme resource we will use, never 0.
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
+ ThumbnailHolder thumbHolder; // where our thumbnails should go.
long launchTime; // when we starting launching this activity
long startTime; // last time this activity was started
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
@@ -86,9 +87,7 @@ class ActivityRecord extends IApplicationToken.Stub {
ArrayList newIntents; // any pending new intents for single-top mode
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
- ProcessRecord app; // if non-null, hosting application
- Bitmap thumbnail; // icon representation of paused screen
- CharSequence description; // textual description of paused screen
+ ProcessRecord app; // if non-null, hosting application
ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
boolean frontOfTask; // is this the root activity of its task?
@@ -100,7 +99,6 @@ class ActivityRecord extends IApplicationToken.Stub {
boolean configDestroy; // need to destroy due to config change?
int configChangeFlags; // which config values have changed
boolean keysPaused; // has key dispatching been paused for it?
- boolean inHistory; // are we in the history stack?
int launchMode; // the launch mode activity attribute.
boolean visible; // does this activity's window need to be shown?
boolean sleeping; // have we told the activity to sleep?
@@ -114,6 +112,8 @@ class ActivityRecord extends IApplicationToken.Stub {
String stringName; // for caching of toString().
+ private boolean inHistory; // are we in the history stack?
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
@@ -175,6 +175,7 @@ class ActivityRecord extends IApplicationToken.Stub {
pw.print(" launchMode="); pw.println(launchMode);
pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded);
+ pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder);
if (launchTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("launchTime=");
TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime=");
@@ -328,10 +329,55 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
+ if (inHistory && !finishing) {
+ if (task != null) {
+ task.numActivities--;
+ }
+ if (newTask != null) {
+ newTask.numActivities++;
+ }
+ }
+ if (newThumbHolder == null) {
+ newThumbHolder = newTask;
+ }
+ task = newTask;
+ if (!isRoot && (intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is the start of a new sub-task.
+ if (thumbHolder == null) {
+ thumbHolder = new ThumbnailHolder();
+ }
+ } else {
+ thumbHolder = newThumbHolder;
+ }
+ }
+
+ void putInHistory() {
+ if (!inHistory) {
+ inHistory = true;
+ if (task != null && !finishing) {
+ task.numActivities++;
+ }
+ }
+ }
+
+ void takeFromHistory() {
+ if (inHistory) {
+ inHistory = false;
+ if (task != null && !finishing) {
+ task.numActivities--;
+ }
+ }
+ }
+
+ boolean isInHistory() {
+ return inHistory;
+ }
+
void makeFinishing() {
if (!finishing) {
finishing = true;
- if (task != null) {
+ if (task != null && inHistory) {
task.numActivities--;
}
}
@@ -430,6 +476,25 @@ class ActivityRecord extends IApplicationToken.Stub {
}
}
+ void updateThumbnail(Bitmap newThumbnail, CharSequence description) {
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
+ // This is a logical break in the task; it repre
+ }
+ if (thumbHolder != null) {
+ if (newThumbnail != null) {
+ thumbHolder.lastThumbnail = newThumbnail;
+ }
+ thumbHolder.lastDescription = description;
+ }
+ }
+
+ void clearThumbnail() {
+ if (thumbHolder != null) {
+ thumbHolder.lastThumbnail = null;
+ thumbHolder.lastDescription = null;
+ }
+ }
+
// IApplicationToken
public boolean mayFreezeScreenLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c087aec..e1d380b 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -69,7 +69,7 @@ import java.util.List;
/**
* State and management of a single stack of activities.
*/
-public class ActivityStack {
+final public class ActivityStack {
static final String TAG = ActivityManagerService.TAG;
static final boolean localLOGV = ActivityManagerService.localLOGV;
static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
@@ -789,10 +789,7 @@ public class ActivityStack {
mLastPausedActivity = prev;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
- prev.thumbnail = screenshotActivities(prev);
- if (prev.task != null) {
- prev.task.lastThumbnail = prev.thumbnail;
- }
+ prev.updateThumbnail(screenshotActivities(prev), null);
mService.updateCpuStats();
@@ -986,7 +983,7 @@ public class ActivityStack {
mService.reportResumedActivityLocked(next);
}
- next.thumbnail = null;
+ next.clearThumbnail();
if (mMainStack) {
mService.setFocusedActivityLocked(next);
}
@@ -1513,8 +1510,7 @@ public class ActivityStack {
addPos = i+1;
if (!startIt) {
mHistory.add(addPos, r);
- r.inHistory = true;
- r.task.numActivities++;
+ r.putInHistory();
mService.mWindowManager.addAppToken(addPos, r, r.task.taskId,
r.info.screenOrientation, r.fullscreen);
if (VALIDATE_TOKENS) {
@@ -1546,9 +1542,8 @@ public class ActivityStack {
// Slot the activity into the history stack and proceed
mHistory.add(addPos, r);
- r.inHistory = true;
+ r.putInHistory();
r.frontOfTask = newTask;
- r.task.numActivities++;
if (NH > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
@@ -1711,7 +1706,7 @@ public class ActivityStack {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
// then merge it into the same task.
- target.task = p.task;
+ target.setTask(p.task, p.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to bottom task " + p.task);
} else {
@@ -1719,7 +1714,8 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- target.task = new TaskRecord(mService.mCurTask, target.info, null);
+ target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
+ null, false);
target.task.affinityIntent = target.intent;
if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+ " out to new task " + target.task);
@@ -1729,6 +1725,7 @@ public class ActivityStack {
replyChainEnd = targetI;
}
int dstPos = 0;
+ ThumbnailHolder curThumbHolder = target.thumbHolder;
for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
p = (ActivityRecord)mHistory.get(srcPos);
if (p.finishing) {
@@ -1736,9 +1733,8 @@ public class ActivityStack {
}
if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
+ " out to target's task " + target.task);
- task.numActivities--;
- p.task = target.task;
- target.task.numActivities++;
+ p.setTask(target.task, curThumbHolder, false);
+ curThumbHolder = p.thumbHolder;
mHistory.remove(srcPos);
mHistory.add(dstPos, p);
mService.mWindowManager.moveAppToken(dstPos, p);
@@ -1867,12 +1863,10 @@ public class ActivityStack {
lastReparentPos--;
}
mHistory.remove(srcPos);
- p.task.numActivities--;
- p.task = task;
+ p.setTask(task, null, false);
mHistory.add(lastReparentPos, p);
if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+ " in to resetting task " + task);
- task.numActivities++;
mService.mWindowManager.moveAppToken(lastReparentPos, p);
mService.mWindowManager.setAppGroupId(p, p.task.taskId);
if (VALIDATE_TOKENS) {
@@ -2525,11 +2519,11 @@ public class ActivityStack {
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
- r.task = new TaskRecord(mService.mCurTask, r.info, intent);
+ r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new task " + r.task);
} else {
- r.task = reuseTask;
+ r.setTask(reuseTask, reuseTask, true);
}
newTask = true;
moveHomeToFrontFromLaunchLocked(launchFlags);
@@ -2572,7 +2566,7 @@ public class ActivityStack {
// An existing activity is starting this new activity, so we want
// to keep the new one in the same task as the one that is starting
// it.
- r.task = sourceRecord.task;
+ r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in existing task " + r.task);
@@ -2583,9 +2577,9 @@ public class ActivityStack {
final int N = mHistory.size();
ActivityRecord prev =
N > 0 ? (ActivityRecord)mHistory.get(N-1) : null;
- r.task = prev != null
+ r.setTask(prev != null
? prev.task
- : new TaskRecord(mService.mCurTask, r.info, intent);
+ : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+ " in new guessed " + r.task);
}
@@ -3403,7 +3397,7 @@ public class ActivityStack {
if (r.state != ActivityState.DESTROYED) {
r.makeFinishing();
mHistory.remove(r);
- r.inHistory = false;
+ r.takeFromHistory();
r.state = ActivityState.DESTROYED;
mService.mWindowManager.removeAppToken(r);
if (VALIDATE_TOKENS) {
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 86cec42..e8c87e1 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -23,7 +23,7 @@ import android.graphics.Bitmap;
import java.io.PrintWriter;
-class TaskRecord {
+class TaskRecord extends ThumbnailHolder {
final int taskId; // Unique identifier for this task.
final String affinity; // The affinity name for this task, or null.
Intent intent; // The original intent that started the task.
@@ -34,8 +34,6 @@ class TaskRecord {
long lastActiveTime; // Last time this task was active, including sleep.
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
- Bitmap lastThumbnail; // Last thumbnail captured for this task.
- CharSequence lastDescription; // Last description captured for this task.
String stringName; // caching of toString() result.
diff --git a/services/java/com/android/server/am/ThumbnailHolder.java b/services/java/com/android/server/am/ThumbnailHolder.java
new file mode 100644
index 0000000..02f4fcb
--- /dev/null
+++ b/services/java/com/android/server/am/ThumbnailHolder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 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.server.am;
+
+import android.graphics.Bitmap;
+
+public class ThumbnailHolder {
+ Bitmap lastThumbnail; // Last thumbnail captured for this item.
+ CharSequence lastDescription; // Last description captured for this item.
+}
diff --git a/tests/ActivityTests/Android.mk b/tests/ActivityTests/Android.mk
new file mode 100644
index 0000000..f3c6b5a
--- /dev/null
+++ b/tests/ActivityTests/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ActivityTest
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
new file mode 100644
index 0000000..6fa27ed
--- /dev/null
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.test.activity">
+ <uses-permission android:name="android.permission.GET_TASKS" />
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+ <application android:label="ActivityTest">
+ <activity android:name="ActivityTestMain">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/ActivityTests/res/values/strings.xml b/tests/ActivityTests/res/values/strings.xml
new file mode 100644
index 0000000..71705cb
--- /dev/null
+++ b/tests/ActivityTests/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources>
+ <string name="act_title">DpiTest: Unknown Screen</string>
+</resources>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
new file mode 100644
index 0000000..8c5c35a
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011 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.google.android.test.activity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.Application;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ScrollView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+public class ActivityTestMain extends Activity {
+ private void addThumbnail(LinearLayout container, Bitmap bm) {
+ ImageView iv = new ImageView(this);
+ if (bm != null) {
+ iv.setImageBitmap(bm);
+ }
+ iv.setBackgroundResource(android.R.drawable.gallery_thumb);
+ int w = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_width);
+ int h = getResources().getDimensionPixelSize(android.R.dimen.thumbnail_height);
+ container.addView(iv, new LinearLayout.LayoutParams(w, h));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ActivityManager am = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
+
+ LinearLayout top = new LinearLayout(this);
+ top.setOrientation(LinearLayout.VERTICAL);
+
+ List<ActivityManager.RecentTaskInfo> recents = am.getRecentTasks(10,
+ ActivityManager.RECENT_WITH_EXCLUDED);
+ if (recents != null) {
+ for (int i=0; i<recents.size(); i++) {
+ ActivityManager.RecentTaskInfo r = recents.get(i);
+ ActivityManager.TaskThumbnails tt = r != null
+ ? am.getTaskThumbnails(r.persistentId) : null;
+ TextView tv = new TextView(this);
+ tv.setText(r.baseIntent.getComponent().flattenToShortString());
+ top.addView(tv, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ LinearLayout item = new LinearLayout(this);
+ item.setOrientation(LinearLayout.HORIZONTAL);
+ addThumbnail(item, tt != null ? tt.mainThumbnail : null);
+ for (int j=0; j<tt.numSubThumbbails; j++) {
+ addThumbnail(item, tt.getSubThumbnail(j));
+ }
+ top.addView(item, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ }
+ }
+
+ setContentView(scrollWrap(top));
+ }
+
+ private View scrollWrap(View view) {
+ ScrollView scroller = new ScrollView(this);
+ scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT,
+ ScrollView.LayoutParams.MATCH_PARENT));
+ return scroller;
+ }
+}