Merge "Fix 3386135: Make recent apps scrollable with more entries"
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
index e4d5a32..7b54daf 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.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
index 9f72549..87a67c9 100644
--- a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.png
new file mode 100644
index 0000000..7f86fb3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xlarge-mdpi/recents_thumbnail_bg_press.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 d7e1633..bfa6c36a 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -22,14 +22,14 @@
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="156dip">
+    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
 
     <ImageView android:id="@+id/app_thumbnail"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
-        android:layout_marginLeft="105dip"
+        android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
         android:scaleType="center"
     />
 
@@ -40,8 +40,8 @@
         android:layout_alignParentTop="true"
         android:layout_marginLeft="123dip"
         android:layout_marginTop="16dip"
-        android:maxWidth="64dip"
-        android:maxHeight="64dip"
+        android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+        android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
         android:adjustViewBounds="true"
     />
 
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 ecd2b6f..4be57a2 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
@@ -36,36 +36,35 @@
         <LinearLayout android:id="@+id/recents_glow"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:layout_marginBottom="-28dip"
+            android:layout_marginBottom="-52dip"
             android:layout_gravity="bottom"
-            android:background="@drawable/recents_blue_glow">
+            android:background="@drawable/recents_blue_glow"
+            android:orientation="horizontal"
+            >
 
-            <LinearLayout android:id="@+id/recents_container"
-                android:layout_width="356dip"
+            <ListView android:id="@+id/recents_container"
+                android:layout_width="@dimen/status_bar_recents_width"
                 android:layout_height="wrap_content"
-                android:orientation="vertical"
                 android:layout_marginRight="100dip"
+                android:divider="@null"
+                android:scrollingCache="true"
+                android:stackFromBottom="true"
+                android:fadingEdge="vertical"
+                android:scrollbars="none"
+                android:fadingEdgeLength="30dip"
+                android:listSelector="@drawable/recents_thumbnail_bg_press"
             />
 
         </LinearLayout>
 
     </FrameLayout>
 
-    <!-- The outer FrameLayout is just used as an opaque background for the dismiss icon -->
-    <FrameLayout
+    <View android:id="@+id/recents_dismiss_button"
         android:layout_width="80px"
         android:layout_height="@*android:dimen/status_bar_height"
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
-        android:background="#ff000000">
-
-        <View android:id="@+id/recents_dismiss_button"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:background="@drawable/ic_sysbar_back_ime"
-        />
-
-    </FrameLayout>
+        android:background="@drawable/ic_sysbar_back_ime"
+    />
 
 </com.android.systemui.statusbar.tablet.RecentAppsPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml
new file mode 100644
index 0000000..4d14d1f
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/listview_footer_padding"
+    android:layout_height="24dip"
+    android:layout_width="match_parent">
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 93cf377..88cd43c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2,21 +2,34 @@
 <!--
  * Copyright (c) 2006, The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); 
- * you may not use this file except in compliance with the License. 
- * You may obtain a copy of the License at 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0 
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software 
- * distributed under the License is distributed on an "AS IS" BASIS, 
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- * See the License for the specific language governing permissions and 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
 */
 -->
 <resources>
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
     <dimen name="status_bar_edge_ignore">5dp</dimen>
