Loading src/com/android/customization/model/mode/DarkModeSectionController.java +7 −1 Original line number Diff line number Diff line Loading @@ -59,12 +59,17 @@ public class DarkModeSectionController implements private Context mContext; private DarkModeSectionView mDarkModeSectionView; private final DarkModeSnapshotRestorer mSnapshotRestorer; public DarkModeSectionController(Context context, Lifecycle lifecycle) { public DarkModeSectionController( Context context, Lifecycle lifecycle, DarkModeSnapshotRestorer snapshotRestorer) { mContext = context; mLifecycle = lifecycle; mPowerManager = context.getSystemService(PowerManager.class); mLifecycle.addObserver(this); mSnapshotRestorer = snapshotRestorer; } @OnLifecycleEvent(Lifecycle.Event.ON_START) Loading Loading @@ -132,6 +137,7 @@ public class DarkModeSectionController implements mDarkModeSectionView.announceForAccessibility( context.getString(R.string.mode_changed)); uiModeManager.setNightModeActivated(viewActivated); mSnapshotRestorer.store(viewActivated); }, /* delayMillis= */ shortDelay); } Loading src/com/android/customization/model/mode/DarkModeSnapshotRestorer.kt 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * 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.customization.model.mode import android.app.UiModeManager import android.content.Context import android.content.res.Configuration import androidx.annotation.VisibleForTesting import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer import com.android.wallpaper.picker.undo.domain.interactor.SnapshotStore import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext class DarkModeSnapshotRestorer : SnapshotRestorer { private val backgroundDispatcher: CoroutineDispatcher private val isActive: () -> Boolean private val setActive: suspend (Boolean) -> Unit private lateinit var store: SnapshotStore constructor( context: Context, manager: UiModeManager, backgroundDispatcher: CoroutineDispatcher, ) : this( backgroundDispatcher = backgroundDispatcher, isActive = { context.applicationContext.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0 }, setActive = { isActive -> manager.setNightModeActivated(isActive) }, ) @VisibleForTesting constructor( backgroundDispatcher: CoroutineDispatcher, isActive: () -> Boolean, setActive: suspend (Boolean) -> Unit, ) { this.backgroundDispatcher = backgroundDispatcher this.isActive = isActive this.setActive = setActive } override suspend fun setUpSnapshotRestorer(store: SnapshotStore): RestorableSnapshot { this.store = store return snapshot( isActivated = isActive(), ) } override suspend fun restoreToSnapshot(snapshot: RestorableSnapshot) { val isActivated = snapshot.args[KEY]?.toBoolean() == true withContext(backgroundDispatcher) { setActive(isActivated) } } fun store( isActivated: Boolean, ) { store.store( snapshot( isActivated = isActivated, ), ) } private fun snapshot( isActivated: Boolean, ): RestorableSnapshot { return RestorableSnapshot( args = buildMap { put( KEY, isActivated.toString(), ) } ) } companion object { private const val KEY = "is_activated" } } src/com/android/customization/module/DefaultCustomizationSections.java +13 −5 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ import com.android.customization.model.color.ColorSectionController2; import com.android.customization.model.grid.GridOptionsManager; import com.android.customization.model.grid.GridSectionController; import com.android.customization.model.mode.DarkModeSectionController; import com.android.customization.model.mode.DarkModeSnapshotRestorer; import com.android.customization.model.themedicon.ThemedIconSectionController; import com.android.customization.model.themedicon.ThemedIconSwitchProvider; import com.android.customization.picker.clock.data.repository.ClockRegistryProvider; Loading Loading @@ -56,6 +57,7 @@ public final class DefaultCustomizationSections implements CustomizationSections mClockCarouselViewModelProvider; private final PreviewWithClockCarouselSectionController.ClockViewFactoryProvider mClockViewFactoryProvider; private final DarkModeSnapshotRestorer mDarkModeSnapshotRestorer; public DefaultCustomizationSections( KeyguardQuickAffordancePickerInteractor keyguardQuickAffordancePickerInteractor, Loading @@ -65,7 +67,8 @@ public final class DefaultCustomizationSections implements CustomizationSections BaseFlags flags, ClockRegistryProvider clockRegistryProvider, ClockCarouselViewModelProvider clockCarouselViewModelProvider, ClockViewFactoryProvider clockViewFactoryProvider) { ClockViewFactoryProvider clockViewFactoryProvider, DarkModeSnapshotRestorer darkModeSnapshotRestorer) { mKeyguardQuickAffordancePickerInteractor = keyguardQuickAffordancePickerInteractor; mKeyguardQuickAffordancePickerViewModelFactory = keyguardQuickAffordancePickerViewModelFactory; Loading @@ -74,6 +77,7 @@ public final class DefaultCustomizationSections implements CustomizationSections mClockRegistryProvider = clockRegistryProvider; mClockCarouselViewModelProvider = clockCarouselViewModelProvider; mClockViewFactoryProvider = clockViewFactoryProvider; mDarkModeSnapshotRestorer = darkModeSnapshotRestorer; } @Override Loading Loading @@ -157,8 +161,10 @@ public final class DefaultCustomizationSections implements CustomizationSections case HOME_SCREEN: // Dark/Light theme section. sectionControllers.add(new DarkModeSectionController(activity, lifecycleOwner.getLifecycle())); sectionControllers.add(new DarkModeSectionController( activity, lifecycleOwner.getLifecycle(), mDarkModeSnapshotRestorer)); // Themed app icon section. sectionControllers.add(new ThemedIconSectionController( Loading Loading @@ -198,8 +204,10 @@ public final class DefaultCustomizationSections implements CustomizationSections activity, wallpaperColorsViewModel, lifecycleOwner, savedInstanceState)); // Dark/Light theme section. sectionControllers.add(new DarkModeSectionController(activity, lifecycleOwner.getLifecycle())); sectionControllers.add(new DarkModeSectionController( activity, lifecycleOwner.getLifecycle(), mDarkModeSnapshotRestorer)); // Themed app icon section. sectionControllers.add(new ThemedIconSectionController( Loading src/com/android/customization/module/ThemePickerInjector.kt +21 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.customization.module import android.app.UiModeManager import android.content.Context import android.content.Intent import android.net.Uri Loading @@ -22,6 +23,7 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import com.android.customization.model.mode.DarkModeSnapshotRestorer import com.android.customization.model.theme.OverlayManagerCompat import com.android.customization.model.theme.ThemeBundleProvider import com.android.customization.model.theme.ThemeManager Loading Loading @@ -86,6 +88,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null private var colorPickerInteractor: ColorPickerInteractor? = null private var colorPickerViewModelFactory: ColorPickerViewModel.Factory? = null private var darkModeSnapshotRestorer: DarkModeSnapshotRestorer? = null override fun getCustomizationSections(activity: ComponentActivity): CustomizationSections { return customizationSections Loading @@ -112,7 +115,8 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject registry = registry, ) } } }, getDarkModeSnapshotRestorer(activity), ) .also { customizationSections = it } } Loading Loading @@ -173,6 +177,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject getKeyguardQuickAffordanceSnapshotRestorer(context) this[KEY_WALLPAPER_SNAPSHOT_RESTORER] = getWallpaperSnapshotRestorer(context) this[KEY_NOTIFICATIONS_SNAPSHOT_RESTORER] = getNotificationsSnapshotRestorer(context) this[KEY_DARK_MODE_SNAPSHOT_RESTORER] = getDarkModeSnapshotRestorer(context) } } Loading Loading @@ -348,6 +353,18 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject .also { colorPickerViewModelFactory = it } } protected fun getDarkModeSnapshotRestorer( context: Context, ): DarkModeSnapshotRestorer { return darkModeSnapshotRestorer ?: DarkModeSnapshotRestorer( context = context, manager = context.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager, backgroundDispatcher = Dispatchers.IO, ) .also { darkModeSnapshotRestorer = it } } companion object { @JvmStatic private val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER = Loading @@ -356,11 +373,13 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject private val KEY_WALLPAPER_SNAPSHOT_RESTORER = KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER + 1 @JvmStatic private val KEY_NOTIFICATIONS_SNAPSHOT_RESTORER = KEY_WALLPAPER_SNAPSHOT_RESTORER + 1 @JvmStatic private val KEY_DARK_MODE_SNAPSHOT_RESTORER = KEY_NOTIFICATIONS_SNAPSHOT_RESTORER + 1 /** * When this injector is overridden, this is the minimal value that should be used by * restorers returns in [getSnapshotRestorers]. */ @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = KEY_NOTIFICATIONS_SNAPSHOT_RESTORER + 1 @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = KEY_DARK_MODE_SNAPSHOT_RESTORER + 1 } } tests/src/com/android/customization/model/mode/DarkModeSnapshotRestorerTest.kt 0 → 100644 +107 −0 Original line number Diff line number Diff line /* * 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.customization.model.mode import androidx.test.filters.SmallTest import com.android.wallpaper.testing.FakeSnapshotStore 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 import org.junit.runner.RunWith import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class DarkModeSnapshotRestorerTest { private lateinit var underTest: DarkModeSnapshotRestorer private lateinit var testScope: TestScope private var isActive = false @Before fun setUp() { val testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) underTest = DarkModeSnapshotRestorer( backgroundDispatcher = testDispatcher, isActive = { isActive }, setActive = { isActive = it }, ) } @Test fun `set up and restore - active`() = testScope.runTest { isActive = true val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val storedSnapshot = store.retrieve() underTest.restoreToSnapshot(snapshot = storedSnapshot) assertThat(isActive).isTrue() } @Test fun `set up and restore - inactive`() = testScope.runTest { isActive = false val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val storedSnapshot = store.retrieve() underTest.restoreToSnapshot(snapshot = storedSnapshot) assertThat(isActive).isFalse() } @Test fun `set up - deactivate - restore to active`() = testScope.runTest { isActive = true val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val initialSnapshot = store.retrieve() underTest.store(isActivated = false) underTest.restoreToSnapshot(snapshot = initialSnapshot) assertThat(isActive).isTrue() } @Test fun `set up - activate - restore to inactive`() = testScope.runTest { isActive = false val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val initialSnapshot = store.retrieve() underTest.store(isActivated = true) underTest.restoreToSnapshot(snapshot = initialSnapshot) assertThat(isActive).isFalse() } } Loading
src/com/android/customization/model/mode/DarkModeSectionController.java +7 −1 Original line number Diff line number Diff line Loading @@ -59,12 +59,17 @@ public class DarkModeSectionController implements private Context mContext; private DarkModeSectionView mDarkModeSectionView; private final DarkModeSnapshotRestorer mSnapshotRestorer; public DarkModeSectionController(Context context, Lifecycle lifecycle) { public DarkModeSectionController( Context context, Lifecycle lifecycle, DarkModeSnapshotRestorer snapshotRestorer) { mContext = context; mLifecycle = lifecycle; mPowerManager = context.getSystemService(PowerManager.class); mLifecycle.addObserver(this); mSnapshotRestorer = snapshotRestorer; } @OnLifecycleEvent(Lifecycle.Event.ON_START) Loading Loading @@ -132,6 +137,7 @@ public class DarkModeSectionController implements mDarkModeSectionView.announceForAccessibility( context.getString(R.string.mode_changed)); uiModeManager.setNightModeActivated(viewActivated); mSnapshotRestorer.store(viewActivated); }, /* delayMillis= */ shortDelay); } Loading
src/com/android/customization/model/mode/DarkModeSnapshotRestorer.kt 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * 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.customization.model.mode import android.app.UiModeManager import android.content.Context import android.content.res.Configuration import androidx.annotation.VisibleForTesting import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer import com.android.wallpaper.picker.undo.domain.interactor.SnapshotStore import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext class DarkModeSnapshotRestorer : SnapshotRestorer { private val backgroundDispatcher: CoroutineDispatcher private val isActive: () -> Boolean private val setActive: suspend (Boolean) -> Unit private lateinit var store: SnapshotStore constructor( context: Context, manager: UiModeManager, backgroundDispatcher: CoroutineDispatcher, ) : this( backgroundDispatcher = backgroundDispatcher, isActive = { context.applicationContext.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES != 0 }, setActive = { isActive -> manager.setNightModeActivated(isActive) }, ) @VisibleForTesting constructor( backgroundDispatcher: CoroutineDispatcher, isActive: () -> Boolean, setActive: suspend (Boolean) -> Unit, ) { this.backgroundDispatcher = backgroundDispatcher this.isActive = isActive this.setActive = setActive } override suspend fun setUpSnapshotRestorer(store: SnapshotStore): RestorableSnapshot { this.store = store return snapshot( isActivated = isActive(), ) } override suspend fun restoreToSnapshot(snapshot: RestorableSnapshot) { val isActivated = snapshot.args[KEY]?.toBoolean() == true withContext(backgroundDispatcher) { setActive(isActivated) } } fun store( isActivated: Boolean, ) { store.store( snapshot( isActivated = isActivated, ), ) } private fun snapshot( isActivated: Boolean, ): RestorableSnapshot { return RestorableSnapshot( args = buildMap { put( KEY, isActivated.toString(), ) } ) } companion object { private const val KEY = "is_activated" } }
src/com/android/customization/module/DefaultCustomizationSections.java +13 −5 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ import com.android.customization.model.color.ColorSectionController2; import com.android.customization.model.grid.GridOptionsManager; import com.android.customization.model.grid.GridSectionController; import com.android.customization.model.mode.DarkModeSectionController; import com.android.customization.model.mode.DarkModeSnapshotRestorer; import com.android.customization.model.themedicon.ThemedIconSectionController; import com.android.customization.model.themedicon.ThemedIconSwitchProvider; import com.android.customization.picker.clock.data.repository.ClockRegistryProvider; Loading Loading @@ -56,6 +57,7 @@ public final class DefaultCustomizationSections implements CustomizationSections mClockCarouselViewModelProvider; private final PreviewWithClockCarouselSectionController.ClockViewFactoryProvider mClockViewFactoryProvider; private final DarkModeSnapshotRestorer mDarkModeSnapshotRestorer; public DefaultCustomizationSections( KeyguardQuickAffordancePickerInteractor keyguardQuickAffordancePickerInteractor, Loading @@ -65,7 +67,8 @@ public final class DefaultCustomizationSections implements CustomizationSections BaseFlags flags, ClockRegistryProvider clockRegistryProvider, ClockCarouselViewModelProvider clockCarouselViewModelProvider, ClockViewFactoryProvider clockViewFactoryProvider) { ClockViewFactoryProvider clockViewFactoryProvider, DarkModeSnapshotRestorer darkModeSnapshotRestorer) { mKeyguardQuickAffordancePickerInteractor = keyguardQuickAffordancePickerInteractor; mKeyguardQuickAffordancePickerViewModelFactory = keyguardQuickAffordancePickerViewModelFactory; Loading @@ -74,6 +77,7 @@ public final class DefaultCustomizationSections implements CustomizationSections mClockRegistryProvider = clockRegistryProvider; mClockCarouselViewModelProvider = clockCarouselViewModelProvider; mClockViewFactoryProvider = clockViewFactoryProvider; mDarkModeSnapshotRestorer = darkModeSnapshotRestorer; } @Override Loading Loading @@ -157,8 +161,10 @@ public final class DefaultCustomizationSections implements CustomizationSections case HOME_SCREEN: // Dark/Light theme section. sectionControllers.add(new DarkModeSectionController(activity, lifecycleOwner.getLifecycle())); sectionControllers.add(new DarkModeSectionController( activity, lifecycleOwner.getLifecycle(), mDarkModeSnapshotRestorer)); // Themed app icon section. sectionControllers.add(new ThemedIconSectionController( Loading Loading @@ -198,8 +204,10 @@ public final class DefaultCustomizationSections implements CustomizationSections activity, wallpaperColorsViewModel, lifecycleOwner, savedInstanceState)); // Dark/Light theme section. sectionControllers.add(new DarkModeSectionController(activity, lifecycleOwner.getLifecycle())); sectionControllers.add(new DarkModeSectionController( activity, lifecycleOwner.getLifecycle(), mDarkModeSnapshotRestorer)); // Themed app icon section. sectionControllers.add(new ThemedIconSectionController( Loading
src/com/android/customization/module/ThemePickerInjector.kt +21 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.customization.module import android.app.UiModeManager import android.content.Context import android.content.Intent import android.net.Uri Loading @@ -22,6 +23,7 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import com.android.customization.model.mode.DarkModeSnapshotRestorer import com.android.customization.model.theme.OverlayManagerCompat import com.android.customization.model.theme.ThemeBundleProvider import com.android.customization.model.theme.ThemeManager Loading Loading @@ -86,6 +88,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null private var colorPickerInteractor: ColorPickerInteractor? = null private var colorPickerViewModelFactory: ColorPickerViewModel.Factory? = null private var darkModeSnapshotRestorer: DarkModeSnapshotRestorer? = null override fun getCustomizationSections(activity: ComponentActivity): CustomizationSections { return customizationSections Loading @@ -112,7 +115,8 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject registry = registry, ) } } }, getDarkModeSnapshotRestorer(activity), ) .also { customizationSections = it } } Loading Loading @@ -173,6 +177,7 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject getKeyguardQuickAffordanceSnapshotRestorer(context) this[KEY_WALLPAPER_SNAPSHOT_RESTORER] = getWallpaperSnapshotRestorer(context) this[KEY_NOTIFICATIONS_SNAPSHOT_RESTORER] = getNotificationsSnapshotRestorer(context) this[KEY_DARK_MODE_SNAPSHOT_RESTORER] = getDarkModeSnapshotRestorer(context) } } Loading Loading @@ -348,6 +353,18 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject .also { colorPickerViewModelFactory = it } } protected fun getDarkModeSnapshotRestorer( context: Context, ): DarkModeSnapshotRestorer { return darkModeSnapshotRestorer ?: DarkModeSnapshotRestorer( context = context, manager = context.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager, backgroundDispatcher = Dispatchers.IO, ) .also { darkModeSnapshotRestorer = it } } companion object { @JvmStatic private val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER = Loading @@ -356,11 +373,13 @@ open class ThemePickerInjector : WallpaperPicker2Injector(), CustomizationInject private val KEY_WALLPAPER_SNAPSHOT_RESTORER = KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER + 1 @JvmStatic private val KEY_NOTIFICATIONS_SNAPSHOT_RESTORER = KEY_WALLPAPER_SNAPSHOT_RESTORER + 1 @JvmStatic private val KEY_DARK_MODE_SNAPSHOT_RESTORER = KEY_NOTIFICATIONS_SNAPSHOT_RESTORER + 1 /** * When this injector is overridden, this is the minimal value that should be used by * restorers returns in [getSnapshotRestorers]. */ @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = KEY_NOTIFICATIONS_SNAPSHOT_RESTORER + 1 @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = KEY_DARK_MODE_SNAPSHOT_RESTORER + 1 } }
tests/src/com/android/customization/model/mode/DarkModeSnapshotRestorerTest.kt 0 → 100644 +107 −0 Original line number Diff line number Diff line /* * 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.customization.model.mode import androidx.test.filters.SmallTest import com.android.wallpaper.testing.FakeSnapshotStore 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 import org.junit.runner.RunWith import org.junit.runners.JUnit4 @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class DarkModeSnapshotRestorerTest { private lateinit var underTest: DarkModeSnapshotRestorer private lateinit var testScope: TestScope private var isActive = false @Before fun setUp() { val testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) underTest = DarkModeSnapshotRestorer( backgroundDispatcher = testDispatcher, isActive = { isActive }, setActive = { isActive = it }, ) } @Test fun `set up and restore - active`() = testScope.runTest { isActive = true val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val storedSnapshot = store.retrieve() underTest.restoreToSnapshot(snapshot = storedSnapshot) assertThat(isActive).isTrue() } @Test fun `set up and restore - inactive`() = testScope.runTest { isActive = false val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val storedSnapshot = store.retrieve() underTest.restoreToSnapshot(snapshot = storedSnapshot) assertThat(isActive).isFalse() } @Test fun `set up - deactivate - restore to active`() = testScope.runTest { isActive = true val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val initialSnapshot = store.retrieve() underTest.store(isActivated = false) underTest.restoreToSnapshot(snapshot = initialSnapshot) assertThat(isActive).isTrue() } @Test fun `set up - activate - restore to inactive`() = testScope.runTest { isActive = false val store = FakeSnapshotStore() store.store(underTest.setUpSnapshotRestorer(store = store)) val initialSnapshot = store.retrieve() underTest.store(isActivated = true) underTest.restoreToSnapshot(snapshot = initialSnapshot) assertThat(isActive).isFalse() } }