Merge "Fix 5023708,4689527: Add popups and overlays to recents. Misc bug fixes."
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_bg_selector.xml b/packages/SystemUI/res/drawable/recents_thumbnail_bg_selector.xml
deleted file mode 100644
index 0e58e12..0000000
--- a/packages/SystemUI/res/drawable/recents_thumbnail_bg_selector.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-	android:exitFadeDuration="@android:integer/config_mediumAnimTime">
-
-    <item android:state_window_focused="false" android:drawable="@android:color/transparent" />
-
-    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
-    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/recents_thumbnail_bg_holo" />
-    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/recents_thumbnail_bg_holo" />
-    <item android:state_focused="true"                                                             android:drawable="@drawable/recents_thumbnail_bg_holo" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_layers.xml b/packages/SystemUI/res/drawable/recents_thumbnail_layers.xml
new file mode 100644
index 0000000..6cae2c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_layers.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:drawable="@drawable/recents_thumbnail_bg" android:id="@+id/base_layer"/>
+    <item android:drawable="@drawable/recents_thumbnail_overlay" android:id="@+id/overlay_layer"/>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml b/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml
new file mode 100644
index 0000000..200bac4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_pressed="true" />
+    <item android:drawable="@*android:color/transparent"/>
+</selector>
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 be4f1d7..8c29042 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -24,14 +24,15 @@
     android:layout_height="wrap_content"
     android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
 
-    <ImageView android:id="@+id/app_thumbnail"
+    <FrameLayout 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="@dimen/status_bar_recents_thumbnail_left_margin"
         android:scaleType="center"
-        android:background="@drawable/recents_thumbnail_bg_selector"
+        android:clickable="true"
+        android:background="@drawable/recents_thumbnail_layers"
     />
 
     <ImageView android:id="@+id/app_icon"
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index efdd9ac..20ef7cf 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -51,7 +51,6 @@
                 android:fadingEdge="horizontal"
                 android:scrollbars="none"
                 android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-                android:listSelector="@drawable/recents_thumbnail_bg_selector"
                 android:layout_gravity="bottom|left"
                 android:orientation="horizontal"
                 android:clipToPadding="false"
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 76965c9..c705a69 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -24,13 +24,15 @@
     android:layout_height="wrap_content"
     android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
 
-    <ImageView android:id="@+id/app_thumbnail"
+    <FrameLayout android:id="@+id/app_thumbnail"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
+        android:clickable="true"
         android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
         android:scaleType="center"
+        android:background="@drawable/recents_thumbnail_layers"
     />
 
     <ImageView android:id="@+id/app_icon"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index 28ef239..c680b8e 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -48,7 +48,6 @@
                 android:fadingEdge="vertical"
                 android:scrollbars="none"
                 android:fadingEdgeLength="@*android:dimen/status_bar_height"
-                android:listSelector="@drawable/recents_thumbnail_bg_selector"
                 android:layout_gravity="bottom|left"
                 android:clipToPadding="false"
                 android:clipChildren="false">
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 9687866..386ce30 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -24,13 +24,15 @@
     android:layout_height="wrap_content"
     android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
 
-    <ImageView android:id="@+id/app_thumbnail"
+    <FrameLayout 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="@dimen/status_bar_recents_thumbnail_left_margin"
         android:scaleType="center"
+        android:clickable="true"
+        android:background="@drawable/recents_thumbnail_layers"
     />
 
     <ImageView android:id="@+id/app_icon"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
index 75fdc67..2c9a152 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
@@ -56,7 +56,6 @@
                 android:scrollbars="none"
                 android:fadingEdgeLength="20dip"
                 android:layout_gravity="bottom|left"
