Delete unused PeopleHub code
Bug: 200269355
Test: atest SystemUITests
Change-Id: I9f045803bad4c4e09db5fd4ed69651b46ab7a188
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
deleted file mode 100644
index ec00429..0000000
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?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="@dimen/notification_section_header_height"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
- android:focusable="true"
- android:clickable="true"
->
-
- <LinearLayout
- android:id="@+id/people_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:forceHasOverlappingRendering="false"
- android:clipChildren="false">
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:layout_weight="1"
- android:forceHasOverlappingRendering="false">
-
- <TextView
- android:id="@+id/header_label"
- style="@style/TextAppearance.NotificationSectionHeaderButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/notification_section_header_conversations"
- />
-
- </FrameLayout>
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:forceHasOverlappingRendering="false"
- android:visibility="gone"
- />
-
- </LinearLayout>
-
-</com.android.systemui.statusbar.notification.stack.PeopleHubView>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt
new file mode 100644
index 0000000..4cf3572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.service.notification.StatusBarNotification
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.NotificationPersonExtractorPlugin
+import com.android.systemui.statusbar.policy.ExtensionController
+import javax.inject.Inject
+
+interface NotificationPersonExtractor {
+ fun isPersonNotification(sbn: StatusBarNotification): Boolean
+}
+
+@SysUISingleton
+class NotificationPersonExtractorPluginBoundary @Inject constructor(
+ extensionController: ExtensionController
+) : NotificationPersonExtractor {
+
+ private var plugin: NotificationPersonExtractorPlugin? = null
+
+ init {
+ plugin = extensionController
+ .newExtension(NotificationPersonExtractorPlugin::class.java)
+ .withPlugin(NotificationPersonExtractorPlugin::class.java)
+ .withCallback { extractor ->
+ plugin = extractor
+ }
+ .build()
+ .get()
+ }
+
+ override fun isPersonNotification(sbn: StatusBarNotification): Boolean =
+ plugin?.isPersonNotification(sbn) ?: false
+}
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
deleted file mode 100644
index 3af6ba8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.graphics.drawable.Drawable
-
-/**
- * `ViewModel` for PeopleHub view.
- *
- * @param people ViewModels for individual people in PeopleHub, in order that they should be
- * displayed
- * @param isVisible Whether or not the whole PeopleHub UI is visible
- **/
-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.
- *
- * @param people Models for individual people in PeopleHub, in order that they should be displayed
- **/
-data class PeopleHubModel(val people: Collection<PersonModel>)
-
-/** `Model` for a single "Person" in PeopleHub. */
-data class PersonModel(
- val key: PersonKey,
- val userId: Int,
- // TODO: these should live in the ViewModel
- val name: CharSequence,
- val avatar: Drawable,
- val clickRunnable: Runnable
-)
-
-/** 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
index 16574ab..c17ffb0 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
@@ -21,25 +21,6 @@
@Module
abstract class PeopleHubModule {
-
- @Binds
- abstract fun peopleHubSectionFooterViewAdapter(
- impl: PeopleHubViewAdapterImpl
- ): PeopleHubViewAdapter
-
- @Binds
- abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
-
- @Binds
- abstract fun peopleHubSettingChangeDataSource(
- impl: PeopleHubSettingChangeDataSourceImpl
- ): DataSource<Boolean>
-
- @Binds
- abstract fun peopleHubViewModelFactoryDataSource(
- impl: PeopleHubViewModelFactoryDataSourceImpl
- ): DataSource<PeopleHubViewModelFactory>
-
@Binds
abstract fun peopleNotificationIdentifier(
impl: PeopleNotificationIdentifierImpl
@@ -49,4 +30,4 @@
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
deleted file mode 100644
index 6062941..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * 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.content.Context
-import android.content.pm.LauncherApps
-import android.content.pm.PackageManager
-import android.content.pm.UserInfo
-import android.graphics.drawable.Drawable
-import android.os.UserManager
-import android.service.notification.NotificationListenerService
-import android.service.notification.NotificationListenerService.REASON_SNOOZED
-import android.service.notification.StatusBarNotification
-import android.util.IconDrawableFactory
-import android.util.SparseArray
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import com.android.internal.widget.MessagingGroup
-import com.android.settingslib.notification.ConversationIconFactory
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.NotificationPersonExtractorPlugin
-import com.android.systemui.statusbar.NotificationListener
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
-import com.android.systemui.statusbar.policy.ExtensionController
-import java.util.ArrayDeque
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-private const val MAX_STORED_INACTIVE_PEOPLE = 10
-
-interface NotificationPersonExtractor {
- fun extractPerson(sbn: StatusBarNotification): PersonModel?
- fun extractPersonKey(sbn: StatusBarNotification): String?
- fun isPersonNotification(sbn: StatusBarNotification): Boolean
-}
-
-@SysUISingleton
-class NotificationPersonExtractorPluginBoundary @Inject constructor(
- extensionController: ExtensionController
-) : NotificationPersonExtractor {
-
- private var plugin: NotificationPersonExtractorPlugin? = null
-
- init {
- plugin = extensionController
- .newExtension(NotificationPersonExtractorPlugin::class.java)
- .withPlugin(NotificationPersonExtractorPlugin::class.java)
- .withCallback { extractor ->
- plugin = extractor
- }
- .build()
- .get()
- }
-
- override fun extractPerson(sbn: StatusBarNotification) =
- plugin?.extractPerson(sbn)?.run {
- PersonModel(key, sbn.user.identifier, name, avatar, clickRunnable)
- }
-
- override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
-
- override fun isPersonNotification(sbn: StatusBarNotification): Boolean =
- plugin?.isPersonNotification(sbn) ?: false
-}
-
-@SysUISingleton
-class PeopleHubDataSourceImpl @Inject constructor(
- private val notifCollection: CommonNotifCollection,
- private val extractor: NotificationPersonExtractor,
- private val userManager: UserManager,
- launcherApps: LauncherApps,
- packageManager: PackageManager,
- context: Context,
- private val notificationListener: NotificationListener,
- @Background private val bgExecutor: Executor,
- @Main private val mainExecutor: Executor,
- private val notifLockscreenUserMgr: NotificationLockscreenUserManager,
- private val peopleNotificationIdentifier: PeopleNotificationIdentifier
-) : DataSource<PeopleHubModel> {
-
- private var userChangeSubscription: Subscription? = null
- private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
- private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
-
- private val iconFactory = run {
- val appContext = context.applicationContext
- ConversationIconFactory(
- appContext,
- launcherApps,
- packageManager,
- IconDrawableFactory.newInstance(appContext),
- appContext.resources.getDimensionPixelSize(
- R.dimen.notification_guts_conversation_icon_size
- )
- )
- }
-
- private val notifCollectionListener = object : NotifCollectionListener {
- override fun onEntryAdded(entry: NotificationEntry) = addVisibleEntry(entry)
- override fun onEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
- removeVisibleEntry(entry, reason)
- }
-
- private fun removeVisibleEntry(entry: NotificationEntry, reason: Int) {
- (extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key ->
- val userId = entry.sbn.user.identifier
- bgExecutor.execute {
- val parentId = userManager.getProfileParent(userId)?.id ?: userId
- mainExecutor.execute {
- if (reason == REASON_SNOOZED) {
- if (peopleHubManagerForUser[parentId]?.migrateActivePerson(key) == true) {
- updateUi()
- }
- } else {
- peopleHubManagerForUser[parentId]?.removeActivePerson(key)
- }
- }
- }
- }
- }
-
- private fun addVisibleEntry(entry: NotificationEntry) {
- entry.extractPerson()?.let { personModel ->
- val userId = entry.sbn.user.identifier
- bgExecutor.execute {
- val parentId = userManager.getProfileParent(userId)?.id ?: userId
- mainExecutor.execute {
- val manager = peopleHubManagerForUser[parentId]
- ?: PeopleHubManager().also { peopleHubManagerForUser.put(parentId, it) }
- if (manager.addActivePerson(personModel)) {
- updateUi()
- }
- }
- }
- }
- }
-
- override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
- val register = dataListeners.isEmpty()
- dataListeners.add(listener)
- if (register) {
- userChangeSubscription = notifLockscreenUserMgr.registerListener(
- object : NotificationLockscreenUserManager.UserChangedListener {
- override fun onUserChanged(userId: Int) = updateUi()
- override fun onCurrentProfilesChanged(
- currentProfiles: SparseArray<UserInfo>?
- ) = updateUi()
- })
- notifCollection.addCollectionListener(notifCollectionListener)
- } else {
- getPeopleHubModelForCurrentUser()?.let(listener::onDataChanged)
- }
- return object : Subscription {
- override fun unsubscribe() {
- dataListeners.remove(listener)
- if (dataListeners.isEmpty()) {
- userChangeSubscription?.unsubscribe()
- userChangeSubscription = null
- notifCollection.removeCollectionListener(notifCollectionListener)
- }
- }
- }
- }
-
- private fun getPeopleHubModelForCurrentUser(): PeopleHubModel? {
- val currentUserId = notifLockscreenUserMgr.currentUserId
- val model = peopleHubManagerForUser[currentUserId]?.getPeopleHubModel()
- ?: return null
- val currentProfiles = notifLockscreenUserMgr.currentProfiles
- return model.copy(people = model.people.filter { person ->
- currentProfiles[person.userId]?.isQuietModeEnabled == false
- })
- }
-
- private fun updateUi() {
- val model = getPeopleHubModelForCurrentUser() ?: return
- for (listener in dataListeners) {
- listener.onDataChanged(model)
- }
- }
-
- private fun NotificationEntry.extractPerson(): PersonModel? {
- val type = peopleNotificationIdentifier.getPeopleNotificationType(this)
- if (type == TYPE_NON_PERSON) {
- return null
- }
- val clickRunnable = Runnable { notificationListener.unsnoozeNotification(key) }
- val extras = sbn.notification.extras
- val name = ranking.conversationShortcutInfo?.label
- ?: extras.getCharSequence(Notification.EXTRA_CONVERSATION_TITLE)
- ?: extras.getCharSequence(Notification.EXTRA_TITLE)
- ?: return null
- val drawable = ranking.getIcon(iconFactory, sbn)
- ?: iconFactory.getConversationDrawable(
- extractAvatarFromRow(this),
- sbn.packageName,
- sbn.uid,
- ranking.channel.isImportantConversation
- )
- return PersonModel(key, sbn.user.identifier, name, drawable, clickRunnable)
- }
-
- private fun NotificationListenerService.Ranking.getIcon(
- iconFactory: ConversationIconFactory,
- sbn: StatusBarNotification
- ): Drawable? =
- conversationShortcutInfo?.let { conversationShortcutInfo ->
- iconFactory.getConversationDrawable(
- conversationShortcutInfo,
- sbn.packageName,
- sbn.uid,
- channel.isImportantConversation
- )
- }
-
- private fun NotificationEntry.extractPersonKey(): PersonKey? {
- // TODO migrate to shortcut id when snoozing is conversation wide
- val type = peopleNotificationIdentifier.getPeopleNotificationType(this)
- return if (type != TYPE_NON_PERSON) key else null
- }
-}
-
-private fun NotificationLockscreenUserManager.registerListener(
- listener: NotificationLockscreenUserManager.UserChangedListener
-): Subscription {
- addUserChangedListener(listener)
- return object : Subscription {
- override fun unsubscribe() {
- removeUserChangedListener(listener)
- }
- }
-}
-
-class PeopleHubManager {
-
- // People currently visible in the notification shade, and so are not in the hub
- private val activePeople = mutableMapOf<PersonKey, PersonModel>()
-
- // People that were once "active" and have been dismissed, and so can be displayed in the hub
- private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
-
- fun migrateActivePerson(key: PersonKey): Boolean {
- activePeople.remove(key)?.let { data ->
- if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
- inactivePeople.removeLast()
- }
- inactivePeople.addFirst(data)
- return true
- }
- return false
- }
-
- fun removeActivePerson(key: PersonKey) {
- activePeople.remove(key)
- }
-
- 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 }
-
-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()
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
deleted file mode 100644
index 55bd77f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * 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.content.Context
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
-import android.view.View
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.plugins.ActivityStarter
-import javax.inject.Inject
-
-/** Boundary between the View and PeopleHub, as seen by the View. */
-interface PeopleHubViewAdapter {
- fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription
-}
-
-/** Abstract `View` representation of PeopleHub. */
-interface PeopleHubViewBoundary {
- /** 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.
- *
- * These listeners should be ordered such that the first element will be bound to the most
- * recent person to be added to the hub, and then continuing in descending order. If there are
- * not enough people to satisfy each listener, `null` will be passed instead, indicating that
- * the `View` should render a placeholder.
- */
- 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 [PeopleHubViewBoundary] in a [DataListener], and connects it to the data
- * pipeline.
- *
- * @param dataSource PeopleHub data pipeline.
- */
-@SysUISingleton
-class PeopleHubViewAdapterImpl @Inject constructor(
- private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
-) : PeopleHubViewAdapter {
-
- override fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription =
- dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
-}
-
-private class PeopleHubDataListenerImpl(
- private val viewBoundary: PeopleHubViewBoundary
-) : DataListener<PeopleHubViewModelFactory> {
-
- override fun onDataChanged(data: PeopleHubViewModelFactory) {
- val viewModel = data.createWithAssociatedClickView(
- viewBoundary.associatedViewForClickAnimation
- )
- viewBoundary.setVisible(viewModel.isVisible)
- val padded = viewModel.people + repeated(null)
- for ((adapter, model) in viewBoundary.personViewAdapters.zip(padded)) {
- adapter.onDataChanged(model)
- }
- }
-}
-
-/**
- * Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s.
- *
- * This class serves as the glue between the View layer (which depends on
- * [PeopleHubViewBoundary]) and the Data layer (which produces [PeopleHubModel]s).
- */
-@SysUISingleton
-class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
- private val activityStarter: ActivityStarter,
- private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
-) : DataSource<PeopleHubViewModelFactory> {
-
- override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>): Subscription {
- var model: PeopleHubModel? = null
-
- fun updateListener() {
- // don't invoke listener until we've received our first model
- model?.let { model ->
- val factory = PeopleHubViewModelFactoryImpl(model, activityStarter)
- listener.onDataChanged(factory)
- }
- }
- val dataSub = dataSource.registerListener(object : DataListener<PeopleHubModel> {
- override fun onDataChanged(data: PeopleHubModel) {
- model = data
- updateListener()
- }
- })
- return object : Subscription {
- override fun unsubscribe() {
- dataSub.unsubscribe()
- }
- }
- }
-}
-
-private object EmptyViewModelFactory : PeopleHubViewModelFactory {
- override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
- return PeopleHubViewModel(emptySequence(), false)
- }
-}
-
-private class PeopleHubViewModelFactoryImpl(
- private val model: PeopleHubModel,
- private val activityStarter: ActivityStarter
-) : PeopleHubViewModelFactory {
-
- override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
- val personViewModels = model.people.asSequence().map { personModel ->
- val onClick = {
- personModel.clickRunnable.run()
- }
- PersonViewModel(personModel.name, personModel.avatar, onClick)
- }
- return PeopleHubViewModel(personViewModels, model.people.isNotEmpty())
- }
-}
-
-@SysUISingleton
-class PeopleHubSettingChangeDataSourceImpl @Inject constructor(
- @Main private val handler: Handler,
- context: Context
-) : DataSource<Boolean> {
-
- private val settingUri = Settings.Secure.getUriFor(Settings.Secure.PEOPLE_STRIP)
- private val contentResolver = context.contentResolver
-
- override fun registerListener(listener: DataListener<Boolean>): Subscription {
- // Immediately report current value of setting
- updateListener(listener)
- val observer = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri?, flags: Int) {
- super.onChange(selfChange, uri, flags)
- updateListener(listener)
- }
- }
- contentResolver.registerContentObserver(settingUri, false, observer, UserHandle.USER_ALL)
- return object : Subscription {
- override fun unsubscribe() = contentResolver.unregisterContentObserver(observer)
- }
- }
-
- private fun updateListener(listener: DataListener<Boolean>) {
- val setting = Settings.Secure.getIntForUser(
- contentResolver,
- Settings.Secure.PEOPLE_STRIP,
- 0,
- UserHandle.USER_CURRENT
- )
- listener.onDataChanged(setting != 0)
- }
-}
-
-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/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 79d883b3..cc87499 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
@@ -6116,9 +6116,6 @@
}
return row.canViewBeDismissed();
}
- if (v instanceof PeopleHubView) {
- return ((PeopleHubView) v).getCanSwipe();
- }
return false;
}
@@ -6130,9 +6127,6 @@
}
return row.canViewBeCleared();
}
- if (v instanceof PeopleHubView) {
- return ((PeopleHubView) v).getCanSwipe();
- }
return false;
}
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
deleted file mode 100644
index b13e7fb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.annotation.ColorInt
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
-import com.android.systemui.statusbar.notification.people.DataListener
-import com.android.systemui.statusbar.notification.people.PersonViewModel
-import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
-
-class PeopleHubView(context: Context, attrs: AttributeSet) :
- StackScrollerDecorView(context, attrs), SwipeableView {
-
- private lateinit var contents: ViewGroup
- private lateinit var label: TextView
-
- lateinit var personViewAdapters: Sequence<DataListener<PersonViewModel?>>
- private set
-
- override fun onFinishInflate() {
- contents = requireViewById(R.id.people_list)
- label = requireViewById(R.id.header_label)
- personViewAdapters = (0 until contents.childCount)
- .asSequence() // so we can map
- .mapNotNull { idx ->
- // get all our people slots
- (contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl)
- }
- .toList() // cache it
- .asSequence() // but don't reveal it's a list
- super.onFinishInflate()
- setVisible(true /* nowVisible */, false /* animate */)
- }
-
- fun setTextColor(@ColorInt color: Int) = label.setTextColor(color)
-
- override fun findContentView(): View = contents
- override fun findSecondaryView(): View? = null
-
- override fun hasFinishedInitialization(): Boolean = true
-
- override fun createMenu(): NotificationMenuRowPlugin? = null
-
- override fun resetTranslation() {
- translationX = 0f
- }
-
- override fun setTranslation(translation: Float) {
- if (canSwipe) {
- super.setTranslation(translation)
- }
- }
-
- var canSwipe: Boolean = false
- set(value) {
- if (field != value) {
- if (field) {
- resetTranslation()
- }
- field = value
- }
- }
-
- override fun needsClippingToShelf(): Boolean = true
-
- override fun applyContentTransformation(contentAlpha: Float, translationY: Float) {
- super.applyContentTransformation(contentAlpha, translationY)
- for (i in 0 until contents.childCount) {
- val view = contents.getChildAt(i)
- view.alpha = contentAlpha
- view.translationY = translationY
- }
- }
-
- fun setOnHeaderClickListener(listener: OnClickListener) = label.setOnClickListener(listener)
-
- private inner class PersonDataListenerImpl(val avatarView: ImageView) :
- DataListener<PersonViewModel?> {
-
- override fun onDataChanged(data: PersonViewModel?) {
- avatarView.visibility = data?.let { View.VISIBLE } ?: View.GONE
- avatarView.setImageDrawable(data?.icon)
- avatarView.setOnClickListener { data?.onClick?.invoke() }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt
deleted file mode 100644
index 1fb4ca1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleFakes.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 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
-
-object EmptySubscription : Subscription {
- override fun unsubscribe() {}
-}
-
-class FakeDataSource<T>(
- private val data: T,
- private val subscription: Subscription = EmptySubscription
-) : DataSource<T> {
- override fun registerListener(listener: DataListener<T>): Subscription {
- listener.onDataChanged(data)
- return subscription
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
deleted file mode 100644
index 5898664..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
-import android.view.View
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.ActivityStarter
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import kotlin.reflect.KClass
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PeopleHubViewControllerTest : SysuiTestCase() {
-
- @JvmField @Rule val mockito: MockitoRule = MockitoJUnit.rule()
-
- @Mock private lateinit var mockViewBoundary: PeopleHubViewBoundary
- @Mock private lateinit var mockActivityStarter: ActivityStarter
-
- @Test
- fun testBindViewModelToViewBoundary() {
- val fakePerson1 = fakePersonViewModel("name")
- val fakeViewModel = PeopleHubViewModel(sequenceOf(fakePerson1), true)
-
- val mockFactory = mock(PeopleHubViewModelFactory::class.java)
- whenever(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel)
-
- val mockClickView = mock(View::class.java)
- whenever(mockViewBoundary.associatedViewForClickAnimation).thenReturn(mockClickView)
-
- val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>()
- val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>()
- whenever(mockViewBoundary.personViewAdapters)
- .thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2))
-
- val adapter = PeopleHubViewAdapterImpl(FakeDataSource(mockFactory))
-
- adapter.bindView(mockViewBoundary)
-
- assertThat(fakePersonViewAdapter1.lastSeen).isEqualTo(Maybe.Just(fakePerson1))
- assertThat(fakePersonViewAdapter2.lastSeen).isEqualTo(Maybe.Just<PersonViewModel?>(null))
- verify(mockViewBoundary).setVisible(true)
- verify(mockFactory).createWithAssociatedClickView(mockClickView)
- }
-
- @Test
- fun testBindViewModelToViewBoundary_moreDataThanCanBeDisplayed_displaysMostRecent() {
- val fakePerson1 = fakePersonViewModel("person1")
- val fakePerson2 = fakePersonViewModel("person2")
- val fakePerson3 = fakePersonViewModel("person3")
- val fakePeople = sequenceOf(fakePerson3, fakePerson2, fakePerson1)
- val fakeViewModel = PeopleHubViewModel(fakePeople, true)
-
- val mockFactory = mock(PeopleHubViewModelFactory::class.java)
- whenever(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel)
-
- whenever(mockViewBoundary.associatedViewForClickAnimation)
- .thenReturn(mock(View::class.java))
-
- val fakePersonViewAdapter1 = FakeDataListener<PersonViewModel?>()
- val fakePersonViewAdapter2 = FakeDataListener<PersonViewModel?>()
- whenever(mockViewBoundary.personViewAdapters)
- .thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2))
-
- val adapter = PeopleHubViewAdapterImpl(FakeDataSource(mockFactory))
-
- adapter.bindView(mockViewBoundary)
-
- assertThat(fakePersonViewAdapter1.lastSeen).isEqualTo(Maybe.Just(fakePerson3))
- assertThat(fakePersonViewAdapter2.lastSeen).isEqualTo(Maybe.Just(fakePerson2))
- }
-
- @Test
- fun testViewModelDataSourceTransformsModel() {
- val fakeClickRunnable = mock(Runnable::class.java)
- val fakePerson = fakePersonModel("id", "name", fakeClickRunnable)
- val fakeModel = PeopleHubModel(listOf(fakePerson))
- val fakeModelDataSource = FakeDataSource(fakeModel)
- val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl(
- mockActivityStarter,
- fakeModelDataSource
- )
- val fakeListener = FakeDataListener<PeopleHubViewModelFactory>()
- val mockClickView = mock(View::class.java)
-
- factoryDataSource.registerListener(fakeListener)
-
- val viewModel = (fakeListener.lastSeen as Maybe.Just).value
- .createWithAssociatedClickView(mockClickView)
- assertThat(viewModel.isVisible).isTrue()
- val people = viewModel.people.toList()
- assertThat(people.size).isEqualTo(1)
- assertThat(people[0].name).isEqualTo("name")
- assertThat(people[0].icon).isSameInstanceAs(fakePerson.avatar)
-
- people[0].onClick()
-
- verify(fakeClickRunnable).run()
- }
-}
-
-/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
-private inline fun <reified T : Any> any(): T {
- return Mockito.any() ?: createInstance(T::class)
-}
-
-/** Works around Mockito matchers returning `null` and breaking non-nullable Kotlin code. */
-private inline fun <reified T : Any> same(value: T): T {
- return Mockito.same(value) ?: createInstance(T::class)
-}
-
-/** Creates an instance of the given class. */
-private fun <T : Any> createInstance(clazz: KClass<T>): T = castNull()
-
-/** Tricks the Kotlin compiler into assigning `null` to a non-nullable variable. */
-@Suppress("UNCHECKED_CAST")
-private fun <T> castNull(): T = null as T
-
-private fun fakePersonModel(
- id: String,
- name: CharSequence,
- clickRunnable: Runnable,
- userId: Int = 0
-): PersonModel =
- PersonModel(id, userId, name, mock(Drawable::class.java), clickRunnable)
-
-private fun fakePersonViewModel(name: CharSequence): PersonViewModel =
- PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass))
-
-sealed class Maybe<T> {
- data class Just<T>(val value: T) : Maybe<T>()
- class Nothing<T> : Maybe<T>() {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
- return true
- }
-
- override fun hashCode(): Int {
- return javaClass.hashCode()
- }
- }
-}
-
-class FakeDataListener<T> : DataListener<T> {
-
- var lastSeen: Maybe<T> = Maybe.Nothing()
-
- override fun onDataChanged(data: T) {
- lastSeen = Maybe.Just(data)
- }
-}