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 /services | |
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
Diffstat (limited to 'services')
5 files changed, 181 insertions, 62 deletions
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. +} |