summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/layout/people_strip.xml240
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt211
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt132
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java4
15 files changed, 901 insertions, 14 deletions
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
new file mode 100644
index 000000000000..b314db8d72fd
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<com.android.systemui.statusbar.notification.stack.PeopleHubView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="105dp">
+
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundNormal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundDimmed"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <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:gravity="center"
+ android:orientation="horizontal">
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ </LinearLayout>
+
+ <com.android.systemui.statusbar.notification.FakeShadowView
+ android:id="@+id/fake_shadow"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</com.android.systemui.statusbar.notification.stack.PeopleHubView> \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
index b0316e22de06..4520a1a6a037 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
@@ -23,6 +23,7 @@ import android.content.pm.PackageManager;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -35,7 +36,7 @@ import dagger.Provides;
* A dagger module for injecting components of System UI that are not overridden by the System UI
* implementation.
*/
-@Module(includes = {AssistModule.class, ComponentBinder.class})
+@Module(includes = {AssistModule.class, ComponentBinder.class, PeopleHubModule.class})
public abstract class SystemUIModule {
@Singleton
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 f56586802a68..6e464f480218 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -126,7 +126,9 @@ public class NotificationEntryManager implements
}
@Inject
- public NotificationEntryManager(NotificationData notificationData, NotifLog notifLog) {
+ public NotificationEntryManager(
+ NotificationData notificationData,
+ NotifLog notifLog) {
mNotificationData = notificationData;
mNotifLog = notifLog;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index 480cb78efbba..009551168010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -66,7 +66,7 @@ class NotificationSectionsFeatureManager @Inject constructor(
private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
if (sUsePeopleFiltering == null) {
sUsePeopleFiltering = proxy.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, false)
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, true)
}
return sUsePeopleFiltering!!
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 623ccca249a5..7e398bb70433 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
@@ -98,6 +98,9 @@ public class NotificationData {
int aRank = getRank(a.getKey());
int bRank = getRank(b.getKey());
+ boolean aPeople = isPeopleNotification(a);
+ boolean bPeople = isPeopleNotification(b);
+
boolean aMedia = isImportantMedia(a);
boolean bMedia = isImportantMedia(b);
@@ -107,8 +110,8 @@ public class NotificationData {
boolean aHeadsUp = a.isRowHeadsUp();
boolean bHeadsUp = b.isRowHeadsUp();
- if (mUsePeopleFiltering && a.hasAssociatedPeople() != b.hasAssociatedPeople()) {
- return a.hasAssociatedPeople() ? -1 : 1;
+ if (mUsePeopleFiltering && aPeople != bPeople) {
+ return aPeople ? -1 : 1;
} else if (aHeadsUp != bHeadsUp) {
return aHeadsUp ? -1 : 1;
} else if (aHeadsUp) {
@@ -447,7 +450,7 @@ public class NotificationData {
boolean isHeadsUp,
boolean isMedia,
boolean isSystemMax) {
- if (mUsePeopleFiltering && e.hasAssociatedPeople()) {
+ if (mUsePeopleFiltering && isPeopleNotification(e)) {
e.setBucket(BUCKET_PEOPLE);
} else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) {
e.setBucket(BUCKET_ALERTING);
@@ -456,6 +459,11 @@ public class NotificationData {
}
}
+ private boolean isPeopleNotification(NotificationEntry e) {
+ return e.getSbn().getNotification().getNotificationStyle()
+ == Notification.MessagingStyle.class;
+ }
+
public void dump(PrintWriter pw, String indent) {
int filteredLen = mSortedAndFiltered.size();
pw.print(indent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
new file mode 100644
index 000000000000..2c0c9420a8c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.PendingIntent
+import android.graphics.drawable.Drawable
+
+/** `ViewModel` for PeopleHub view. */
+data class PeopleHubViewModel(val people: Sequence<PersonViewModel>, val isVisible: Boolean)
+
+/** `ViewModel` for a single "Person' in PeopleHub. */
+data class PersonViewModel(
+ val name: CharSequence,
+ val icon: Drawable,
+ val onClick: () -> Unit
+)
+
+/** `Model` for PeopleHub. */
+data class PeopleHubModel(val people: Collection<PersonModel>)
+
+/** `Model` for a single "Person" in PeopleHub. */
+data class PersonModel(
+ val key: PersonKey,
+ val name: CharSequence,
+ val avatar: Drawable,
+ val clickIntent: PendingIntent
+)
+
+/** Unique identifier for a Person in PeopleHub. */
+typealias PersonKey = String \ No newline at end of file
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
new file mode 100644
index 000000000000..8c067b79482c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -0,0 +1,37 @@
+/*
+ * 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 dagger.Binds
+import dagger.Module
+
+@Module
+abstract class PeopleHubModule {
+
+ @Binds
+ abstract fun peopleHubSectionFooterViewController(
+ viewAdapter: PeopleHubSectionFooterViewAdapterImpl
+ ): PeopleHubSectionFooterViewAdapter
+
+ @Binds
+ abstract fun peopleHubDataSource(s: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
+
+ @Binds
+ abstract fun peopleHubViewModelFactoryDataSource(
+ dataSource: PeopleHubViewModelFactoryDataSourceImpl
+ ): DataSource<PeopleHubViewModelFactory>
+} \ 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
new file mode 100644
index 000000000000..90a860a35a3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -0,0 +1,211 @@
+/*
+ * 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.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.internal.widget.MessagingGroup
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+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> {
+
+ private var dataListener: DataListener<PeopleHubModel>? = null
+
+ init {
+ notificationEntryManager.addNotificationEntryListener(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) {
+ updateUi()
+ }
+ }
+
+ private fun addVisibleEntry(entry: NotificationEntry?) {
+ if (entry?.extractPerson()?.let(peopleHubManager::addActivePerson) == true) {
+ updateUi()
+ }
+ }
+
+ override fun setListener(listener: DataListener<PeopleHubModel>) {
+ this.dataListener = listener
+ updateUi()
+ }
+
+ private fun updateUi() {
+ dataListener?.onDataChanged(peopleHubManager.getPeopleHubModel())
+ }
+}
+
+@Singleton
+class PeopleHubManager @Inject constructor() {
+
+ private val activePeople = mutableMapOf<PersonKey, PersonModel>()
+ private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
+
+ fun removeActivePerson(key: PersonKey): Boolean {
+ activePeople.remove(key)?.let { data ->
+ if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
+ inactivePeople.removeLast()
+ }
+ inactivePeople.push(data)
+ return true
+ }
+ return false
+ }
+
+ fun addActivePerson(person: PersonModel): Boolean {
+ activePeople[person.key] = person
+ return inactivePeople.removeIf { it.key == person.key }
+ }
+
+ fun getPeopleHubModel(): PeopleHubModel = PeopleHubModel(inactivePeople)
+}
+
+private val ViewGroup.children
+ get(): Sequence<View> = sequence {
+ for (i in 0 until childCount) {
+ yield(getChildAt(i))
+ }
+ }
+
+private fun ViewGroup.childrenWithId(id: Int): Sequence<View> = children.filter { it.id == id }
+
+private fun NotificationEntry.extractPerson(): PersonModel? {
+ 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 context = row.context
+ val pm = context.packageManager
+ val appInfo = pm.getApplicationInfo(sbn.packageName, 0)
+
+ val badgedAvatar = object : Drawable() {
+ override fun draw(canvas: Canvas) {
+ val iconBounds = getBounds()
+ val factory = object : BaseIconFactory(
+ context,
+ 0 /* unused */,
+ iconBounds.width(),
+ true) {}
+ val badge = factory.createBadgedIconBitmap(
+ appInfo.loadIcon(pm),
+ sbn.user,
+ true,
+ appInfo.isInstantApp,
+ null)
+ val badgeDrawable = BitmapDrawable(context.resources, badge.icon)
+ .apply {
+ alpha = drawable.alpha
+ colorFilter = drawable.colorFilter
+ val badgeWidth = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 16f,
+ context.resources.displayMetrics
+ ).toInt()
+ setBounds(
+ iconBounds.left + (iconBounds.width() - badgeWidth),
+ iconBounds.top + (iconBounds.height() - badgeWidth),
+ iconBounds.right,
+ iconBounds.bottom)
+ }
+ drawable.bounds = iconBounds
+ drawable.draw(canvas)
+ badgeDrawable.draw(canvas)
+ }
+
+ override fun setAlpha(alpha: Int) {
+ drawable.alpha = alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ drawable.colorFilter = colorFilter
+ }
+
+ @PixelFormat.Opacity
+ override fun getOpacity(): Int = PixelFormat.OPAQUE
+ }
+
+ return PersonModel(key, name, badgedAvatar, clickIntent)
+}
+
+private fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
+ entry.row
+ ?.childrenWithId(R.id.expanded)
+ ?.mapNotNull { it as? ViewGroup }
+ ?.flatMap {
+ it.childrenWithId(com.android.internal.R.id.status_bar_latest_event_content)
+ }
+ ?.mapNotNull {
+ it.findViewById<ViewGroup>(com.android.internal.R.id.notification_messaging)
+ }
+ ?.mapNotNull { messagesView ->
+ messagesView.children
+ .mapNotNull { it as? MessagingGroup }
+ .lastOrNull()
+ ?.findViewById<ImageView>(com.android.internal.R.id.message_icon)
+ ?.drawable
+ }
+ ?.firstOrNull()
+
+private fun NotificationEntry.extractPersonKey(): PersonKey? =
+ if (isMessagingNotification()) key else null
+
+private fun NotificationEntry.isMessagingNotification() =
+ sbn.notification.notificationStyle == Notification.MessagingStyle::class.java \ No newline at end of file
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
new file mode 100644
index 000000000000..8d1253b457cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.view.View
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Boundary between the View and PeopleHub, as seen by the View. */
+interface PeopleHubSectionFooterViewAdapter {
+ fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary)
+}
+
+/** Abstract `View` representation of PeopleHub footer in [NotificationSectionsManager]. */
+interface PeopleHubSectionFooterViewBoundary {
+ /** View used for animating the activity launch caused by clicking a person in the hub. */
+ val associatedViewForClickAnimation: View
+
+ /** [DataListener]s for individual people in the hub. */
+ val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
+
+ /** Sets the visibility of the Hub in the notification shade. */
+ fun setVisible(isVisible: Boolean)
+}
+
+/** Creates a [PeopleHubViewModel] given some additional information required from the `View`. */
+interface PeopleHubViewModelFactory {
+
+ /**
+ * Creates a [PeopleHubViewModel] that, when clicked, starts an activity using an animation
+ * involving the given [view].
+ */
+ fun createWithAssociatedClickView(view: View): PeopleHubViewModel
+}
+
+/**
+ * Wraps a [PeopleHubSectionFooterViewBoundary] in a [DataListener], and connects it to the data
+ * pipeline.
+ *
+ * @param dataSource PeopleHub data pipeline.
+ */
+@Singleton
+class PeopleHubSectionFooterViewAdapterImpl @Inject constructor(
+ private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
+) : PeopleHubSectionFooterViewAdapter {
+
+ override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) =
+ dataSource.setListener(PeopleHubDataListenerImpl(viewBoundary))
+}
+
+private class PeopleHubDataListenerImpl(
+ private val viewBoundary: PeopleHubSectionFooterViewBoundary
+) : DataListener<PeopleHubViewModelFactory> {
+
+ override fun onDataChanged(data: PeopleHubViewModelFactory) {
+ val viewModel = data.createWithAssociatedClickView(
+ viewBoundary.associatedViewForClickAnimation
+ )
+ viewBoundary.setVisible(viewModel.isVisible)
+ val padded = viewModel.people + repeated(null)
+ for ((personAdapter, personModel) in viewBoundary.personViewAdapters.zip(padded)) {
+ personAdapter.onDataChanged(personModel)
+ }
+ }
+}
+
+/**
+ * Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s.
+ *
+ * This class serves as the glue between the View layer (which depends on
+ * [PeopleHubSectionFooterViewBoundary]) and the Data layer (which produces [PeopleHubModel]s).
+ */
+@Singleton
+class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
+ private val activityStarter: ActivityStarter,
+ private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
+) : DataSource<PeopleHubViewModelFactory> {
+
+ override fun setListener(listener: DataListener<PeopleHubViewModelFactory>) =
+ dataSource.setListener(PeopleHubModelListenerImpl(activityStarter, listener))
+}
+
+private class PeopleHubModelListenerImpl(
+ private val activityStarter: ActivityStarter,
+ private val dataListener: DataListener<PeopleHubViewModelFactory>
+) : DataListener<PeopleHubModel> {
+
+ override fun onDataChanged(data: PeopleHubModel) =
+ dataListener.onDataChanged(PeopleHubViewModelFactoryImpl(data, activityStarter))
+}
+
+private class PeopleHubViewModelFactoryImpl(
+ private val data: PeopleHubModel,
+ private val activityStarter: ActivityStarter
+) : PeopleHubViewModelFactory {
+
+ override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
+ val personViewModels = data.people.asSequence().map { personModel ->
+ val onClick = {
+ activityStarter.startPendingIntentDismissingKeyguard(
+ personModel.clickIntent,
+ null,
+ view
+ )
+ }
+ PersonViewModel(personModel.name, personModel.avatar, onClick)
+ }
+ return PeopleHubViewModel(personViewModels, data.people.isNotEmpty())
+ }
+}
+
+private fun <T> repeated(value: T): Sequence<T> = sequence {
+ while (true) {
+ yield(value)
+ }
+} \ 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
new file mode 100644
index 000000000000..33e3bb883d53
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
@@ -0,0 +1,37 @@
+/*
+ * 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
+
+/** Boundary between a View and data pipeline, as seen by the pipeline. */
+interface DataListener<in T> {
+ fun onDataChanged(data: T)
+}
+
+/** Convert all data using the given [mapper] before invoking this [DataListener]. */
+fun <S, T> DataListener<T>.contraMap(mapper: (S) -> T): DataListener<S> = object : DataListener<S> {
+ override fun onDataChanged(data: S) = onDataChanged(mapper(data))
+}
+
+/** Boundary between a View and data pipeline, as seen by the View. */
+interface DataSource<out T> {
+ fun setListener(listener: DataListener<T>)
+}
+
+/** 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))
+} \ 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 6ed4a576f441..bd87d7774e2c 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
@@ -23,6 +23,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.PendingIntent;
import android.content.Intent;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -33,6 +34,10 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.people.DataListener;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewBoundary;
+import com.android.systemui.statusbar.notification.people.PersonViewModel;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -42,6 +47,8 @@ import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
+import kotlin.sequences.Sequence;
+
/**
* Manages the boundaries of the two notification sections (high priority and low priority). Also
* shows/hides the headers for those sections where appropriate.
@@ -58,11 +65,39 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private final StatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
private final int mNumberOfSections;
-
private boolean mInitialized = false;
+
private SectionHeaderView mGentleHeader;
private boolean mGentleHeaderVisible = false;
+ private boolean mPeopleHubVisible = false;
+ private PeopleHubView mPeopleHubView;
+ private final PeopleHubSectionFooterViewAdapter mPeopleHubViewAdapter;
+ private final PeopleHubSectionFooterViewBoundary mPeopleHubViewBoundary =
+ new PeopleHubSectionFooterViewBoundary() {
+ @Override
+ public void setVisible(boolean isVisible) {
+ if (mPeopleHubVisible != isVisible) {
+ mPeopleHubVisible = isVisible;
+ if (mInitialized) {
+ updateSectionBoundaries();
+ }
+ }
+ }
+
+ @NonNull
+ @Override
+ public View getAssociatedViewForClickAnimation() {
+ return mPeopleHubView;
+ }
+
+ @NonNull
+ @Override
+ public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() {
+ return mPeopleHubView.getPersonViewAdapters();
+ }
+ };
+
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
NotificationSectionsManager(
@@ -70,11 +105,13 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ PeopleHubSectionFooterViewAdapter peopleHubViewAdapter,
int numberOfSections) {
mParent = parent;
mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController;
mConfigurationController = configurationController;
+ mPeopleHubViewAdapter = peopleHubViewAdapter;
mNumberOfSections = numberOfSections;
}
@@ -101,23 +138,43 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
* Reinflates the entire notification header, including all decoration views.
*/
void reinflateViews(LayoutInflater layoutInflater) {
- int oldPos = -1;
+ int oldGentleHeaderPos = -1;
+ int oldPeopleHubPos = -1;
if (mGentleHeader != null) {
if (mGentleHeader.getTransientContainer() != null) {
mGentleHeader.getTransientContainer().removeView(mGentleHeader);
} else if (mGentleHeader.getParent() != null) {
- oldPos = mParent.indexOfChild(mGentleHeader);
+ oldGentleHeaderPos = mParent.indexOfChild(mGentleHeader);
mParent.removeView(mGentleHeader);
}
}
+ if (mPeopleHubView != null) {
+ if (mPeopleHubView.getTransientContainer() != null) {
+ mPeopleHubView.getTransientContainer().removeView(mPeopleHubView);
+ } else if (mPeopleHubView.getParent() != null) {
+ oldPeopleHubPos = mParent.indexOfChild(mPeopleHubView);
+ mParent.removeView(mPeopleHubView);
+ }
+ }
mGentleHeader = (SectionHeaderView) layoutInflater.inflate(
R.layout.status_bar_notification_section_header, mParent, false);
mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick);
- if (oldPos != -1) {
- mParent.addView(mGentleHeader, oldPos);
+ if (oldGentleHeaderPos != -1) {
+ mParent.addView(mGentleHeader, oldGentleHeaderPos);
+ }
+
+ mPeopleHubView = (PeopleHubView) layoutInflater.inflate(
+ R.layout.people_strip, mParent, false);
+
+ if (oldPeopleHubPos != -1) {
+ mParent.addView(mPeopleHubView, oldPeopleHubPos);
+ }
+
+ if (!mInitialized) {
+ mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
}
}
@@ -145,7 +202,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
if (!begin) {
- begin = view == mGentleHeader;
+ begin = view == mGentleHeader || previous == mPeopleHubView;
}
return begin;
@@ -161,6 +218,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return ((ExpandableNotificationRow) view).getEntry().getBucket();
} else if (view == mGentleHeader) {
return BUCKET_SILENT;
+ } else if (view == mPeopleHubView) {
+ return BUCKET_PEOPLE;
}
throw new IllegalArgumentException("I don't know how to find a bucket for this view :(");
@@ -175,6 +234,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return;
}
+ int lastPersonIndex = -1;
int firstGentleNotifIndex = -1;
final int n = mParent.getChildCount();
@@ -183,6 +243,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
if (child instanceof ExpandableNotificationRow
&& child.getVisibility() != View.GONE) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (row.getEntry().getBucket() == BUCKET_PEOPLE) {
+ lastPersonIndex = i;
+ }
if (row.getEntry().getBucket() == BUCKET_SILENT) {
firstGentleNotifIndex = i;
break;
@@ -190,6 +253,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ if (adjustPeopleHubVisibilityAndPosition(lastPersonIndex)) {
+ // make room for peopleHub
+ firstGentleNotifIndex++;
+ }
+
adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
mGentleHeader.setAreThereDismissableGentleNotifs(
@@ -232,6 +300,36 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ private boolean adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
+ final boolean showPeopleHeader = mPeopleHubVisible
+ && mNumberOfSections > 2
+ && mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
+ final int currentHubIndex = mParent.indexOfChild(mPeopleHubView);
+ final boolean currentlyVisible = currentHubIndex >= 0;
+ int targetIndex = lastPersonIndex + 1;
+
+ if (!showPeopleHeader) {
+ if (currentlyVisible) {
+ mParent.removeView(mPeopleHubView);
+ }
+ } else {
+ if (!currentlyVisible) {
+ if (mPeopleHubView.getTransientContainer() != null) {
+ mPeopleHubView.getTransientContainer().removeTransientView(mPeopleHubView);
+ mPeopleHubView.setTransientContainer(null);
+ }
+ mParent.addView(mPeopleHubView, targetIndex);
+ return true;
+ } else if (currentHubIndex != targetIndex - 1) {
+ if (currentHubIndex < targetIndex) {
+ targetIndex--;
+ }
+ mParent.changeViewPosition(mPeopleHubView, targetIndex);
+ }
+ }
+ return false;
+ }
+
/**
* Updates the boundaries (as tracked by their first and last views) of the priority sections.
*
@@ -324,6 +422,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ private void handlePeopleHubClick(PendingIntent pendingIntent) {
+ mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent, null, mPeopleHubView);
+ }
+
/**
* For now, declare the available notification buckets (sections) here so that other
* presentation code can decide what to do based on an entry's buckets
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 5a1a2176672e..6dca7ee9e872 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -116,6 +116,7 @@ import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorCom
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -518,7 +519,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
FalsingManager falsingManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGutsManager notificationGutsManager,
- NotificationSectionsFeatureManager sectionsFeatureManager) {
+ NotificationSectionsFeatureManager sectionsFeatureManager,
+ PeopleHubSectionFooterViewAdapter peopleHubViewAdapter) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -541,6 +543,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
activityStarter,
statusBarStateController,
configurationController,
+ peopleHubViewAdapter,
buckets.length);
mSectionsManager.initialize(LayoutInflater.from(context));
mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
new file mode 100644
index 000000000000..e31ee024fa36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.stack
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.people.PersonViewModel
+import com.android.systemui.statusbar.notification.people.DataListener
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
+
+class PeopleHubView(context: Context, attrs: AttributeSet) :
+ ActivatableNotificationView(context, attrs) {
+
+ private lateinit var contents: ViewGroup
+ private lateinit var personControllers: List<PersonDataListenerImpl>
+ val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
+ get() = personControllers.asSequence()
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ contents = requireViewById(R.id.people_list)
+ personControllers = (0 until contents.childCount)
+ .asSequence()
+ .mapNotNull { idx ->
+ (contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl)
+ }
+ .toList()
+ }
+
+ override fun getContentView(): View = contents
+
+ private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) :
+ DataListener<PersonViewModel?> {
+
+ val nameView = viewGroup.requireViewById<TextView>(R.id.person_name)
+ val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon)
+
+ override fun onDataChanged(data: PersonViewModel?) {
+ viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE
+ nameView.text = data?.name
+ avatarView.setImageDrawable(data?.icon)
+ viewGroup.setOnClickListener { data?.onClick?.invoke() }
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 56ed0e3a6af3..003d80376c40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -44,6 +44,7 @@ import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -66,6 +67,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private ActivityStarterDelegate mActivityStarterDelegate;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
+ @Mock private PeopleHubSectionFooterViewAdapter mPeopleHubAdapter;
private NotificationSectionsManager mSectionsManager;
@@ -77,6 +79,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mActivityStarterDelegate,
mStatusBarStateController,
mConfigurationController,
+ mPeopleHubAdapter,
2);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 4b82f59e2a35..012ebf728c50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -171,7 +172,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
new FalsingManagerFake(),
mock(NotificationLockscreenUserManager.class),
mock(NotificationGutsManager.class),
- new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext));
+ new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
+ mock(PeopleHubSectionFooterViewAdapter.class));
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelf(notificationShelf);
mStackScroller.setStatusBar(mBar);