Show notification icons in the system bar.
Caveats: - Ongoing and normal are lumped together - Currently no limit on number of icons (should be 4) - Still can't see the notifications in the panel Change-Id: I83ac474db6ff290207d37747b672a1a56788a238
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user