-                android:listSelector="@drawable/recents_thumbnail_bg_selector"
                 android:clipToPadding="false"
                 android:clipChildren="false">
 
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_bg_holo.xml b/packages/SystemUI/res/menu/recent_popup_menu.xml
similarity index 70%
rename from packages/SystemUI/res/drawable/recents_thumbnail_bg_holo.xml
rename to packages/SystemUI/res/menu/recent_popup_menu.xml
index f9bba2a..eecfb9a 100644
--- a/packages/SystemUI/res/drawable/recents_thumbnail_bg_holo.xml
+++ b/packages/SystemUI/res/menu/recent_popup_menu.xml
@@ -17,7 +17,7 @@
 ** limitations under the License.
 */
 -->
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@drawable/recents_thumbnail_bg_press"/>
-    <item android:drawable="@drawable/recents_thumbnail_bg_press"/>
-</transition>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/recent_remove_item" android:title="@string/status_bar_recent_remove_item_title" />
+    <item android:id="@+id/recent_inspect_item" android:title="@string/status_bar_recent_inspect_item_title" />
+</menu>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 882455e..01cf2dc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -35,6 +35,14 @@
          shown again. [CHAR LIMIT=25] -->
     <string name="status_bar_please_disturb_button">Show notifications</string>
 
+    <!-- Title shown in recents popup for removing an application from the list -->
+    <string name="status_bar_recent_remove_item_title">Remove</string>
+
+    <!-- Title shown in recents popup for inspecting an application's properties -->
+    <string name="status_bar_recent_inspect_item_title">Inspect</string>
+
+
+
 
     <!-- The label in the bar at the top of the status bar when there are no notifications
          showing.  [CHAR LIMIT=40]-->
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index 37a9913..2d327c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -53,8 +53,6 @@
     void createAnimation(boolean appearing) {
         float start, end;
 
-        if (RecentsPanelView.DEBUG) Log.e(TAG, "createAnimation()", new Exception());
-
         // 0: on-screen
         // height: off-screen
         float y = mContentView.getTranslationY();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
index 5d29e2a..797f94c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
@@ -26,5 +26,5 @@
 
     void handleOnClick(View selectedView);
     void handleSwipe(View selectedView, int direction);
-    void handleLongPress(View selectedView);
+    void handleLongPress(View selectedView, View anchorView);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index f984aac..12d6cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -20,6 +20,7 @@
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
@@ -30,7 +31,6 @@
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -42,10 +42,9 @@
 
 import com.android.systemui.R;
 
-public class RecentsHorizontalScrollView extends HorizontalScrollView
-        implements View.OnClickListener, View.OnTouchListener {
-    private static final boolean DEBUG_INVALIDATE = false;
+public class RecentsHorizontalScrollView extends HorizontalScrollView {
     private static final String TAG = RecentsPanelView.TAG;
+    private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
     private LinearLayout mLinearLayout;
     private ActivityDescriptionAdapter mAdapter;
@@ -57,6 +56,15 @@
     private VelocityTracker mVelocityTracker;
     private float mDensityScale;
     private float mPagingTouchSlop;
+    private OnLongClickListener mOnLongClick = new OnLongClickListener() {
+        public boolean onLongClick(View v) {
+            final View anchorView = v.findViewById(R.id.app_description);
+            mCurrentView = v;
+            mCallback.handleLongPress(v, anchorView);
+            mCurrentView = null; // make sure we don't accept the return click from this
+            return true;
+        }
+    };
 
     public RecentsHorizontalScrollView(Context context) {
         this(context, null);
@@ -72,13 +80,12 @@
         return mLinearLayout.getWidth() - getWidth();
     }
 
-    public void update() {
+    private void update() {
         mLinearLayout.removeAllViews();
         for (int i = 0; i < mAdapter.getCount(); i++) {
-            View view = mAdapter.getView(i, null, mLinearLayout);
+            final View view = mAdapter.getView(i, null, mLinearLayout);
             view.setClickable(true);
-            view.setOnClickListener(this);
-            view.setOnTouchListener(this);
+            view.setOnLongClickListener(mOnLongClick);
             mLinearLayout.addView(view);
         }
         // Scroll to end after layout.
@@ -91,7 +98,20 @@
     }
 
     @Override
+    public void removeViewInLayout(final View view) {
+        ObjectAnimator anim = animateClosed(view, Constants.MAX_ESCAPE_ANIMATION_DURATION,
+                "y", view.getY(), view.getY() + view.getHeight());
+        anim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                RecentsHorizontalScrollView.super.removeViewInLayout(view);
+            }
+        });
+        anim.start();
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -100,6 +120,18 @@
             case MotionEvent.ACTION_DOWN:
                 mDragging = false;
                 mLastY = ev.getY();
+                final float x = ev.getX() + getScrollX();
+                final float y = ev.getY() + getScrollY();
+                mCurrentView = null;
+                for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+                    View item = mLinearLayout.getChildAt(i);
+                    if (x >= item.getLeft() && x < item.getRight()
+                            && y >= item.getTop() && y < item.getBottom()) {
+                        mCurrentView = item;
+                        if (DEBUG) Log.v(TAG, "Hit item " + item);
+                        break;
+                    }
+                }
                 break;
 
             case MotionEvent.ACTION_MOVE:
@@ -111,6 +143,9 @@
                 break;
 
             case MotionEvent.ACTION_UP:
+                if (mCurrentView != null) {
+                    mCallback.handleOnClick(mCurrentView);
+                }
                 mDragging = false;
                 break;
         }
