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;