Refactoring loading of recent apps
- fix bugs 5278690 and 5432097
- no longer forcing a reload when screen is rotated
- moving recents loading code into a seaprate class
- changing variable names to use "Task" rather than "Activity" in Recent Apps
Change-Id: Ib0c8c5d537cf9d46d65b2ccb790015b601bb1bf1
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index fbf00d2..886a14d 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -121,6 +121,7 @@
createAnimation(appearing);
mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mContentView.buildLayer();
mContentAnim.start();
mVisible = appearing;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
new file mode 100644
index 0000000..47aa849
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -0,0 +1,309 @@
+/*
+ * 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.systemui.recent;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.tablet.TabletStatusBar;
+
+public class RecentTasksLoader {
+ static final String TAG = "RecentTasksLoader";
+ static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
+
+ private static final int DISPLAY_TASKS = 20;
+ private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
+
+ private Context mContext;
+ private RecentsPanelView mRecentsPanel;
+
+ private AsyncTask<Void, Integer, Void> mThumbnailLoader;
+ private final Handler mHandler;
+
+ private int mIconDpi;
+ private Bitmap mDefaultThumbnailBackground;
+
+ public RecentTasksLoader(Context context) {
+ mContext = context;
+
+ final Resources res = context.getResources();
+
+ // get the icon size we want -- on tablets, we use bigger icons
+ boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets);
+ int density = res.getDisplayMetrics().densityDpi;
+ if (isTablet) {
+ if (density == DisplayMetrics.DENSITY_LOW) {
+ mIconDpi = DisplayMetrics.DENSITY_MEDIUM;
+ } else if (density == DisplayMetrics.DENSITY_MEDIUM) {
+ mIconDpi = DisplayMetrics.DENSITY_HIGH;
+ } else if (density == DisplayMetrics.DENSITY_HIGH) {
+ mIconDpi = DisplayMetrics.DENSITY_XHIGH;
+ } else if (density == DisplayMetrics.DENSITY_XHIGH) {
+ // We'll need to use a denser icon, or some sort of a mipmap
+ mIconDpi = DisplayMetrics.DENSITY_XHIGH;
+ }
+ } else {
+ mIconDpi = res.getDisplayMetrics().densityDpi;
+ }
+ mIconDpi = isTablet ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
+
+ // Render the default thumbnail background
+ int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
+ int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
+ int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
+
+ mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(mDefaultThumbnailBackground);
+ c.drawColor(color);
+
+ // If we're using the cache, begin listening to the activity manager for
+ // updated thumbnails
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+
+ mHandler = new Handler();
+ }
+
+ public void setRecentsPanel(RecentsPanelView recentsPanel) {
+ mRecentsPanel = recentsPanel;
+ }
+
+ // Create an TaskDescription, returning null if the title or icon is null, or if it's the
+ // home activity
+ TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
+ ComponentName origActivity, CharSequence description, ActivityInfo homeInfo) {
+ Intent intent = new Intent(baseIntent);
+ if (origActivity != null) {
+ intent.setComponent(origActivity);
+ }
+ final PackageManager pm = mContext.getPackageManager();
+ if (homeInfo == null) {
+ homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+ .resolveActivityInfo(pm, 0);
+ }
+
+ intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+ if (resolveInfo != null) {
+ final ActivityInfo info = resolveInfo.activityInfo;
+ final String title = info.loadLabel(pm).toString();
+ Drawable icon = getFullResIcon(resolveInfo, pm);
+
+ if (title != null && title.length() > 0 && icon != null) {
+ if (DEBUG) Log.v(TAG, "creating activity desc for id="
+ + persistentTaskId + ", label=" + title);
+
+ TaskDescription item = new TaskDescription(taskId,
+ persistentTaskId, resolveInfo, baseIntent, info.packageName,
+ description);
+ item.setLabel(title);
+ item.setIcon(icon);
+
+ // Don't load the current home activity.
+ if (homeInfo != null
+ && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+ && homeInfo.name.equals(intent.getComponent().getClassName())) {
+ return null;
+ }
+
+ return item;
+ } else {
+ if (DEBUG) Log.v(TAG, "SKIPPING item " + persistentTaskId);
+ }
+ }
+ return null;
+ }
+
+ void loadThumbnail(TaskDescription td) {
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId);
+
+ if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
+ + td + ": " + thumbs.mainThumbnail);
+ synchronized (td) {
+ if (thumbs != null && thumbs.mainThumbnail != null) {
+ td.setThumbnail(thumbs.mainThumbnail);
+ } else {
+ td.setThumbnail(mDefaultThumbnailBackground);
+ }
+ }
+ }
+
+ Drawable getFullResDefaultActivityIcon() {
+ return getFullResIcon(Resources.getSystem(),
+ com.android.internal.R.mipmap.sym_def_app_icon);
+ }
+
+ Drawable getFullResIcon(Resources resources, int iconId) {
+ try {
+ return resources.getDrawableForDensity(iconId, mIconDpi);
+ } catch (Resources.NotFoundException e) {
+ return getFullResDefaultActivityIcon();
+ }
+ }
+
+ private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
+ Resources resources;
+ try {
+ resources = packageManager.getResourcesForApplication(
+ info.activityInfo.applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ resources = null;
+ }
+ if (resources != null) {
+ int iconId = info.activityInfo.getIconResource();
+ if (iconId != 0) {
+ return getFullResIcon(resources, iconId);
+ }
+ }
+ return getFullResDefaultActivityIcon();
+ }
+
+ public void cancelLoadingThumbnails() {
+ if (mThumbnailLoader != null) {
+ mThumbnailLoader.cancel(false);
+ mThumbnailLoader = null;
+ }
+ }
+
+ // return a snapshot of the current list of recent apps
+ ArrayList<TaskDescription> getRecentTasks() {
+ cancelLoadingThumbnails();
+
+ ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
+ final PackageManager pm = mContext.getPackageManager();
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+
+ final List<ActivityManager.RecentTaskInfo> recentTasks =
+ am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+
+ ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+ .resolveActivityInfo(pm, 0);
+
+ HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
+ int numTasks = recentTasks.size();
+
+ // skip the first task - assume it's either the home screen or the current activity.
+ final int first = 1;
+ recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
+ for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+ final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+
+ TaskDescription item = createTaskDescription(recentInfo.id,
+ recentInfo.persistentId, recentInfo.baseIntent,
+ recentInfo.origActivity, recentInfo.description, homeInfo);
+
+ if (item != null) {
+ tasks.add(item);
+ ++index;
+ }
+ }
+
+ // when we're not using the TaskDescription cache, we load the thumbnails in the
+ // background
+ loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
+ return tasks;
+ }
+
+ private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) {
+ if (descriptions.size() > 0) {
+ if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks");
+ loadThumbnail(descriptions.get(0));
+ if (descriptions.size() > 1) {
+ mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ final TaskDescription td = descriptions.get(values[0]);
+ if (!isCancelled()) {
+ mRecentsPanel.onTaskThumbnailLoaded(td);
+ }
+ // This is to prevent the loader thread from getting ahead
+ // of our UI updates.
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ synchronized (td) {
+ td.notifyAll();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ final int origPri = Process.getThreadPriority(Process.myTid());
+ Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ long nextTime = SystemClock.uptimeMillis();
+ for (int i=1; i<descriptions.size(); i++) {
+ TaskDescription td = descriptions.get(i);
+ loadThumbnail(td);
+ long now = SystemClock.uptimeMillis();
+ nextTime += 150;
+ if (nextTime > now) {
+ try {
+ Thread.sleep(nextTime-now);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ if (isCancelled()) {
+ break;
+ }
+ synchronized (td) {
+ publishProgress(i);
+ try {
+ td.wait(500);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ Process.setThreadPriority(origPri);
+ return null;
+ }
+ };
+ mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 58af255..f971d2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -32,14 +32,14 @@
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
+import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
public class RecentsHorizontalScrollView extends HorizontalScrollView
implements SwipeHelper.Callback {
private static final String TAG = RecentsPanelView.TAG;
private static final boolean DEBUG = RecentsPanelView.DEBUG;
private LinearLayout mLinearLayout;
- private ActivityDescriptionAdapter mAdapter;
+ private TaskDescriptionAdapter mAdapter;
private RecentsCallback mCallback;
protected int mLastScrollPosition;
private SwipeHelper mSwipeHelper;
@@ -304,7 +304,7 @@
}
}
- public void setAdapter(ActivityDescriptionAdapter adapter) {
+ public void setAdapter(TaskDescriptionAdapter adapter) {
mAdapter = adapter;
mAdapter.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index e3f5cdb..5b4c33e 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -17,32 +17,20 @@
package com.android.systemui.recent;
import java.util.ArrayList;
-import java.util.List;
import android.animation.Animator;
import android.animation.LayoutTransition;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Process;
-import android.os.SystemClock;
import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -72,66 +60,24 @@
implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener {
static final String TAG = "RecentsPanelView";
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
- private static final int DISPLAY_TASKS = 20;
- private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
+ private Context mContext;
private StatusBar mBar;
- private ArrayList<ActivityDescription> mActivityDescriptions;
- private AsyncTask<Void, Integer, Void> mThumbnailLoader;
- private int mIconDpi;
private View mRecentsScrim;
private View mRecentsGlowView;
private View mRecentsNoApps;
private ViewGroup mRecentsContainer;
- private Bitmap mDefaultThumbnailBackground;
private boolean mShowing;
private Choreographer mChoreo;
private View mRecentsDismissButton;
- private ActivityDescriptionAdapter mListAdapter;
- private final Handler mHandler = new Handler();
- /* package */ final class ActivityDescription {
- final ActivityManager.RecentTaskInfo recentTaskInfo;
- final ResolveInfo resolveInfo;
- final int taskId; // application task id for curating apps
- final int persistentTaskId; // persistent id
- final Intent intent; // launch intent for application
- final String packageName; // used to override animations (see onClick())
- final int position; // position in list
+ private RecentTasksLoader mRecentTasksLoader;
+ private ArrayList<TaskDescription> mRecentTaskDescriptions;
+ private TaskDescriptionAdapter mListAdapter;
+ private int mThumbnailWidth;
- Matrix matrix; // arbitrary rotation matrix to correct orientation
-
- private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
- private Drawable mIcon; // application package icon
- private CharSequence mLabel; // application package label
-
- public ActivityDescription(ActivityManager.RecentTaskInfo _recentInfo,
- ResolveInfo _resolveInfo, Intent _intent,
- int _pos, String _packageName) {
- recentTaskInfo = _recentInfo;
- resolveInfo = _resolveInfo;
- intent = _intent;
- taskId = _recentInfo.id;
- persistentTaskId = _recentInfo.persistentId;
- position = _pos;
- packageName = _packageName;
- }
-
- public CharSequence getLabel() {
- return mLabel;
- }
-
- public Drawable getIcon() {
- return mIcon;
- }
-
- public void setThumbnail(Bitmap thumbnail) {
- mThumbnail = thumbnail;
- }
-
- public Bitmap getThumbnail() {
- return mThumbnail;
- }
+ public void setRecentTasksLoader(RecentTasksLoader loader) {
+ mRecentTasksLoader = loader;
}
private final class OnLongClickDelegate implements View.OnLongClickListener {
@@ -150,18 +96,18 @@
ImageView iconView;
TextView labelView;
TextView descriptionView;
- ActivityDescription activityDescription;
+ TaskDescription taskDescription;
}
- /* package */ final class ActivityDescriptionAdapter extends BaseAdapter {
+ /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
private LayoutInflater mInflater;
- public ActivityDescriptionAdapter(Context context) {
+ public TaskDescriptionAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
public int getCount() {
- return mActivityDescriptions != null ? mActivityDescriptions.size() : 0;
+ return mRecentTaskDescriptions != null ? mRecentTaskDescriptions.size() : 0;
}
public Object getItem(int position) {
@@ -189,18 +135,15 @@
holder = (ViewHolder) convertView.getTag();
}
- // activityId is reverse since most recent appears at the bottom...
- final int activityId = mActivityDescriptions.size() - position - 1;
+ // index is reverse since most recent appears at the bottom...
+ final int index = mRecentTaskDescriptions.size() - position - 1;
- final ActivityDescription activityDescription = mActivityDescriptions.get(activityId);
- holder.thumbnailViewImage.setImageBitmap(activityDescription.getThumbnail());
- holder.iconView.setImageDrawable(activityDescription.getIcon());
- holder.labelView.setText(activityDescription.getLabel());
- holder.descriptionView.setText(activityDescription.recentTaskInfo.description);
- holder.thumbnailView.setTag(activityDescription);
+ final TaskDescription taskDescription = mRecentTaskDescriptions.get(index);
+ applyTaskDescription(holder, taskDescription, false);
+
+ holder.thumbnailView.setTag(taskDescription);
holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
- holder.thumbnailView.setContentDescription(activityDescription.getLabel());
- holder.activityDescription = activityDescription;
+ holder.taskDescription = taskDescription;
return convertView;
}
@@ -226,14 +169,19 @@
}
public void show(boolean show, boolean animate) {
+ show(show, animate, null);
+ }
+
+ public void show(boolean show, boolean animate,
+ ArrayList<TaskDescription> recentTaskDescriptions) {
if (show) {
// Need to update list of recent apps before we set visibility so this view's
// content description is updated before it gets focus for TalkBack mode
- refreshApplicationList();
+ refreshRecentTasksList(recentTaskDescriptions);
// if there are no apps, either bring up a "No recent apps" message, or just
// quit early
- boolean noApps = (mActivityDescriptions.size() == 0);
+ boolean noApps = (mRecentTaskDescriptions.size() == 0);
if (mRecentsNoApps != null) { // doesn't exist on large devices
mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
} else {
@@ -242,6 +190,8 @@
return;
}
}
+ } else {
+ mRecentTasksLoader.cancelLoadingThumbnails();
}
if (animate) {
if (mShowing != show) {
@@ -255,6 +205,7 @@
mShowing = show;
setVisibility(show ? View.VISIBLE : View.GONE);
mChoreo.jumpTo(show);
+ onAnimationEnd(null);
}
if (show) {
setFocusable(true);
@@ -298,6 +249,9 @@
createCustomAnimations(transitioner);
} else {
((ViewGroup)mRecentsContainer).setLayoutTransition(null);
+ // Clear memory used by screenshots
+ mRecentTaskDescriptions.clear();
+ mListAdapter.notifyDataSetInvalidated();
}
}
@@ -349,21 +303,13 @@
public RecentsPanelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mContext = context;
+ updateValuesFromResources();
+ }
- Resources res = context.getResources();
- boolean xlarge = (res.getConfiguration().screenLayout
- & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
-
- mIconDpi = xlarge ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
-
- int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
- int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
- int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
-
- // Render the default thumbnail background
- mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(mDefaultThumbnailBackground);
- c.drawColor(color);
+ public void updateValuesFromResources() {
+ mThumbnailWidth =
+ (int) mContext.getResources().getDimension(R.dimen.status_bar_recents_thumbnail_width);
}
@Override
@@ -371,7 +317,7 @@
super.onFinishInflate();
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
- mListAdapter = new ActivityDescriptionAdapter(mContext);
+ mListAdapter = new TaskDescriptionAdapter(mContext);
if (mRecentsContainer instanceof RecentsHorizontalScrollView){
RecentsHorizontalScrollView scrollView
= (RecentsHorizontalScrollView) mRecentsContainer;
@@ -427,122 +373,50 @@
}
}
- Drawable getFullResDefaultActivityIcon() {
- return getFullResIcon(Resources.getSystem(),
- com.android.internal.R.mipmap.sym_def_app_icon);
- }
- Drawable getFullResIcon(Resources resources, int iconId) {
- try {
- return resources.getDrawableForDensity(iconId, mIconDpi);
- } catch (Resources.NotFoundException e) {
- return getFullResDefaultActivityIcon();
- }
- }
-
- private Drawable getFullResIcon(ResolveInfo info, PackageManager packageManager) {
- Resources resources;
- try {
- resources = packageManager.getResourcesForApplication(
- info.activityInfo.applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- resources = null;
- }
- if (resources != null) {
- int iconId = info.activityInfo.getIconResource();
- if (iconId != 0) {
- return getFullResIcon(resources, iconId);
+ void applyTaskDescription(ViewHolder h, TaskDescription td, boolean anim) {
+ h.iconView.setImageDrawable(td.getIcon());
+ if (h.iconView.getVisibility() != View.VISIBLE) {
+ if (anim) {
+ h.iconView.setAnimation(AnimationUtils.loadAnimation(
+ mContext, R.anim.recent_appear));
}
+ h.iconView.setVisibility(View.VISIBLE);
}
- return getFullResDefaultActivityIcon();
- }
-
- private ArrayList<ActivityDescription> getRecentTasks() {
- ArrayList<ActivityDescription> activityDescriptions = new ArrayList<ActivityDescription>();
- final PackageManager pm = mContext.getPackageManager();
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
-
- final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
-
- ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
- .resolveActivityInfo(pm, 0);
-
- int numTasks = recentTasks.size();
-
- // skip the first activity - assume it's either the home screen or the current app.
- final int first = 1;
- for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
- final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
-
- Intent intent = new Intent(recentInfo.baseIntent);
- if (recentInfo.origActivity != null) {
- intent.setComponent(recentInfo.origActivity);
+ h.labelView.setText(td.getLabel());
+ h.thumbnailView.setContentDescription(td.getLabel());
+ if (h.labelView.getVisibility() != View.VISIBLE) {
+ if (anim) {
+ h.labelView.setAnimation(AnimationUtils.loadAnimation(
+ mContext, R.anim.recent_appear));
}
-
- // Skip the current home activity.
- if (homeInfo != null
- && homeInfo.packageName.equals(intent.getComponent().getPackageName())
- && homeInfo.name.equals(intent.getComponent().getClassName())) {
- continue;
- }
-
- intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
- if (resolveInfo != null) {
- final ActivityInfo info = resolveInfo.activityInfo;
- final String title = info.loadLabel(pm).toString();
- Drawable icon = getFullResIcon(resolveInfo, pm);
- if (title != null && title.length() > 0 && icon != null) {
- if (DEBUG) Log.v(TAG, "creating activity desc for id="
- + recentInfo.id + ", label=" + title);
- ActivityDescription item = new ActivityDescription(recentInfo,
- resolveInfo, intent, index, info.packageName);
- activityDescriptions.add(item);
- ++index;
- } else {
- if (DEBUG) Log.v(TAG, "SKIPPING item " + recentInfo.id);
+ h.labelView.setVisibility(View.VISIBLE);
+ }
+ Bitmap thumbnail = td.getThumbnail();
+ if (thumbnail != null) {
+ // Should remove the default image in the frame
+ // that this now covers, to improve scrolling speed.
+ // That can't be done until the anim is complete though.
+ h.thumbnailViewImage.setImageBitmap(thumbnail);
+ // scale to fill up the full width
+ Matrix scaleMatrix = new Matrix();
+ float scale = mThumbnailWidth / (float) thumbnail.getWidth();
+ scaleMatrix.setScale(scale, scale);
+ h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
+ h.thumbnailViewImage.setImageMatrix(scaleMatrix);
+ if (h.thumbnailViewImage.getVisibility() != View.VISIBLE) {
+ if (anim) {
+ h.thumbnailViewImage.setAnimation(
+ AnimationUtils.loadAnimation(
+ mContext, R.anim.recent_appear));
}
+ h.thumbnailViewImage.setVisibility(View.VISIBLE);
}
}
- return activityDescriptions;
+ //h.descriptionView.setText(ad.description);
}
- ActivityDescription findActivityDescription(int id)
- {
- ActivityDescription desc = null;
- for (int i = 0; i < mActivityDescriptions.size(); i++) {
- ActivityDescription item = mActivityDescriptions.get(i);
- if (item != null && item.taskId == id) {
- desc = item;
- break;
- }
- }
- return desc;
- }
-
- void loadActivityDescription(ActivityDescription ad, int index) {
- final ActivityManager am = (ActivityManager)
- mContext.getSystemService(Context.ACTIVITY_SERVICE);
- final PackageManager pm = mContext.getPackageManager();
- ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(
- ad.recentTaskInfo.persistentId);
- CharSequence label = ad.resolveInfo.activityInfo.loadLabel(pm);
- Drawable icon = getFullResIcon(ad.resolveInfo, pm);
- if (DEBUG) Log.v(TAG, "Loaded bitmap for #" + index + " in "
- + ad + ": " + thumbs.mainThumbnail);
- synchronized (ad) {
- ad.mLabel = label;
- ad.mIcon = icon;
- if (thumbs != null && thumbs.mainThumbnail != null) {
- ad.setThumbnail(thumbs.mainThumbnail);
- }
- }
- }
-
- void applyActivityDescription(ActivityDescription ad, int index, boolean anim) {
+ void onTaskThumbnailLoaded(TaskDescription ad) {
synchronized (ad) {
if (mRecentsContainer != null) {
ViewGroup container = mRecentsContainer;
@@ -556,44 +430,8 @@
View v = container.getChildAt(i);
if (v.getTag() instanceof ViewHolder) {
ViewHolder h = (ViewHolder)v.getTag();
- if (h.activityDescription == ad) {
- if (DEBUG) Log.v(TAG, "Updating thumbnail #" + index + " in "
- + h.activityDescription
- + ": " + ad.getThumbnail());
- h.iconView.setImageDrawable(ad.getIcon());
- if (anim) {
- h.iconView.setAnimation(AnimationUtils.loadAnimation(
- mContext, R.anim.recent_appear));
- }
- h.iconView.setVisibility(View.VISIBLE);
- h.labelView.setText(ad.getLabel());
- h.thumbnailView.setContentDescription(ad.getLabel());
- if (anim) {
- h.labelView.setAnimation(AnimationUtils.loadAnimation(
- mContext, R.anim.recent_appear));
- }
- h.labelView.setVisibility(View.VISIBLE);
- Bitmap thumbnail = ad.getThumbnail();
- if (thumbnail != null) {
- // Should remove the default image in the frame
- // that this now covers, to improve scrolling speed.
- // That can't be done until the anim is complete though.
- h.thumbnailViewImage.setImageBitmap(thumbnail);
-
- // scale to fill up the full width
- Matrix scaleMatrix = new Matrix();
- float thumbnailViewWidth = h.thumbnailViewImage.getWidth();
- float scale = thumbnailViewWidth / thumbnail.getWidth();
- scaleMatrix.setScale(scale, scale);
- h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
- h.thumbnailViewImage.setImageMatrix(scaleMatrix);
-
- if (anim) {
- h.thumbnailViewImage.setAnimation(AnimationUtils.loadAnimation(
- mContext, R.anim.recent_appear));
- }
- h.thumbnailViewImage.setVisibility(View.VISIBLE);
- }
+ if (h.taskDescription == ad) {
+ applyTaskDescription(h, ad, true);
}
}
}
@@ -601,14 +439,28 @@
}
}
- private void refreshApplicationList() {
- if (mThumbnailLoader != null) {
- mThumbnailLoader.cancel(false);
- mThumbnailLoader = null;
+ private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
+ if (recentTasksList != null) {
+ mRecentTaskDescriptions = recentTasksList;
+ } else {
+ mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
}
+ mListAdapter.notifyDataSetInvalidated();
+ updateUiElements(getResources().getConfiguration());
+ }
- mActivityDescriptions = getRecentTasks();
- int numRecentApps = mActivityDescriptions.size();
+ public ArrayList<TaskDescription> getRecentTasksList() {
+ return mRecentTaskDescriptions;
+ }
+
+ private void updateUiElements(Configuration config) {
+ final int items = mRecentTaskDescriptions.size();
+
+ mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
+ mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
+
+ // Set description for accessibility
+ int numRecentApps = mRecentTaskDescriptions.size();
String recentAppsAccessibilityDescription;
if (numRecentApps == 0) {
recentAppsAccessibilityDescription =
@@ -618,81 +470,10 @@
R.plurals.status_bar_accessibility_recent_apps, numRecentApps, numRecentApps);
}
setContentDescription(recentAppsAccessibilityDescription);
- for (ActivityDescription ad : mActivityDescriptions) {
- ad.setThumbnail(mDefaultThumbnailBackground);
- }
- mListAdapter.notifyDataSetInvalidated();
- if (mActivityDescriptions.size() > 0) {
- if (DEBUG) Log.v(TAG, "Showing " + mActivityDescriptions.size() + " apps");
- updateUiElements(getResources().getConfiguration());
- final ArrayList<ActivityDescription> descriptions =
- new ArrayList<ActivityDescription>(mActivityDescriptions);
- loadActivityDescription(descriptions.get(0), 0);
- applyActivityDescription(descriptions.get(0), 0, false);
- if (descriptions.size() > 1) {
- mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
- @Override
- protected void onProgressUpdate(Integer... values) {
- final ActivityDescription ad = descriptions.get(values[0]);
- if (!isCancelled()) {
- applyActivityDescription(ad, values[0], true);
- }
- // This is to prevent the loader thread from getting ahead
- // of our UI updates.
- mHandler.post(new Runnable() {
- @Override public void run() {
- synchronized (ad) {
- ad.notifyAll();
- }
- }
- });
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- final int origPri = Process.getThreadPriority(Process.myTid());
- Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
- long nextTime = SystemClock.uptimeMillis();
- for (int i=1; i<descriptions.size(); i++) {
- ActivityDescription ad = descriptions.get(i);
- loadActivityDescription(ad, i);
- long now = SystemClock.uptimeMillis();
- nextTime += 150;
- if (nextTime > now) {
- try {
- Thread.sleep(nextTime-now);
- } catch (InterruptedException e) {
- }
- }
- if (isCancelled()) {
- break;
- }
- synchronized (ad) {
- publishProgress(i);
- try {
- ad.wait(500);
- } catch (InterruptedException e) {
- }
- }
- }
- Process.setThreadPriority(origPri);
- return null;
- }
- };
- mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- }
- }
-
- private void updateUiElements(Configuration config) {
- final int items = mActivityDescriptions.size();
-
- mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
- mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
}
public void handleOnClick(View view) {
- ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
final Context context = view.getContext();
final ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
@@ -714,14 +495,14 @@
}
public void handleSwipe(View view) {
- ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
+ TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
if (DEBUG) Log.v(TAG, "Jettison " + ad.getLabel());
- mActivityDescriptions.remove(ad);
+ mRecentTaskDescriptions.remove(ad);
// Handled by widget containers to enable LayoutTransitions properly
// mListAdapter.notifyDataSetChanged();
- if (mActivityDescriptions.size() == 0) {
+ if (mRecentTaskDescriptions.size() == 0) {
hide(false);
}
@@ -751,7 +532,7 @@
} else if (item.getItemId() == R.id.recent_inspect_item) {
ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
if (viewHolder != null) {
- final ActivityDescription ad = viewHolder.activityDescription;
+ final TaskDescription ad = viewHolder.taskDescription;
startApplicationDetailsActivity(ad.packageName);
mBar.animateCollapse();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 7853402..dc13092 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -31,13 +31,13 @@
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.recent.RecentsPanelView.ActivityDescriptionAdapter;
+import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
private static final String TAG = RecentsPanelView.TAG;
private static final boolean DEBUG = RecentsPanelView.DEBUG;
private LinearLayout mLinearLayout;
- private ActivityDescriptionAdapter mAdapter;
+ private TaskDescriptionAdapter mAdapter;
private RecentsCallback mCallback;
protected int mLastScrollPosition;
private SwipeHelper mSwipeHelper;
@@ -319,7 +319,7 @@
}
}
- public void setAdapter(ActivityDescriptionAdapter adapter) {
+ public void setAdapter(TaskDescriptionAdapter adapter) {
mAdapter = adapter;
mAdapter.registerDataSetObserver(new DataSetObserver() {
public void onChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
new file mode 100644
index 0000000..dcfd6d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
@@ -0,0 +1,72 @@
+/*
+ * 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.systemui.recent;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+public final class TaskDescription {
+ final ResolveInfo resolveInfo;
+ final int taskId; // application task id for curating apps
+ final int persistentTaskId; // persistent id
+ final Intent intent; // launch intent for application
+ final String packageName; // used to override animations (see onClick())
+ final CharSequence description;
+
+ private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
+ private Drawable mIcon; // application package icon
+ private CharSequence mLabel; // application package label
+
+ public TaskDescription(int _taskId, int _persistentTaskId,
+ ResolveInfo _resolveInfo, Intent _intent,
+ String _packageName, CharSequence _description) {
+ resolveInfo = _resolveInfo;
+ intent = _intent;
+ taskId = _taskId;
+ persistentTaskId = _persistentTaskId;
+
+ description = _description;
+ packageName = _packageName;
+ }
+
+ // mark all these as locked?
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(CharSequence label) {
+ mLabel = label;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ }
+
+ public void setThumbnail(Bitmap thumbnail) {
+ mThumbnail = thumbnail;
+ }
+
+ public Bitmap getThumbnail() {
+ return mThumbnail;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 09ea6ad..e54de59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -74,7 +74,9 @@
import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
+import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentsPanelView;
+import com.android.systemui.recent.TaskDescription;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBar;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -193,6 +195,7 @@
// Recent apps
private RecentsPanelView mRecentsPanel;
+ private RecentTasksLoader mRecentTasksLoader;
// Tracking finger for opening/closing.
int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
@@ -363,6 +366,7 @@
signalCluster.setNetworkController(mNetworkController);
// Recents Panel
+ mRecentTasksLoader = new RecentTasksLoader(context);
updateRecentsPanel();
// receive broadcasts
@@ -399,16 +403,21 @@
protected void updateRecentsPanel() {
// Recents Panel
boolean visible = false;
+ ArrayList<TaskDescription> recentTasksList = null;
if (mRecentsPanel != null) {
visible = mRecentsPanel.isShowing();
WindowManagerImpl.getDefault().removeView(mRecentsPanel);
+ if (visible) {
+ recentTasksList = mRecentsPanel.getRecentTasksList();
+ }
}
// Provide RecentsPanelView with a temporary parent to allow layout params to work.
LinearLayout tmpRoot = new LinearLayout(mContext);
mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_recent_panel, tmpRoot, false);
-
+ mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
+ mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
mRecentsPanel));
mRecentsPanel.setVisibility(View.GONE);
@@ -417,7 +426,7 @@
WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
mRecentsPanel.setBar(this);
if (visible) {
- mRecentsPanel.show(true, false);
+ mRecentsPanel.show(true, false, recentTasksList);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index ba52fb8..271e508 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -77,6 +77,7 @@
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
+import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.recent.RecentsPanelView;
public class TabletStatusBar extends StatusBar implements
@@ -179,6 +180,7 @@
int mDisabled = 0;
private RecentsPanelView mRecentsPanel;
+ private RecentTasksLoader mRecentTasksLoader;
private InputMethodsPanel mInputMethodsPanel;
private CompatModePanel mCompatModePanel;
@@ -299,12 +301,15 @@
}
// Recents Panel
+ mRecentTasksLoader = new RecentTasksLoader(context);
mRecentsPanel = (RecentsPanelView) View.inflate(context,
R.layout.status_bar_recent_panel, null);
mRecentsPanel.setVisibility(View.GONE);
mRecentsPanel.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
mRecentsPanel));
+ mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader);
+ mRecentTasksLoader.setRecentsPanel(mRecentsPanel);
mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel);
lp = new WindowManager.LayoutParams(
@@ -392,6 +397,7 @@
mNotificationPanelParams.height = getNotificationPanelHeight();
WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel,
mNotificationPanelParams);
+ mRecentsPanel.updateValuesFromResources();
}
protected void loadDimens() {