Merge "Plugin interface for PeopleHub"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
new file mode 100644
index 0000000..802a8da
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.annotation.Nullable;
+import android.app.PendingIntent;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/** Custom logic that can extract a PeopleHub "person" from a notification. */
+@ProvidesInterface(
+        action = NotificationPersonExtractorPlugin.ACTION,
+        version = NotificationPersonExtractorPlugin.VERSION)
+@DependsOn(target = NotificationPersonExtractorPlugin.PersonData.class)
+public interface NotificationPersonExtractorPlugin extends Plugin {
+
+    String ACTION = "com.android.systemui.action.PEOPLE_HUB_PERSON_EXTRACTOR";
+    int VERSION = 0;
+
+    /**
+     * Attempts to extract a person from a notification. Returns {@code null} if one is not found.
+     */
+    @Nullable PersonData extractPerson(StatusBarNotification sbn);
+
+    /**
+     * Attempts to extract a person id from a notification. Returns {@code null} if one is not
+     * found.
+     *
+     * This method can be overridden in order to provide a faster implementation.
+     */
+    @Nullable
+    default String extractPersonKey(StatusBarNotification sbn) {
+        return extractPerson(sbn).key;
+    }
+
+    /** A person to be surfaced in PeopleHub. */
+    @ProvidesInterface(version = PersonData.VERSION)
+    final class PersonData {
+
+        public static final int VERSION = 0;
+
+        public final String key;
+        public final CharSequence name;
+        public final Drawable avatar;
+        public final PendingIntent clickIntent;
+
+        public PersonData(String key, CharSequence name, Drawable avatar,
+                PendingIntent clickIntent) {
+            this.key = key;
+            this.name = name;
+            this.avatar = avatar;
+            this.clickIntent = clickIntent;
+        }
+    }
+}
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
index b314db8..f0ac08b 100644
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -33,10 +33,9 @@
     <LinearLayout
         android:id="@+id/people_list"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:paddingTop="8dp"
-        android:paddingBottom="8dp"
+        android:layout_height="match_parent"
+        android:paddingTop="12dp"
+        android:paddingBottom="12dp"
         android:gravity="center"
         android:orientation="horizontal">
 
@@ -49,7 +48,7 @@
         <LinearLayout
             android:layout_width="70dp"
             android:layout_height="match_parent"
-            android:gravity="center"
+            android:gravity="center_horizontal"
             android:orientation="vertical"
             android:visibility="invisible">
 
@@ -87,7 +86,7 @@
         <LinearLayout
             android:layout_width="70dp"
             android:layout_height="match_parent"
-            android:gravity="center"
+            android:gravity="center_horizontal"
             android:orientation="vertical"
             android:visibility="invisible">
 
@@ -125,7 +124,7 @@
         <LinearLayout
             android:layout_width="70dp"
             android:layout_height="match_parent"
-            android:gravity="center"
+            android:gravity="center_horizontal"
             android:orientation="vertical"
             android:visibility="invisible">
 
@@ -163,7 +162,7 @@
         <LinearLayout
             android:layout_width="70dp"
             android:layout_height="match_parent"
-            android:gravity="center"
+            android:gravity="center_horizontal"
             android:orientation="vertical"
             android:visibility="invisible">
 
@@ -201,7 +200,7 @@
         <LinearLayout
             android:layout_width="70dp"
             android:layout_height="match_parent"
-            android:gravity="center"
+            android:gravity="center_horizontal"
             android:orientation="vertical"
             android:visibility="invisible">
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 6e464f4..b4dc538 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -138,6 +138,14 @@
         mNotificationEntryListeners.add(listener);
     }
 
+    /**
+     * Removes a {@link NotificationEntryListener} previously registered via
+     * {@link #addNotificationEntryListener(NotificationEntryListener)}.
+     */
+    public void removeNotificationEntryListener(NotificationEntryListener listener) {
+        mNotificationEntryListeners.remove(listener);
+    }
+
     /** Sets the {@link NotificationRemoveInterceptor}. */
     public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
         mRemoveInterceptor = interceptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 7e398bb..7d0ce5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -37,6 +37,7 @@
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.logging.NotifEvent;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -76,12 +77,16 @@
     private final Ranking mTmpRanking = new Ranking();
     private final boolean mUsePeopleFiltering;
     private final NotifLog mNotifLog;
+    private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
 
     @Inject