+
+    <!-- Recent Applications parameters -->
+    <!-- Width of a recent app view, including all content -->
+    <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
+    <!-- How far the thumbnail for a recent app appears from left edge -->
+    <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen>
+    <!-- Upper width limit for application icon -->
+    <dimen name="status_bar_recents_thumbnail_max_width">64dp</dimen>
+    <!-- Upper height limit for application icon -->
+    <dimen name="status_bar_recents_thumbnail_max_height">64dp</dimen>
+    <!-- Width of scrollable area in recents -->
+    <dimen name="status_bar_recents_width">356dp</dimen>
+
 </resources>
 
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 e0d558f..ebe1a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -40,37 +40,43 @@
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
+import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.DecelerateInterpolator;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 
-public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, OnClickListener {
+public class RecentAppsPanel extends RelativeLayout implements StatusBarPanel, OnItemClickListener {
     private static final int GLOW_PADDING = 15;
     private static final String TAG = "RecentAppsPanel";
     private static final boolean DEBUG = TabletStatusBar.DEBUG;
-    private static final int DISPLAY_TASKS_PORTRAIT = 7; // Limited by max binder transaction size
-    private static final int DISPLAY_TASKS_LANDSCAPE = 5; // number of recent tasks to display
-    private static final int MAX_TASKS = DISPLAY_TASKS_PORTRAIT + 1; // allow extra for non-apps
+    private static final int DISPLAY_TASKS = 20;
+    private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
+    private static final int BOTTOM_OFFSET = 28; // TODO: Get from dimens.xml
     private TabletStatusBar mBar;
     private ArrayList<ActivityDescription> mActivityDescriptions;
     private int mIconDpi;
     private View mRecentsScrim;
     private View mRecentsGlowView;
-    private LinearLayout mRecentsContainer;
+    private ListView mRecentsContainer;
     private Bitmap mGlowBitmap;
     private boolean mShowing;
     private Choreographer mChoreo;
     private View mRecentsDismissButton;
+    private ActvityDescriptionAdapter mListAdapter;
+    protected int mLastVisibleItem;
 
     static class ActivityDescription {
         int id;
@@ -98,6 +104,63 @@
         }
     };
 
+    private static class ViewHolder {
+        private ImageView thumbnailView;
+        private ImageView iconView;
+        private TextView labelView;
+        private TextView descriptionView;
+        private ActivityDescription activityDescription;
+    }
+
+    private class ActvityDescriptionAdapter extends BaseAdapter {
+        private LayoutInflater mInflater;
+
+        public ActvityDescriptionAdapter(Context context) {
+            mInflater = LayoutInflater.from(context);
+        }
+
+        public int getCount() {
+            return mActivityDescriptions != null ? mActivityDescriptions.size() : 0;
+        }
+
+        public Object getItem(int position) {
+            return position; // we only need the index
+        }
+
+        public long getItemId(int position) {
+            return position; // we just need something unique for this position
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewHolder holder;
+            if (convertView == null) {
+                convertView = mInflater.inflate(R.layout.status_bar_recent_item, null);
+                holder = new ViewHolder();
+                holder.thumbnailView = (ImageView) convertView.findViewById(R.id.app_thumbnail);
+                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);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            // activityId is reverse since most recent appears at the bottom...
+            final int activityId = mActivityDescriptions.size() - position - 1;
+
+            final ActivityDescription activityDescription = mActivityDescriptions.get(activityId);
+            final Bitmap thumb = activityDescription.thumbnail;
+            holder.thumbnailView.setImageBitmap(compositeBitmap(mGlowBitmap, thumb));
+            holder.iconView.setImageDrawable(activityDescription.icon);
+            holder.labelView.setText(activityDescription.label);
+            holder.descriptionView.setText(activityDescription.description);
+            holder.thumbnailView.setTag(activityDescription);
+            holder.activityDescription = activityDescription;
+
+            return convertView;
+        }
+    }
+
     public boolean isInContentArea(int x, int y) {
         // use mRecentsContainer's exact bounds to determine horizontal position
         final int l = mRecentsContainer.getLeft();
@@ -267,9 +330,41 @@
     }
 
     @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        // Keep track of the last visible item in the list so we can restore it
+        // to the bottom when the orientation changes.
+        int childCount = mRecentsContainer.getChildCount();
+        if (childCount > 0) {
+            mLastVisibleItem = mRecentsContainer.getFirstVisiblePosition() + childCount - 1;
+            View view = mRecentsContainer.getChildAt(childCount - 1);
+            final int distanceFromBottom = mRecentsContainer.getHeight() - view.getTop();
+            //final int distanceFromBottom = view.getHeight() + BOTTOM_OFFSET;
+
+            // This has to happen post-layout, so run it "in the future"
+            post(new Runnable() {
+                public void run() {
+                    mRecentsContainer.setSelectionFromTop(mLastVisibleItem,
+                            mRecentsContainer.getHeight() - distanceFromBottom);
+                }
+            });
+        }
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mRecentsContainer = (LinearLayout) findViewById(R.id.recents_container);
+        LayoutInflater inflater = (LayoutInflater)
+        mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+        mRecentsContainer = (ListView) findViewById(R.id.recents_container);
+        View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer,
+                mRecentsContainer, false);
+        mRecentsContainer.setScrollbarFadingEnabled(true);
+        mRecentsContainer.addFooterView(footer);
+        mRecentsContainer.setAdapter(mListAdapter = new ActvityDescriptionAdapter(mContext));
+        mRecentsContainer.setOnItemClickListener(this);
+
         mRecentsGlowView = findViewById(R.id.recents_glow);
         mRecentsScrim = (View) findViewById(R.id.recents_bg_protect);
         mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView);
@@ -287,20 +382,16 @@
     }
 
     @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);
         if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
         if (visibility == View.VISIBLE && changedView == this) {
             refreshApplicationList();
-            mRecentsContainer.setScrollbarFadingEnabled(true);
-            mRecentsContainer.scrollTo(0, 0);
+            post(new Runnable() {
+                public void run() {
+                    mRecentsContainer.setSelection(mActivityDescriptions.size() - 1);
+                }
+            });
         }
     }
 
@@ -402,11 +493,12 @@
 
     private void refreshApplicationList() {
         mActivityDescriptions = getRecentTasks();
+        mListAdapter.notifyDataSetInvalidated();
         if (mActivityDescriptions.size() > 0) {
-            updateUiElements(getResources().getConfiguration(), true);
+            mLastVisibleItem = mActivityDescriptions.size() - 1; // scroll to bottom after reloading
+            updateUiElements(getResources().getConfiguration());
         } else {
             // Immediately hide this panel
-            mShowing = false;
             hide(false);
         }
     }
