summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorDaniel Sandler <dsandler@google.com>2010-07-29 12:45:15 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-07-29 12:45:15 -0700
commit571a629f2929d86cfdde228ea53d7b4275d8d054 (patch)
tree676bb469bcc0959d60b9f77f4bcca0658d8fbfcb /packages
parentbbda99d2086d79ac70b403cad38a47c35af84adf (diff)
parent3eebd1ffbf4583270e6926e9c74ff088a2113e07 (diff)
downloadframeworks_base-571a629f2929d86cfdde228ea53d7b4275d8d054.zip
frameworks_base-571a629f2929d86cfdde228ea53d7b4275d8d054.tar.gz
frameworks_base-571a629f2929d86cfdde228ea53d7b4275d8d054.tar.bz2
Merge "Show notification icons in the system bar."
Diffstat (limited to 'packages')
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java262
3 files changed, 270 insertions, 20 deletions
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 4d82d84..9ae2c25 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -27,16 +27,6 @@
android:descendantFocusability="afterDescendants"
>
-<!--
- <LinearLayout android:id="@+id/statusIcons"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentRight="true"
- android:paddingRight="6dip"
- android:gravity="center_vertical"
- android:orientation="horizontal"/>
--->
-
<com.android.systemui.statusbar.tablet.NotificationIconArea
android:id="@+id/notificationIcons"
android:layout_width="wrap_content"
@@ -61,7 +51,7 @@
class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
android:id="@+id/icons"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="@*android:dimen/status_bar_icon_size"
android:layout_marginLeft="8dip"
/>
<view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 7a82267..973c50d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -35,6 +35,12 @@ public class NotificationData {
public View row; // the outer expanded view
public View content; // takes the click events and sends the PendingIntent
public View expanded; // the inflated RemoteViews
+ public Entry() {}
+ public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
+ this.key = key;
+ this.notification = n;
+ this.icon = ic;
+ }
}
private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
@@ -57,6 +63,12 @@ public class NotificationData {
return -1;
}
+ public int add(Entry entry) {
+ final int index = chooseIndex(entry.notification.notification.when);
+ mEntries.add(index, entry);
+ return index;
+ }
+
public int add(IBinder key, StatusBarNotification notification, View row, View content,
View expanded, StatusBarIconView icon) {
Entry entry = new Entry();
@@ -66,9 +78,7 @@ public class NotificationData {
entry.content = content;
entry.expanded = expanded;
entry.icon = icon;
- final int index = chooseIndex(notification.notification.when);
- mEntries.add(index, entry);
- return index;
+ return add(entry);
}
public Entry remove(IBinder key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 3500729..76969d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -16,22 +16,30 @@
package com.android.systemui.statusbar.tablet;
+import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Handler;
-import android.os.Message;
import android.os.IBinder;
+import android.os.Message;
+import android.util.Slog;
import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.widget.RemoteViews;
+import android.app.ActivityManagerNative;
+import android.app.PendingIntent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.graphics.Rect;
+import android.os.RemoteException;
import android.view.WindowManagerImpl;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import android.util.Slog;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
@@ -41,7 +49,7 @@ import com.android.systemui.statusbar.*;
import com.android.systemui.R;
public class TabletStatusBarService extends StatusBarService {
- public static final boolean DEBUG = false;
+ public static final boolean DEBUG = true;
public static final String TAG = "TabletStatusBar";
View mStatusBarView;
@@ -53,6 +61,10 @@ public class TabletStatusBarService extends StatusBarService {
private View mNotificationPanel;
private View mSystemPanel;
+
+ private NotificationIconArea.IconLayout mIconLayout;
+
+ private NotificationData mHaps = new NotificationData();
protected void addPanelWindows() {
if (mNotificationPanel == null) {
@@ -124,6 +136,9 @@ public class TabletStatusBarService extends StatusBarService {
// the more notifications icon
mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
+ // where the icons go
+ mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
+
return sb;
}
@@ -172,15 +187,86 @@ public class TabletStatusBarService extends StatusBarService {
}
public void addNotification(IBinder key, StatusBarNotification notification) {
- // TODO
+ if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
+ addNotificationViews(key, notification);
+
+ // TODO: kicker; immersive mode
}
public void updateNotification(IBinder key, StatusBarNotification notification) {
- // TODO
+ if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ") // TODO");
+ NotificationData oldList = mHaps;
+
+ int oldIndex = oldList.findEntry(key);
+ if (oldIndex < 0) {
+ Slog.w(TAG, "updateNotification for unknown key: " + key);
+ return;
+ }
+
+ final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
+ final StatusBarNotification oldNotification = oldEntry.notification;
+ final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+ final RemoteViews contentView = notification.notification.contentView;
+
+ if (false) {
+ Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " expanded=" + oldEntry.expanded
+ + " contentView=" + oldContentView);
+ Slog.d(TAG, "new notification: when=" + notification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " contentView=" + contentView);
+ }
+
+ // Can we just reapply the RemoteViews in place? If when didn't change, the order
+ // didn't change.
+ if (notification.notification.when == oldNotification.notification.when
+ && notification.isOngoing() == oldNotification.isOngoing()
+ && oldEntry.expanded != null
+ && contentView != null
+ && oldContentView != null
+ && contentView.getPackage() != null
+ && oldContentView.getPackage() != null
+ && oldContentView.getPackage().equals(contentView.getPackage())
+ && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+ if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
+ oldEntry.notification = notification;
+ try {
+ // Reapply the RemoteViews
+ contentView.reapply(this, oldEntry.content);
+ // update the contentIntent
+ final PendingIntent contentIntent = notification.notification.contentIntent;
+ if (contentIntent != null) {
+ oldEntry.content.setOnClickListener(new NotificationClicker(contentIntent,
+ notification.pkg, notification.tag, notification.id));
+ }
+ // Update the icon.
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+ notification.notification.icon, notification.notification.iconLevel,
+ notification.notification.number);
+ if (!oldEntry.icon.set(ic)) {
+ handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+ return;
+ }
+ }
+ catch (RuntimeException e) {
+ // It failed to add cleanly. Log, and remove the view from the panel.
+ Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+ } else {
+ if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+ // TODO: kicker; immersive mode
}
public void removeNotification(IBinder key) {
- // TODO
+ if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ") // TODO");
+ removeNotificationViews(key);
}
public void disable(int state) {
@@ -216,4 +302,168 @@ public class TabletStatusBarService extends StatusBarService {
mHandler.removeMessages(msg);
mHandler.sendEmptyMessage(msg);
}
+
+ /**
+ * Cancel this notification and tell the status bar service about the failure. Hold no locks.
+ */
+ void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+ removeNotification(key);
+ try {
+ mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
+ }
+
+ private class NotificationClicker implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
+ mIntent = intent;
+ mPkg = pkg;
+ mTag = tag;
+ mId = id;
+ }
+
+ public void onClick(View v) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+
+ if (mIntent != null) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ Intent overlay = new Intent();
+ overlay.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ try {
+ mIntent.send(TabletStatusBarService.this, 0, overlay);
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here. Just log the exception message.
+ Slog.w(TAG, "Sending contentIntent failed: " + e);
+ }
+ }
+
+ try {
+ mBarService.onNotificationClick(mPkg, mTag, mId);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+
+ // close the shade if it was open
+ animateCollapse();
+
+ // If this click was on the intruder alert, hide that instead
+// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ }
+ }
+
+ StatusBarNotification removeNotificationViews(IBinder key) {
+ NotificationData.Entry entry = mHaps.remove(key);
+ if (entry == null) {
+ Slog.w(TAG, "removeNotification for unknown key: " + key);
+ return null;
+ }
+ // Remove the expanded view.
+ ViewGroup rowParent = (ViewGroup)entry.row.getParent();
+ if (rowParent != null) rowParent.removeView(entry.row);
+ // Remove the icon.
+ ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
+ if (iconParent != null) iconParent.removeView(entry.icon);
+
+ return entry.notification;
+ }
+
+ StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
+ NotificationData list = mHaps;
+ ViewGroup parent = null;
+ final boolean isOngoing = notification.isOngoing();
+ if (isOngoing) {
+// parent = mOngoingItems;
+ } else {
+// parent = mIconLayout;
+ }
+ // Construct the icon.
+ final StatusBarIconView iconView = new StatusBarIconView(this,
+ notification.pkg + "/0x" + Integer.toHexString(notification.id));
+ iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+ notification.notification.icon,
+ notification.notification.iconLevel,
+ notification.notification.number);
+ if (!iconView.set(ic)) {
+ handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic);
+ return null;
+ }
+ // Construct the expanded view.
+ NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
+ if (!inflateViews(entry, parent)) {
+ handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ + notification);
+ return null;
+ }
+ // Add the expanded view.
+ final int viewIndex = list.add(entry);
+ if (parent != null) parent.addView(entry.row, viewIndex);
+ // Add the icon.
+ final int iconIndex = 0; // XXX: sort into ongoing and regular buckets
+ mIconLayout.addView(iconView, iconIndex,
+ new LinearLayout.LayoutParams(mIconSize, mIconSize));
+ return iconView;
+ }
+
+ private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
+ StatusBarNotification sbn = entry.notification;
+ RemoteViews remoteViews = sbn.notification.contentView;
+ if (remoteViews == null) {
+ return false;
+ }
+
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+ // XXX: update to allow controls within notification views
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+// content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = sbn.notification.contentIntent;
+ if (contentIntent != null) {
+ content.setOnClickListener(new NotificationClicker(contentIntent,
+ sbn.pkg, sbn.tag, sbn.id));
+ }
+
+ View expanded = null;
+ Exception exception = null;
+ try {
+ expanded = remoteViews.apply(this, content);
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (expanded == null) {
+ String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+ return false;
+ } else {
+ content.addView(expanded);
+ row.setDrawingCacheEnabled(true);
+ }
+
+ entry.row = row;
+ entry.content = content;
+ entry.expanded = expanded;
+
+ return true;
+ }
}