diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java index a9d2ee3cdfe6b99fbd4ef05f82bd024f8dc8b351..403c7c500426edeed83e3dd4e8d75a2701ee427a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java @@ -19,7 +19,6 @@ package com.android.systemui.plugins; import android.graphics.Color; import android.graphics.Rect; import android.view.View; -import android.widget.ImageView; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.annotations.DependsOn; @@ -50,21 +49,11 @@ public interface DarkIconDispatcher { */ void addDarkReceiver(DarkReceiver receiver); - /** - * Adds a receiver to receive callbacks onDarkChanged - */ - void addDarkReceiver(ImageView imageView); - /** * Must have been previously been added through one of the addDarkReceive methods above. */ void removeDarkReceiver(DarkReceiver object); - /** - * Must have been previously been added through one of the addDarkReceive methods above. - */ - void removeDarkReceiver(ImageView object); - /** * Used to reapply darkness on an object, must have previously been added through * addDarkReceiver. @@ -104,8 +93,8 @@ public interface DarkIconDispatcher { } /** - * @return true if more than half of the view area are in any of the given - * areas, false otherwise + * @return true if more than half of the view's area is in any of the given area Rects, false + * otherwise */ static boolean isInAreas(Collection<Rect> areas, View view) { if (areas.isEmpty()) { @@ -120,9 +109,40 @@ public interface DarkIconDispatcher { } /** - * @return true if more than half of the view area are in area, false + * @return true if more than half of the viewBounds are in any of the given area Rects, false * otherwise */ + static boolean isInAreas(Collection<Rect> areas, Rect viewBounds) { + if (areas.isEmpty()) { + return true; + } + for (Rect area : areas) { + if (isInArea(area, viewBounds)) { + return true; + } + } + return false; + } + + /** @return true if more than half of the viewBounds are in the area Rect, false otherwise */ + static boolean isInArea(Rect area, Rect viewBounds) { + if (area.isEmpty()) { + return true; + } + sTmpRect.set(area); + int left = viewBounds.left; + int width = viewBounds.width(); + + int intersectStart = Math.max(left, area.left); + int intersectEnd = Math.min(left + width, area.right); + int intersectAmount = Math.max(0, intersectEnd - intersectStart); + + boolean coversFullStatusBar = area.top <= 0; + boolean majorityOfWidth = 2 * intersectAmount > width; + return majorityOfWidth && coversFullStatusBar; + } + + /** @return true if more than half of the view's area is in the area Rect, false otherwise */ static boolean isInArea(Rect area, View view) { if (area.isEmpty()) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt new file mode 100644 index 0000000000000000000000000000000000000000..8d5b84fea9ab214b5b7f9c1267fca376341cb388 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 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.common.ui + +import android.content.Context +import androidx.annotation.AttrRes +import androidx.annotation.ColorInt +import androidx.annotation.DimenRes +import com.android.settingslib.Utils +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged +import com.android.systemui.statusbar.policy.onThemeChanged +import com.android.systemui.util.kotlin.emitOnStart +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** Configuration-aware-state-tracking utilities. */ +class ConfigurationState +@Inject +constructor( + private val configurationController: ConfigurationController, + @Application private val context: Context, +) { + /** + * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device + * configuration. + * + * @see android.content.res.Resources.getDimensionPixelSize + */ + fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> { + return configurationController.onDensityOrFontScaleChanged.emitOnStart().map { + context.resources.getDimensionPixelSize(id) + } + } + + /** + * Returns a [Flow] that emits a color that is kept in sync with the device theme. + * + * @see Utils.getColorAttrDefaultColor + */ + fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> { + return configurationController.onThemeChanged.emitOnStart().map { + Utils.getColorAttrDefaultColor(context, id, defaultValue) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..b0e69317e0ee15a42465d55c3a896d21c8050756 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/CommonUiDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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.common.ui.data + +import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryModule +import dagger.Module + +@Module(includes = [ConfigurationRepositoryModule::class]) object CommonUiDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt index b8de8d8226a68a87d03e8802d51a45b872b46001..e4492743271961482362ce6381e416dc859d9fee 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt @@ -13,18 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License */ +@file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.common.ui.data.repository import android.content.Context import android.content.res.Configuration import android.view.DisplayInfo +import androidx.annotation.DimenRes import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.wrapper.DisplayUtilsWrapper +import dagger.Binds +import dagger.Module import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -48,7 +52,6 @@ interface ConfigurationRepository { fun getDimensionPixelSize(id: Int): Int } -@ExperimentalCoroutinesApi @SysUISingleton class ConfigurationRepositoryImpl @Inject @@ -119,7 +122,12 @@ constructor( return 1f } - override fun getDimensionPixelSize(id: Int): Int { + override fun getDimensionPixelSize(@DimenRes id: Int): Int { return context.resources.getDimensionPixelSize(id) } } + +@Module +interface ConfigurationRepositoryModule { + @Binds fun bindImpl(impl: ConfigurationRepositoryImpl): ConfigurationRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index f0d7592d8940cb397191efc312e7b82c49e6f070..04b2852db9e2deae13a112732990837262ddbce2 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -41,7 +41,7 @@ import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule; import com.android.systemui.bouncer.ui.BouncerViewModule; import com.android.systemui.classifier.FalsingModule; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule; -import com.android.systemui.common.ui.data.repository.CommonRepositoryModule; +import com.android.systemui.common.ui.data.CommonUiDataLayerModule; import com.android.systemui.communal.dagger.CommunalModule; import com.android.systemui.complication.dagger.ComplicationComponent; import com.android.systemui.controls.dagger.ControlsModule; @@ -170,7 +170,7 @@ import javax.inject.Named; BouncerViewModule.class, ClipboardOverlayModule.class, ClockRegistryModule.class, - CommonRepositoryModule.class, + CommonUiDataLayerModule.class, CommunalModule.class, ConnectivityModule.class, ControlsModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index e2de37fcbcbe83fee4e84f2992d8ef6f2076b386..22912df71334f6ce711f809616846e246939adc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -17,18 +17,13 @@ package com.android.systemui.statusbar.dagger import com.android.systemui.CoreStartable -import com.android.systemui.statusbar.core.StatusBarInitializer -import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepository -import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryImpl -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository -import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl +import com.android.systemui.statusbar.data.StatusBarDataLayerModule import com.android.systemui.statusbar.phone.LightBarController import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap -import dagger.multibindings.IntoSet /** * A module for **only** classes related to the status bar **UI element**. This module specifically @@ -38,23 +33,8 @@ import dagger.multibindings.IntoSet * ([com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule], * [com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule], etc.). */ -@Module +@Module(includes = [StatusBarDataLayerModule::class]) abstract class StatusBarModule { - @Binds - abstract fun bindStatusBarModeRepository( - impl: StatusBarModeRepositoryImpl - ): StatusBarModeRepository - - @Binds - @IntoMap - @ClassKey(StatusBarModeRepositoryImpl::class) - abstract fun bindStatusBarModeRepositoryStart(impl: StatusBarModeRepositoryImpl): CoreStartable - - @Binds - abstract fun bindKeyguardStatusBarRepository( - impl: KeyguardStatusBarRepositoryImpl - ): KeyguardStatusBarRepository - @Binds @IntoMap @ClassKey(OngoingCallController::class) @@ -64,10 +44,4 @@ abstract class StatusBarModule { @IntoMap @ClassKey(LightBarController::class) abstract fun bindLightBarController(impl: LightBarController): CoreStartable - - @Binds - @IntoSet - abstract fun statusBarInitializedListener( - statusBarModeRepository: StatusBarModeRepository, - ): StatusBarInitializer.OnStatusBarViewInitializedListener } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..29d53fc15e8b08b2198cb6ffdf0e945b43ada1bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 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.data + +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule +import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule +import dagger.Module + +@Module( + includes = + [ + KeyguardStatusBarRepositoryModule::class, + StatusBarModeRepositoryModule::class, + StatusBarPhoneDataLayerModule::class + ] +) +object StatusBarDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt index 8136de9b7ac411a0c58fe5556d154dee015bacf2..d1594ef2e4041886a713364967411ac24ebc020d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt @@ -22,6 +22,8 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.user.data.repository.UserSwitcherRepository +import dagger.Binds +import dagger.Module import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -78,3 +80,8 @@ constructor( isEnabled && isKeyguardEnabled } } + +@Module +interface KeyguardStatusBarRepositoryModule { + @Binds fun bindImpl(impl: KeyguardStatusBarRepositoryImpl): KeyguardStatusBarRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt index 2b059944798f20b5e3cd378d96a4bf2ef43295f6..47994d92d22bb06c721eacec90885d827ec35e98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt @@ -30,7 +30,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.statusbar.CommandQueue -import com.android.systemui.statusbar.core.StatusBarInitializer +import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener import com.android.systemui.statusbar.data.model.StatusBarAppearance import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.phone.BoundsPair @@ -38,6 +38,11 @@ import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator import com.android.systemui.statusbar.phone.StatusBarBoundsProvider import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import dagger.multibindings.IntoSet import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -56,7 +61,7 @@ import kotlinx.coroutines.flow.stateIn * Note: These status bar modes are status bar *window* states that are sent to us from * WindowManager, not determined internally. */ -interface StatusBarModeRepository : StatusBarInitializer.OnStatusBarViewInitializedListener { +interface StatusBarModeRepository { /** * True if the status bar window is showing transiently and will disappear soon, and false * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR @@ -112,7 +117,7 @@ constructor( private val commandQueue: CommandQueue, private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator, ongoingCallRepository: OngoingCallRepository, -) : StatusBarModeRepository, CoreStartable { +) : StatusBarModeRepository, CoreStartable, OnStatusBarViewInitializedListener { private val commandQueueCallback = object : CommandQueue.Callbacks { @@ -334,3 +339,17 @@ constructor( val statusBarBounds: BoundsPair, ) } + +@Module +interface StatusBarModeRepositoryModule { + @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepository + + @Binds + @IntoMap + @ClassKey(StatusBarModeRepositoryImpl::class) + fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable + + @Binds + @IntoSet + fun bindViewInitListener(impl: StatusBarModeRepositoryImpl): OnStatusBarViewInitializedListener +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index 62a0d138fd054bd9c17c2c87887537a2a1d6fd43..c2a021d0803f10df14cfd39520f60171bbedc723 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -16,25 +16,32 @@ package com.android.systemui.statusbar.notification.collection.coordinator +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl import com.android.systemui.statusbar.notification.collection.render.NotifStackController import com.android.systemui.statusbar.notification.collection.render.NotifStats +import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.util.traceSection import javax.inject.Inject /** - * A small coordinator which updates the notif stack (the view layer which holds notifications) - * with high-level data after the stack is populated with the final entries. + * A small coordinator which updates the notif stack (the view layer which holds notifications) with + * high-level data after the stack is populated with the final entries. */ @CoordinatorScope -class StackCoordinator @Inject internal constructor( +class StackCoordinator +@Inject +internal constructor( + private val featureFlags: FeatureFlagsClassic, private val groupExpansionManagerImpl: GroupExpansionManagerImpl, - private val notificationIconAreaController: NotificationIconAreaController + private val notificationIconAreaController: NotificationIconAreaController, + private val renderListInteractor: RenderNotificationListInteractor, ) : Coordinator { override fun attach(pipeline: NotifPipeline) { @@ -46,6 +53,9 @@ class StackCoordinator @Inject internal constructor( traceSection("StackCoordinator.onAfterRenderList") { controller.setNotifStats(calculateNotifStats(entries)) notificationIconAreaController.updateNotificationIcons(entries) + if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + renderListInteractor.setRenderedList(entries) + } } private fun calculateNotifStats(entries: List<ListEntry>): NotifStats { @@ -75,4 +85,4 @@ class StackCoordinator @Inject internal constructor( hasClearableSilentNotifs = hasClearableSilentNotifs ) } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt index 5435fb5449cd81948d9ec7d71ff35fcbf05fe2fc..4126c5eecba31909c5963a0c80010d05f8476289 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt @@ -15,8 +15,15 @@ */ package com.android.systemui.statusbar.notification.data +import com.android.systemui.statusbar.notification.data.repository.NotificationStackRepositoryModule import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardStateRepositoryModule import dagger.Module -@Module(includes = [NotificationsKeyguardStateRepositoryModule::class]) +@Module( + includes = + [ + NotificationStackRepositoryModule::class, + NotificationsKeyguardStateRepositoryModule::class, + ] +) interface NotificationDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationStackRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationStackRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..c371f4fee59ec5326d75455fe04d423224a57ef9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationStackRepository.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 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.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.ListEntry +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * Repository of notifications in the notification stack. + * + * This repository serves as the boundary between the + * [com.android.systemui.statusbar.notification.collection.NotifPipeline] and the modern + * notifications presentation codebase. + */ +interface NotificationStackRepository { + /** + * Notifications actively presented to the user in the notification stack. + * + * @see com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener + */ + val renderedEntries: Flow<List<ListEntry>> +} + +/** + * A mutable implementation of [NotificationStackRepository]. Like other "mutable" objects, the + * mutable type should only be exposed where necessary; most consumers should only have access to it + * from behind the immutable [NotificationStackRepository] interface. + */ +@SysUISingleton +class MutableNotificationStackRepository @Inject constructor() : NotificationStackRepository { + override val renderedEntries = MutableStateFlow(emptyList<ListEntry>()) +} + +@Module +interface NotificationStackRepositoryModule { + @Binds fun bindImpl(impl: MutableNotificationStackRepository): NotificationStackRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt index 8f7e269d34b0a8369519b8a0eeb396059e92922c..02a667f9706d29b05edd87cfaed88d59bf702fd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt @@ -18,7 +18,10 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.data.repository.NotificationStackRepository import javax.inject.Inject +import kotlinx.coroutines.flow.Flow /** Interactor for notifications in general. */ @SysUISingleton @@ -26,9 +29,12 @@ class NotificationsInteractor @Inject constructor( private val disableFlagsRepository: DisableFlagsRepository, + stackRepository: NotificationStackRepository, ) { /** Returns true if notification alerts are allowed. */ - fun areNotificationAlertsEnabled(): Boolean { - return disableFlagsRepository.disableFlags.value.areNotificationAlertsEnabled() - } + fun areNotificationAlertsEnabled(): Boolean = + disableFlagsRepository.disableFlags.value.areNotificationAlertsEnabled() + + /** Notifications actively presented to the user in the notification stack. */ + val notifications: Flow<List<ListEntry>> = stackRepository.renderedEntries } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..ea9b4e70c398e7e70fe9108696fd1ff6acb985be --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 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.domain.interactor + +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.data.repository.MutableNotificationStackRepository +import javax.inject.Inject + +/** + * Logic for passing information from the + * [com.android.systemui.statusbar.notification.collection.NotifPipeline] to the presentation + * layers. + */ +class RenderNotificationListInteractor +@Inject +constructor( + private val repository: MutableNotificationStackRepository, +) { + /** + * Sets the current list of rendered notification entries as displayed in the notification + * stack. + * + * @see com.android.systemui.statusbar.notification.data.repository.NotificationStackRepository.renderedEntries + */ + fun setRenderedList(entries: List<ListEntry>) { + repository.renderedEntries.value = entries.toList() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt index eb5c1fa3b0ca8044b3dd333e3ed777de7c1e343d..de011dbd1ab5f2985f7d4b14545ffb52342f3642 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt @@ -16,32 +16,27 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder import android.content.Context -import android.graphics.Color import android.graphics.Rect import android.os.Bundle import android.os.Trace import android.view.LayoutInflater import android.view.View import android.widget.FrameLayout -import androidx.annotation.ColorInt import androidx.annotation.VisibleForTesting import androidx.collection.ArrayMap import com.android.internal.statusbar.StatusBarIcon -import com.android.internal.util.ContrastColorUtil -import com.android.settingslib.Utils +import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoModeController import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.flags.RefactorFlag -import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.NotificationShelfController import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.NotificationUtils import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -55,7 +50,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.wm.shell.bubbles.Bubbles import java.util.Optional @@ -75,44 +69,34 @@ class NotificationIconAreaControllerViewBinderWrapperImpl @Inject constructor( private val context: Context, + private val configuration: ConfigurationState, private val wakeUpCoordinator: NotificationWakeUpCoordinator, private val bypassController: KeyguardBypassController, - private val configurationController: ConfigurationController, private val mediaManager: NotificationMediaManager, notificationListener: NotificationListener, private val dozeParameters: DozeParameters, private val sectionStyleProvider: SectionStyleProvider, private val bubblesOptional: Optional<Bubbles>, demoModeController: DemoModeController, - darkIconDispatcher: DarkIconDispatcher, private val featureFlags: FeatureFlagsClassic, private val statusBarWindowController: StatusBarWindowController, private val screenOffAnimationController: ScreenOffAnimationController, private val shelfIconsViewModel: NotificationIconContainerShelfViewModel, private val statusBarIconsViewModel: NotificationIconContainerStatusBarViewModel, private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, -) : - NotificationIconAreaController, - DarkIconDispatcher.DarkReceiver, - NotificationWakeUpCoordinator.WakeUpListener, - DemoMode { +) : NotificationIconAreaController, NotificationWakeUpCoordinator.WakeUpListener, DemoMode { - private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context) private val updateStatusBarIcons = Runnable { updateStatusBarIcons() } private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR) - private val tintAreas = ArrayList<Rect>() private var iconSize = 0 private var iconHPadding = 0 - private var iconTint = Color.WHITE private var notificationEntries = listOf<ListEntry>() private var notificationIconArea: View? = null private var notificationIcons: NotificationIconContainer? = null private var shelfIcons: NotificationIconContainer? = null private var aodIcons: NotificationIconContainer? = null private var aodBindJob: DisposableHandle? = null - private var aodIconAppearTranslation = 0 - private var aodIconTint = 0 private var showLowPriority = true @VisibleForTesting @@ -129,8 +113,6 @@ constructor( demoModeController.addCallback(this) notificationListener.addNotificationSettingsListener(settingsListener) initializeNotificationAreaViews(context) - reloadAodColor() - darkIconDispatcher.addDarkReceiver(this) } @VisibleForTesting @@ -152,7 +134,7 @@ constructor( NotificationIconContainerViewBinder.bind( aodIcons, aodIconsViewModel, - configurationController, + configuration, dozeParameters, featureFlags, screenOffAnimationController, @@ -167,16 +149,17 @@ constructor( NotificationShelfViewBinderWrapperControllerImpl.unsupported override fun setShelfIcons(icons: NotificationIconContainer) { - if (shelfRefactor.isUnexpectedlyInLegacyMode()) return - NotificationIconContainerViewBinder.bind( - icons, - shelfIconsViewModel, - configurationController, - dozeParameters, - featureFlags, - screenOffAnimationController, - ) - shelfIcons = icons + if (shelfRefactor.isUnexpectedlyInLegacyMode()) { + NotificationIconContainerViewBinder.bind( + icons, + shelfIconsViewModel, + configuration, + dozeParameters, + featureFlags, + screenOffAnimationController, + ) + shelfIcons = icons + } } override fun onDensityOrFontScaleChanged(context: Context) { @@ -188,22 +171,6 @@ constructor( return notificationIconArea } - /** - * See [com.android.systemui.statusbar.policy.DarkIconDispatcher.setIconsDarkArea]. Sets the - * color that should be used to tint any icons in the notification area. - * - * @param tintAreas the areas in which to tint the icons, specified in screen coordinates - * @param darkIntensity - */ - override fun onDarkChanged(tintAreas: ArrayList<Rect>, darkIntensity: Float, iconTint: Int) { - this.tintAreas.clear() - this.tintAreas.addAll(tintAreas) - if (DarkIconDispatcher.isInAreas(tintAreas, notificationIconArea)) { - this.iconTint = iconTint - } - applyNotificationIconsTint() - } - /** Updates the notifications with the given list of notifications to display. */ override fun updateNotificationIcons(entries: List<ListEntry>) { notificationEntries = entries @@ -249,10 +216,7 @@ constructor( override fun setAnimationsEnabled(enabled: Boolean) = unsupported - override fun onThemeChanged() { - reloadAodColor() - updateAodIconColors() - } + override fun onThemeChanged() = unsupported override fun getHeight(): Int { return if (aodIcons == null) 0 else aodIcons!!.height @@ -260,7 +224,6 @@ constructor( override fun onFullyHiddenChanged(isFullyHidden: Boolean) { updateAodNotificationIcons() - updateAodIconColors() } override fun demoCommands(): List<String> { @@ -296,7 +259,7 @@ constructor( NotificationIconContainerViewBinder.bind( notificationIcons!!, statusBarIconsViewModel, - configurationController, + configuration, dozeParameters, featureFlags, screenOffAnimationController, @@ -335,7 +298,6 @@ constructor( val res = context.resources iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size_sp) iconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin) - aodIconAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation) } private fun shouldShowNotificationIcon( @@ -383,7 +345,6 @@ constructor( updateStatusBarIcons() updateShelfIcons() updateAodNotificationIcons() - applyNotificationIconsTint() Trace.endSection() } @@ -526,55 +487,7 @@ constructor( hostLayout.setReplacingIcons(null) } - /** Applies [.mIconTint] to the notification icons. */ - private fun applyNotificationIconsTint() { - for (i in 0 until notificationIcons!!.childCount) { - val iv = notificationIcons!!.getChildAt(i) as StatusBarIconView - if (iv.width != 0) { - updateTintForIcon(iv, iconTint) - } else { - iv.executeOnLayout { updateTintForIcon(iv, iconTint) } - } - } - updateAodIconColors() - } - - private fun updateTintForIcon(v: StatusBarIconView, tint: Int) { - val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L) - var color = StatusBarIconView.NO_COLOR - val colorize = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil) - if (colorize) { - color = DarkIconDispatcher.getTint(tintAreas, v, tint) - } - v.staticDrawableColor = color - v.setDecorColor(tint) - } - - private fun reloadAodColor() { - aodIconTint = - Utils.getColorAttrDefaultColor( - context, - R.attr.wallpaperTextColor, - DEFAULT_AOD_ICON_COLOR - ) - } - - private fun updateAodIconColors() { - if (aodIcons != null) { - for (i in 0 until aodIcons!!.childCount) { - val iv = aodIcons!!.getChildAt(i) as StatusBarIconView - if (iv.width != 0) { - updateTintForIcon(iv, aodIconTint) - } else { - iv.executeOnLayout { updateTintForIcon(iv, aodIconTint) } - } - } - } - } - companion object { - @ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1 - val unsupported: Nothing get() = error( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index 0d2f00aa362711a80beeca0e449ca9a1a5611b0a..079004c2a60a3f95234064e45c667fc06d8a2a82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -15,27 +15,30 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewbinder -import android.content.res.Resources +import android.graphics.Rect import android.view.View -import androidx.annotation.DimenRes import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.internal.util.ContrastColorUtil +import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.statusbar.CrossFadeHelper +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.NotificationUtils import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged -import com.android.systemui.util.kotlin.stateFlow -import kotlinx.coroutines.CoroutineScope +import com.android.systemui.util.children import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch /** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */ @@ -43,11 +46,12 @@ object NotificationIconContainerViewBinder { fun bind( view: NotificationIconContainer, viewModel: NotificationIconContainerViewModel, - configurationController: ConfigurationController, + configuration: ConfigurationState, dozeParameters: DozeParameters, featureFlags: FeatureFlagsClassic, screenOffAnimationController: ScreenOffAnimationController, ): DisposableHandle { + val contrastColorUtil = ContrastColorUtil.getInstance(view.context) return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.animationsEnabled.collect(view::setAnimationsEnabled) } @@ -59,15 +63,13 @@ object NotificationIconContainerViewBinder { } } } - // TODO(278765923): this should live where AOD is bound, not inside of the NIC + // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC // view-binder launch { val iconAppearTranslation = - view.resources.getConfigAwareDimensionPixelSize( - this, - configurationController, - R.dimen.shelf_appear_translation, - ) + configuration + .getDimensionPixelSize(R.dimen.shelf_appear_translation) + .stateIn(this) bindVisibility( viewModel, view, @@ -78,9 +80,40 @@ object NotificationIconContainerViewBinder { viewModel.completeVisibilityAnimation() } } + launch { + viewModel.iconColors + .mapNotNull { lookup -> lookup.iconColors(view.viewBounds) } + .collect { iconLookup -> applyTint(view, iconLookup, contrastColorUtil) } + } + } + } + } + + // TODO(b/305739416): Once SBIV has its own Recommended Architecture stack, this can be moved + // there and cleaned up. + private fun applyTint( + view: NotificationIconContainer, + iconColors: IconColors, + contrastColorUtil: ContrastColorUtil, + ) { + view.children.filterIsInstance<StatusBarIconView>().forEach { iv -> + if (iv.width != 0) { + updateTintForIcon(iv, iconColors, contrastColorUtil) } } } + + private fun updateTintForIcon( + v: StatusBarIconView, + iconColors: IconColors, + contrastColorUtil: ContrastColorUtil, + ) { + val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L) + val isColorized = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil) + v.staticDrawableColor = iconColors.staticDrawableColor(v.viewBounds, isColorized) + v.setDecorColor(iconColors.tint) + } + private suspend fun bindVisibility( viewModel: NotificationIconContainerViewModel, view: NotificationIconContainer, @@ -173,14 +206,16 @@ object NotificationIconContainerViewBinder { } private const val AOD_ICONS_APPEAR_DURATION: Long = 200 -} -fun Resources.getConfigAwareDimensionPixelSize( - scope: CoroutineScope, - configurationController: ConfigurationController, - @DimenRes id: Int, -): StateFlow<Int> = - scope.stateFlow( - changedSignals = configurationController.onDensityOrFontScaleChanged, - getValue = { getDimensionPixelSize(id) } - ) + private val View.viewBounds: Rect + get() { + val tmpArray = intArrayOf(0, 0) + getLocationOnScreen(tmpArray) + return Rect( + /* left = */ tmpArray[0], + /* top = */ tmpArray[1], + /* right = */ left + width, + /* bottom = */ top + height, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt index 3289a3ce5574fad9cd30928883f91c1b062a3db7..e9de4bd654d3fb8c2449dcfbadb51f9bec95aa60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt @@ -15,6 +15,10 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import android.graphics.Color +import android.graphics.Rect +import androidx.annotation.ColorInt +import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.flags.FeatureFlagsClassic @@ -23,8 +27,11 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.util.kotlin.pairwise @@ -44,6 +51,7 @@ import kotlinx.coroutines.flow.map class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor( + configuration: ConfigurationState, private val deviceEntryInteractor: DeviceEntryInteractor, private val dozeParameters: DozeParameters, private val featureFlags: FeatureFlagsClassic, @@ -57,6 +65,11 @@ constructor( private val onDozeAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) private val onVisAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1) + override val iconColors: Flow<ColorLookup> = + configuration.getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR).map { tint -> + ColorLookup { IconColorsImpl(tint) } + } + override val animationsEnabled: Flow<Boolean> = combine( shadeInteractor.isShadeTouchable, @@ -157,4 +170,12 @@ constructor( } .toAnimatedValueFlow(completionEvents = onVisAnimationComplete) } + + private class IconColorsImpl(override val tint: Int) : IconColors { + override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int = tint + } + + companion object { + @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt index c44a2b60142c9d67728339858e5b166ca638cd15..f305155e9b3d823c8755b48d68f24d56154a31c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -29,4 +30,5 @@ class NotificationIconContainerShelfViewModel @Inject constructor() : override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() override fun completeDozeAnimation() {} override fun completeVisibilityAnimation() {} + override val iconColors: Flow<ColorLookup> = emptyFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt index 035687a4a91b05239e62903f99f6d9c9d23feef8..40cc294016e558e7b82898e05a7c7d462b2696f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt @@ -15,8 +15,14 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import android.graphics.Rect import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.shade.domain.interactor.ShadeInteractor +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors +import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor import com.android.systemui.util.ui.AnimatedValue import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -27,7 +33,9 @@ import kotlinx.coroutines.flow.emptyFlow class NotificationIconContainerStatusBarViewModel @Inject constructor( + darkIconInteractor: DarkIconInteractor, keyguardInteractor: KeyguardInteractor, + notificationsInteractor: NotificationsInteractor, shadeInteractor: ShadeInteractor, ) : NotificationIconContainerViewModel { override val animationsEnabled: Flow<Boolean> = @@ -37,9 +45,36 @@ constructor( ) { panelTouchesEnabled, isKeyguardShowing -> panelTouchesEnabled && !isKeyguardShowing } - + override val iconColors: Flow<ColorLookup> = + combine( + darkIconInteractor.tintAreas, + darkIconInteractor.tintColor, + // Included so that tints are re-applied after entries are changed. + notificationsInteractor.notifications, + ) { areas, tint, _ -> + ColorLookup { viewBounds: Rect -> + if (DarkIconDispatcher.isInAreas(areas, viewBounds)) { + IconColorsImpl(tint, areas) + } else { + null + } + } + } override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow() override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow() override fun completeDozeAnimation() {} override fun completeVisibilityAnimation() {} + + private class IconColorsImpl( + override val tint: Int, + private val areas: Collection<Rect>, + ) : IconColors { + override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int { + return if (isColorized && DarkIconDispatcher.isInAreas(areas, viewBounds)) { + tint + } else { + DarkIconDispatcher.DEFAULT_ICON_TINT + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt index 65eb22075ec7208a0c3bd1f3879ec80f69a03afd..c98811b0e28552fdb2ee37e71319824893514578 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import android.graphics.Rect import com.android.systemui.util.ui.AnimatedValue import kotlinx.coroutines.flow.Flow @@ -23,6 +24,7 @@ import kotlinx.coroutines.flow.Flow * AOD. */ interface NotificationIconContainerViewModel { + /** Are changes to the icon container animated? */ val animationsEnabled: Flow<Boolean> @@ -32,15 +34,39 @@ interface NotificationIconContainerViewModel { /** Is the icon container visible? */ val isVisible: Flow<AnimatedValue<Boolean>> + /** The colors with which to display the notification icons. */ + val iconColors: Flow<ColorLookup> + /** * Signal completion of the [isDozing] animation; if [isDozing]'s [AnimatedValue.isAnimating] - * property was `true`, calling this method will update it to `false. + * property was `true`, calling this method will update it to `false`. */ fun completeDozeAnimation() /** * Signal completion of the [isVisible] animation; if [isVisible]'s [AnimatedValue.isAnimating] - * property was `true`, calling this method will update it to `false. + * property was `true`, calling this method will update it to `false`. */ fun completeVisibilityAnimation() + + /** + * Lookup the colors to use for the notification icons based on the bounds of the icon + * container. A result of `null` indicates that no color changes should be applied. + */ + fun interface ColorLookup { + fun iconColors(viewBounds: Rect): IconColors? + } + + /** Colors to apply to notification icons. */ + interface IconColors { + + /** A tint to apply to the icons. */ + val tint: Int + + /** + * Returns the color to be applied to an icon, based on that icon's view bounds and whether + * or not the notification icon is colorized. + */ + fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 7cd32f9774220d186284def79e838794cb4caf6e..cb85966ca581d789db5e75b47f632a1093ccebb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -3124,7 +3124,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mAmbientIndicationContainer instanceof AutoReinflateContainer) { ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout(); } - mNotificationIconAreaController.onThemeChanged(); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) { + mNotificationIconAreaController.onThemeChanged(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/CommonRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/StatusBarPhoneDataLayerModule.kt similarity index 73% rename from packages/SystemUI/src/com/android/systemui/common/ui/data/repository/CommonRepositoryModule.kt rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/data/StatusBarPhoneDataLayerModule.kt index 9b0c3fa7e92bb37a2bfe996237b3fa70fbf9c7bc..9645c69ab0892188fbec12a2cd1bbaef0597827f 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/CommonRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/StatusBarPhoneDataLayerModule.kt @@ -13,13 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.systemui.statusbar.phone.data -package com.android.systemui.common.ui.data.repository - -import dagger.Binds +import com.android.systemui.statusbar.phone.data.repository.DarkIconRepositoryModule import dagger.Module -@Module -interface CommonRepositoryModule { - @Binds fun bindRepository(impl: ConfigurationRepositoryImpl): ConfigurationRepository -} +@Module(includes = [DarkIconRepositoryModule::class]) object StatusBarPhoneDataLayerModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..ba377497bce46d35eae9599c7296f33526e74130 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.phone.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher +import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +/** Dark-mode state for tinting icons. */ +interface DarkIconRepository { + val darkState: StateFlow<DarkChange> +} + +@SysUISingleton +class DarkIconRepositoryImpl +@Inject +constructor( + darkIconDispatcher: SysuiDarkIconDispatcher, +) : DarkIconRepository { + override val darkState: StateFlow<DarkChange> = darkIconDispatcher.darkChangeFlow() +} + +@Module +interface DarkIconRepositoryModule { + @Binds fun bindImpl(impl: DarkIconRepositoryImpl): DarkIconRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..246645ee0eadefc4395beb14a61623ccbfcee139 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.phone.domain.interactor + +import android.graphics.Rect +import com.android.systemui.statusbar.phone.data.repository.DarkIconRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** States pertaining to calculating colors for icons in dark mode. */ +class DarkIconInteractor @Inject constructor(repository: DarkIconRepository) { + /** @see com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange.areas */ + val tintAreas: Flow<Collection<Rect>> = repository.darkState.map { it.areas } + /** + * @see com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange.darkIntensity + */ + val darkIntensity: Flow<Float> = repository.darkState.map { it.darkIntensity } + /** @see com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange.tint */ + val tintColor: Flow<Int> = repository.darkState.map { it.tint } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt index 21acfb41f10c6fc8c2a2d1ba70c81358c8bbef07..25d67aff50a9cb5c5d4b0d8dbc2c132bd1a2c2d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt @@ -13,7 +13,7 @@ */ package com.android.systemui.statusbar.policy -import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -23,14 +23,30 @@ import kotlinx.coroutines.flow.Flow * @see ConfigurationController.ConfigurationListener.onDensityOrFontScaleChanged */ val ConfigurationController.onDensityOrFontScaleChanged: Flow<Unit> - get() = - ConflatedCallbackFlow.conflatedCallbackFlow { - val listener = - object : ConfigurationController.ConfigurationListener { - override fun onDensityOrFontScaleChanged() { - trySend(Unit) - } + get() = conflatedCallbackFlow { + val listener = + object : ConfigurationController.ConfigurationListener { + override fun onDensityOrFontScaleChanged() { + trySend(Unit) } - addCallback(listener) - awaitClose { removeCallback(listener) } - } + } + addCallback(listener) + awaitClose { removeCallback(listener) } + } + +/** + * A [Flow] that emits whenever the theme has changed. + * + * @see ConfigurationController.ConfigurationListener.onThemeChanged + */ +val ConfigurationController.onThemeChanged: Flow<Unit> + get() = conflatedCallbackFlow { + val listener = + object : ConfigurationController.ConfigurationListener { + override fun onThemeChanged() { + trySend(Unit) + } + } + addCallback(listener) + awaitClose { removeCallback(listener) } + } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt index b3834f58be2f865fff83d72a576d3b9998f4d5c1..31b90bae27eb116b563acb8d4c407038f2973f95 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt @@ -373,4 +373,11 @@ inline fun <T1, T2, T3, T4, T5, T6, T7, T8, T9, R> combine( args[6] as T9, ) } -} \ No newline at end of file +} + +/** + * Returns a [Flow] that immediately emits [Unit] when started, then emits from the given upstream + * [Flow] as normal. + */ +@Suppress("NOTHING_TO_INLINE") +inline fun Flow<Unit>.emitOnStart(): Flow<Unit> = onStart { emit(Unit) } diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt index 0b3c3b483d1c50fc1e62fbd1302b66c83a5242fa..ed0541ada77b79f2e8bbd192bceaea7ab8217f03 100644 --- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt +++ b/packages/SystemUI/tests/src/com/android/SysUITestModule.kt @@ -17,11 +17,17 @@ package com.android import android.content.Context import android.content.res.Resources +import android.testing.TestableContext +import android.testing.TestableResources import com.android.systemui.FakeSystemUiModule import com.android.systemui.SysuiTestCase +import com.android.systemui.SysuiTestableContext import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.broadcast.FakeBroadcastDispatcher import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.notification.data.repository.NotificationStackRepositoryModule +import dagger.Binds import dagger.Module import dagger.Provides @@ -33,15 +39,29 @@ import dagger.Provides FakeSystemUiModule::class, ] ) -class SysUITestModule { - @Provides fun provideContext(test: SysuiTestCase): Context = test.context +interface SysUITestModule { - @Provides @Application fun provideAppContext(test: SysuiTestCase): Context = test.context + @Binds fun bindTestableContext(sysuiTestableContext: SysuiTestableContext): TestableContext + @Binds fun bindContext(testableContext: TestableContext): Context + @Binds @Application fun bindAppContext(context: Context): Context + @Binds @Application fun bindAppResources(resources: Resources): Resources + @Binds @Main fun bindMainResources(resources: Resources): Resources + @Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher - @Provides @Main - fun provideResources(@Application context: Context): Resources = context.resources + companion object { + @Provides + fun provideSysuiTestableContext(test: SysuiTestCase): SysuiTestableContext = test.context - @Provides - fun provideBroadcastDispatcher(test: SysuiTestCase): BroadcastDispatcher = - test.fakeBroadcastDispatcher + @Provides + fun provideTestableResources(context: TestableContext): TestableResources = + context.getOrCreateTestableResources() + + @Provides + fun provideResources(testableResources: TestableResources): Resources = + testableResources.resources + + @Provides + fun provideFakeBroadcastDispatcher(test: SysuiTestCase): FakeBroadcastDispatcher = + test.fakeBroadcastDispatcher + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt index 655bd7243836d584f2ca61f12bc69e8fa725f804..a736182b63293ba72885fad5b3869f8f15434101 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt @@ -19,6 +19,8 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlagsClassic +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder @@ -27,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfte import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl import com.android.systemui.statusbar.notification.collection.render.NotifStackController import com.android.systemui.statusbar.notification.collection.render.NotifStats +import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.phone.NotificationIconAreaController @@ -37,8 +40,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations.initMocks import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations.initMocks @SmallTest @RunWith(AndroidTestingRunner::class) @@ -52,13 +55,23 @@ class StackCoordinatorTest : SysuiTestCase() { @Mock private lateinit var pipeline: NotifPipeline @Mock private lateinit var groupExpansionManagerImpl: GroupExpansionManagerImpl @Mock private lateinit var notificationIconAreaController: NotificationIconAreaController + @Mock private lateinit var renderListInteractor: RenderNotificationListInteractor @Mock private lateinit var stackController: NotifStackController @Mock private lateinit var section: NotifSection + val featureFlags = + FakeFeatureFlagsClassic().apply { setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR) } + @Before fun setUp() { initMocks(this) - coordinator = StackCoordinator(groupExpansionManagerImpl, notificationIconAreaController) + coordinator = + StackCoordinator( + featureFlags, + groupExpansionManagerImpl, + notificationIconAreaController, + renderListInteractor, + ) coordinator.attach(pipeline) afterRenderListListener = withArgCaptor { verify(pipeline).addOnAfterRenderListListener(capture()) @@ -68,10 +81,18 @@ class StackCoordinatorTest : SysuiTestCase() { @Test fun testUpdateNotificationIcons() { + featureFlags.set(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR, false) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry))) } + @Test + fun testSetRenderedListOnInteractor() { + featureFlags.set(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR, true) + afterRenderListListener.onAfterRenderList(listOf(entry), stackController) + verify(renderListInteractor).setRenderedList(eq(listOf(entry))) + } + @Test fun testSetNotificationStats_clearableAlerting() { whenever(section.bucket).thenReturn(BUCKET_ALERTING) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt index fe49016a5d45a5a0f2a45194d8746564287f002b..6bf9e350cd50342b5859e58e0e62e3352ceb705d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt @@ -16,84 +16,53 @@ package com.android.systemui.statusbar.notification.domain.interactor import android.app.StatusBarManager import androidx.test.filters.SmallTest +import com.android.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.dump.DumpManager -import com.android.systemui.log.LogBufferFactory -import com.android.systemui.statusbar.CommandQueue -import com.android.systemui.statusbar.disableflags.DisableFlagsLogger -import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository -import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepositoryImpl -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.mock +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import org.junit.Before +import dagger.BindsInstance +import dagger.Component import org.junit.Test -import org.mockito.Mockito.verify @SmallTest -@OptIn(ExperimentalCoroutinesApi::class) class NotificationsInteractorTest : SysuiTestCase() { - private lateinit var underTest: NotificationsInteractor + @Component(modules = [SysUITestModule::class]) + @SysUISingleton + interface TestComponent { + val underTest: NotificationsInteractor + val disableFlags: FakeDisableFlagsRepository - private val testScope = TestScope(UnconfinedTestDispatcher()) - private val commandQueue: CommandQueue = mock() - private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10) - private val disableFlagsLogger = DisableFlagsLogger() - private lateinit var disableFlagsRepository: DisableFlagsRepository - - @Before - fun setUp() { - disableFlagsRepository = - DisableFlagsRepositoryImpl( - commandQueue, - DISPLAY_ID, - testScope.backgroundScope, - mock(), - logBuffer, - disableFlagsLogger, - ) - underTest = NotificationsInteractor(disableFlagsRepository) + @Component.Factory + interface Factory { + fun create(@BindsInstance test: SysuiTestCase): TestComponent + } } - @Test - fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() { - val callback = getCommandQueueCallback() - - callback.disable( - DISPLAY_ID, - StatusBarManager.DISABLE_NONE, - StatusBarManager.DISABLE2_NONE, - /* animate= */ false - ) - - assertThat(underTest.areNotificationAlertsEnabled()).isTrue() - } + private val testComponent: TestComponent = + DaggerNotificationsInteractorTest_TestComponent.factory().create(test = this) @Test - fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() { - val callback = getCommandQueueCallback() - - callback.disable( - DISPLAY_ID, - StatusBarManager.DISABLE_NOTIFICATION_ALERTS, - StatusBarManager.DISABLE2_NONE, - /* animate= */ false - ) + fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() = + with(testComponent) { + disableFlags.disableFlags.value = + DisableFlagsModel( + StatusBarManager.DISABLE_NONE, + StatusBarManager.DISABLE2_NONE, + ) + assertThat(underTest.areNotificationAlertsEnabled()).isTrue() + } - assertThat(underTest.areNotificationAlertsEnabled()).isFalse() - } - - private fun getCommandQueueCallback(): CommandQueue.Callbacks { - val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>() - verify(commandQueue).addCallback(callbackCaptor.capture()) - return callbackCaptor.value - } - - private companion object { - const val DISPLAY_ID = 1 - } + @Test + fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() = + with(testComponent) { + disableFlags.disableFlags.value = + DisableFlagsModel( + StatusBarManager.DISABLE_NOTIFICATION_ALERTS, + StatusBarManager.DISABLE2_NONE, + ) + assertThat(underTest.areNotificationAlertsEnabled()).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt index e1e7f92265f0fcb0d6d0d82830fe97952ab803c8..e21ebeb77f96f1d4a58e7609b1958800fa1300d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.icon.ui.viewmodel +import android.graphics.Rect import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.SysUITestModule @@ -34,10 +35,13 @@ import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher +import com.android.systemui.statusbar.phone.data.repository.FakeDarkIconRepository import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.user.domain.UserDomainLayerModule import com.android.systemui.util.mockito.whenever @@ -61,18 +65,6 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { @Mock lateinit var dozeParams: DozeParameters private lateinit var testComponent: TestComponent - private val underTest: NotificationIconContainerStatusBarViewModel - get() = testComponent.underTest - private val deviceProvisioningRepository - get() = testComponent.deviceProvisioningRepository - private val keyguardTransitionRepository - get() = testComponent.keyguardTransitionRepository - private val keyguardRepository - get() = testComponent.keyguardRepository - private val powerRepository - get() = testComponent.powerRepository - private val scope - get() = testComponent.scope @Before fun setup() { @@ -82,7 +74,6 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { DaggerNotificationIconContainerStatusBarViewModelTest_TestComponent.factory() .create( test = this, - // Configurable bindings featureFlags = FakeFeatureFlagsClassicModule { set(Flags.FACE_AUTH_REFACTOR, value = false) @@ -93,155 +84,247 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { dozeParameters = dozeParams, ), ) - - keyguardRepository.setKeyguardShowing(false) - deviceProvisioningRepository.setFactoryResetProtectionActive(false) - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.OTHER, - lastSleepReason = WakeSleepReason.OTHER, - ) + .apply { + keyguardRepository.setKeyguardShowing(false) + deviceProvisioningRepository.setFactoryResetProtectionActive(false) + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + } } @Test fun animationsEnabled_isFalse_whenFrpIsActive() = - scope.runTest { - deviceProvisioningRepository.setFactoryResetProtectionActive(true) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, + with(testComponent) { + scope.runTest { + deviceProvisioningRepository.setFactoryResetProtectionActive(true) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) ) - ) - val animationsEnabled by collectLastValue(underTest.animationsEnabled) - runCurrent() - assertThat(animationsEnabled).isFalse() + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } } @Test fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() = - scope.runTest { - powerRepository.updateWakefulness( - rawState = WakefulnessState.ASLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, + with(testComponent) { + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) ) - ) - keyguardRepository.setDozeTransitionModel( - DozeTransitionModel( - to = DozeStateModel.DOZE_AOD, + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_AOD, + ) ) - ) - val animationsEnabled by collectLastValue(underTest.animationsEnabled) - runCurrent() - assertThat(animationsEnabled).isFalse() + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } } @Test fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() = - scope.runTest { - powerRepository.updateWakefulness( - rawState = WakefulnessState.ASLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, + with(testComponent) { + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) ) - ) - keyguardRepository.setDozeTransitionModel( - DozeTransitionModel( - to = DozeStateModel.DOZE_PULSING, + keyguardRepository.setDozeTransitionModel( + DozeTransitionModel( + to = DozeStateModel.DOZE_PULSING, + ) ) - ) - val animationsEnabled by collectLastValue(underTest.animationsEnabled) - runCurrent() - assertThat(animationsEnabled).isTrue() + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } } @Test fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() = - scope.runTest { - powerRepository.updateWakefulness( - rawState = WakefulnessState.STARTING_TO_SLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - transitionState = TransitionState.STARTED, + with(testComponent) { + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, ) - ) - whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) - val animationsEnabled by collectLastValue(underTest.animationsEnabled) - runCurrent() - assertThat(animationsEnabled).isFalse() + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) + ) + whenever(dozeParams.shouldControlScreenOff()).thenReturn(false) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isFalse() + } } @Test fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() = - scope.runTest { - powerRepository.updateWakefulness( - rawState = WakefulnessState.STARTING_TO_SLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - transitionState = TransitionState.STARTED, + with(testComponent) { + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + transitionState = TransitionState.STARTED, + ) ) - ) - whenever(dozeParams.shouldControlScreenOff()).thenReturn(true) - val animationsEnabled by collectLastValue(underTest.animationsEnabled) - runCurrent() - assertThat(animationsEnabled).isTrue() + whenever(dozeParams.shouldControlScreenOff()).thenReturn(true) + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } } @Test fun animationsEnabled_isTrue_whenNotAsleep() = - scope.runTest { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, + with(testComponent) { + scope.runTest { + powerRepository.updateWakefulness( + rawState = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.OTHER, + ) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) ) - ) - val animationsEnabled by collectLastValue(underTest.animationsEnabled) - runCurrent() - assertThat(animationsEnabled).isTrue() + val animationsEnabled by collectLastValue(underTest.animationsEnabled) + runCurrent() + assertThat(animationsEnabled).isTrue() + } } @Test fun animationsEnabled_isTrue_whenKeyguardIsNotShowing() = - scope.runTest { - val animationsEnabled by collectLastValue(underTest.animationsEnabled) + with(testComponent) { + scope.runTest { + val animationsEnabled by collectLastValue(underTest.animationsEnabled) - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - transitionState = TransitionState.STARTED, + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + transitionState = TransitionState.STARTED, + ) ) - ) - keyguardRepository.setKeyguardShowing(true) - runCurrent() + keyguardRepository.setKeyguardShowing(true) + runCurrent() + + assertThat(animationsEnabled).isFalse() + + keyguardRepository.setKeyguardShowing(false) + runCurrent() + + assertThat(animationsEnabled).isTrue() + } + } + + @Test + fun iconColors_testsDarkBounds() = + with(testComponent) { + scope.runTest { + darkIconRepository.darkState.value = + SysuiDarkIconDispatcher.DarkChange( + emptyList(), + 0f, + 0xAABBCC, + ) + val iconColorsLookup by collectLastValue(underTest.iconColors) + assertThat(iconColorsLookup).isNotNull() + + val iconColors = iconColorsLookup?.iconColors(Rect()) + assertThat(iconColors).isNotNull() + iconColors!! + + assertThat(iconColors.tint).isEqualTo(0xAABBCC) - assertThat(animationsEnabled).isFalse() + val staticDrawableColor = iconColors.staticDrawableColor(Rect(), isColorized = true) - keyguardRepository.setKeyguardShowing(false) - runCurrent() + assertThat(staticDrawableColor).isEqualTo(0xAABBCC) + } + } - assertThat(animationsEnabled).isTrue() + @Test + fun iconColors_staticDrawableColor_nonColorized() = + with(testComponent) { + scope.runTest { + darkIconRepository.darkState.value = + SysuiDarkIconDispatcher.DarkChange( + emptyList(), + 0f, + 0xAABBCC, + ) + val iconColorsLookup by collectLastValue(underTest.iconColors) + val iconColors = iconColorsLookup?.iconColors(Rect()) + val staticDrawableColor = + iconColors?.staticDrawableColor(Rect(), isColorized = false) + assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT) + } + } + + @Test + fun iconColors_staticDrawableColor_isColorized_notInDarkTintArea() = + with(testComponent) { + scope.runTest { + darkIconRepository.darkState.value = + SysuiDarkIconDispatcher.DarkChange( + listOf(Rect(0, 0, 5, 5)), + 0f, + 0xAABBCC, + ) + val iconColorsLookup by collectLastValue(underTest.iconColors) + val iconColors = iconColorsLookup?.iconColors(Rect(1, 1, 4, 4)) + val staticDrawableColor = + iconColors?.staticDrawableColor(Rect(6, 6, 7, 7), isColorized = true) + assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT) + } + } + + @Test + fun iconColors_notInDarkTintArea() = + with(testComponent) { + scope.runTest { + darkIconRepository.darkState.value = + SysuiDarkIconDispatcher.DarkChange( + listOf(Rect(0, 0, 5, 5)), + 0f, + 0xAABBCC, + ) + val iconColorsLookup by collectLastValue(underTest.iconColors) + val iconColors = iconColorsLookup?.iconColors(Rect(6, 6, 7, 7)) + assertThat(iconColors).isNull() + } } @SysUISingleton @@ -249,7 +332,6 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { modules = [ SysUITestModule::class, - // Real impls BiometricsDomainLayerModule::class, UserDomainLayerModule::class, ] @@ -258,6 +340,7 @@ class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() { val underTest: NotificationIconContainerStatusBarViewModel + val darkIconRepository: FakeDarkIconRepository val deviceProvisioningRepository: FakeDeviceProvisioningRepository val keyguardTransitionRepository: FakeKeyguardTransitionRepository val keyguardRepository: FakeKeyguardRepository diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt index aaf8d0761dce89358edb7a0c404b905cf862eeca..6e3a732aa8ec1623d498c7b0add88a5f8c4015e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.util.ui import android.testing.AndroidTestingRunner @@ -20,6 +22,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.launchIn diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt index 5dcc7423ecc62480ec06195d134053484961b303..8353cf78d983d1ee72cff1477d9c6696a7bd08ab 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt @@ -34,7 +34,10 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor private val _scaleForResolution = MutableStateFlow(1f) override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow() - suspend fun onAnyConfigurationChange() { + private val pixelSizes = mutableMapOf<Int, MutableStateFlow<Int>>() + private val colors = mutableMapOf<Int, MutableStateFlow<Int>>() + + fun onAnyConfigurationChange() { _onAnyConfigurationChange.tryEmit(Unit) } @@ -42,12 +45,12 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor _scaleForResolution.value = scale } - override fun getResolutionScale(): Float { - return _scaleForResolution.value - } + override fun getResolutionScale(): Float = _scaleForResolution.value + + override fun getDimensionPixelSize(id: Int): Int = pixelSizes[id]?.value ?: 0 - override fun getDimensionPixelSize(id: Int): Int { - return 0 + fun setDimensionPixelSize(id: Int, pixelSize: Int) { + pixelSizes.getOrPut(id) { MutableStateFlow(pixelSize) }.value = pixelSize } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt index e59f642071fb6376aaae3b4ba4b75054dbdf41c7..8f18e13314272e73e1d25b6ae0fdb7a2e7e1c868 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt @@ -15,8 +15,10 @@ */ package com.android.systemui.statusbar.data +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepositoryModule import com.android.systemui.statusbar.disableflags.data.FakeStatusBarDisableFlagsDataLayerModule import com.android.systemui.statusbar.notification.data.FakeStatusBarNotificationsDataLayerModule +import com.android.systemui.statusbar.phone.data.FakeStatusBarPhoneDataLayerModule import com.android.systemui.statusbar.pipeline.data.FakeStatusBarPipelineDataLayerModule import com.android.systemui.statusbar.policy.data.FakeStatusBarPolicyDataLayerModule import dagger.Module @@ -25,7 +27,9 @@ import dagger.Module includes = [ FakeStatusBarDisableFlagsDataLayerModule::class, + FakeStatusBarModeRepositoryModule::class, FakeStatusBarNotificationsDataLayerModule::class, + FakeStatusBarPhoneDataLayerModule::class, FakeStatusBarPipelineDataLayerModule::class, FakeStatusBarPolicyDataLayerModule::class, ] diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt similarity index 81% rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt index 61ba4649f559b28d0da413609fa43e5e4df58027..f25d282208f006ff1153ed98f6c3c28c634857b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt @@ -18,17 +18,17 @@ package com.android.systemui.statusbar.data.repository import com.android.systemui.statusbar.data.model.StatusBarAppearance import com.android.systemui.statusbar.data.model.StatusBarMode -import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import dagger.Binds +import dagger.Module +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow -class FakeStatusBarModeRepository : StatusBarModeRepository { +class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepository { override val isTransientShown = MutableStateFlow(false) override val isInFullscreenMode = MutableStateFlow(false) override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null) override val statusBarMode = MutableStateFlow(StatusBarMode.TRANSPARENT) - override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {} - override fun showTransient() { isTransientShown.value = true } @@ -36,3 +36,8 @@ class FakeStatusBarModeRepository : StatusBarModeRepository { isTransientShown.value = false } } + +@Module +interface FakeStatusBarModeRepositoryModule { + @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepository +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt index 788e3aa9c41a32e9a5fac897f43169838c796015..465b93a5b1e91e46ebbce32f0759498c8f8b76b5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt @@ -16,7 +16,14 @@ package com.android.systemui.statusbar.notification.data import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardStateRepositoryModule +import com.android.systemui.statusbar.notification.data.repository.NotificationStackRepositoryModule import dagger.Module -@Module(includes = [FakeNotificationsKeyguardStateRepositoryModule::class]) +@Module( + includes = + [ + FakeNotificationsKeyguardStateRepositoryModule::class, + NotificationStackRepositoryModule::class, + ] +) object FakeStatusBarNotificationsDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/FakeStatusBarPhoneDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/FakeStatusBarPhoneDataLayerModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..d2c3b7a090bdab0bc273dd957556bbfffcb03e48 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/FakeStatusBarPhoneDataLayerModule.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.statusbar.phone.data + +import com.android.systemui.statusbar.phone.data.repository.FakeDarkIconRepositoryModule +import dagger.Module + +@Module(includes = [FakeDarkIconRepositoryModule::class]) object FakeStatusBarPhoneDataLayerModule diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..50d3f0a106f21616827d0e0522f34ed57d2d4937 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/FakeDarkIconRepository.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.statusbar.phone.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange +import dagger.Binds +import dagger.Module +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +@SysUISingleton +class FakeDarkIconRepository @Inject constructor() : DarkIconRepository { + override val darkState = MutableStateFlow(DarkChange(emptyList(), 0f, 0)) +} + +@Module +interface FakeDarkIconRepositoryModule { + @Binds fun bindFake(fake: FakeDarkIconRepository): DarkIconRepository +}