@@ -125,7 +160,6 @@
         } else if (view.getY() < thumbHeight * (1.0f - Constants.ALPHA_FADE_START)) {
             result = 1.0f + (thumbHeight * Constants.ALPHA_FADE_START + view.getY()) / fadeHeight;
         }
-        if (DEBUG) Log.v(TAG, "FADE AMOUNT: " + result);
         return result;
     }
 
@@ -138,12 +172,12 @@
         mVelocityTracker.addMovement(ev);
 
         final View animView = mCurrentView;
-        // TODO: Cache thumbnail
-        final View thumb = animView.findViewById(R.id.app_thumbnail);
+
         switch (ev.getAction()) {
             case MotionEvent.ACTION_MOVE:
                 if (animView != null) {
                     final float delta = ev.getY() - mLastY;
+                    final View thumb = animView.findViewById(R.id.app_thumbnail);
                     animView.setY(animView.getY() + delta);
                     animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
                     invalidateGlobalRegion(animView);
@@ -167,35 +201,18 @@
                         long duration =
                             (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY));
                         duration = Math.min(duration, Constants.MAX_ESCAPE_ANIMATION_DURATION);
-                        anim = ObjectAnimator.ofFloat(animView, "y", curY, newY);
-                        anim.setInterpolator(new LinearInterpolator());
-                        final int swipeDirection = animView.getY() >= 0.0f ?
-                                RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
-                        anim.addListener(new AnimatorListener() {
-                            public void onAnimationStart(Animator animation) {
-                            }
-                            public void onAnimationRepeat(Animator animation) {
-                            }
-                            public void onAnimationEnd(Animator animation) {
-                                mLinearLayout.removeView(mCurrentView);
-                                mCallback.handleSwipe(animView, swipeDirection);
-                            }
-                            public void onAnimationCancel(Animator animation) {
-                                mLinearLayout.removeView(mCurrentView);
-                                mCallback.handleSwipe(animView, swipeDirection);
-                            }
-                        });
-                        anim.setDuration(duration);
+                        anim = animateClosed(animView, duration, "y", curY, newY);
                     } else { // Animate back to position
                         long duration = Math.abs(velocityY) > 0.0f ?
                                 (long) (Math.abs(newY - curY) * 1000.0f / Math.abs(velocityY))
                                 : Constants.SNAP_BACK_DURATION;
                         duration = Math.min(duration, Constants.SNAP_BACK_DURATION);
                         anim = ObjectAnimator.ofFloat(animView, "y", animView.getY(), 0.0f);
-                        anim.setInterpolator(new DecelerateInterpolator(2.0f));
+                        anim.setInterpolator(new DecelerateInterpolator(4.0f));
                         anim.setDuration(duration);
                     }
 
+                    final View thumb = animView.findViewById(R.id.app_thumbnail);
                     anim.addUpdateListener(new AnimatorUpdateListener() {
                         public void onAnimationUpdate(ValueAnimator animation) {
                             animView.setAlpha(getAlphaForOffset(animView, thumb.getHeight()));
@@ -212,6 +229,26 @@
         return true;
     }
 
+    private ObjectAnimator animateClosed(final View animView, long duration,
+            String attr, float from, float to) {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(animView, attr, from, to);
+        anim.setInterpolator(new LinearInterpolator());
+        final int swipeDirection = animView.getX() >= 0.0f ?
+                RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
+        anim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                mLinearLayout.removeView(animView);
+                mCallback.handleSwipe(animView, swipeDirection);
+            }
+            public void onAnimationCancel(Animator animation) {
+                mLinearLayout.removeView(animView);
+                mCallback.handleSwipe(animView, swipeDirection);
+            }
+        });
+        anim.setDuration(duration);
+        return anim;
+    }
+
     void invalidateGlobalRegion(View view) {
         RectF childBounds
                 = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
@@ -236,13 +273,8 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        LayoutInflater inflater = (LayoutInflater)
-                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
         setScrollbarFadingEnabled(true);
-
         mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
-
         final int leftPadding = mContext.getResources()
             .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
         setOverScrollEffectPadding(leftPadding, 0);
@@ -306,16 +338,7 @@
         mLinearLayout.setLayoutTransition(transition);
     }
 
