diff options
| author | 2023-06-26 21:15:23 +0000 | |
|---|---|---|
| committer | 2023-07-12 19:59:22 +0000 | |
| commit | a7f2b1a6393717c91c77b88a7089ad0f0c98141c (patch) | |
| tree | 02bf4e32604e50817168dcd817673d4df04a1f83 | |
| parent | f4517f851a1ca00b07691907fa3da7bb38338ad6 (diff) | |
[CS] 2/N: Define WallpaperRepository.wallpaperSupportsAmbientMode.
This will replace CentralSurfacesImpl.mWallpaperChangedReceiver. This CL
moves ScrimController to the new repo; other controllers will be moved
in future CLs.
Reasoning around NoopWallpaperRepository: Previously,
CentralSurfacesImpl was the only class calling
ScrimController.setWallpaperSupportsAmbientMode. CentralSurfacesImpl is
only included in AOSP SysUI and SysUIGo. So, the new WallpaperRepository
should also only be enabled for AOSP SysUI and SysUIGo. However,
ScrimController is included in *more* than just those two variants.
NoopWallpaperRepository should be used for those variants to ensure that
they can still use ScrimController and also still *not* pay attention to
wallpaper changes.
Bug: 277762009
Bug: 277764509
Test: manual: verify via logging that WallpaperRepository re-calcuates
supportsAmbientMode at the same pace as CentralSurfacesImpl with the
same values. Specifically verify the value is re-calcuated whenever the
wallpaper is changed or the user is switched.
Test: manual: switch users *without first switching the wallpaper* and
verify via logging that the repository re-fetches supportsAmbientMode.
(This specifically tests that the `wallpaperChanged` flow uses `.onStart
{ emit(Unit) }`, which was missing in the first iteration of this CL.)
Test: atest WallpaperRepositoryImplTest ScrimControllerTest
Change-Id: Icb69f842c52c19d89857218d07077ad90d83c1e6
12 files changed, 562 insertions, 13 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index f68bd49230d9..35cf4a1ecf0a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -71,6 +71,7 @@ import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerIm import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.volume.dagger.VolumeModule; +import com.android.systemui.wallpapers.dagger.WallpaperModule; import dagger.Binds; import dagger.Module; @@ -106,6 +107,7 @@ import javax.inject.Named; StatusBarEventsModule.class, StartCentralSurfacesModule.class, VolumeModule.class, + WallpaperModule.class, KeyboardShortcutsModule.class }) public abstract class ReferenceSystemUIModule { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index b1f513d0945c..a560accfff68 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -51,6 +51,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.statusbar.phone.LockscreenWallpaper +import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.theme.ThemeOverlayController @@ -59,6 +60,7 @@ import com.android.systemui.usb.StorageNotification import com.android.systemui.util.NotificationChannels import com.android.systemui.util.StartBinderLoggerModule import com.android.systemui.volume.VolumeUI +import com.android.systemui.wallpapers.dagger.WallpaperModule import com.android.systemui.wmshell.WMShell import dagger.Binds import dagger.Module @@ -72,6 +74,7 @@ import dagger.multibindings.IntoMap MultiUserUtilsModule::class, StartControlsStartableModule::class, StartBinderLoggerModule::class, + WallpaperModule::class, ]) abstract class SystemUICoreStartableModule { /** Inject into AuthController. */ @@ -316,4 +319,9 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(LockscreenWallpaper::class) abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable + + @Binds + @IntoMap + @ClassKey(ScrimController::class) + abstract fun bindScrimController(impl: ScrimController): CoreStartable } 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 278ae95b7f28..72915a5ce111 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -3565,6 +3565,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }; + /** + * @deprecated See {@link com.android.systemui.wallpapers.data.repository.WallpaperRepository} + * instead. + */ private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -3584,7 +3588,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { && (info != null && info.supportsAmbientMode()); mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); - mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index e4e912e1ceec..33bf06aa798a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -48,6 +48,7 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; +import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; @@ -71,8 +72,10 @@ import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.AlarmTimeout; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; +import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -89,7 +92,8 @@ import kotlinx.coroutines.CoroutineDispatcher; * security method gets shown). */ @SysUISingleton -public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { +public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable, + CoreStartable { static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -207,6 +211,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; private final Handler mHandler; private final Executor mMainExecutor; + private final JavaAdapter mJavaAdapter; private final ScreenOffAnimationController mScreenOffAnimationController; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -268,6 +273,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mKeyguardOccluded; private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final WallpaperRepository mWallpaperRepository; private CoroutineDispatcher mMainDispatcher; private boolean mIsBouncerToGoneTransitionRunning = false; private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @@ -297,11 +303,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump DockManager dockManager, ConfigurationController configurationController, @Main Executor mainExecutor, + JavaAdapter javaAdapter, ScreenOffAnimationController screenOffAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, KeyguardTransitionInteractor keyguardTransitionInteractor, + WallpaperRepository wallpaperRepository, @Main CoroutineDispatcher mainDispatcher, LargeScreenShadeInterpolator largeScreenShadeInterpolator, FeatureFlags featureFlags) { @@ -317,6 +325,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mKeyguardVisibilityCallback = new KeyguardVisibilityCallback(); mHandler = handler; mMainExecutor = mainExecutor; + mJavaAdapter = javaAdapter; mScreenOffAnimationController = screenOffAnimationController; mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, "hide_aod_wallpaper", mHandler); @@ -348,9 +357,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mColors = new GradientColors(); mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel; mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mWallpaperRepository = wallpaperRepository; mMainDispatcher = mainDispatcher; } + @Override + public void start() { + mJavaAdapter.alwaysCollectFlow( + mWallpaperRepository.getWallpaperSupportsAmbientMode(), + this::setWallpaperSupportsAmbientMode); + } + /** * Attach the controller to the supplied views. */ @@ -1551,7 +1568,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump pw.println(mState.getMaxLightRevealScrimAlpha()); } - public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { + private void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode; ScrimState[] states = ScrimState.values(); for (int i = 0; i < states.length; i++) { diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java index 5e489b0f38ac..82589d3ea1d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java @@ -27,6 +27,7 @@ import com.android.systemui.recents.RecentsModule; import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.row.NotificationRowModule; +import com.android.systemui.wallpapers.dagger.NoopWallpaperModule; import dagger.Subcomponent; @@ -39,6 +40,7 @@ import dagger.Subcomponent; DefaultComponentBinder.class, DependencyProvider.class, KeyguardModule.class, + NoopWallpaperModule.class, NotificationRowModule.class, NotificationsModule.class, RecentsModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt new file mode 100644 index 000000000000..baf88b0d9cbb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt @@ -0,0 +1,30 @@ +/* + * 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.wallpapers.dagger + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.wallpapers.data.repository.NoopWallpaperRepository +import com.android.systemui.wallpapers.data.repository.WallpaperRepository +import dagger.Binds +import dagger.Module + +@Module +interface NoopWallpaperModule { + @Binds + @SysUISingleton + fun bindWallpaperRepository(impl: NoopWallpaperRepository): WallpaperRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt new file mode 100644 index 000000000000..1b899784038e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt @@ -0,0 +1,30 @@ +/* + * 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.wallpapers.dagger + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.wallpapers.data.repository.WallpaperRepository +import com.android.systemui.wallpapers.data.repository.WallpaperRepositoryImpl +import dagger.Binds +import dagger.Module + +@Module +interface WallpaperModule { + @Binds + @SysUISingleton + fun bindWallpaperRepository(impl: WallpaperRepositoryImpl): WallpaperRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt new file mode 100644 index 000000000000..a64058968581 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.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.wallpapers.data.repository + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * A no-op implementation of [WallpaperRepository]. + * + * Used for variants of SysUI that do not support wallpaper but require other SysUI classes that + * have a wallpaper dependency. + */ +@SysUISingleton +class NoopWallpaperRepository @Inject constructor() : WallpaperRepository { + override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow() +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt new file mode 100644 index 000000000000..48895ffcacb9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt @@ -0,0 +1,105 @@ +/* + * 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.wallpapers.data.repository + +import android.app.WallpaperManager +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.UserHandle +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.user.data.model.SelectedUserModel +import com.android.systemui.user.data.model.SelectionStatus +import com.android.systemui.user.data.repository.UserRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +/** A repository storing information about the current wallpaper. */ +interface WallpaperRepository { + /** Emits true if the current user's current wallpaper supports ambient mode. */ + val wallpaperSupportsAmbientMode: StateFlow<Boolean> +} + +@SysUISingleton +class WallpaperRepositoryImpl +@Inject +constructor( + @Application scope: CoroutineScope, + broadcastDispatcher: BroadcastDispatcher, + userRepository: UserRepository, + private val wallpaperManager: WallpaperManager, + context: Context, +) : WallpaperRepository { + private val deviceSupportsAodWallpaper = + context.resources.getBoolean(com.android.internal.R.bool.config_dozeSupportsAodWallpaper) + + private val wallpaperChanged: Flow<Unit> = + broadcastDispatcher + .broadcastFlow( + IntentFilter(Intent.ACTION_WALLPAPER_CHANGED), + user = UserHandle.ALL, + ) + // The `combine` defining `wallpaperSupportsAmbientMode` will not run until both of the + // input flows emit at least once. Since this flow is an input flow, it needs to emit + // when it starts up to ensure that the `combine` will run if the user changes before we + // receive a ACTION_WALLPAPER_CHANGED intent. + // Note that the `selectedUser` flow does *not* need to emit on start because + // [UserRepository.selectedUser] is a state flow which will automatically emit a value + // on start. + .onStart { emit(Unit) } + + private val selectedUser: Flow<SelectedUserModel> = + userRepository.selectedUser + // Only update the wallpaper status once the user selection has finished. + .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE } + + override val wallpaperSupportsAmbientMode: StateFlow<Boolean> = + if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) { + MutableStateFlow(false).asStateFlow() + } else { + combine(wallpaperChanged, selectedUser) { _, selectedUser -> + doesWallpaperSupportAmbientMode(selectedUser) + } + .stateIn( + scope, + // Always be listening for wallpaper changes. + SharingStarted.Eagerly, + initialValue = + doesWallpaperSupportAmbientMode(userRepository.selectedUser.value), + ) + } + + private fun doesWallpaperSupportAmbientMode(selectedUser: SelectedUserModel): Boolean { + return wallpaperManager + .getWallpaperInfoForUser( + selectedUser.userInfo.id, + ) + // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode. + ?.supportsAmbientMode() == true + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index bbc75c9eeae8..0244e7e4b5a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -59,6 +59,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.TestScopeProvider; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; @@ -77,9 +78,11 @@ import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator; import com.android.systemui.statusbar.policy.FakeConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.utils.os.FakeHandler; +import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository; import com.google.common.truth.Expect; @@ -100,6 +103,7 @@ import java.util.HashSet; import java.util.Map; import kotlinx.coroutines.CoroutineDispatcher; +import kotlinx.coroutines.test.TestScope; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -113,6 +117,9 @@ public class ScrimControllerTest extends SysuiTestCase { private final LargeScreenShadeInterpolator mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator(); + private final TestScope mTestScope = TestScopeProvider.getTestScope(); + private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); + private ScrimController mScrimController; private ScrimView mScrimBehind; private ScrimView mNotificationsScrim; @@ -136,6 +143,7 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository(); @Mock private CoroutineDispatcher mMainDispatcher; @Mock private TypedArray mMockTypedArray; @@ -274,20 +282,26 @@ public class ScrimControllerTest extends SysuiTestCase { mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), + mJavaAdapter, mScreenOffAnimationController, mKeyguardUnlockAnimationController, mStatusBarKeyguardViewManager, mPrimaryBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, + mWallpaperRepository, mMainDispatcher, mLinearLargeScreenShadeInterpolator, mFeatureFlags); + mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); mScrimController.setHasBackdrop(false); - mScrimController.setWallpaperSupportsAmbientMode(false); + + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); } @@ -388,7 +402,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToAod_withAodWallpaper() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); @@ -410,7 +426,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() { mScrimController.setHasBackdrop(true); - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); @@ -427,7 +445,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void setHasBackdrop_withAodWallpaperAndAlbumArt() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); mScrimController.setHasBackdrop(true); @@ -540,7 +560,9 @@ public class ScrimControllerTest extends SysuiTestCase { // Pre-condition // Need to go to AoD first because PULSING doesn't change // the back scrim opacity - otherwise it would hide AoD wallpapers. - mScrimController.setWallpaperSupportsAmbientMode(false); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); assertScrimAlpha(Map.of( @@ -968,19 +990,23 @@ public class ScrimControllerTest extends SysuiTestCase { mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), + mJavaAdapter, mScreenOffAnimationController, mKeyguardUnlockAnimationController, mStatusBarKeyguardViewManager, mPrimaryBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, + mWallpaperRepository, mMainDispatcher, mLinearLargeScreenShadeInterpolator, mFeatureFlags); + mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); mScrimController.setHasBackdrop(false); - mScrimController.setWallpaperSupportsAmbientMode(false); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); + mTestScope.getTestScheduler().runCurrent(); mScrimController.transitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); @@ -1105,7 +1131,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testWillHideAodWallpaper() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any()); mScrimController.transitionTo(ScrimState.KEYGUARD); @@ -1116,7 +1144,8 @@ public class ScrimControllerTest extends SysuiTestCase { public void testWillHideDockedWallpaper() { mAlwaysOnEnabled = false; when(mDockManager.isDocked()).thenReturn(true); - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); mScrimController.transitionTo(ScrimState.AOD); @@ -1165,7 +1194,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testHidesShowWhenLockedActivity() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.setKeyguardOccluded(true); mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); @@ -1182,7 +1213,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testHidesShowWhenLockedActivity_whenAlreadyInAod() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); assertScrimAlpha(Map.of( diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt new file mode 100644 index 000000000000..6fc36b08250b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt @@ -0,0 +1,24 @@ +/* + * 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.wallpapers.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow + +/** Fake implementation of the wallpaper repository. */ +class FakeWallpaperRepository : WallpaperRepository { + override val wallpaperSupportsAmbientMode = MutableStateFlow(false) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt new file mode 100644 index 000000000000..132b9b4a02e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt @@ -0,0 +1,262 @@ +/* + * 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.wallpapers.data.repository + +import android.app.WallpaperInfo +import android.app.WallpaperManager +import android.content.Intent +import android.content.pm.UserInfo +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.user.data.model.SelectedUserModel +import com.android.systemui.user.data.model.SelectionStatus +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class WallpaperRepositoryImplTest : SysuiTestCase() { + + private val testScope = TestScope(StandardTestDispatcher()) + private val userRepository = FakeUserRepository() + private val wallpaperManager: WallpaperManager = mock() + + private val underTest: WallpaperRepositoryImpl by lazy { + WallpaperRepositoryImpl( + testScope.backgroundScope, + fakeBroadcastDispatcher, + userRepository, + wallpaperManager, + context, + ) + } + + @Before + fun setUp() { + whenever(wallpaperManager.isWallpaperSupported).thenReturn(true) + context.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_dozeSupportsAodWallpaper, + true, + ) + } + + @Test + fun wallpaperSupportsAmbientMode_nullInfo_false() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_infoDoesNotSupport_false() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_infoSupports_true() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + assertThat(latest).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_initialValueIsFetched_true() = + testScope.runTest { + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id)) + .thenReturn(SUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP)) + userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP) + + // WHEN the repo initially starts up (underTest is lazy), then it fetches the current + // value for the wallpaper + assertThat(underTest.wallpaperSupportsAmbientMode.value).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_initialValueIsFetched_false() = + testScope.runTest { + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id)) + .thenReturn(UNSUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_UNSUPPORTED_WP)) + userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP) + + // WHEN the repo initially starts up (underTest is lazy), then it fetches the current + // value for the wallpaper + assertThat(underTest.wallpaperSupportsAmbientMode.value).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_updatesOnUserChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id)) + .thenReturn(SUPPORTED_WP) + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id)) + .thenReturn(UNSUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP)) + + // WHEN a user with supported wallpaper is selected + userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP) + + // THEN it's true + assertThat(latest).isTrue() + + // WHEN the user is switched to a user with unsupported wallpaper + userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP) + + // THEN it's false + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_doesNotUpdateOnUserChanging() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id)) + .thenReturn(SUPPORTED_WP) + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id)) + .thenReturn(UNSUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP)) + + // WHEN a user with supported wallpaper is selected + userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP) + + // THEN it's true + assertThat(latest).isTrue() + + // WHEN the user has started switching to a user with unsupported wallpaper but hasn't + // finished yet + userRepository.selectedUser.value = + SelectedUserModel(USER_WITH_UNSUPPORTED_WP, SelectionStatus.SELECTION_IN_PROGRESS) + + // THEN it still matches the old user + assertThat(latest).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_updatesOnIntent() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + val info: WallpaperInfo = mock() + whenever(info.supportsAmbientMode()).thenReturn(false) + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(info) + + assertThat(latest).isFalse() + + // WHEN the info now supports ambient mode and a broadcast is sent + whenever(info.supportsAmbientMode()).thenReturn(true) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + // THEN the flow updates + assertThat(latest).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_wallpaperNotSupported_alwaysFalse() = + testScope.runTest { + whenever(wallpaperManager.isWallpaperSupported).thenReturn(false) + + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + assertThat(latest).isFalse() + + // Even WHEN the current wallpaper *does* support ambient mode + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + // THEN the value is still false because wallpaper isn't supported + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() = + testScope.runTest { + context.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_dozeSupportsAodWallpaper, + false + ) + + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + assertThat(latest).isFalse() + + // Even WHEN the current wallpaper *does* support ambient mode + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + // THEN the value is still false because the device doesn't support it + assertThat(latest).isFalse() + } + + private companion object { + val USER_WITH_UNSUPPORTED_WP = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0) + val UNSUPPORTED_WP = + mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(false) } + + val USER_WITH_SUPPORTED_WP = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0) + val SUPPORTED_WP = + mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(true) } + } +} |