diff options
| author | 2022-08-25 00:15:16 -0400 | |
|---|---|---|
| committer | 2022-08-29 12:18:44 -0400 | |
| commit | 207bcfaf59859f2ba585d72b1ac43f2de700de58 (patch) | |
| tree | 2b194a1f00f112dce0c98f96cdbb650862405a57 | |
| parent | 28ea00f4abed69d6a428e4f7360dcb720f7dabce (diff) | |
Delete unused PeopleHub code
Bug: 200269355
Test: atest SystemUITests
Change-Id: I9f045803bad4c4e09db5fd4ed69651b46ab7a188
10 files changed, 50 insertions, 1008 deletions
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml deleted file mode 100644 index ec004296ff9d..000000000000 --- 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 000000000000..4cf3572b5bda --- /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 3af6ba8434a5..000000000000 --- 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 16574abab7aa..c17ffb0adacf 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 @@ import dagger.Module @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 class PeopleHubModule { 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 6062941d44a6..000000000000 --- 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 55bd77fdab68..000000000000 --- 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 79d883b32a98..cc87499d39cb 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 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } return row.canViewBeDismissed(); } - if (v instanceof PeopleHubView) { - return ((PeopleHubView) v).getCanSwipe(); - } return false; } @@ -6130,9 +6127,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } 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 b13e7fb839ff..000000000000 --- 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 1fb4ca1ac5db..000000000000 --- 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 5898664dea8e..000000000000 --- 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) - } -} |