diff options
author | Dianne Hackborn <hackbod@google.com> | 2011-04-08 18:14:09 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2011-04-08 18:16:21 -0700 |
commit | f26fd99a7c2f554b0297760bb66336473c7db61f (patch) | |
tree | 292bc65e6d068857bda10f2f45727a7c88601b64 | |
parent | bdf7b013f81b0b56a18cc9dd2fb987b56d595650 (diff) | |
download | frameworks_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.mk | 1 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 67 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 12 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 4 | ||||
-rw-r--r-- | core/java/android/app/IThumbnailRetriever.aidl | 24 | ||||
-rw-r--r-- | core/res/res/values/dimens.xml | 4 | ||||
-rw-r--r-- | packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java | 4 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 96 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityRecord.java | 77 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityStack.java | 42 | ||||
-rw-r--r-- | services/java/com/android/server/am/TaskRecord.java | 4 | ||||
-rw-r--r-- | services/java/com/android/server/am/ThumbnailHolder.java | 24 | ||||
-rw-r--r-- | tests/ActivityTests/Android.mk | 11 | ||||
-rw-r--r-- | tests/ActivityTests/AndroidManifest.xml | 29 | ||||
-rw-r--r-- | tests/ActivityTests/res/values/strings.xml | 19 | ||||
-rw-r--r-- | tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java | 99 |
16 files changed, 442 insertions, 75 deletions
@@ -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; + } +} |