Make Recent Apps faster

- start loading on touch down
- avoid unneeded calls to onLayout
- don't fade in thumbnails if they've been loaded before we show recent apps
- don't pause between loading thumbnails
- fade in thumbnails+shadow (rather than just thumbnail as before)

Change-Id: I6dd4be7f52f9e8b51284ae052614719db8e71dc5
This commit is contained in:
Michael Jurka
2011-10-17 09:05:00 -07:00
parent 87bc53de2a
commit 412cba8aa1
8 changed files with 119 additions and 73 deletions

View File

@@ -16,5 +16,5 @@
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_mediumAnimTime"
android:duration="@android:integer/config_shortAnimTime"
/>

View File

@@ -39,11 +39,11 @@
android:layout_marginTop="@dimen/status_bar_recents_thumbnail_top_margin"
android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
android:background="@drawable/recents_thumbnail_bg"
android:foreground="@drawable/recents_thumbnail_fg">
android:foreground="@drawable/recents_thumbnail_fg"
android:visibility="invisible">
<ImageView android:id="@+id/app_thumbnail_image"
android:layout_width="@dimen/status_bar_recents_thumbnail_width"
android:layout_height="@dimen/status_bar_recents_thumbnail_height"
android:visibility="invisible"
/>
</FrameLayout>
@@ -58,7 +58,6 @@
android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
android:scaleType="centerInside"
android:adjustViewBounds="true"
android:visibility="invisible"
/>
<TextView android:id="@+id/app_label"
@@ -74,7 +73,6 @@
android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
android:singleLine="true"
android:ellipsize="marquee"
android:visibility="invisible"
android:textColor="@color/status_bar_recents_app_label_color"
/>

View File

@@ -52,11 +52,11 @@
android:layout_toRightOf="@id/app_label"
android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
android:background="@drawable/recents_thumbnail_bg"
android:foreground="@drawable/recents_thumbnail_fg">
android:foreground="@drawable/recents_thumbnail_fg"
android:visibility="invisible">
<ImageView android:id="@+id/app_thumbnail_image"
android:layout_width="@dimen/status_bar_recents_thumbnail_width"
android:layout_height="@dimen/status_bar_recents_thumbnail_height"
android:visibility="invisible"
/>
</FrameLayout>
<View android:id="@+id/recents_callout_line"

View File

@@ -31,11 +31,11 @@
android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
android:scaleType="center"
android:background="@drawable/recents_thumbnail_bg"
android:foreground="@drawable/recents_thumbnail_fg">
android:foreground="@drawable/recents_thumbnail_fg"
android:visibility="invisible">
<ImageView android:id="@+id/app_thumbnail_image"
android:layout_width="@dimen/status_bar_recents_thumbnail_width"
android:layout_height="@dimen/status_bar_recents_thumbnail_height"
android:visibility="invisible"
/>
<ImageView android:id="@+id/app_icon"
android:layout_width="wrap_content"

View File