-    public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager,
-            NotifLog notifLog) {
+    public NotificationData(
+            NotificationSectionsFeatureManager sectionsFeatureManager,
+            NotifLog notifLog,
+            PeopleNotificationIdentifier peopleNotificationIdentifier) {
         mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
         mNotifLog = notifLog;
+        mPeopleNotificationIdentifier = peopleNotificationIdentifier;
     }
 
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
@@ -460,8 +465,7 @@
     }
 
     private boolean isPeopleNotification(NotificationEntry e) {
-        return e.getSbn().getNotification().getNotificationStyle()
-                == Notification.MessagingStyle.class;
+        return mPeopleNotificationIdentifier.isPeopleNotification(e.getSbn());
     }
 
     public void dump(PrintWriter pw, String indent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
index 8c067b7..4570989 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -24,14 +24,24 @@
 
     @Binds
     abstract fun peopleHubSectionFooterViewController(
-        viewAdapter: PeopleHubSectionFooterViewAdapterImpl
+        impl: PeopleHubSectionFooterViewAdapterImpl
     ): PeopleHubSectionFooterViewAdapter
 
     @Binds
-    abstract fun peopleHubDataSource(s: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
+    abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
 
     @Binds
     abstract fun peopleHubViewModelFactoryDataSource(
-        dataSource: PeopleHubViewModelFactoryDataSourceImpl
+        impl: PeopleHubViewModelFactoryDataSourceImpl
     ): DataSource<PeopleHubViewModelFactory>
+
+    @Binds
+    abstract fun peopleNotificationIdentifier(
+        impl: PeopleNotificationIdentifierImpl
+    ): PeopleNotificationIdentifier
+
+    @Binds
+    abstract fun notificationPersonExtractor(
+        pluginImpl: NotificationPersonExtractorPluginBoundary
+    ): NotificationPersonExtractor
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index c8a3c1a..e6239df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -17,11 +17,14 @@
 package com.android.systemui.statusbar.notification.people
 
 import android.app.Notification
+import android.content.Context
 import android.graphics.Canvas
 import android.graphics.ColorFilter
 import android.graphics.PixelFormat
 import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.service.notification.StatusBarNotification
 import android.util.TypedValue
 import android.view.View
 import android.view.ViewGroup
@@ -30,59 +33,112 @@
 import com.android.internal.widget.MessagingGroup
 import com.android.launcher3.icons.BaseIconFactory
 import com.android.systemui.R
+import com.android.systemui.plugins.NotificationPersonExtractorPlugin
 import com.android.systemui.statusbar.notification.NotificationEntryListener
 import com.android.systemui.statusbar.notification.NotificationEntryManager
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.policy.ExtensionController
 import java.util.ArrayDeque
 import javax.inject.Inject
 import javax.inject.Singleton
 
 private const val MAX_STORED_INACTIVE_PEOPLE = 10
 
-@Singleton
-class PeopleHubDataSourceImpl @Inject constructor(
-    notificationEntryManager: NotificationEntryManager,
-    private val peopleHubManager: PeopleHubManager
-) : DataSource<PeopleHubModel> {
+interface NotificationPersonExtractor {
+    fun extractPerson(sbn: StatusBarNotification): PersonModel?
+    fun extractPersonKey(sbn: StatusBarNotification): String?
+}
 
-    private var dataListener: DataListener<PeopleHubModel>? = null
+@Singleton
+class NotificationPersonExtractorPluginBoundary @Inject constructor(
+    extensionController: ExtensionController,
+    private val context: Context
+) : NotificationPersonExtractor {
+
+    private var plugin: NotificationPersonExtractorPlugin? = null
 
     init {
-        notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
-            override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
-                    addVisibleEntry(entry)
+        plugin = extensionController
+                .newExtension(NotificationPersonExtractorPlugin::class.java)
+                .withPlugin(NotificationPersonExtractorPlugin::class.java)
+                .withCallback { extractor ->
+                    plugin = extractor
+                }
+                .build()
+                .get()
+    }
 
-            override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry)
+    override fun extractPerson(sbn: StatusBarNotification) =
+            plugin?.extractPerson(sbn)?.let { data ->
+                val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user)
+                PersonModel(data.key, data.name, badged, data.clickIntent)
+            }
 
-            override fun onPostEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
+    override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
+}
 
-            override fun onEntryRemoved(
-                entry: NotificationEntry,
-                visibility: NotificationVisibility?,
-                removedByUser: Boolean
-            ) = removeVisibleEntry(entry)
-        })
+@Singleton
+class PeopleHubDataSourceImpl @Inject constructor(
+    private val notificationEntryManager: NotificationEntryManager,
+    private val peopleHubManager: PeopleHubManager,
+    private val extractor: NotificationPersonExtractor
+) : DataSource<PeopleHubModel> {
+
+    private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
+
+    private val notificationEntryListener = object : NotificationEntryListener {
+        override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
+                addVisibleEntry(entry)
+
+        override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry)
+
+        override fun onPostEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
+
+        override fun onEntryRemoved(
+            entry: NotificationEntry,
+            visibility: NotificationVisibility?,
+            removedByUser: Boolean
+        ) = removeVisibleEntry(entry)
     }
 
     private fun removeVisibleEntry(entry: NotificationEntry) {
-        if (entry.extractPersonKey()?.let(peopleHubManager::removeActivePerson) == true) {
+        val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey()
+        if (key?.let(peopleHubManager::removeActivePerson) == true) {
             updateUi()
         }
     }
 
     private fun addVisibleEntry(entry: NotificationEntry) {
-        if (entry.extractPerson()?.let(peopleHubManager::addActivePerson) == true) {
+        val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson()
+        if (personModel?.let(peopleHubManager::addActivePerson) == true) {
             updateUi()
         }
     }
 
-    override fun setListener(listener: DataListener<PeopleHubModel>) {
-        this.dataListener = listener
-        updateUi()
+    override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
+        val registerWithNotificationEntryManager = dataListeners.isEmpty()
+        dataListeners.add(listener)
+        if (registerWithNotificationEntryManager) {
+            notificationEntryManager.addNotificationEntryListener(notificationEntryListener)
+        } else {
+            listener.onDataChanged(peopleHubManager.getPeopleHubModel())
+        }
+        return object : Subscription {
+            override fun unsubscribe() {
+                dataListeners.remove(listener)
+                if (dataListeners.isEmpty()) {
+                    notificationEntryManager
+                            .removeNotificationEntryListener(notificationEntryListener)
+                }
+            }
+        }
     }
 
     private fun updateUi() {
-        dataListener?.onDataChanged(peopleHubManager.getPeopleHubModel())
+        val model = peopleHubManager.getPeopleHubModel()
+        for (listener in dataListeners) {
+            listener.onDataChanged(model)
+        }
     }
 }
 
@@ -124,19 +180,25 @@
     if (!isMessagingNotification()) {
         return null
     }
-
     val clickIntent = sbn.notification.contentIntent
     val extras = sbn.notification.extras
     val name = extras.getString(Notification.EXTRA_CONVERSATION_TITLE)
             ?: extras.getString(Notification.EXTRA_TITLE)
             ?: return null
     val drawable = extractAvatarFromRow(this) ?: return null
+    val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user)
+    return PersonModel(key, name, badgedAvatar, clickIntent)
+}
 
