Fix 3253629: Initial pass at updating RecentsPanel UI
Change-Id: I578d0efd9f4b67675ff531494259f54182851e0e
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index c67bbb7..8f590b7 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -34,9 +34,9 @@
<dimen name="password_keyboard_key_height_numeric">0.47in</dimen>
<!-- The width that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_width">160dp</dimen>
+ <dimen name="thumbnail_width">230dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
- <dimen name="thumbnail_height">100dp</dimen>
+ <dimen name="thumbnail_height">135dp</dimen>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">64dip</dimen>
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_bg_protect_tile.png
new file mode 100644
index 0000000..66a54f0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..4f4ae78
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_callout_line.png
new file mode 100644
index 0000000..5f4c035
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_callout_line.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png
new file mode 100644
index 0000000..9f72549
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
index e7a3a61..2989be0 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -19,41 +19,63 @@
-->
<!-- android:background="@drawable/status_bar_closed_default_background" -->
-<LinearLayout
+<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:layout_margin="10dip">
+ android:layout_width="156dip">
<ImageView android:id="@+id/app_thumbnail"
- android:layout_width="88dip"
- android:layout_height="56dip"
- android:layout_margin="10dip"
- android:background="#80808080">
- </ImageView>
-
- <LinearLayout
- android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="10dip"
- android:layout_marginBottom="10dip"
- android:layout_marginRight="10dip">
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="105dip"
+ android:scaleType="center"
+ android:background="@drawable/recents_thumbnail_bg"
+ />
- <ImageView android:id="@+id/app_icon"
- android:layout_width="23dip"
- android:layout_height="23dip"
- android:gravity="bottom"
- android:layout_margin="5dip"
- />
+ <ImageView android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="123dip"
+ android:layout_marginTop="16dip"
+ />
- <TextView android:id="@+id/app_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="22dip"
- />
+ <View android:id="@+id/recents_callout_line"
+ android:layout_width="97dip"
+ android:layout_height="1dip"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="61dip"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="16dip"
+ android:layout_toLeftOf="@id/app_thumbnail"
+ android:layout_marginRight="3dip"
+ android:background="@drawable/recents_callout_line"
+ />
- </LinearLayout>
+ <TextView android:id="@+id/app_label"
+ android:layout_width="113dip"
+ android:layout_height="wrap_content"
+ android:textSize="18dip"
+ android:fadingEdge="none"
+ android:fadingEdgeLength="0dp"
+ android:scrollHorizontally="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="32dip"
+ />
-</LinearLayout>
+ <TextView android:id="@+id/app_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18dip"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="16dip"
+ android:layout_marginTop="61dip"
+ />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
index ac038a7..c7e6dbd 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
@@ -20,34 +20,37 @@
<com.android.systemui.statusbar.tablet.RecentAppsPanel
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_width="wrap_content"
- android:background="@drawable/sysbar_panel_recents_bg"
+ android:layout_marginBottom="48dip"
+ android:background="@drawable/recents_bg_protect_tile"
+ android:id="@+id/recents_bg_protect"
android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/recent_tasks_app_label"
- android:textSize="22dip"
- android:drawableLeft="@drawable/app_icon"
- android:layout_margin="10dip"
- />
-
<TextView android:id="@+id/recents_no_recents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/recent_tasks_empty"
android:textSize="22dip"
android:gravity="center_horizontal|center_vertical"
- android:visibility="gone"
- android:layout_margin="10dip">
+ android:visibility="gone">
</TextView>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
+
<LinearLayout android:id="@+id/recents_container"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="16dip"
+ android:layout_alignParentLeft="true"
+ android:layout_marginRight="100dip"
+ android:background="@drawable/recents_blue_glow"
/>
</com.android.systemui.statusbar.tablet.RecentAppsPanel>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
index 546750b..25a2f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -19,9 +19,14 @@
import java.util.ArrayList;
import java.util.List;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
import android.app.IThumbnailReceiver;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -29,30 +34,44 @@
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.Paint;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.animation.AnimationSet;
+import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import android.widget.ImageView.ScaleType;
import com.android.systemui.R;
public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnClickListener {
private static final String TAG = "RecentAppsPanel";
private static final boolean DEBUG = TabletStatusBar.DEBUG;
- private static final int DISPLAY_TASKS = 4; // number of recent tasks to display
- private static final int MAX_TASKS = 2 * DISPLAY_TASKS; // give some slack for non-apps
- private static final boolean DBG = true;
+ private static final int DISPLAY_TASKS_PORTRAIT = 8;
+ private static final int DISPLAY_TASKS_LANDSCAPE = 5; // number of recent tasks to display
+ private static final int MAX_TASKS = 2 * DISPLAY_TASKS_PORTRAIT; // allow extra for non-apps
private TabletStatusBar mBar;
private TextView mNoRecents;
private LinearLayout mRecentsContainer;
private ArrayList<ActivityDescription> mActivityDescriptions;
+ private int mIconDpi;
+ private AnimatorSet mAnimationSet;
+ private View mBackgroundProtector;
static class ActivityDescription {
int id;
@@ -62,10 +81,12 @@
CharSequence description; // generated by Activity.onCreateDescription()
Intent intent; // launch intent for application
Matrix matrix; // arbitrary rotation matrix to correct orientation
+ String packageName; // used to override animations (see onClick())
int position; // position in list
public ActivityDescription(Bitmap _thumbnail,
- Drawable _icon, String _label, String _desc, Intent _intent, int _id, int _pos)
+ Drawable _icon, String _label, String _desc, Intent _intent, int _id, int _pos,
+ String _packageName)
{
thumbnail = _thumbnail;
icon = _icon;
@@ -74,6 +95,7 @@
intent = _intent;
id = _id;
position = _pos;
+ packageName = _packageName;
}
};
@@ -93,10 +115,10 @@
};
public boolean isInContentArea(int x, int y) {
- final int l = getPaddingLeft();
- final int r = getWidth() - getPaddingRight();
- final int t = getPaddingTop();
- final int b = getHeight() - getPaddingBottom();
+ final int l = mRecentsContainer.getPaddingLeft();
+ final int r = mRecentsContainer.getWidth() - mRecentsContainer.getPaddingRight();
+ final int t = mRecentsContainer.getPaddingTop();
+ final int b = mRecentsContainer.getHeight() - mRecentsContainer.getPaddingBottom();
return x >= l && x < r && y >= t && y < b;
}
@@ -110,6 +132,12 @@
public RecentAppsPanel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ 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;
}
@Override
@@ -117,12 +145,26 @@
super.onFinishInflate();
mNoRecents = (TextView) findViewById(R.id.recents_no_recents);
mRecentsContainer = (LinearLayout) findViewById(R.id.recents_container);
+ mBackgroundProtector = (View) findViewById(R.id.recents_bg_protect);
+
+ // In order to save space, we make the background texture repeat in the Y direction
+ View view = findViewById(R.id.recents_bg_protect);
+ if (view != null && view.getBackground() instanceof BitmapDrawable) {
+ ((BitmapDrawable) view.getBackground()).setTileModeY(TileMode.REPEAT);
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ // we show more in portrait mode, so update UI if orientation changes
+ updateUiElements(newConfig, false);
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
- Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
+ if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
if (visibility == View.VISIBLE && changedView == this) {
refreshApplicationList();
mRecentsContainer.setScrollbarFadingEnabled(true);
@@ -130,6 +172,32 @@
}
}
+ private Drawable getFullResDefaultActivityIcon() {
+ return getFullResIcon(Resources.getSystem(),
+ com.android.internal.R.drawable.sym_def_app_icon);
+ }
+
+ private Drawable getFullResIcon(Resources resources, int iconId) {
+ return resources.getDrawableForDensity(iconId, mIconDpi);
+ }
+
+ 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();
+ }
+
private ArrayList<ActivityDescription> getRecentTasks() {
ArrayList<ActivityDescription> activityDescriptions = new ArrayList<ActivityDescription>();
final PackageManager pm = mContext.getPackageManager();
@@ -164,16 +232,17 @@
if (resolveInfo != null) {
final ActivityInfo info = resolveInfo.activityInfo;
final String title = info.loadLabel(pm).toString();
- Drawable icon = info.loadIcon(pm);
+ // Drawable icon = info.loadIcon(pm);
+ Drawable icon = getFullResIcon(resolveInfo, pm);
int id = recentTasks.get(i).id;
if (title != null && title.length() > 0 && icon != null) {
- Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
+ if (DEBUG) Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
ActivityDescription item = new ActivityDescription(
- null, icon, title, null, intent, id, index);
+ null, icon, title, null, intent, id, index, info.packageName);
activityDescriptions.add(item);
++index;
} else {
- if (DBG) Log.v(TAG, "SKIPPING item " + id);
+ if (DEBUG) Log.v(TAG, "SKIPPING item " + id);
}
}
}
@@ -196,18 +265,18 @@
private void getThumbnails(ArrayList<ActivityDescription> tasks) {
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> runningTasks = am.getRunningTasks(MAX_TASKS, 0, mThumbnailReceiver);
- for (RunningTaskInfo r : runningTasks) {
+ for (RunningTaskInfo runningTaskInfo : runningTasks) {
// Find the activity description associted with the given id
- ActivityDescription desc = findActivityDescription(r.id);
+ ActivityDescription desc = findActivityDescription(runningTaskInfo.id);
if (desc != null) {
- if (r.thumbnail != null) {
- desc.thumbnail = r.thumbnail;
- desc.description = r.description;
+ if (runningTaskInfo.thumbnail != null) {
+ desc.thumbnail = crop(runningTaskInfo.thumbnail);
+ desc.description = runningTaskInfo.description;
} else {
- if (DBG) Log.v(TAG, "*** RUNNING THUMBNAIL WAS NULL ***");
+ if (DEBUG) Log.v(TAG, "*** RUNNING THUMBNAIL WAS NULL ***");
}
} else {
- if (DBG) Log.v(TAG, "Couldn't find ActivityDesc for id=" + r.id);
+ if (DEBUG) Log.v(TAG, "Couldn't find ActivityDesc for id=" + runningTaskInfo.id);
}
}
}
@@ -215,45 +284,101 @@
private void refreshApplicationList() {
mActivityDescriptions = getRecentTasks();
getThumbnails(mActivityDescriptions);
- updateUiElements();
+ updateUiElements(getResources().getConfiguration(), true);
}
- private void updateUiElements() {
+ private Bitmap crop(Bitmap bitmap) {
+ if (bitmap == null || bitmap.getWidth() >= bitmap.getHeight()) {
+ return bitmap;
+ }
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+ Bitmap outBitmap = Bitmap.createBitmap(height, width, bitmap.getConfig());
+ Canvas canvas = new Canvas(outBitmap);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(bitmap,
+ new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight() - height * width / height),
+ new Rect(0, 0, outBitmap.getWidth(), outBitmap.getHeight()),
+ paint);
+ return outBitmap;
+ }
+
+ private void updateUiElements(Configuration config, boolean animate) {
mRecentsContainer.removeAllViews();
+
+
+ // TODO: disabled until I have a chance to tune animations with UX
+ animate = false;
+
+
+ final float initialAlpha = 0.0f;
final int first = 0;
- final int last = Math.min(mActivityDescriptions.size(), DISPLAY_TASKS) - 1;
+ final boolean isPortrait = config.orientation == Configuration.ORIENTATION_PORTRAIT;
+ final int taskCount = isPortrait ? DISPLAY_TASKS_PORTRAIT : DISPLAY_TASKS_LANDSCAPE;
+ final int last = Math.min(mActivityDescriptions.size(), taskCount) - 1;
+ ArrayList<Animator> anims = new ArrayList<Animator>(last+1);
+ DecelerateInterpolator interp = new DecelerateInterpolator();
for (int i = last; i >= first; i--) {
ActivityDescription activityDescription = mActivityDescriptions.get(i);
View view = View.inflate(mContext, R.layout.status_bar_recent_item, null);
ImageView appThumbnail = (ImageView) view.findViewById(R.id.app_thumbnail);
ImageView appIcon = (ImageView) view.findViewById(R.id.app_icon);
- TextView appDescription = (TextView) view.findViewById(R.id.app_label);
- if (activityDescription.thumbnail != null) {
- Log.v(TAG, "thumbnail res = " + activityDescription.thumbnail.getWidth()
- + "x" + activityDescription.thumbnail.getHeight());
- } else {
- Log.v(TAG, "thumbnail for " + activityDescription.label + " was null");
- }
- appThumbnail.setImageBitmap(activityDescription.thumbnail);
+ TextView appLabel = (TextView) view.findViewById(R.id.app_label);
+ TextView appDesc = (TextView) view.findViewById(R.id.app_description);
+ final Bitmap thumb = activityDescription.thumbnail;
+ appThumbnail.setImageBitmap(crop(thumb));
appIcon.setImageDrawable(activityDescription.icon);
- appDescription.setText(activityDescription.label);
+ appLabel.setText(activityDescription.label);
+ appDesc.setText(activityDescription.description);
view.setOnClickListener(this);
view.setTag(activityDescription);
- Log.v(TAG, "Adding task: " + activityDescription.label);
mRecentsContainer.addView(view);
+
+ if (animate) {
+ view.setAlpha(initialAlpha);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", initialAlpha, 1.0f);
+ anim.setDuration(25);
+ anim.setStartDelay((last-i)*25);
+ anim.setInterpolator(interp);
+ anims.add(anim);
+ }
}
int views = mRecentsContainer.getChildCount();
- mNoRecents.setVisibility(views == 0 ? View.VISIBLE : View.GONE);
+ mNoRecents.setVisibility(View.GONE); // views == 0 ? View.VISIBLE : View.GONE);
mRecentsContainer.setVisibility(views > 0 ? View.VISIBLE : View.GONE);
+
+ if (animate) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mRecentsContainer, "alpha",
+ initialAlpha, 1.0f);
+ anim.setDuration(last*25);
+ anim.setInterpolator(interp);
+ anims.add(anim);
+ }
+
+ if (animate) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mBackgroundProtector, "alpha",
+ initialAlpha, 1.0f);
+ anim.setDuration(last*25);
+ anim.setInterpolator(interp);
+ anims.add(anim);
+ }
+
+ if (anims.size() > 0) {
+ mAnimationSet = new AnimatorSet();
+ mAnimationSet.playTogether(anims);
+ mAnimationSet.start();
+ }
}
public void onClick(View v) {
ActivityDescription ad = (ActivityDescription)v.getTag();
+ final ActivityManager am = (ActivityManager)
+ getContext().getSystemService(Context.ACTIVITY_SERVICE);
if (ad.id >= 0) {
// This is an active task; it should just go to the foreground.
- final ActivityManager am = (ActivityManager)
- getContext().getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(ad.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = ad.intent;