diff options
author | Daniel Sandler <dsandler@google.com> | 2010-07-29 12:45:15 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-07-29 12:45:15 -0700 |
commit | 571a629f2929d86cfdde228ea53d7b4275d8d054 (patch) | |
tree | 676bb469bcc0959d60b9f77f4bcca0658d8fbfcb /packages | |
parent | bbda99d2086d79ac70b403cad38a47c35af84adf (diff) | |
parent | 3eebd1ffbf4583270e6926e9c74ff088a2113e07 (diff) | |
download | frameworks_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')
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; + } } |