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:
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user