-    public void onClick(View view) {
-        mCallback.handleOnClick(view);
-    }
-
     public void setCallback(RecentsCallback callback) {
         mCallback = callback;
     }
-
-    public boolean onTouch(View v, MotionEvent event) {
-        mCurrentView = v;
-        return false;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index a55fe9c..bc0a508 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -22,6 +22,7 @@
 import android.animation.Animator;
 import android.animation.LayoutTransition;
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -39,16 +40,22 @@
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.Uri;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
+import android.widget.Button;
 import android.widget.ImageView;
+import android.widget.PopupMenu;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
@@ -69,7 +76,7 @@
     private int mIconDpi;
     private View mRecentsScrim;
     private View mRecentsGlowView;
-    private View mRecentsContainer;
+    private ViewGroup mRecentsContainer;
     private Bitmap mGlowBitmap;
     // TODO: add these widgets attributes to the layout file
     private int mGlowBitmapPaddingLeftPx;
@@ -107,8 +114,18 @@
         }
     };
 
+    private final class OnLongClickDelegate implements View.OnLongClickListener {
+        View mOtherView;
+        OnLongClickDelegate(View other) {
+            mOtherView = other;
+        }
+        public boolean onLongClick(View v) {
+            return mOtherView.performLongClick();
+        }
+    }
+
     /* package */ final static class ViewHolder {
-        ImageView thumbnailView;
+        View thumbnailView;
         ImageView iconView;
         TextView labelView;
         TextView descriptionView;
@@ -139,7 +156,7 @@
             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.thumbnailView = 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);
@@ -153,11 +170,12 @@
 
             final ActivityDescription activityDescription = mActivityDescriptions.get(activityId);
             final Bitmap thumb = activityDescription.thumbnail;
-            holder.thumbnailView.setImageBitmap(compositeBitmap(mGlowBitmap, thumb));
+            updateDrawable(holder.thumbnailView, compositeBitmap(mGlowBitmap, thumb));
             holder.iconView.setImageDrawable(activityDescription.icon);
             holder.labelView.setText(activityDescription.label);
             holder.descriptionView.setText(activityDescription.description);
             holder.thumbnailView.setTag(activityDescription);
+            holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
             holder.activityDescription = activityDescription;
 
             return convertView;
@@ -174,6 +192,20 @@
         return x >= l && x < r && y >= t && y < b;
     }
 