-    val context = row.context
+private fun addBadgeToDrawable(
+    drawable: Drawable,
+    context: Context,
+    packageName: String,
+    user: UserHandle
+): Drawable {
     val pm = context.packageManager
-    val appInfo = pm.getApplicationInfoAsUser(sbn.packageName, 0, sbn.user)
-
-    val badgedAvatar = object : Drawable() {
+    val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user)
+    return object : Drawable() {
         override fun draw(canvas: Canvas) {
             val iconBounds = getBounds()
             val factory = object : BaseIconFactory(
@@ -146,7 +208,7 @@
                     true) {}
             val badge = factory.createBadgedIconBitmap(
                     appInfo.loadIcon(pm),
-                    sbn.user,
+                    user,
                     true,
                     appInfo.isInstantApp,
                     null)
@@ -156,7 +218,7 @@
                         colorFilter = drawable.colorFilter
                         val badgeWidth = TypedValue.applyDimension(
                                 TypedValue.COMPLEX_UNIT_DIP,
-                                16f,
+                                15f,
                                 context.resources.displayMetrics
                         ).toInt()
                         setBounds(
@@ -181,8 +243,6 @@
         @PixelFormat.Opacity
         override fun getOpacity(): Int = PixelFormat.OPAQUE
     }
-
-    return PersonModel(key, name, badgedAvatar, clickIntent)
 }
 
 private fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
index 8d1253b..5c35408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -60,8 +60,9 @@
     private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
 ) : PeopleHubSectionFooterViewAdapter {
 
-    override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) =
-            dataSource.setListener(PeopleHubDataListenerImpl(viewBoundary))
+    override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) {
+        dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
+    }
 }
 
 private class PeopleHubDataListenerImpl(
@@ -92,8 +93,8 @@
     private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
 ) : DataSource<PeopleHubViewModelFactory> {
 
-    override fun setListener(listener: DataListener<PeopleHubViewModelFactory>) =
-            dataSource.setListener(PeopleHubModelListenerImpl(activityStarter, listener))
+    override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>) =
+            dataSource.registerListener(PeopleHubModelListenerImpl(activityStarter, listener))
 }
 
 private class PeopleHubModelListenerImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
