diff options
8 files changed, 506 insertions, 328 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java index fbf00d2e3ff3..886a14d9e756 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 @@ import android.view.View; 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 000000000000..47aa84946379 --- /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 58af25548dd6..f971d2d50632 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 android.widget.LinearLayout; 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 class RecentsHorizontalScrollView extends HorizontalScrollView } } - 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 e3f5cdb6d335..5b4c33e1890d 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 @@ public class RecentsPanelView extends RelativeLayout 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 - - 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; - } + private RecentTasksLoader mRecentTasksLoader; + private ArrayList<TaskDescription> mRecentTaskDescriptions; + private TaskDescriptionAdapter mListAdapter; + private int mThumbnailWidth; - 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 @@ public class RecentsPanelView extends RelativeLayout 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 @@ public class RecentsPanelView extends RelativeLayout 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 class RecentsPanelView extends RelativeLayout } 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 @@ public class RecentsPanelView extends RelativeLayout return; } } + } else { + mRecentTasksLoader.cancelLoadingThumbnails(); } if (animate) { if (mShowing != show) { @@ -255,6 +205,7 @@ public class RecentsPanelView extends RelativeLayout mShowing = show; setVisibility(show ? View.VISIBLE : View.GONE); mChoreo.jumpTo(show); + onAnimationEnd(null); } if (show) { setFocusable(true); @@ -298,6 +249,9 @@ public class RecentsPanelView extends RelativeLayout createCustomAnimations(transitioner); } else { ((ViewGroup)mRecentsContainer).setLayoutTransition(null); + // Clear memory used by screenshots + mRecentTaskDescriptions.clear(); + mListAdapter.notifyDataSetInvalidated(); } } @@ -349,21 +303,13 @@ public class RecentsPanelView extends RelativeLayout 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 @@ public class RecentsPanelView extends RelativeLayout 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 @@ public class RecentsPanelView extends RelativeLayout } } - 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)); } - } - 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); - } - - // Skip the current home activity. - if (homeInfo != null - && homeInfo.packageName.equals(intent.getComponent().getPackageName()) - && homeInfo.name.equals(intent.getComponent().getClassName())) { - continue; + h.iconView.setVisibility(View.VISIBLE); + } + 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)); } - - 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; - } - - 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); - } - } + //h.descriptionView.setText(ad.description); } - void applyActivityDescription(ActivityDescription ad, int index, boolean anim) { + void onTaskThumbnailLoaded(TaskDescription ad) { synchronized (ad) { if (mRecentsContainer != null) { ViewGroup container = mRecentsContainer; @@ -556,44 +430,8 @@ public class RecentsPanelView extends RelativeLayout 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 @@ public class RecentsPanelView extends RelativeLayout } } - 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 @@ public class RecentsPanelView extends RelativeLayout 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 class RecentsPanelView extends RelativeLayout } 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 @@ public class RecentsPanelView extends RelativeLayout } 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 7853402490f9..dc13092c2c3d 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 android.widget.ScrollView; 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 class RecentsVerticalScrollView extends ScrollView implements SwipeHelper } } - 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 000000000000..dcfd6d8feb1c --- /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 09ea6ad4055f..e54de59ced75 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.StatusBarIcon; 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 @@ public class PhoneStatusBar extends StatusBar { // 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 @@ public class PhoneStatusBar extends StatusBar { signalCluster.setNetworkController(mNetworkController); // Recents Panel + mRecentTasksLoader = new RecentTasksLoader(context); updateRecentsPanel(); // receive broadcasts @@ -399,16 +403,21 @@ public class PhoneStatusBar extends StatusBar { 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 @@ public class PhoneStatusBar extends StatusBar { 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 ba52fb8c8f65..271e508bd714 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.CompatModeButton; 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 @@ public class TabletStatusBar extends StatusBar implements int mDisabled = 0; private RecentsPanelView mRecentsPanel; + private RecentTasksLoader mRecentTasksLoader; private InputMethodsPanel mInputMethodsPanel; private CompatModePanel mCompatModePanel; @@ -299,12 +301,15 @@ public class TabletStatusBar extends StatusBar implements } // 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 @@ public class TabletStatusBar extends StatusBar implements mNotificationPanelParams.height = getNotificationPanelHeight(); WindowManagerImpl.getDefault().updateViewLayout(mNotificationPanel, mNotificationPanelParams); + mRecentsPanel.updateValuesFromResources(); } protected void loadDimens() { |