Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3eebd1ff authored by Daniel Sandler's avatar Daniel Sandler
Browse files

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
parent 7d72e5ad
Loading
Loading
Loading
Loading
+1 −11
Original line number Original line Diff line number Diff line
@@ -27,16 +27,6 @@
    android:descendantFocusability="afterDescendants"
    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
        <com.android.systemui.statusbar.tablet.NotificationIconArea
            android:id="@+id/notificationIcons"
            android:id="@+id/notificationIcons"
            android:layout_width="wrap_content"
            android:layout_width="wrap_content"
@@ -61,7 +51,7 @@
                class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
                class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
                android:id="@+id/icons"
                android:id="@+id/icons"
                android:layout_width="wrap_content"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_height="@*android:dimen/status_bar_icon_size"
                android:layout_marginLeft="8dip"
                android:layout_marginLeft="8dip"
                />
                />
            <view
            <view
+13 −3
Original line number Original line Diff line number Diff line
@@ -35,6 +35,12 @@ public class NotificationData {
        public View row; // the outer expanded view
        public View row; // the outer expanded view
        public View content; // takes the click events and sends the PendingIntent
        public View content; // takes the click events and sends the PendingIntent
        public View expanded; // the inflated RemoteViews
        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>();
    private final ArrayList<Entry> mEntries = new ArrayList<Entry>();


@@ -57,6 +63,12 @@ public class NotificationData {
        return -1;
        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,
    public int add(IBinder key, StatusBarNotification notification, View row, View content,
            View expanded, StatusBarIconView icon) {
            View expanded, StatusBarIconView icon) {
        Entry entry = new Entry();
        Entry entry = new Entry();
@@ -66,9 +78,7 @@ public class NotificationData {
        entry.content = content;
        entry.content = content;
        entry.expanded = expanded;
        entry.expanded = expanded;
        entry.icon = icon;
        entry.icon = icon;
        final int index = chooseIndex(notification.notification.when);
        return add(entry);
        mEntries.add(index, entry);
        return index;
    }
    }


    public Entry remove(IBinder key) {
    public Entry remove(IBinder key) {
+256 −6
Original line number Original line Diff line number Diff line
@@ -16,22 +16,30 @@


package com.android.systemui.statusbar.tablet;
package com.android.systemui.statusbar.tablet;


import android.app.Notification;
import android.app.Service;
import android.app.Service;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Handler;
import android.os.Message;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Message;
import android.util.Slog;
import android.view.Gravity;
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.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager;
import android.graphics.Rect;
import android.os.RemoteException;
import android.view.WindowManagerImpl;
import android.view.WindowManagerImpl;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView;
import android.util.Slog;


import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.statusbar.StatusBarIconList;
@@ -41,7 +49,7 @@ import com.android.systemui.statusbar.*;
import com.android.systemui.R;
import com.android.systemui.R;


public class TabletStatusBarService extends StatusBarService {
public class TabletStatusBarService extends StatusBarService {
    public static final boolean DEBUG = false;
    public static final boolean DEBUG = true;
    public static final String TAG = "TabletStatusBar";
    public static final String TAG = "TabletStatusBar";


    View mStatusBarView;
    View mStatusBarView;
@@ -54,6 +62,10 @@ public class TabletStatusBarService extends StatusBarService {
    private View mNotificationPanel;
    private View mNotificationPanel;
    private View mSystemPanel;
    private View mSystemPanel;


    private NotificationIconArea.IconLayout mIconLayout;

    private NotificationData mHaps = new NotificationData();
    
    protected void addPanelWindows() {
    protected void addPanelWindows() {
        if (mNotificationPanel == null) {
        if (mNotificationPanel == null) {
            mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
            mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
@@ -124,6 +136,9 @@ public class TabletStatusBarService extends StatusBarService {
        // the more notifications icon
        // the more notifications icon
        mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
        mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);


        // where the icons go
        mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);

        return sb;
        return sb;
    }
    }


@@ -172,15 +187,86 @@ public class TabletStatusBarService extends StatusBarService {
    }
    }


    public void addNotification(IBinder key, StatusBarNotification notification) {
    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) {
    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) {
    public void removeNotification(IBinder key) {
        // TODO
        if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ") // TODO");
        removeNotificationViews(key);
    }
    }


    public void disable(int state) {
    public void disable(int state) {
@@ -216,4 +302,168 @@ public class TabletStatusBarService extends StatusBarService {
        mHandler.removeMessages(msg);
        mHandler.removeMessages(msg);
        mHandler.sendEmptyMessage(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;
    }
}
}