+    private void updateDrawable(View thumbnailView, Bitmap bitmap) {
+        Drawable d = thumbnailView.getBackground();
+        if (d instanceof LayerDrawable) {
+            LayerDrawable layerD = (LayerDrawable) d;
+            Drawable thumb = layerD.findDrawableByLayerId(R.id.base_layer);
+            if (thumb != null) {
+                layerD.setDrawableByLayerId(R.id.base_layer,
+                        new BitmapDrawable(getResources(), bitmap));
+                return;
+            }
+        }
+        Log.w(TAG, "Failed to update drawable");
+    }
+
     public void show(boolean show, boolean animate) {
         if (animate) {
             if (mShowing != show) {
@@ -260,7 +292,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mRecentsContainer = findViewById(R.id.recents_container);
+        mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
         mListAdapter = new ActivityDescriptionAdapter(mContext);
         if (mRecentsContainer instanceof RecentsListView) {
             RecentsListView listView = (RecentsListView) mRecentsContainer;
@@ -503,7 +535,35 @@
         am.removeTask(ad.taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
     }
 
-    public void handleLongPress(View selectedView) {
-        // TODO show context menu : "Remove from list", "Show properties"
+    private void startApplicationDetailsActivity(String packageName) {
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+                Uri.fromParts("package", packageName, null));
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getContext().startActivity(intent);
+    }
+
+    public void handleLongPress(final View selectedView, final View anchorView) {
+        PopupMenu popup = new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
+        popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
+        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                if (item.getItemId() == R.id.recent_remove_item) {
+                    mRecentsContainer.removeViewInLayout(selectedView);
+                } else if (item.getItemId() == R.id.recent_inspect_item) {
+                    ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
+                    if (viewHolder != null) {
+                        final ActivityDescription ad = viewHolder.activityDescription;
+                        startApplicationDetailsActivity(ad.packageName);
+                        mBar.animateCollapse();
+                    } else {
+                        throw new IllegalStateException("Oops, no tag on view " + selectedView);
+                    }
+                } else {
+                    return false;
+                }
+                return true;
+            }
+        });
+        popup.show();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 27bb0b5..9dd170c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -20,6 +20,7 @@
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
@@ -42,8 +43,7 @@
 
 import com.android.systemui.R;
 
-public class RecentsVerticalScrollView extends ScrollView
-        implements View.OnClickListener, View.OnTouchListener {
+public class RecentsVerticalScrollView extends ScrollView {
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
@@ -57,6 +57,15 @@
     private VelocityTracker mVelocityTracker;
     private float mDensityScale;
     private float mPagingTouchSlop;
+    private OnLongClickListener mOnLongClick = new OnLongClickListener() {
+        public boolean onLongClick(View v) {
+            final View anchorView = v.findViewById(R.id.app_description);
+            mCurrentView = v;
+            mCallback.handleLongPress(v, anchorView);
+            mCurrentView = null; // make sure we don't accept the return click from this
+            return true;
+        }
+    };
 
     public RecentsVerticalScrollView(Context context) {
         this(context, null);
@@ -72,13 +81,12 @@
         return mLinearLayout.getHeight() - getHeight();
     }
 
-    public void update() {
+    private void update() {
         mLinearLayout.removeAllViews();
         for (int i = 0; i < mAdapter.getCount(); i++) {
-            View view = mAdapter.getView(i, null, mLinearLayout);
+            final View view = mAdapter.getView(i, null, mLinearLayout);
             view.setClickable(true);
-            view.setOnClickListener(this);
-            view.setOnTouchListener(this);
+            view.setOnLongClickListener(mOnLongClick);
             mLinearLayout.addView(view);
         }
         // Scroll to end after layout.
@@ -91,7 +99,20 @@
     }
 
     @Override
+    public void removeViewInLayout(final View view) {
+        ObjectAnimator anim = animateClosed(view, Constants.MAX_ESCAPE_ANIMATION_DURATION,
+                "x", view.getX(), view.getX() + view.getWidth());
+        anim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                RecentsVerticalScrollView.super.removeViewInLayout(view);
+            }
+        });
+        anim.start();
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -100,6 +121,18 @@
             case MotionEvent.ACTION_DOWN:
                 mDragging = false;
                 mLastX = ev.getX();
+                final float x = ev.getX() + getScrollX();
+                final float y = ev.getY() + getScrollY();
+                mCurrentView = null;
+                for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+                    View item = mLinearLayout.getChildAt(i);
+                    if (x >= item.getLeft() && x < item.getRight()
+                            && y >= item.getTop() && y < item.getBottom()) {
+                        mCurrentView = item;
+                        Log.v(TAG, "Hit item " + item);
+                        break;
+                    }
+                }
                 break;
 
             case MotionEvent.ACTION_MOVE:
@@ -111,6 +144,9 @@
                 break;
 
             case MotionEvent.ACTION_UP:
+                if (mCurrentView != null) {
+                    mCallback.handleOnClick(mCurrentView);
+                }
                 mDragging = false;
                 break;
         }
@@ -125,7 +161,6 @@
         } else if (view.getX() < thumbWidth* (1.0f - Constants.ALPHA_FADE_START)) {
             result = 1.0f + (thumbWidth*Constants.ALPHA_FADE_START + view.getX()) / fadeWidth;
         }
-        if (DEBUG) Log.v(TAG, "FADE AMOUNT: " + result);
         return result;
     }
 
@@ -138,12 +173,12 @@
         mVelocityTracker.addMovement(ev);
 
         final View animView = mCurrentView;
