Merge "Add notification listener plugin hook" into oc-dr1-dev
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java
new file mode 100644
index 0000000..fac9e98
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationListenerController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.plugins.NotificationListenerController.NotificationProvider;
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+@ProvidesInterface(action = NotificationListenerController.ACTION,
+        version = NotificationListenerController.VERSION)
+@DependsOn(target = NotificationProvider.class)
+public interface NotificationListenerController extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_ASSISTANT";
+    int VERSION = 1;
+
+    void onListenerConnected(NotificationProvider provider);
+
+    default boolean onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+        return false;
+    }
+    default boolean onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+        return false;
+    }
+
+    default StatusBarNotification[] getActiveNotifications(
+            StatusBarNotification[] activeNotifications) {
+        return activeNotifications;
+    }
+
+    default RankingMap getCurrentRanking(RankingMap currentRanking) {
+        return currentRanking;
+    }
+
+    @ProvidesInterface(version = NotificationProvider.VERSION)
+    interface NotificationProvider {
+        int VERSION = 1;
+
+        // Methods to get info about current notifications
+        StatusBarNotification[] getActiveNotifications();
+        RankingMap getRankingMap();
+
+        // Methods to notify sysui of changes to notification list.
+        void addNotification(StatusBarNotification sbn);
+        void removeNotification(StatusBarNotification sbn);
+        void updateRanking();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
new file mode 100644
index 0000000..9ff907b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.RemoteException;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.NotificationListenerController;
+import com.android.systemui.plugins.NotificationListenerController.NotificationProvider;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+
+import java.util.ArrayList;
+
+/**
+ * A version of NotificationListenerService that passes all info to
+ * any plugins connected. Also allows those plugins the chance to cancel
+ * any incoming callbacks or to trigger new ones.
+ */
+public class NotificationListenerWithPlugins extends NotificationListenerService implements
+        PluginListener<NotificationListenerController> {
+
+    private ArrayList<NotificationListenerController> mPlugins = new ArrayList<>();
+    private boolean mConnected;
+
+    @Override
+    public void registerAsSystemService(Context context, ComponentName componentName,
+            int currentUser) throws RemoteException {
+        super.registerAsSystemService(context, componentName, currentUser);
+        Dependency.get(PluginManager.class).addPluginListener(this,
+                NotificationListenerController.class);
+    }
+
+    @Override
+    public void unregisterAsSystemService() throws RemoteException {
+        super.unregisterAsSystemService();
+        Dependency.get(PluginManager.class).removePluginListener(this);
+    }
+
+    @Override
+    public StatusBarNotification[] getActiveNotifications() {
+        StatusBarNotification[] activeNotifications = super.getActiveNotifications();
+        for (NotificationListenerController plugin : mPlugins) {
+            activeNotifications = plugin.getActiveNotifications(activeNotifications);
+        }
+        return activeNotifications;
+    }
+
+    @Override
+    public RankingMap getCurrentRanking() {
+        RankingMap currentRanking = super.getCurrentRanking();
+        for (NotificationListenerController plugin : mPlugins) {
+            currentRanking = plugin.getCurrentRanking(currentRanking);
+        }
+        return currentRanking;
+    }
+
+    public void onPluginConnected() {
+        mConnected = true;
+        mPlugins.forEach(p -> p.onListenerConnected(getProvider()));
+    }
+
+    /**
+     * Called when listener receives a onNotificationPosted.
+     * Returns true to indicate this callback should be skipped.
+     */
+    public boolean onPluginNotificationPosted(StatusBarNotification sbn,
+            final RankingMap rankingMap) {
+        for (NotificationListenerController plugin : mPlugins) {
+            if (plugin.onNotificationPosted(sbn, rankingMap)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called when listener receives a onNotificationRemoved.
+     * Returns true to indicate this callback should be skipped.
+     */
+    public boolean onPluginNotificationRemoved(StatusBarNotification sbn,
+            final RankingMap rankingMap) {
+        for (NotificationListenerController plugin : mPlugins) {
+            if (plugin.onNotificationRemoved(sbn, rankingMap)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public RankingMap onPluginRankingUpdate(RankingMap rankingMap) {
+        return getCurrentRanking();
+    }
+
+    @Override
+    public void onPluginConnected(NotificationListenerController plugin, Context pluginContext) {
+        mPlugins.add(plugin);
+        if (mConnected) {
+            plugin.onListenerConnected(getProvider());
+        }
+    }
+
+    @Override
+    public void onPluginDisconnected(NotificationListenerController plugin) {
+        mPlugins.remove(plugin);
+    }
+
+    private NotificationProvider getProvider() {
+        return new NotificationProvider() {
+            @Override
+            public StatusBarNotification[] getActiveNotifications() {
+                return NotificationListenerWithPlugins.super.getActiveNotifications();
+            }
+
+            @Override
+            public RankingMap getRankingMap() {
+                return NotificationListenerWithPlugins.super.getCurrentRanking();
+            }
+
+            @Override
+            public void addNotification(StatusBarNotification sbn) {
+                onNotificationPosted(sbn, getRankingMap());
+            }
+
+            @Override
+            public void removeNotification(StatusBarNotification sbn) {
+                onNotificationRemoved(sbn, getRankingMap());
+            }
+
+            @Override
+            public void updateRanking() {
+                onNotificationRankingUpdate(getRankingMap());
+            }
+        };
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 10cb567..e8b6e07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5769,11 +5769,12 @@
         }
     };
 
-    private final NotificationListenerService mNotificationListener =
-            new NotificationListenerService() {
+    private final NotificationListenerWithPlugins mNotificationListener =
+            new NotificationListenerWithPlugins() {
         @Override
         public void onListenerConnected() {
             if (DEBUG) Log.d(TAG, "onListenerConnected");
+            onPluginConnected();
             final StatusBarNotification[] notifications = getActiveNotifications();
             if (notifications == null) {
                 Log.w(TAG, "onListenerConnected unable to get active notifications.");
@@ -5798,7 +5799,7 @@
         public void onNotificationPosted(final StatusBarNotification sbn,
                 final RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
-            if (sbn != null) {
+            if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
@@ -5842,14 +5843,9 @@
         public void onNotificationRemoved(StatusBarNotification sbn,
                 final RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
-            if (sbn != null) {
+            if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
                 final String key = sbn.getKey();
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        removeNotification(key, rankingMap);
-                    }
-                });
+                mHandler.post(() -> removeNotification(key, rankingMap));
             }
         }
 
@@ -5857,13 +5853,10 @@
         public void onNotificationRankingUpdate(final RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onRankingUpdate");
             if (rankingMap != null) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    updateNotificationRanking(rankingMap);
-                }
-            });
-        }                            }
+                RankingMap r = onPluginRankingUpdate(rankingMap);
+                mHandler.post(() -> updateNotificationRanking(r));
+            }
+        }
 
     };