Merge "Notify user that scans are still active" into jb-mr2-dev
diff --git a/res/layout/notification_log_row.xml b/res/layout/notification_log_row.xml
index 26e72cb..284e9ea 100644
--- a/res/layout/notification_log_row.xml
+++ b/res/layout/notification_log_row.xml
@@ -13,84 +13,105 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/widget_frame"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_toStartOf="@+id/divider"
-    android:background="?android:attr/selectableItemBackground" >
+    android:paddingBottom="6dp"
+    android:paddingTop="6dp"
+    android:orientation="vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
 
-    <!-- Dream icon -->
-
-    <ImageView
-        android:id="@+id/pkgicon"
-        android:layout_width="@*android:dimen/status_bar_icon_size"
+    <RelativeLayout
+        android:layout_width="match_parent"
         android:layout_height="@*android:dimen/status_bar_icon_size"
-        android:layout_centerVertical="true"
-        android:layout_marginBottom="6dp"
-        android:layout_marginStart="0dp"
-        android:layout_marginEnd="6dp"
-        android:layout_marginTop="6dp"
-        android:contentDescription="@null"
-        android:adjustViewBounds="true"
-        android:maxHeight="@*android:dimen/status_bar_icon_size"
-        android:maxWidth="@*android:dimen/status_bar_icon_size"
-        android:scaleType="fitCenter" />
+        android:layout_marginBottom="4dp"
+        >
 
-    <ImageView
-        android:id="@android:id/icon"
-        android:layout_width="@*android:dimen/status_bar_icon_size"
-        android:layout_height="@*android:dimen/status_bar_icon_size"
-        android:layout_centerVertical="true"
-        android:layout_toEndOf="@id/pkgicon"
-        android:layout_marginBottom="6dp"
-        android:layout_marginStart="0dp"
-        android:layout_marginEnd="8dp"
-        android:layout_marginTop="6dp"
-        android:contentDescription="@null"
-        android:adjustViewBounds="true"
-        android:maxHeight="@*android:dimen/status_bar_icon_size"
-        android:maxWidth="@*android:dimen/status_bar_icon_size"
-        android:scaleType="fitCenter" />
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/pkgicon"
+            android:layout_marginStart="0dp"
+            android:layout_marginEnd="8dp"
+            android:contentDescription="@null"
+            android:adjustViewBounds="true"
+            android:maxHeight="@*android:dimen/status_bar_icon_size"
+            android:maxWidth="@*android:dimen/status_bar_icon_size"
+            android:scaleType="fitCenter" />
 
-    <!-- Dream caption -->
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toStartOf="@+id/timestamp"
+            android:layout_toEndOf="@android:id/icon"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textStyle="bold"
+            android:textAlignment="viewStart"
+            android:labelFor="@android:id/button2" />
+
+        <DateTimeView
+            android:id="@+id/timestamp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignBottom="@android:id/widget_frame"
+            android:layout_alignParentEnd="true"
+            android:layout_alignTop="@android:id/widget_frame"
+            android:layout_centerVertical="true"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewEnd"
+            />
+    </RelativeLayout>
 
     <TextView
-        android:id="@android:id/title"
+        android:id="@+id/extra"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_toStartOf="@+id/timestamp"
-        android:layout_toEndOf="@android:id/icon"
+        android:orientation="horizontal"
+        android:layout_marginStart="30dp"
         android:ellipsize="end"
         android:singleLine="true"
         android:textAppearance="?android:attr/textAppearanceSmall"
         android:textAlignment="viewStart"
-        android:labelFor="@android:id/button2" />
-
-    <!-- Dream radio button -->
-
-    <!--<RadioButton
-        android:id="@android:id/button1"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_alignParentEnd="true"
-        android:layout_centerVertical="true"
-        android:duplicateParentState="true"
-        android:clickable="false"
-        android:focusable="false" />-->
-
-    <DateTimeView
-        android:id="@+id/timestamp"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_alignBottom="@android:id/widget_frame"
-        android:layout_alignParentEnd="true"
-        android:layout_alignTop="@android:id/widget_frame"
-        android:layout_centerVertical="true"
-        android:ellipsize="end"
-        android:singleLine="true"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textAlignment="viewEnd"
         />