-        // TODO: Cache thumbnail
-        final View thumb = animView.findViewById(R.id.app_thumbnail);
+
         switch (ev.getAction()) {
             case MotionEvent.ACTION_MOVE:
                 if (animView != null) {
                     final float delta = ev.getX() - mLastX;
+                    final View thumb = animView.findViewById(R.id.app_thumbnail);
                     animView.setX(animView.getX() + delta);
                     animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth()));
                     invalidateGlobalRegion(animView);
@@ -163,29 +198,11 @@
                     final float maxVelocity = Constants.ESCAPE_VELOCITY * mDensityScale;
                     if (Math.abs(velocityX) > Math.abs(velocityY)
                             && Math.abs(velocityX) > maxVelocity
-                            && (velocityX > 0.0f) == (animView.getX() >= 0)) {
+                            && (velocityX >= 0.0f) == (animView.getX() >= 0)) {
                         long duration =
                             (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX));
                         duration = Math.min(duration, Constants.MAX_ESCAPE_ANIMATION_DURATION);
-                        anim = ObjectAnimator.ofFloat(animView, "x", curX, newX);
-                        anim.setInterpolator(new LinearInterpolator());
-                        final int swipeDirection = animView.getX() >= 0.0f ?
-                                RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
-                        anim.addListener(new AnimatorListener() {
-                            public void onAnimationStart(Animator animation) {
-                            }
-                            public void onAnimationRepeat(Animator animation) {
-                            }
-                            public void onAnimationEnd(Animator animation) {
-                                mLinearLayout.removeView(mCurrentView);
-                                mCallback.handleSwipe(animView, swipeDirection);
-                            }
-                            public void onAnimationCancel(Animator animation) {
-                                mLinearLayout.removeView(mCurrentView);
-                                mCallback.handleSwipe(animView, swipeDirection);
-                            }
-                        });
-                        anim.setDuration(duration);
+                        anim = animateClosed(animView, duration, "x", curX, newX);
                     } else { // Animate back to position
                         long duration = Math.abs(velocityX) > 0.0f ?
                                 (long) (Math.abs(newX-curX) * 1000.0f / Math.abs(velocityX))
@@ -196,6 +213,7 @@
                         anim.setDuration(duration);
                     }
 
+                    final View thumb = animView.findViewById(R.id.app_thumbnail);
                     anim.addUpdateListener(new AnimatorUpdateListener() {
                         public void onAnimationUpdate(ValueAnimator animation) {
                             animView.setAlpha(getAlphaForOffset(animView, thumb.getWidth()));
@@ -212,6 +230,26 @@
         return true;
     }
 
+    private ObjectAnimator animateClosed(final View animView, long duration,
+            String attr, float from, float to) {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(animView, attr, from, to);
+        anim.setInterpolator(new LinearInterpolator());
+        final int swipeDirection = animView.getX() >= 0.0f ?
+                RecentsCallback.SWIPE_RIGHT : RecentsCallback.SWIPE_LEFT;
+        anim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animation) {
+                mLinearLayout.removeView(animView);
+                mCallback.handleSwipe(animView, swipeDirection);
+            }
+            public void onAnimationCancel(Animator animation) {
+                mLinearLayout.removeView(animView);
+                mCallback.handleSwipe(animView, swipeDirection);
+            }
+        });
+        anim.setDuration(duration);
+        return anim;
+    }
+
     void invalidateGlobalRegion(View view) {
         RectF childBounds
                 = new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
@@ -236,13 +274,8 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        LayoutInflater inflater = (LayoutInflater)
-                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
         setScrollbarFadingEnabled(true);
-
         mLinearLayout = (LinearLayout) findViewById(R.id.recents_linear_layout);
-
         final int leftPadding = mContext.getResources()
             .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
         setOverScrollEffectPadding(leftPadding, 0);
@@ -306,16 +339,7 @@
         mLinearLayout.setLayoutTransition(transition);
     }
 
-    public void onClick(View view) {
-        mCallback.handleOnClick(view);
-    }
-
     public void setCallback(RecentsCallback callback) {
         mCallback = callback;
     }
-
-    public boolean onTouch(View v, MotionEvent event) {
-        mCurrentView = v;
-        return false;
-    }
 }