diff options
| author | 2023-10-30 15:30:06 +0000 | |
|---|---|---|
| committer | 2023-11-22 11:59:28 +0000 | |
| commit | 0f95290d22432cf9d8fbd855ef930b4751880d4f (patch) | |
| tree | fe6749d3ea2cdc68ec9db69c730e6a57d074b85b | |
| parent | d2639cb97625fe06f7a02aecb6f5036e2e510d0a (diff) | |
Modernize LightsOutNotifController
- Refactor LightsOutNotifController to stop using NotifLiveData.
- Change the API of StatusBarModeRepository, to make it explicit which display is used when subscribing for StatusBarMode updates.
Bug: 308623704
Test: atest SystemUITests:CollapsedStatusBarViewModelImplTest SystemUITests:LightsOutInteractorTest SystemUITests:CollapsedStatusBarFragmentTest
Flag: ACONFIG com.android.systemui.notifications_live_data_store_refactor DEVELOPMENT
Change-Id: I908aedf856657cbbdb5f330a70e1100fea6337c1
25 files changed, 497 insertions, 121 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 83c16ae9ea78..6a0e88246027 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -19,6 +19,7 @@ package com.android.systemui.flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import javax.inject.Inject /** A class in which engineers can define flag dependencies */ @@ -26,6 +27,7 @@ import javax.inject.Inject class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, handler: Handler) : FlagDependenciesBase(featureFlags, handler) { override fun defineDependencies() { + NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt index 747efe3fb2c9..933d0ab880bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt @@ -36,7 +36,8 @@ enum class StatusBarMode { /** * A mode where notification icons in the status bar are hidden and replaced by a dot (this mode * can be requested by apps). See - * [com.android.systemui.statusbar.phone.LightsOutNotifController]. + * [com.android.systemui.statusbar.phone.LegacyLightsOutNotifController] and + * [com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor]. */ LIGHTS_OUT, /** Similar to [LIGHTS_OUT], but also with a transparent background for the status bar. */ 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 d1594ef2e404..04152123e42d 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 @@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.merge /** * Repository for data that's specific to the status bar **on keyguard**. For data that applies to - * all status bars, use [StatusBarModeRepository]. + * all status bars, use [StatusBarModeRepositoryStore]. */ interface KeyguardStatusBarRepository { /** True if we can show the user switcher on keyguard and false otherwise. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt index 47994d92d22b..6429815bcb9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt @@ -25,10 +25,8 @@ import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BA import android.view.WindowInsetsController.Appearance import com.android.internal.statusbar.LetterboxDetails import com.android.internal.view.AppearanceRegion -import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.Dumpable 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.OnStatusBarViewInitializedListener import com.android.systemui.statusbar.data.model.StatusBarAppearance @@ -38,13 +36,10 @@ 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 dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.io.PrintWriter -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -61,7 +56,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 { +interface StatusBarModePerDisplayRepository { /** * 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 @@ -108,16 +103,15 @@ interface StatusBarModeRepository { fun clearTransient() } -@SysUISingleton -class StatusBarModeRepositoryImpl -@Inject +class StatusBarModePerDisplayRepositoryImpl +@AssistedInject constructor( @Application scope: CoroutineScope, - @DisplayId thisDisplayId: Int, + @Assisted("displayId") thisDisplayId: Int, private val commandQueue: CommandQueue, private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator, ongoingCallRepository: OngoingCallRepository, -) : StatusBarModeRepository, CoreStartable, OnStatusBarViewInitializedListener { +) : StatusBarModePerDisplayRepository, OnStatusBarViewInitializedListener, Dumpable { private val commandQueueCallback = object : CommandQueue.Callbacks { @@ -166,7 +160,7 @@ constructor( } } - override fun start() { + fun start() { commandQueue.addCallback(commandQueueCallback) } @@ -340,16 +334,7 @@ constructor( ) } -@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 +@AssistedFactory +interface StatusBarModePerDisplayRepositoryFactory { + fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt new file mode 100644 index 000000000000..962cb0953f97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt @@ -0,0 +1,83 @@ +/* + * 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.repository + +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.statusbar.core.StatusBarInitializer +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +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 + +interface StatusBarModeRepositoryStore { + val defaultDisplay: StatusBarModePerDisplayRepository + fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository +} + +@SysUISingleton +class StatusBarModeRepositoryImpl +@Inject +constructor( + @DisplayId private val displayId: Int, + factory: StatusBarModePerDisplayRepositoryFactory +) : + StatusBarModeRepositoryStore, + CoreStartable, + StatusBarInitializer.OnStatusBarViewInitializedListener { + override val defaultDisplay = factory.create(displayId) + + override fun forDisplay(displayId: Int) = + if (this.displayId == displayId) { + defaultDisplay + } else { + TODO("b/127878649 implement multi-display state management") + } + + override fun start() { + defaultDisplay.start() + } + + override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) { + defaultDisplay.onStatusBarViewInitialized(component) + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + defaultDisplay.dump(pw, args) + } +} + +@Module +interface StatusBarModeRepositoryModule { + @Binds fun bindImpl(impl: StatusBarModeRepositoryImpl): StatusBarModeRepositoryStore + + @Binds + @IntoMap + @ClassKey(StatusBarModeRepositoryStore::class) + fun bindCoreStartable(impl: StatusBarModeRepositoryImpl): CoreStartable + + @Binds + @IntoSet + fun bindViewInitListener( + impl: StatusBarModeRepositoryImpl + ): StatusBarInitializer.OnStatusBarViewInitializedListener +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt index 44387c225ef1..8fc7106ecada 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataStoreRefactor.kt @@ -50,4 +50,4 @@ object NotificationsLiveDataStoreRefactor { */ @JvmStatic inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) -}
\ No newline at end of file +} 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 645769cdd586..2d5fe18ade9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -200,7 +200,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.data.model.StatusBarMode; -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository; +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; @@ -388,7 +388,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarInitializer mStatusBarInitializer; private final StatusBarWindowController mStatusBarWindowController; - private final StatusBarModeRepository mStatusBarModeRepository; + private final StatusBarModeRepositoryStore mStatusBarModeRepository; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @VisibleForTesting DozeServiceHost mDozeServiceHost; @@ -606,7 +606,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { StatusBarInitializer statusBarInitializer, StatusBarWindowController statusBarWindowController, StatusBarWindowStateController statusBarWindowStateController, - StatusBarModeRepository statusBarModeRepository, + StatusBarModeRepositoryStore statusBarModeRepository, KeyguardUpdateMonitor keyguardUpdateMonitor, StatusBarSignalPolicy statusBarSignalPolicy, PulseExpansionHandler pulseExpansionHandler, @@ -900,7 +900,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { setUpPresenter(); if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) { - mStatusBarModeRepository.showTransient(); + mStatusBarModeRepository.getDefaultDisplay().showTransient(); } mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior, @@ -1147,9 +1147,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mDemoModeController.addCallback(mDemoModeCallback); mJavaAdapter.alwaysCollectFlow( - mStatusBarModeRepository.isTransientShown(), this::onTransientShownChanged); + mStatusBarModeRepository.getDefaultDisplay().isTransientShown(), + this::onTransientShownChanged); mJavaAdapter.alwaysCollectFlow( - mStatusBarModeRepository.getStatusBarMode(), + mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(), this::updateBarMode); mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get(); @@ -1209,7 +1210,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void hide() { - mStatusBarModeRepository.clearTransient(); + mStatusBarModeRepository.getDefaultDisplay().clearTransient(); } }); @@ -1657,7 +1658,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mDemoModeController.isInDemoMode()) return; if (mStatusBarTransitions != null) { checkBarMode( - mStatusBarModeRepository.getStatusBarMode().getValue(), + mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(), mStatusBarWindowState, mStatusBarTransitions); } @@ -1668,7 +1669,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { /** Temporarily hides Bubbles if the status bar is hidden. */ @Override public void updateBubblesVisibility() { - StatusBarMode mode = mStatusBarModeRepository.getStatusBarMode().getValue(); + StatusBarMode mode = + mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(); mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged( mode != StatusBarMode.LIGHTS_OUT && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT @@ -2993,7 +2995,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // End Extra BaseStatusBarMethods. boolean isTransientShown() { - return mStatusBarModeRepository.isTransientShown().getValue(); + return mStatusBarModeRepository.getDefaultDisplay().isTransientShown().getValue(); } private void updateLightRevealScrimVisibility() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java index eba7fe09a8af..7c871e183740 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifController.java @@ -36,6 +36,7 @@ import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.view.AppearanceRegion; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope; import com.android.systemui.util.ViewController; @@ -51,7 +52,7 @@ import javax.inject.Named; * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}. */ @StatusBarFragmentScope -public class LightsOutNotifController extends ViewController<View> { +public class LegacyLightsOutNotifController extends ViewController<View> { private final CommandQueue mCommandQueue; private final NotifLiveDataStore mNotifDataStore; private final WindowManager mWindowManager; @@ -63,7 +64,7 @@ public class LightsOutNotifController extends ViewController<View> { private int mDisplayId; @Inject - LightsOutNotifController( + LegacyLightsOutNotifController( @Named(LIGHTS_OUT_NOTIF_VIEW) View lightsOutNotifView, WindowManager windowManager, NotifLiveDataStore notifDataStore, @@ -72,7 +73,12 @@ public class LightsOutNotifController extends ViewController<View> { mWindowManager = windowManager; mNotifDataStore = notifDataStore; mCommandQueue = commandQueue; + } + @Override + protected void onInit() { + super.onInit(); + NotificationsLiveDataStoreRefactor.assertInLegacyMode(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 4d3e2ad4813a..eec617bf91d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -42,7 +42,7 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.data.model.StatusBarAppearance; -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository; +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Compile; import com.android.systemui.util.kotlin.JavaAdapter; @@ -68,7 +68,7 @@ public class LightBarController implements private final JavaAdapter mJavaAdapter; private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; - private final StatusBarModeRepository mStatusBarModeRepository; + private final StatusBarModeRepositoryStore mStatusBarModeRepository; private BiometricUnlockController mBiometricUnlockController; private LightBarTransitionsController mNavigationBarController; @@ -126,7 +126,7 @@ public class LightBarController implements DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, - StatusBarModeRepository statusBarModeRepository, + StatusBarModeRepositoryStore statusBarModeRepository, DumpManager dumpManager, DisplayTracker displayTracker) { mJavaAdapter = javaAdapter; @@ -146,7 +146,7 @@ public class LightBarController implements @Override public void start() { mJavaAdapter.alwaysCollectFlow( - mStatusBarModeRepository.getStatusBarAppearance(), + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance(), this::onStatusBarAppearanceChanged); } @@ -476,7 +476,7 @@ public class LightBarController implements private final DarkIconDispatcher mDarkIconDispatcher; private final BatteryController mBatteryController; private final NavigationModeController mNavModeController; - private final StatusBarModeRepository mStatusBarModeRepository; + private final StatusBarModeRepositoryStore mStatusBarModeRepository; private final DumpManager mDumpManager; private final DisplayTracker mDisplayTracker; @@ -486,7 +486,7 @@ public class LightBarController implements DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, - StatusBarModeRepository statusBarModeRepository, + StatusBarModeRepositoryStore statusBarModeRepository, DumpManager dumpManager, DisplayTracker displayTracker) { mJavaAdapter = javaAdapter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt new file mode 100644 index 000000000000..ed8b3e8922f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt @@ -0,0 +1,45 @@ +/* + * 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 com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.data.model.StatusBarMode +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where status + * bar and navigation icons dim. In this mode, a notification dot appears where the notification + * icons would appear if they would be shown outside of this mode. + * + * This interactor knows whether the device is in [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE]. + */ +@SysUISingleton +class LightsOutInteractor +@Inject +constructor(private val repository: StatusBarModeRepositoryStore) { + + fun isLowProfile(displayId: Int): Flow<Boolean> = + repository.forDisplay(displayId).statusBarMode.map { + when (it) { + StatusBarMode.LIGHTS_OUT, + StatusBarMode.LIGHTS_OUT_TRANSPARENT -> true + else -> false + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 7adc08ca00c0..49880d4475da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -43,7 +43,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeExpansionStateManager; @@ -139,7 +138,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final OngoingCallController mOngoingCallController; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mLocationPublisher; - private final FeatureFlagsClassic mFeatureFlags; private final NotificationIconAreaController mNotificationIconAreaController; private final ShadeExpansionStateManager mShadeExpansionStateManager; private final StatusBarIconController mStatusBarIconController; @@ -228,7 +226,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue StatusBarLocationPublisher locationPublisher, NotificationIconAreaController notificationIconAreaController, ShadeExpansionStateManager shadeExpansionStateManager, - FeatureFlagsClassic featureFlags, StatusBarIconController statusBarIconController, DarkIconManager.Factory darkIconManagerFactory, CollapsedStatusBarViewModel collapsedStatusBarViewModel, @@ -258,7 +255,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mLocationPublisher = locationPublisher; mNotificationIconAreaController = notificationIconAreaController; mShadeExpansionStateManager = shadeExpansionStateManager; - mFeatureFlags = featureFlags; mStatusBarIconController = statusBarIconController; mCollapsedStatusBarViewModel = collapsedStatusBarViewModel; mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java index 0618abbf00d8..96faa359d43e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java @@ -18,8 +18,9 @@ package com.android.systemui.statusbar.phone.fragment.dagger; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.LightsOutNotifController; +import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; @@ -78,7 +79,9 @@ public interface StatusBarFragmentComponent { getBatteryMeterViewController().init(); getHeadsUpAppearanceController().init(); getPhoneStatusBarViewController().init(); - getLightsOutNotifController().init(); + if (!NotificationsLiveDataStoreRefactor.isEnabled()) { + getLegacyLightsOutNotifController().init(); + } getStatusBarDemoMode().init(); } @@ -101,7 +104,7 @@ public interface StatusBarFragmentComponent { /** */ @StatusBarFragmentScope - LightsOutNotifController getLightsOutNotifController(); + LegacyLightsOutNotifController getLegacyLightsOutNotifController(); /** */ @StatusBarFragmentScope diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index b0532ce1817b..0bdd1a5b4d5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -36,7 +36,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.data.repository.StatusBarModeRepository +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -68,7 +68,7 @@ class OngoingCallController @Inject constructor( private val dumpManager: DumpManager, private val statusBarWindowController: StatusBarWindowController, private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler, - private val statusBarModeRepository: StatusBarModeRepository, + private val statusBarModeRepository: StatusBarModeRepositoryStore, ) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable { private var isFullscreen: Boolean = false /** Non-null if there's an active call notification. */ @@ -129,7 +129,7 @@ class OngoingCallController @Inject constructor( dumpManager.registerDumpable(this) notifCollection.addCollectionListener(notifListener) scope.launch { - statusBarModeRepository.isInFullscreenMode.collect { + statusBarModeRepository.defaultDisplay.isInFullscreenMode.collect { isFullscreen = it updateChipClickListener() updateGestureListening() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt index da9c45ad5ada..9c78ab42a14a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.asStateFlow * * This class is used to break a dependency cycle between * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController] and - * [com.android.systemui.statusbar.data.repository.StatusBarModeRepository]. Instead, those two + * [com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore]. Instead, those two * classes both refer to this repository. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index b9b88f4b762c..7d7f49bb8d17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -16,11 +16,15 @@ package com.android.systemui.statusbar.pipeline.shared.ui.binder +import android.animation.Animator +import android.animation.AnimatorListenerAdapter import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel import javax.inject.Inject import kotlinx.coroutines.launch @@ -61,9 +65,49 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa listener.onTransitionFromLockscreenToDreamStarted() } } + + if (NotificationsLiveDataStoreRefactor.isEnabled) { + val displayId = view.display.displayId + val lightsOutView: View = view.requireViewById(R.id.notification_lights_out) + launch { + viewModel.areNotificationsLightsOut(displayId).collect { show -> + animateLightsOutView(lightsOutView, show) + } + } + } } } } + + private fun animateLightsOutView(view: View, visible: Boolean) { + view.animate().cancel() + + val alpha = if (visible) 1f else 0f + val duration = if (visible) 750L else 250L + val visibility = if (visible) View.VISIBLE else View.GONE + + if (visible) { + view.alpha = 0f + view.visibility = View.VISIBLE + } + + view + .animate() + .alpha(alpha) + .setDuration(duration) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + view.alpha = alpha + view.visibility = visibility + // Unset the listener, otherwise this may persist for + // another view property animation + view.animate().setListener(null) + } + } + ) + .start() + } } /** Listener for various events that may affect the status bar's visibility. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt index 15ab143a7aeb..52a6d8cf0952 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt @@ -20,11 +20,17 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -48,12 +54,25 @@ interface CollapsedStatusBarViewModel { /** Emits whenever a transition from lockscreen to dream has started. */ val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> + + /** + * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where + * status bar and navigation icons dim. In this mode, a notification dot appears where the + * notification icons would appear if they would be shown outside of this mode. + * + * This flow tells when to show or hide the notification dot in the status bar to indicate + * whether there are notifications when the device is in + * [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE]. + */ + fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> } @SysUISingleton class CollapsedStatusBarViewModelImpl @Inject constructor( + private val lightsOutInteractor: LightsOutInteractor, + private val notificationsInteractor: ActiveNotificationsInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, @Application coroutineScope: CoroutineScope, ) : CollapsedStatusBarViewModel { @@ -69,4 +88,17 @@ constructor( keyguardTransitionInteractor.lockscreenToDreamingTransition .filter { it.transitionState == TransitionState.STARTED } .map {} + + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = + if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) { + emptyFlow() + } else { + combine( + notificationsInteractor.areAnyNotificationsPresent, + lightsOutInteractor.isLowProfile(displayId), + ) { hasNotifications, isLowProfile -> + hasNotifications && isLowProfile + } + .distinctUntilChanged() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt index d1a46fca21c8..057dcb2a156e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt @@ -62,7 +62,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() { private val ongoingCallRepository = OngoingCallRepository() private val underTest = - StatusBarModeRepositoryImpl( + StatusBarModePerDisplayRepositoryImpl( testScope.backgroundScope, DISPLAY_ID, commandQueue, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java index 287ebba4db24..bde2243db4e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java @@ -54,7 +54,7 @@ import java.util.Objects; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper -public class LightsOutNotifControllerTest extends SysuiTestCase { +public class LegacyLightsOutNotifControllerTest extends SysuiTestCase { private static final int LIGHTS_ON = 0; private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS; @@ -68,7 +68,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor; private View mLightsOutView; - private LightsOutNotifController mLightsOutNotifController; + private LegacyLightsOutNotifController mLightsOutNotifController; private int mDisplayId; private Observer<Boolean> mHaActiveNotifsObserver; private CommandQueue.Callbacks mCallbacks; @@ -83,7 +83,7 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { when(mNotifLiveDataStore.getHasActiveNotifs()).thenReturn(mHasActiveNotifs); when(mHasActiveNotifs.getValue()).thenReturn(false); - mLightsOutNotifController = new LightsOutNotifController( + mLightsOutNotifController = new LegacyLightsOutNotifController( mLightsOutView, mWindowManager, mNotifLiveDataStore, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index c45ecf3fe225..f6419a9c4784 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -121,7 +121,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -142,7 +142,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(0 /* appearance */, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -165,7 +165,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -190,7 +190,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, thirdBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -214,7 +214,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(0 /* appearance */, secondBounds) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -231,7 +231,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1)) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -249,7 +249,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(0, new Rect(0, 0, 1, 1)) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -266,7 +266,7 @@ public class LightBarControllerTest extends SysuiTestCase { new AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, new Rect(0, 0, 1, 1)) ); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, STATUS_BAR_BOUNDS, @@ -276,7 +276,7 @@ public class LightBarControllerTest extends SysuiTestCase { reset(mStatusBarIconController); // WHEN the same appearance regions but different status bar mode is sent - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.LIGHTS_OUT_TRANSPARENT, STATUS_BAR_BOUNDS, @@ -298,7 +298,7 @@ public class LightBarControllerTest extends SysuiTestCase { /* start= */ new Rect(0, 0, 10, 10), /* end= */ new Rect(0, 0, 20, 20)); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, startingBounds, @@ -311,7 +311,7 @@ public class LightBarControllerTest extends SysuiTestCase { BoundsPair newBounds = new BoundsPair( /* start= */ new Rect(0, 0, 30, 30), /* end= */ new Rect(0, 0, 40, 40)); - mStatusBarModeRepository.getStatusBarAppearance().setValue( + mStatusBarModeRepository.getDefaultDisplay().getStatusBarAppearance().setValue( new StatusBarAppearance( StatusBarMode.TRANSPARENT, newBounds, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt new file mode 100644 index 000000000000..5a0e13d02b92 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt @@ -0,0 +1,62 @@ +/* + * 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 androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.data.model.StatusBarMode +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@SmallTest +class LightsOutInteractorTest : SysuiTestCase() { + + private val statusBarModeRepository = FakeStatusBarModeRepository() + private val interactor: LightsOutInteractor = LightsOutInteractor(statusBarModeRepository) + + @Test + fun isLowProfile_lightsOutStatusBarMode_false() = runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT + + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + + assertThat(actual).isTrue() + } + + @Test + fun isLowProfile_lightsOutTransparentStatusBarMode_true() = runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.LIGHTS_OUT_TRANSPARENT + + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + + assertThat(actual).isTrue() + } + + @Test + fun isLowProfile_transparentStatusBarMode_false() = runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + + assertThat(actual).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 0b87fe8da184..17c29382b39a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -46,7 +46,6 @@ import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.common.ui.ConfigurationState; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.plugins.DarkIconDispatcher; @@ -695,7 +694,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mLocationPublisher, mMockNotificationAreaController, mShadeExpansionStateManager, - mock(FeatureFlagsClassic.class), mStatusBarIconController, mIconManagerFactory, mCollapsedStatusBarViewModel, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index 49de5125cb4d..7b73528cd932 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -549,7 +549,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun fullscreenIsTrue_chipStillClickable() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() assertThat(chipView.hasOnClickListeners()).isTrue() @@ -559,7 +559,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun callStartedInImmersiveMode_swipeGestureCallbackAdded() { - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) @@ -570,7 +570,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun callStartedNotInImmersiveMode_swipeGestureCallbackNotAdded() { - statusBarModeRepository.isInFullscreenMode.value = false + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false testScope.runCurrent() notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) @@ -583,7 +583,7 @@ class OngoingCallControllerTest : SysuiTestCase() { fun transitionToImmersiveMode_swipeGestureCallbackAdded() { notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() verify(mockSwipeStatusBarAwayGestureHandler) @@ -592,11 +592,11 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun transitionOutOfImmersiveMode_swipeGestureCallbackRemoved() { - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) reset(mockSwipeStatusBarAwayGestureHandler) - statusBarModeRepository.isInFullscreenMode.value = false + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false testScope.runCurrent() verify(mockSwipeStatusBarAwayGestureHandler) @@ -605,7 +605,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun callEndedWhileInImmersiveMode_swipeGestureCallbackRemoved() { - statusBarModeRepository.isInFullscreenMode.value = true + statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true testScope.runCurrent() val ongoingCallNotifEntry = createOngoingCallNotifEntry() notifCollectionListener.onEntryAdded(ongoingCallNotifEntry) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index 688f739f61f8..09dc1e537e9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -17,49 +17,77 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import androidx.test.filters.SmallTest +import com.android.systemui.CoroutineTestScopeModule +import com.android.systemui.Flags +import com.android.systemui.SysUITestComponent +import com.android.systemui.SysUITestModule import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectValues +import com.android.systemui.collectLastValue +import com.android.systemui.collectValues +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory 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.runTest +import com.android.systemui.statusbar.data.model.StatusBarMode +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.google.common.truth.Truth.assertThat +import dagger.BindsInstance +import dagger.Component import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { - private lateinit var underTest: CollapsedStatusBarViewModel + @SysUISingleton + @Component( + modules = + [ + SysUITestModule::class, + ] + ) + interface TestComponent : SysUITestComponent<CollapsedStatusBarViewModelImpl> { + val statusBarModeRepository: FakeStatusBarModeRepository + val activeNotificationListRepository: ActiveNotificationListRepository + val keyguardTransitionRepository: FakeKeyguardTransitionRepository + + @Component.Factory + interface Factory { + fun create( + @BindsInstance test: SysuiTestCase, + testScope: CoroutineTestScopeModule, + ): TestComponent + } + } - private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository - private lateinit var testScope: TestScope + @OptIn(ExperimentalCoroutinesApi::class) + private val testComponent: TestComponent = + DaggerCollapsedStatusBarViewModelImplTest_TestComponent.factory() + .create( + test = this, + testScope = CoroutineTestScopeModule(TestScope(UnconfinedTestDispatcher())), + ) @Before fun setUp() { - testScope = TestScope(UnconfinedTestDispatcher()) - - keyguardTransitionRepository = FakeKeyguardTransitionRepository() - val interactor = - KeyguardTransitionInteractorFactory.create( - scope = TestScope().backgroundScope, - repository = keyguardTransitionRepository, - ) - .keyguardTransitionInteractor - underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope) + mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_REFACTOR) } @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -77,8 +105,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_running_isTrue() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -96,13 +124,13 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_finished_isFalse() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, to = KeyguardState.OCCLUDED, - this.testScheduler, + testScope.testScheduler, ) assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse() @@ -112,8 +140,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_canceled_isFalse() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -131,8 +159,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_irrelevantTransition_isFalse() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -150,8 +178,8 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isTransitioningFromLockscreenToOccluded_followsRepoUpdates() = - testScope.runTest { - val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this) + testComponent.runTest { + val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(testScope) keyguardTransitionRepository.sendTransitionStep( TransitionStep( @@ -182,7 +210,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_started_emitted() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -199,7 +227,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_startedMultiple_emittedMultiple() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -234,7 +262,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_startedThenRunning_emittedOnlyOne() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -283,7 +311,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransition_notEmitted() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -300,7 +328,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { @Test fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransitionState_notEmitted() = - testScope.runTest { + testComponent.runTest { val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent) keyguardTransitionRepository.sendTransitionStep( @@ -317,4 +345,65 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(emissions).isEmpty() } + + @Test + fun areNotificationsLightsOut_lowProfileWithNotifications_true() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.LIGHTS_OUT_TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(testNotifications) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isTrue() + } + + @Test + fun areNotificationsLightsOut_lowProfileWithoutNotifications_false() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = + StatusBarMode.LIGHTS_OUT_TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(emptyList()) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isFalse() + } + + @Test + fun areNotificationsLightsOut_defaultStatusBarModeWithoutNotifications_false() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(emptyList()) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isFalse() + } + + @Test + fun areNotificationsLightsOut_defaultStatusBarModeWithNotifications_false() = + testComponent.runTest { + statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT + activeNotificationListRepository.activeNotifications.value = + activeNotificationsStore(testNotifications) + + val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID)) + + assertThat(actual).isFalse() + } + + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = + ActiveNotificationsStore.Builder() + .apply { notifications.forEach(::addIndividualNotif) } + .build() + + private val testNotifications = + listOf( + activeNotificationModel(key = "notif1"), + activeNotificationModel(key = "notif2"), + ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt index 88587b2db0f9..bc50f7967403 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt @@ -16,11 +16,20 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { + private val areNotificationLightsOut = MutableStateFlow(false) + override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false) override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>() + + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut + + fun setNotificationLightsOut(lightsOut: Boolean) { + areNotificationLightsOut.value = lightsOut + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt index f25d282208f0..60690838fcdf 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt @@ -16,14 +16,33 @@ package com.android.systemui.statusbar.data.repository +import android.view.Display +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.data.model.StatusBarAppearance import com.android.systemui.statusbar.data.model.StatusBarMode +import com.google.common.truth.Truth.assertThat import dagger.Binds import dagger.Module import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow -class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepository { +@SysUISingleton +class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositoryStore { + + companion object { + const val DISPLAY_ID = Display.DEFAULT_DISPLAY + } + + override val defaultDisplay: FakeStatusBarModePerDisplayRepository = + FakeStatusBarModePerDisplayRepository() + + override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository { + assertThat(displayId).isEqualTo(DISPLAY_ID) + return defaultDisplay + } +} + +class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository { override val isTransientShown = MutableStateFlow(false) override val isInFullscreenMode = MutableStateFlow(false) override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null) @@ -39,5 +58,5 @@ class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositor @Module interface FakeStatusBarModeRepositoryModule { - @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepository + @Binds fun bindFake(fake: FakeStatusBarModeRepository): StatusBarModeRepositoryStore } |