-</RelativeLayout>
\ No newline at end of file
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="@*android:dimen/status_bar_icon_size"
+        android:orientation="horizontal"
+        android:layout_marginStart="30dp"
+        >
+
+        <ImageView
+            android:id="@+id/pkgicon"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_marginStart="0dp"
+            android:layout_marginEnd="6dp"
+            android:contentDescription="@null"
+            android:adjustViewBounds="true"
+            android:maxHeight="@*android:dimen/status_bar_icon_size"
+            android:maxWidth="@*android:dimen/status_bar_icon_size"
+            android:scaleType="fitCenter" />
+
+        <TextView
+            android:id="@+id/pkgname"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewStart"
+            />
+
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/src/com/android/settings/NotificationStation.java b/src/com/android/settings/NotificationStation.java
index 1242d85..1badfca 100644
--- a/src/com/android/settings/NotificationStation.java
+++ b/src/com/android/settings/NotificationStation.java
@@ -25,11 +25,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -46,6 +48,7 @@
 import com.android.internal.statusbar.StatusBarNotification;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 
 public class NotificationStation extends SettingsPreferenceFragment {
@@ -55,34 +58,57 @@
     private static final boolean SHOW_HISTORICAL_NOTIFICATIONS = true;
 
     private final PackageReceiver mPackageReceiver = new PackageReceiver();
-
+    private PackageManager mPm;
     private INotificationManager mNoMan;
+
+    private Runnable mRefreshListRunnable = new Runnable() {
+        @Override
+        public void run() {
+            refreshList();
+        }
+    };
+
     private INotificationListener.Stub mListener = new INotificationListener.Stub() {
         @Override
         public void onNotificationPosted(StatusBarNotification notification) throws RemoteException {
             Log.v(TAG, "onNotificationPosted: " + notification);
-            getListView().post(new Runnable() { public void run() { refreshList(); }});
+            final Handler h = getListView().getHandler();
+            h.removeCallbacks(mRefreshListRunnable);
+            h.postDelayed(mRefreshListRunnable, 100);
         }
 
         @Override
         public void onNotificationRemoved(StatusBarNotification notification) throws RemoteException {
-            Log.v(TAG, "onNotificationRemoved: " + notification);
-            getListView().post(new Runnable() { public void run() { refreshList(); }});
+            final Handler h = getListView().getHandler();
+            h.removeCallbacks(mRefreshListRunnable);
+            h.postDelayed(mRefreshListRunnable, 100);
         }
     };
 
     private NotificationHistoryAdapter mAdapter;
     private Context mContext;
 
+    private final Comparator<HistoricalNotificationInfo> mNotificationSorter
+            = new Comparator<HistoricalNotificationInfo>() {
+                @Override
+                public int compare(HistoricalNotificationInfo lhs,
+                                   HistoricalNotificationInfo rhs) {
+                    return (int)(rhs.timestamp - lhs.timestamp);
+                }
+            };
+
     @Override
     public void onAttach(Activity activity) {
         logd("onAttach(%s)", activity.getClass().getSimpleName());
         super.onAttach(activity);
         mContext = activity;
+        mPm = mContext.getPackageManager();
         mNoMan = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
         try {
-            mNoMan.registerListener(mListener, ActivityManager.getCurrentUser());
+            mNoMan.registerListener(mListener,
+                    mContext.getPackageName(),
+                    ActivityManager.getCurrentUser());
         } catch (RemoteException e) {
             // well, that didn't work out
         }
@@ -144,6 +170,7 @@
             logd("adding %d infos", infos.size());
             mAdapter.clear();
             mAdapter.addAll(infos);
+            mAdapter.sort(mNotificationSorter);
         }
     }
 
