Merge "Long-press a notification to find out who sent it."
diff --git a/packages/SystemUI/res/menu/notification_popup_menu.xml b/packages/SystemUI/res/menu/notification_popup_menu.xml
new file mode 100644
index 0000000..8923fb6
--- /dev/null
+++ b/packages/SystemUI/res/menu/notification_popup_menu.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2012, 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.
+*/
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/notification_inspect_item" android:title="@string/status_bar_notification_inspect_item_title" />
+</menu>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6dbe9d3..236ca6b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -365,4 +365,8 @@
 
     <!-- Description of the desk dock action that invokes the Android Dreams screen saver feature -->
     <string name="dreams_dock_launcher">Activate screen saver</string>
+
+    <!-- Title shown in notification popup for inspecting the responsible
+         application -->
+    <string name="status_bar_notification_inspect_item_title">App info</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 19657a9..414af89 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -23,11 +23,14 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.graphics.RectF;
+import android.os.Handler;
 import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.LinearInterpolator;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 public class SwipeHelper implements Gefingerpoken {
     static final String TAG = "com.android.systemui.SwipeHelper";
@@ -57,6 +60,7 @@
 
     private float mPagingTouchSlop;
     private Callback mCallback;
+    private Handler mHandler;
     private int mSwipeDirection;
     private VelocityTracker mVelocityTracker;
 
@@ -67,15 +71,24 @@
     private boolean mCanCurrViewBeDimissed;
     private float mDensityScale;
 
+    private boolean mLongPressSent;
+    private View.OnLongClickListener mLongPressListener;
+    private Runnable mWatchLongPress;
+
     public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
             float pagingTouchSlop) {
         mCallback = callback;
+        mHandler = new Handler();
         mSwipeDirection = swipeDirection;
         mVelocityTracker = VelocityTracker.obtain();
         mDensityScale = densityScale;
         mPagingTouchSlop = pagingTouchSlop;
     }
 
+    public void setLongPressListener(View.OnLongClickListener listener) {
+        mLongPressListener = listener;
+    }
+
     public void setDensityScale(float densityScale) {
         mDensityScale = densityScale;
     }
@@ -167,12 +180,19 @@
         }
     }
 
+    private void removeLongPressCallback() {
+        if (mWatchLongPress != null) {
+            mHandler.removeCallbacks(mWatchLongPress);
+        }
+    }
+
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         final int action = ev.getAction();
 
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mDragging = false;
+                mLongPressSent = false;
                 mCurrView = mCallback.getChildAtPosition(ev);
                 mVelocityTracker.clear();
                 if (mCurrView != null) {
@@ -180,10 +200,28 @@
                     mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
                     mVelocityTracker.addMovement(ev);
                     mInitialTouchPos = getPos(ev);
+
+                    if (mLongPressListener != null) {
+                        if (mWatchLongPress == null) {
+                            mWatchLongPress = new Runnable() {
+                                @Override
+                                public void run() {
+                                    if (mCurrView != null && !mLongPressSent) {
+                                        mLongPressSent = true;
+                                        mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+                                        mLongPressListener.onLongClick(mCurrView);
+                                    }
+                                }
+                            };
+                        }
+                        mHandler.postDelayed(mWatchLongPress, ViewConfiguration.getLongPressTimeout());
+                    }
+
                 }
                 break;
+
             case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null) {
+                if (mCurrView != null && !mLongPressSent) {
                     mVelocityTracker.addMovement(ev);
                     float pos = getPos(ev);
                     float delta = pos - mInitialTouchPos;
@@ -191,14 +229,19 @@
                         mCallback.onBeginDrag(mCurrView);
                         mDragging = true;
                         mInitialTouchPos = getPos(ev) - getTranslation(mCurrAnimView);
+
+                        removeLongPressCallback();
                     }
                 }
+
                 break;
+
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mDragging = false;
                 mCurrView = null;
                 mCurrAnimView = null;
+                mLongPressSent = false;
                 break;
         }
         return mDragging;
@@ -269,6 +312,10 @@
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
+        if (mLongPressSent) {
+            return true;
+        }
+
         if (!mDragging) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index dba70be..3803092 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -26,17 +26,20 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,6 +48,7 @@
 import android.view.WindowManagerImpl;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
+import android.widget.PopupMenu;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -214,6 +218,39 @@
         }
     }
 
+    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);
+        mContext.startActivity(intent);
+    }
+
+    protected View.OnLongClickListener getNotificationLongClicker() { 
+        return new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                final String packageNameF = (String) v.getTag();
+                if (packageNameF == null) return false;
+                PopupMenu popup = new PopupMenu(mContext, v);
+                popup.getMenuInflater().inflate(R.menu.notification_popup_menu, popup.getMenu());
+                popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+                    public boolean onMenuItemClick(MenuItem item) {
+                        if (item.getItemId() == R.id.notification_inspect_item) {
+                            startApplicationDetailsActivity(packageNameF);
+                            animateCollapse();
+                        } else {
+                            return false;
+                        }
+                        return true;
+                    }
+                });
+                popup.show();
+
+                return true;
+            }
+        };
+    }
+
     public void dismissIntruder() {
         // pass
     }
@@ -355,6 +392,9 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
 
+        // for blaming (see SwipeHelper.setLongPressListener)
+        row.setTag(sbn.pkg);
+
         // XXX: temporary: while testing big notifications, auto-expand all of them
         ViewGroup.LayoutParams lp = row.getLayoutParams();
         if (large != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 76f7ea6..b7becf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -314,6 +314,7 @@
 
         mExpandedDialog = new ExpandedDialog(context);
         mPile = (NotificationRowLayout)expanded.findViewById(R.id.latestItems);
+        mPile.setLongPressListener(getNotificationLongClicker());
         mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
 
         mClearButton = expanded.findViewById(R.id.clear_all_button);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 5369317..5c38db5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -104,6 +104,10 @@
         mExpandHelper = new ExpandHelper(mContext, this, minHeight, maxHeight);
     }
 
+    public void setLongPressListener(View.OnLongClickListener listener) {
+        mSwipeHelper.setLongPressListener(listener);
+    }
+
     public void setAnimateBounds(boolean anim) {
         mAnimateBounds = anim;
     }
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 8c1509b..c868f78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -74,6 +74,7 @@
 import com.android.systemui.statusbar.policy.CompatModeButton;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NotificationRowLayout;
 import com.android.systemui.statusbar.policy.Prefs;
 
 import java.io.FileDescriptor;
@@ -153,7 +154,7 @@
     int mNotificationPeekTapDuration;
     int mNotificationFlingVelocity;
 
-    ViewGroup mPile;
+    NotificationRowLayout mPile;
 
     BatteryController mBatteryController;
     BluetoothController mBluetoothController;
@@ -375,8 +376,9 @@
         
         mRecentButton.setOnTouchListener(mRecentsPanel);
 
-        mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
+        mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content);
         mPile.removeAllViews();
+        mPile.setLongPressListener(getNotificationLongClicker());
 
         ScrollView scroller = (ScrollView)mPile.getParent();
         scroller.setFillViewport(true);