new file mode 100644
index 0000000..bfd4070
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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.notification.people
+
+import android.app.Notification
+import android.service.notification.StatusBarNotification
+import javax.inject.Inject
+import javax.inject.Singleton
+
+interface PeopleNotificationIdentifier {
+    fun isPeopleNotification(sbn: StatusBarNotification): Boolean
+}
+
+@Singleton
+class PeopleNotificationIdentifierImpl @Inject constructor(
+    private val personExtractor: NotificationPersonExtractor
+) : PeopleNotificationIdentifier {
+
+    override fun isPeopleNotification(sbn: StatusBarNotification) =
+            sbn.notification.notificationStyle == Notification.MessagingStyle::class.java ||
+                    personExtractor.extractPersonKey(sbn) != null
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
index 33e3bb8..3ca3792 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
@@ -28,10 +28,17 @@
 
 /** Boundary between a View and data pipeline, as seen by the View. */
 interface DataSource<out T> {
-    fun setListener(listener: DataListener<T>)
+    fun registerListener(listener: DataListener<T>): Subscription
+}
+
+/** Represents a registration with a [DataSource]. */
+interface Subscription {
+    /** Removes the previously registered [DataListener] from the [DataSource] */
+    fun unsubscribe()
 }
 
 /** Transform all data coming out of this [DataSource] using the given [mapper]. */
 fun <S, T> DataSource<S>.map(mapper: (S) -> T): DataSource<T> = object : DataSource<T> {
-    override fun setListener(listener: DataListener<T>) = setListener(listener.contraMap(mapper))
+    override fun registerListener(listener: DataListener<T>) =
+            registerListener(listener.contraMap(mapper))
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index bd87d77..54d4066 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -131,6 +131,7 @@
         }
         mInitialized = true;
         reinflateViews(layoutInflater);
+        mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
         mConfigurationController.addCallback(mConfigurationListener);
     }
 
@@ -172,10 +173,6 @@
         if (oldPeopleHubPos != -1) {
             mParent.addView(mPeopleHubView, oldPeopleHubPos);
         }
-
-        if (!mInitialized) {
-            mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
-        }
     }
 
     /** Listener for when the "clear all" buttton is clciked on the gentle notification header. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
index cade5dc..3ce6239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -57,7 +57,7 @@
         ExtensionBuilder<T> withCallback(Consumer<T> callback);
         ExtensionBuilder<T> withUiMode(int mode, Supplier<T> def);
         ExtensionBuilder<T> withFeature(String feature, Supplier<T> def);
-        Extension build();
+        Extension<T> build();
     }
 
     public interface PluginConverter<T, P> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index fd030d1..eeef726 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -135,7 +135,7 @@
         }
 
         @Override
-        public ExtensionController.Extension build() {
+        public ExtensionController.Extension<T> build() {
             // Sort items in ascending order
             Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder));
             mExtension.notifyChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index ebdf851..bde7ef9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -82,6 +82,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -148,8 +149,12 @@
         private final CountDownLatch mCountDownLatch;
 
         TestableNotificationEntryManager() {
-            super(new NotificationData(mock(NotificationSectionsFeatureManager.class),
-                    mock(NotifLog.class)), mock(NotifLog.class));
+            super(
+                    new NotificationData(
+                            mock(NotificationSectionsFeatureManager.class),
+                            mock(NotifLog.class),
+                            mock(PeopleNotificationIdentifier.class)),
+                    mock(NotifLog.class));
             mCountDownLatch = new CountDownLatch(1);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 9202c51..2435bb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -78,8 +79,10 @@
 
     // TODO: Remove this once EntryManager no longer needs to be mocked
     private NotificationData mNotificationData =
-            new NotificationData(new NotificationSectionsFeatureManager(
-                    new DeviceConfigProxyFake(), mContext), mock(NotifLog.class));
+            new NotificationData(
+                    new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
+                    mock(NotifLog.class),
+                    mock(PeopleNotificationIdentifier.class));
 
     private int mNextNotifId = 0;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 640984b..dba0174 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -81,6 +81,7 @@
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -639,7 +640,10 @@
 
     public static class TestableNotificationData extends NotificationData {
         public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
-            super(sectionsFeatureManager, mock(NotifLog.class));
+            super(
+                    sectionsFeatureManager,
+                    mock(NotifLog.class),
+                    mock(PeopleNotificationIdentifier.class));
         }
 
         public static final String OVERRIDE_RANK = "r";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index cff6635..4853f20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -236,8 +237,11 @@
                     mock(PluginManager.class),
                     mock(ShadeController.class),
                     mock(NotificationLockscreenUserManager.class),
-                    new NotificationEntryManager(new NotificationData(mock(
-                            NotificationSectionsFeatureManager.class), mock(NotifLog.class)),
+                    new NotificationEntryManager(
+                            new NotificationData(
+                                    mock(NotificationSectionsFeatureManager.class),
+                                    mock(NotifLog.class),
+                                    mock(PeopleNotificationIdentifier.class)),
                             mock(NotifLog.class)),
                     mock(KeyguardStateController.class),
                     statusBarStateController,