@@ -426,44 +518,29 @@
             canvas.drawBitmap(thumbnail,
                     new Rect(0, 0, srcWidth-1, srcHeight-1),
                     new RectF(GLOW_PADDING,
-                            GLOW_PADDING - 4.0f,
-                            outBitmap.getWidth() - GLOW_PADDING + 2.0f,
-                            outBitmap.getHeight() - GLOW_PADDING + 3.0f), paint);
+                            GLOW_PADDING - 7.0f,
+                            outBitmap.getWidth() - GLOW_PADDING + 3.0f,
+                            outBitmap.getHeight() - GLOW_PADDING + 7.0f), paint);
         }
         return outBitmap;
     }
 
-    private void updateUiElements(Configuration config, boolean animate) {
-        mRecentsContainer.removeAllViews();
+    private void updateUiElements(Configuration config) {
+        final int items = mActivityDescriptions.size();
 
-        final int first = 0;
-        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;
-        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 appLabel = (TextView) view.findViewById(R.id.app_label);
-            TextView appDesc = (TextView) view.findViewById(R.id.app_description);
-            final Bitmap thumb = activityDescription.thumbnail;
-            appThumbnail.setImageBitmap(compositeBitmap(mGlowBitmap, thumb));
-            appIcon.setImageDrawable(activityDescription.icon);
-            appLabel.setText(activityDescription.label);
-            appDesc.setText(activityDescription.description);
-            appThumbnail.setOnClickListener(this);
-            appThumbnail.setTag(activityDescription);
-            mRecentsContainer.addView(view);
-        }
-
-        int views = mRecentsContainer.getChildCount();
-        mRecentsContainer.setVisibility(views > 0 ? View.VISIBLE : View.GONE);
-        mRecentsGlowView.setVisibility(views > 0 ? View.VISIBLE : View.GONE);
+        mRecentsContainer.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
+        mRecentsGlowView.setVisibility(items > 0 ? View.VISIBLE : View.GONE);
     }
 
-    public void onClick(View v) {
-        ActivityDescription ad = (ActivityDescription)v.getTag();
+    private void hide(boolean animate) {
+        if (!animate) {
+            setVisibility(View.GONE);
+        }
+        mBar.animateCollapse();
+    }
+
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        ActivityDescription ad = ((ViewHolder) view.getTag()).activityDescription;
         final ActivityManager am = (ActivityManager)
                 getContext().getSystemService(Context.ACTIVITY_SERVICE);
         if (ad.id >= 0) {
@@ -478,11 +555,4 @@
         }
         hide(true);
     }
-
-    private void hide(boolean animate) {
-        setVisibility(View.GONE);
-        if (animate) {
-            mBar.animateCollapse();
-        }
-    }
 }
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 233d601..f0408a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -267,6 +267,8 @@
         lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
         lp.setTitle("RecentsPanel");
         lp.windowAnimations = R.style.Animation_RecentPanel;
+        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 
         WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
         mRecentsPanel.setBar(this);
@@ -509,7 +511,7 @@
                         final int peekIndex = m.arg1;
                         if (peekIndex < N) {
                             //Slog.d(TAG, "loading peek: " + peekIndex);
-                            NotificationData.Entry entry = 
+                            NotificationData.Entry entry =
                                 mNotificationDNDMode
                                     ? mNotificationDNDDummyEntry
                                     : mNotificationData.get(N-1-peekIndex);
@@ -555,7 +557,7 @@
 
                     final int N = mNotificationData.size();
                     if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
-                        NotificationData.Entry entry = 
+                        NotificationData.Entry entry =
                             mNotificationDNDMode
                                 ? mNotificationDNDDummyEntry
                                 : mNotificationData.get(N-1-mNotificationPeekIndex);
@@ -584,6 +586,8 @@
                 case MSG_OPEN_RECENTS_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening recents panel");
                     if (mRecentsPanel != null) {
+                        disable(StatusBarManager.DISABLE_NAVIGATION
+                                | StatusBarManager.DISABLE_BACK);
                         mRecentsPanel.setVisibility(View.VISIBLE);
                         mRecentsPanel.show(true, true);
                     }
@@ -591,6 +595,7 @@
                 case MSG_CLOSE_RECENTS_PANEL:
                     if (DEBUG) Slog.d(TAG, "closing recents panel");
                     if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
+                        disable(StatusBarManager.DISABLE_NONE);
                         mRecentsPanel.show(false, true);
                     }
                     break;
@@ -701,7 +706,7 @@
                 && oldContentView.getLayoutId() == contentView.getLayoutId();
         ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
-                && notification.priority == oldNotification.priority; 
+                && notification.priority == oldNotification.priority;
                 // priority now encompasses isOngoing()
         boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1;
         if (contentsUnchanged && (orderUnchanged || isLastAnyway)) {