@@ -86,8 +86,8 @@ public class RecentTasksLoader {
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 width = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
int height = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -106,6 +106,10 @@ public class RecentTasksLoader {
mRecentsPanel = recentsPanel;
}
public Bitmap getDefaultThumbnail() {
return mDefaultThumbnailBackground;
}
// 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,
@@ -278,7 +282,7 @@ public class RecentTasksLoader {
TaskDescription td = descriptions.get(i);
loadThumbnail(td);
long now = SystemClock.uptimeMillis();
nextTime += 150;
nextTime += 0;
if (nextTime > now) {
try {
Thread.sleep(nextTime-now);

View File

@@ -37,6 +37,7 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
@@ -57,8 +58,8 @@ import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.tablet.StatusBarPanel;
import com.android.systemui.statusbar.tablet.TabletStatusBar;
public class RecentsPanelView extends RelativeLayout
implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener {
public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener {
static final String TAG = "RecentsPanelView";
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
private Context mContext;
@@ -74,6 +75,7 @@ public class RecentsPanelView extends RelativeLayout
private RecentTasksLoader mRecentTasksLoader;
private ArrayList<TaskDescription> mRecentTaskDescriptions;
private boolean mRecentTasksDirty = true;
private TaskDescriptionAdapter mListAdapter;
private int mThumbnailWidth;
@@ -94,6 +96,7 @@ public class RecentsPanelView extends RelativeLayout
/* package */ final static class ViewHolder {
View thumbnailView;
ImageView thumbnailViewImage;
Bitmap thumbnailViewImageBitmap;
ImageView iconView;
TextView labelView;
TextView descriptionView;
@@ -127,6 +130,10 @@ public class RecentsPanelView extends RelativeLayout
holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
holder.thumbnailViewImage = (ImageView) convertView.findViewById(
R.id.app_thumbnail_image);
// If we set the default thumbnail now, we avoid an onLayout when we update
// the thumbnail later (if they both have the same dimensions)
updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
@@ -139,12 +146,15 @@ public class RecentsPanelView extends RelativeLayout
// index is reverse since most recent appears at the bottom...
final int index = mRecentTaskDescriptions.size() - position - 1;
final TaskDescription taskDescription = mRecentTaskDescriptions.get(index);
applyTaskDescription(holder, taskDescription, false);
final TaskDescription td = mRecentTaskDescriptions.get(index);
holder.iconView.setImageDrawable(td.getIcon());
holder.labelView.setText(td.getLabel());
holder.thumbnailView.setContentDescription(td.getLabel());
updateThumbnail(holder, td.getThumbnail(), true, false);
holder.thumbnailView.setTag(taskDescription);
holder.thumbnailView.setTag(td);
holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
holder.taskDescription = taskDescription;
holder.taskDescription = td;
return convertView;
}
@@ -193,6 +203,7 @@ public class RecentsPanelView extends RelativeLayout
}
} else {
mRecentTasksLoader.cancelLoadingThumbnails();
mRecentTasksDirty = true;
}
if (animate) {
if (mShowing != show) {
@@ -250,9 +261,7 @@ public class RecentsPanelView extends RelativeLayout
createCustomAnimations(transitioner);
} else {
((ViewGroup)mRecentsContainer).setLayoutTransition(null);
// Clear memory used by screenshots
mRecentTaskDescriptions.clear();
mListAdapter.notifyDataSetInvalidated();
clearRecentTasksList();
}
}
@@ -374,47 +383,33 @@ public class RecentsPanelView extends RelativeLayout
}
}
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);
}
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));
}
h.labelView.setVisibility(View.VISIBLE);
}
Bitmap thumbnail = td.getThumbnail();
private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
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);
// scale the image to fill the full width of the ImageView. do this only if
// we haven't set a bitmap before, or if the bitmap size has changed
if (h.thumbnailViewImageBitmap == null ||
h.thumbnailViewImageBitmap.getWidth() != thumbnail.getWidth() ||
h.thumbnailViewImageBitmap.getHeight() != thumbnail.getHeight()) {
Matrix scaleMatrix = new Matrix();
float scale = mThumbnailWidth / (float) thumbnail.getWidth();
scaleMatrix.setScale(scale, scale);
h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
h.thumbnailViewImage.setImageMatrix(scaleMatrix);
}
if (show && h.thumbnailView.getVisibility() != View.VISIBLE) {
if (anim) {
h.thumbnailView.setAnimation(
AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
}
h.thumbnailView.setVisibility(View.VISIBLE);
}
h.thumbnailViewImageBitmap = thumbnail;
}
//h.descriptionView.setText(ad.description);
}
void onTaskThumbnailLoaded(TaskDescription ad) {
@@ -432,7 +427,11 @@ public class RecentsPanelView extends RelativeLayout
if (v.getTag() instanceof ViewHolder) {
ViewHolder h = (ViewHolder)v.getTag();
if (h.taskDescription == ad) {
applyTaskDescription(h, ad, true);
// only fade in the thumbnail if recents is already visible-- we
// show it immediately otherwise
boolean animateShow = mShowing &&
mRecentsGlowView.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
updateThumbnail(h, ad.getThumbnail(), true, animateShow);
}
}
}
@@ -440,14 +439,55 @@ public class RecentsPanelView extends RelativeLayout
}
}
private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
if (recentTasksList != null) {
mRecentTaskDescriptions = recentTasksList;
} else {
mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
// additional optimization when we have sofware system buttons - start loading the recent
// tasks on touch down
@Override
public boolean onTouch(View v, MotionEvent ev) {
if (!mShowing) {
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
// If we set our visibility to INVISIBLE here, we avoid an extra call to onLayout
// later when we become visible
setVisibility(INVISIBLE);
refreshRecentTasksList();
} else if (action == MotionEvent.ACTION_CANCEL) {
setVisibility(GONE);
clearRecentTasksList();
} else if (action == MotionEvent.ACTION_UP) {
if (!v.isPressed()) {
setVisibility(GONE);
clearRecentTasksList();
}
}
}
return false;
}
public void clearRecentTasksList() {
// Clear memory used by screenshots
if (mRecentTaskDescriptions != null) {
mRecentTasksLoader.cancelLoadingThumbnails();
mRecentTaskDescriptions.clear();
mListAdapter.notifyDataSetInvalidated();
mRecentTasksDirty = true;
}
}
public void refreshRecentTasksList() {
refreshRecentTasksList(null);
}
private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
if (mRecentTasksDirty) {
if (recentTasksList != null) {
mRecentTaskDescriptions = recentTasksList;
} else {
mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
}
mListAdapter.notifyDataSetInvalidated();
updateUiElements(getResources().getConfiguration());
mRecentTasksDirty = false;
}
mListAdapter.notifyDataSetInvalidated();
updateUiElements(getResources().getConfiguration());
}
public ArrayList<TaskDescription> getRecentTasksList() {

View File

@@ -435,13 +435,18 @@ public class PhoneStatusBar extends StatusBar {
}
};
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
}
// For small-screen devices (read: phones) that lack hardware navigation buttons
private void addNavigationBar() {
if (mNavigationBarView == null) return;
mNavigationBarView.reorient();
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
prepareNavigationBarView();
WindowManagerImpl.getDefault().addView(
mNavigationBarView, getNavigationBarLayoutParams());
@@ -450,9 +455,7 @@ public class PhoneStatusBar extends StatusBar {
private void repositionNavigationBar() {
if (mNavigationBarView == null) return;
mNavigationBarView.reorient();
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
prepareNavigationBarView();
WindowManagerImpl.getDefault().updateViewLayout(
mNavigationBarView, getNavigationBarLayoutParams());
@@ -2007,8 +2010,8 @@ public class PhoneStatusBar extends StatusBar {
}
public void toggleRecentApps() {
int msg = (mRecentsPanel.getVisibility() == View.GONE)
? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
mHandler.removeMessages(msg);
mHandler.sendEmptyMessage(msg);
}

View File

@@ -587,6 +587,7 @@ public class TabletStatusBar extends StatusBar implements
// Add the windows
addPanelWindows();
mRecentButton.setOnTouchListener(mRecentsPanel);
mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
mPile.removeAllViews();
@@ -1805,8 +1806,8 @@ public class TabletStatusBar extends StatusBar implements
}
public void toggleRecentApps() {
int msg = (mRecentsPanel.getVisibility() == View.GONE)
? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
mHandler.removeMessages(msg);
mHandler.sendEmptyMessage(msg);
}