@@ -155,6 +182,7 @@
     private static class HistoricalNotificationInfo {
         public String pkg;
         public Drawable pkgicon;
+        public CharSequence pkgname;
         public Drawable icon;
         public CharSequence title;
         public int priority;
@@ -180,8 +208,19 @@
                     info.user = sbn.getUserId();
                     info.icon = loadIconDrawable(info.pkg, info.user, sbn.notification.icon);
                     info.pkgicon = loadPackageIconDrawable(info.pkg, info.user);
+                    info.pkgname = loadPackageName(info.pkg);
                     if (sbn.notification.extras != null) {
                         info.title = sbn.notification.extras.getString(Notification.EXTRA_TITLE);
+                        if (info.title == null || "".equals(info.title)) {
+                            info.title = sbn.notification.extras.getString(Notification.EXTRA_TEXT);
+                        }
+                    }
+                    if (info.title == null || "".equals(info.title)) {
+                        info.title = sbn.notification.tickerText;
+                    }
+                    // still nothing? come on, give us something!
+                    if (info.title == null || "".equals(info.title)) {
+                        info.title = info.pkgname;
                     }
                     info.timestamp = sbn.postTime;
                     info.priority = sbn.notification.priority;
@@ -211,8 +250,7 @@
                 if (userId == UserHandle.USER_ALL) {
                     userId = UserHandle.USER_OWNER;
                 }
-                r = mContext.getPackageManager()
-                        .getResourcesForApplicationAsUser(pkg, userId);
+                r = mPm.getResourcesForApplicationAsUser(pkg, userId);
             } catch (PackageManager.NameNotFoundException ex) {
                 Log.e(TAG, "Icon package not found: " + pkg);
                 return null;
@@ -226,13 +264,23 @@
     private Drawable loadPackageIconDrawable(String pkg, int userId) {
         Drawable icon = null;
         try {
-            icon = mContext.getPackageManager().getApplicationIcon(pkg);
+            icon = mPm.getApplicationIcon(pkg);
         } catch (PackageManager.NameNotFoundException e) {
         }
 
         return icon;
     }
 
+    private CharSequence loadPackageName(String pkg) {
+        try {
+            ApplicationInfo info = mPm.getApplicationInfo(pkg,
+                    PackageManager.GET_UNINSTALLED_PACKAGES);
+            if (info != null) return mPm.getApplicationLabel(info);
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        return pkg;
+    }
+
     private Drawable loadIconDrawable(String pkg, int userId, int resId) {
         Resources r = getResourcesForUserPackage(pkg, userId);
 
@@ -261,9 +309,9 @@
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            HistoricalNotificationInfo info = getItem(position);
+            final HistoricalNotificationInfo info = getItem(position);
             logd("getView(%s/%s)", info.pkg, info.title);
-            final View row = convertView != null ? convertView : createRow(parent, info.pkg);
+            final View row = convertView != null ? convertView : createRow(parent);
             row.setTag(info);
 
             // bind icon
@@ -279,8 +327,25 @@
             // bind caption
             ((TextView) row.findViewById(android.R.id.title)).setText(info.title);
 
+            // app name
+            ((TextView) row.findViewById(R.id.pkgname)).setText(info.pkgname);
+
+            // extra goodies -- not implemented yet
+//            ((TextView) row.findViewById(R.id.extra)).setText(
+//              ...
+//            );
+            row.findViewById(R.id.extra).setVisibility(View.GONE);
+
             row.setAlpha(info.active ? 1.0f : 0.5f);
 
+            // set up click handler
+            row.setOnClickListener(new OnClickListener(){
+                @Override
+                public void onClick(View v) {
+                    v.setPressed(true);
+                    startApplicationDetailsActivity(info.pkg);
+                }});
+
 //            // bind radio button
 //            RadioButton radioButton = (RadioButton) row.findViewById(android.R.id.button1);
 //            radioButton.setChecked(dreamInfo.isActive);
@@ -310,14 +375,8 @@
             return row;
         }
 
-        private View createRow(ViewGroup parent, final String pkg) {
+        private View createRow(ViewGroup parent) {
             final View row =  mInflater.inflate(R.layout.notification_log_row, parent, false);
-            row.setOnClickListener(new OnClickListener(){
-                @Override
-                public void onClick(View v) {
-                    v.setPressed(true);
-                    startApplicationDetailsActivity(pkg);
-                }});
             return row;
         }
 
@@ -326,7 +385,7 @@
     private void startApplicationDetailsActivity(String packageName) {
         Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                 Uri.fromParts("package", packageName, null));
-        intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
+        intent.setComponent(intent.resolveActivity(mPm));
         startActivity(intent);
     }