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
diff --git a/packages/SystemUI/res/anim/recent_appear.xml b/packages/SystemUI/res/anim/recent_appear.xml
index 20fe052..4400d9d 100644
--- a/packages/SystemUI/res/anim/recent_appear.xml
+++ b/packages/SystemUI/res/anim/recent_appear.xml
@@ -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"
/>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 83c4faf..2d76455 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -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"
/>
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index 3d8b9d6..b653fcd 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -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"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index e6336718..18a31f7 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -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"
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 47aa849..dcda9c2 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -86,8 +86,8 @@
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 @@
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 @@
TaskDescription td = descriptions.get(i);
loadThumbnail(td);
long now = SystemClock.uptimeMillis();
- nextTime += 150;
+ nextTime += 0;
if (nextTime > now) {
try {
Thread.sleep(nextTime-now);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index bd1fcfc..343b33514 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -37,6 +37,7 @@
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.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 @@
private RecentTasksLoader mRecentTasksLoader;
private ArrayList<TaskDescription> mRecentTaskDescriptions;
+ private boolean mRecentTasksDirty = true;
private TaskDescriptionAdapter mListAdapter;
private int mThumbnailWidth;
@@ -94,6 +96,7 @@
/* package */ final static class ViewHolder {
View thumbnailView;
ImageView thumbnailViewImage;
+ Bitmap thumbnailViewImageBitmap;
ImageView iconView;
TextView labelView;
TextView descriptionView;
@@ -127,6 +130,10 @@
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 @@
// 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 @@
}
} else {
mRecentTasksLoader.cancelLoadingThumbnails();
+ mRecentTasksDirty = true;
}
if (animate) {
if (mShowing != show) {
@@ -250,9 +261,7 @@
createCustomAnimations(transitioner);
} else {
((ViewGroup)mRecentsContainer).setLayoutTransition(null);
- // Clear memory used by screenshots
- mRecentTaskDescriptions.clear();
- mListAdapter.notifyDataSetInvalidated();
+ clearRecentTasksList();
}
}
@@ -374,47 +383,33 @@
}
}
-
- 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 @@
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 @@
}
}
- 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();
+ }
+ }
}
- mListAdapter.notifyDataSetInvalidated();
- updateUiElements(getResources().getConfiguration());
+ 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;
+ }
}
public ArrayList<TaskDescription> getRecentTasksList() {
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 b724552..77a7f51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -435,13 +435,18 @@
}
};
+ 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 @@
private void repositionNavigationBar() {
if (mNavigationBarView == null) return;
- mNavigationBarView.reorient();
-
- mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
+ prepareNavigationBarView();
WindowManagerImpl.getDefault().updateViewLayout(
mNavigationBarView, getNavigationBarLayoutParams());
@@ -2007,8 +2010,8 @@
}
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);
}
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 f0a10f3..de8226b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -587,6 +587,7 @@
// Add the windows
addPanelWindows();
+ mRecentButton.setOnTouchListener(mRecentsPanel);
mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
mPile.removeAllViews();
@@ -1805,8 +1806,8 @@
}
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);
}