Skip to content

Commit 78bbcd3

Browse files
mikejurkaAndroid (Google) Code Review
authored andcommitted
Merge "Refactoring loading of recent apps" into ics-mr0
2 parents 1a4c433 + ab48b68 commit 78bbcd3

File tree

8 files changed

+506
-328
lines changed

8 files changed

+506
-328
lines changed

packages/SystemUI/src/com/android/systemui/recent/Choreographer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ void startAnimation(boolean appearing) {
121121
createAnimation(appearing);
122122

123123
mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
124+
mContentView.buildLayer();
124125
mContentAnim.start();
125126

126127
mVisible = appearing;
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/*
2+
* Copyright (C) 2011 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.android.systemui.recent;
18+
19+
import java.util.ArrayList;
20+
import java.util.HashSet;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Set;
24+
25+
import android.app.ActivityManager;
26+
import android.content.ComponentName;
27+
import android.content.Context;
28+
import android.content.Intent;
29+
import android.content.pm.ActivityInfo;
30+
import android.content.pm.PackageManager;
31+
import android.content.pm.ResolveInfo;
32+
import android.content.res.Resources;
33+
import android.graphics.Bitmap;
34+
import android.graphics.Canvas;
35+
import android.graphics.drawable.Drawable;
36+
import android.os.AsyncTask;
37+
import android.os.Handler;
38+
import android.os.Process;
39+
import android.os.SystemClock;
40+
import android.util.DisplayMetrics;
41+
import android.util.Log;
42+
import android.util.LruCache;
43+
44+
import com.android.systemui.R;
45+
import com.android.systemui.statusbar.phone.PhoneStatusBar;
46+
import com.android.systemui.statusbar.tablet.TabletStatusBar;
47+
48+
public class RecentTasksLoader {
49+
static final String TAG = "RecentTasksLoader";
50+
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
51+
52+
private static final int DISPLAY_TASKS = 20;
53+
private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
54+
55+
private Context mContext;
56+
private RecentsPanelView mRecentsPanel;
57+
58+
private AsyncTask<Void, Integer, Void> mThumbnailLoader;
59+
private final Handler mHandler;
60+
61+
private int mIconDpi;
62+
private Bitmap mDefaultThumbnailBackground;
63+
64+
public RecentTasksLoader(Context context) {
65+
mContext = context;
66+
67+
final Resources res = context.getResources();
68+
69+
// get the icon size we want -- on tablets, we use bigger icons
70+
boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets);
71+
int density = res.getDisplayMetrics().densityDpi;
72+
if (isTablet) {
73+
if (density == DisplayMetrics.DENSITY_LOW) {
74+
mIconDpi = DisplayMetrics.DENSITY_MEDIUM;
75+
} else if (density == DisplayMetrics.DENSITY_MEDIUM) {
76+
mIconDpi = DisplayMetrics.DENSITY_HIGH;
77+
} else if (density == DisplayMetrics.DENSITY_HIGH) {
78+
mIconDpi = DisplayMetrics.DENSITY_XHIGH;
79+
} else if (density == DisplayMetrics.DENSITY_XHIGH) {
80+
// We'll need to use a denser icon, or some sort of a mipmap
81+
mIconDpi = DisplayMetrics.DENSITY_XHIGH;
82+
}
83+
} else {
84+
mIconDpi = res.getDisplayMetrics().densityDpi;
85+
}
86+
mIconDpi = isTablet ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
87+
88+
// Render the default thumbnail background
89+
int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
90+
int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
91+
int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
92+
93+
mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
94+
Canvas c = new Canvas(mDefaultThumbnailBackground);
95+
c.drawColor(color);
96+
97+
// If we're using the cache, begin listening to the activity manager for
98+
// updated thumbnails
99+
final ActivityManager am = (ActivityManager)
100+
mContext.getSystemService(Context.ACTIVITY_SERVICE);
101+
102+
mHandler = new Handler();
103+
}
104+
105+
public void setRecentsPanel(RecentsPanelView recentsPanel) {
106+
mRecentsPanel = recentsPanel;
107+
}
108+
109+
// Create an TaskDescription, returning null if the title or icon is null, or if it's the
110+
// home activity
111+
TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
112+
ComponentName origActivity, CharSequence description, ActivityInfo homeInfo) {
113+
Intent intent = new Intent(baseIntent);
114+
if (origActivity != null) {
115+
intent.setComponent(origActivity);
116+
}
117+
final PackageManager pm = mContext.getPackageManager();
118+
if (homeInfo == null) {
119+
homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
120+
.resolveActivityInfo(pm, 0);
121+
}
122+
123+
intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
124+
| Intent.FLAG_ACTIVITY_NEW_TASK);
125+
final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
126+
if (resolveInfo != null) {
127+
final ActivityInfo info = resolveInfo.activityInfo;
128+
final String title = info.loadLabel(pm).toString();
129+
Drawable icon = getFullResIcon(resolveInfo, pm);
130+
131+
if (title != null && title.length() > 0 && icon != null) {
132+
if (DEBUG) Log.v(TAG, "creating activity desc for id="
133+
+ persistentTaskId + ", label=" + title);
134+
135+
TaskDescription item = new TaskDescription(taskId,
136+
persistentTaskId, resolveInfo, baseIntent, info.packageName,
137+
description);
138+
item.setLabel(title);
139+
item.setIcon(icon);
140+
141+
// Don't load the current home activity.
142+
if (homeInfo != null
143+
&& homeInfo.packageName.equals(intent.getComponent().getPackageName())
144+
&& homeInfo.name.equals(intent.getComponent().getClassName())) {
145+
return null;
146+
}
147+
148+
return item;
149+
} else {
150+
if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
151+
}
152+
}
153+
return null;
154+
}
155+
156+
void loadThumbnail(TaskDescription td) {
157+
final ActivityManager am = (ActivityManager)
158+
mContext.getSystemService(Context.ACTIVITY_SERVICE);
159+
ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId);
160+
161+
if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
162+
+ td + ": " + thumbs.mainThumbnail);
163+
synchronized (td) {
164+
if (thumbs != null && thumbs.mainThumbnail != null) {
165+
td.setThumbnail(thumbs.mainThumbnail);
166+
} else {
167+
td.setThumbnail(mDefaultThumbnailBackground);
168+
}
169+
}
170+
}
171+
172+
Drawable getFullResDefaultActivityIcon() {
173+
return getFullResIcon(Resources.getSystem(),
174+
com.android.internal.R.mipmap.sym_def_app_icon);
175+
}
176+
177+
Drawable getFullResIcon(Resources resources, int iconId) {
178+
try {
179+
return resources.getDrawableForDensity(iconId, mIconDpi);
180+
} catch (Resources.NotFoundException e) {
181+
return getFullResDefaultActivityIcon();
182+
}
183+
}
184+
185+
private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
186+
Resources resources;
187+
try {
188+
resources = packageManager.getResourcesForApplication(
189+
info.activityInfo.applicationInfo);
190+
} catch (PackageManager.NameNotFoundException e) {
191+
resources = null;
192+
}
193+
if (resources != null) {
194+
int iconId = info.activityInfo.getIconResource();
195+
if (iconId != 0) {
196+
return getFullResIcon(resources, iconId);
197+
}
198+
}
199+
return getFullResDefaultActivityIcon();
200+
}
201+
202+
public void cancelLoadingThumbnails() {
203+
if (mThumbnailLoader != null) {
204+
mThumbnailLoader.cancel(false);
205+
mThumbnailLoader = null;
206+
}
207+
}
208+
209+
// return a snapshot of the current list of recent apps
210+
ArrayList<TaskDescription> getRecentTasks() {
211+
cancelLoadingThumbnails();
212+
213+
ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
214+
final PackageManager pm = mContext.getPackageManager();
215+
final ActivityManager am = (ActivityManager)
216+
mContext.getSystemService(Context.ACTIVITY_SERVICE);
217+
218+
final List<ActivityManager.RecentTaskInfo> recentTasks =
219+
am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
220+
221+
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
222+
.resolveActivityInfo(pm, 0);
223+
224+
HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
225+
int numTasks = recentTasks.size();
226+
227+
// skip the first task - assume it's either the home screen or the current activity.
228+
final int first = 1;
229+
recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
230+
for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
231+
final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
232+
233+
TaskDescription item = createTaskDescription(recentInfo.id,
234+
recentInfo.persistentId, recentInfo.baseIntent,
235+
recentInfo.origActivity, recentInfo.description, homeInfo);
236+
237+
if (item != null) {
238+
tasks.add(item);
239+
++index;
240+
}
241+
}
242+
243+
// when we're not using the TaskDescription cache, we load the thumbnails in the
244+
// background
245+
loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
246+
return tasks;
247+
}
248+
249+
private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) {
250+
if (descriptions.size() > 0) {
251+
if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks");
252+
loadThumbnail(descriptions.get(0));
253+
if (descriptions.size() > 1) {
254+
mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
255+
@Override
256+
protected void onProgressUpdate(Integer... values) {
257+
final TaskDescription td = descriptions.get(values[0]);
258+
if (!isCancelled()) {
259+
mRecentsPanel.onTaskThumbnailLoaded(td);
260+
}
261+
// This is to prevent the loader thread from getting ahead
262+
// of our UI updates.
263+
mHandler.post(new Runnable() {
264+
@Override public void run() {
265+
synchronized (td) {
266+
td.notifyAll();
267+
}
268+
}
269+
});
270+
}
271+
272+
@Override
273+
protected Void doInBackground(Void... params) {
274+
final int origPri = Process.getThreadPriority(Process.myTid());
275+
Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
276+
long nextTime = SystemClock.uptimeMillis();
277+
for (int i=1; i<descriptions.size(); i++) {
278+
TaskDescription td = descriptions.get(i);
279+
loadThumbnail(td);
280+
long now = SystemClock.uptimeMillis();
281+
nextTime += 150;
282+
if (nextTime > now) {
283+
try {
284+
Thread.sleep(nextTime-now);
285+
} catch (InterruptedException e) {
286+
}
287+
}
288+
289+
if (isCancelled()) {
290+
break;
291+
}
292+
synchronized (td) {
293+
publishProgress(i);
294+
try {
295+
td.wait(500);
296+
} catch (InterruptedException e) {
297+
}
298+
}
299+
}
300+
Process.setThreadPriority(origPri);
301+
return null;
302+
}
303+
};
304+
mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
305+
}
306+
}
307+
}
308+
309+
}

packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@
3232

3333
import com.android.systemui.R;
3434
import com.android.systemui.SwipeHelper;
35-
import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
35+
import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
3636

3737
public class RecentsHorizontalScrollView extends HorizontalScrollView
3838
implements SwipeHelper.Callback {
3939
private static final String TAG = RecentsPanelView.TAG;
4040
private static final boolean DEBUG = RecentsPanelView.DEBUG;
4141
private LinearLayout mLinearLayout;
42-
private ActivityDescriptionAdapter mAdapter;
42+
private TaskDescriptionAdapter mAdapter;
4343
private RecentsCallback mCallback;
4444
protected int mLastScrollPosition;
4545
private SwipeHelper mSwipeHelper;
@@ -304,7 +304,7 @@ public void run() {
304304
}
305305
}
306306

307-
public void setAdapter(ActivityDescriptionAdapter adapter) {
307+
public void setAdapter(TaskDescriptionAdapter adapter) {
308308
mAdapter = adapter;
309309
mAdapter.registerDataSetObserver(new DataSetObserver() {
310310
public void onChanged() {

0 commit comments

Comments
 (0)