Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27cccd86 authored by Catherine Liang's avatar Catherine Liang Committed by Android (Google) Code Review
Browse files

Merge "Fix dismiss dialog shows on dark mode apply (2/2)" into main

parents b8520b15 fa76527f
Loading
Loading
Loading
Loading
+25 −3
Original line number Diff line number Diff line
@@ -18,17 +18,26 @@ package com.android.customization.picker.mode.ui.viewmodel

import com.android.customization.module.logging.ThemesUserEventLogger
import com.android.customization.picker.mode.domain.interactor.DarkModeInteractor
import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch

@ViewModelScoped
class DarkModeViewModel
@Inject
constructor(private val interactor: DarkModeInteractor, private val logger: ThemesUserEventLogger) {
constructor(
    private val colorUpdateViewModel: ColorUpdateViewModel,
    private val interactor: DarkModeInteractor,
    private val logger: ThemesUserEventLogger,
) {
    private val isDarkMode = interactor.isDarkMode
    val isEnabled = interactor.isEnabled

@@ -54,9 +63,22 @@ constructor(private val interactor: DarkModeInteractor, private val logger: Them
        combine(overridingIsDarkMode, isDarkMode, isEnabled) { override, current, isEnabled ->
            if (override != null && override != current && isEnabled) {
                {
                    interactor.setIsDarkMode(override)
                    coroutineScope {
                        launch { interactor.setIsDarkMode(override) }
                        // Dark mode change also invokes a color update. Suspend until both dark
                        // mode and color are updated.
                        combine(
                                // Omit the first value which is emitted on subscribe.
                                isDarkMode.drop(1).take(1),
                                colorUpdateViewModel.systemColorsUpdatedNoReplay.take(1),
                                ::Pair,
                            )
                            .collect { (_, _) ->
                                return@collect
                            }
                        logger.logDarkThemeApplied(override)
                    }
                }
            } else null
        }

+12 −9
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import dagger.assisted.AssistedInject
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -169,7 +170,8 @@ constructor(
                    null
                } else {
                    {
                        interactor.select(it)
                        coroutineScope {
                            launch { interactor.select(it) }
                            // Suspend until first color update
                            colorUpdateViewModel.systemColorsUpdatedNoReplay.take(1).collect {
                                return@collect
@@ -183,6 +185,7 @@ constructor(
                    }
                }
            }
        }

    fun resetPreview() {
        overridingColorOption.value = null
+30 −7
Original line number Diff line number Diff line
@@ -16,20 +16,27 @@

package com.android.customization.picker.mode.ui.viewmodel

import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import com.android.customization.module.logging.TestThemesUserEventLogger
import com.android.customization.picker.mode.data.repository.DarkModeRepository
import com.android.customization.picker.mode.data.repository.DarkModeStateRepository
import com.android.customization.picker.mode.domain.interactor.DarkModeInteractor
import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
import com.android.wallpaper.testing.FakePowerManager
import com.android.wallpaper.testing.FakeUiModeManager
import com.android.wallpaper.testing.collectLastValue
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.internal.lifecycle.RetainedLifecycleImpl
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.Before
@@ -46,20 +53,26 @@ class DarkModeViewModelTest {

    @Inject lateinit var uiModeManager: FakeUiModeManager
    @Inject lateinit var powerManager: FakePowerManager
    @Inject lateinit var darkModeStateRepository: DarkModeStateRepository
    @Inject lateinit var darkModeRepository: DarkModeRepository
    @Inject lateinit var darkModeInteractor: DarkModeInteractor
    @Inject lateinit var logger: TestThemesUserEventLogger
    private lateinit var darkModeViewModel: DarkModeViewModel

    @Inject lateinit var testDispatcher: TestDispatcher
    @Inject lateinit var testScope: TestScope

    private lateinit var context: Context
    private lateinit var colorUpdateViewModel: ColorUpdateViewModel
    private lateinit var darkModeViewModel: DarkModeViewModel

    @Before
    fun setUp() {
        hiltRule.inject()
        Dispatchers.setMain(testDispatcher)

        darkModeViewModel = DarkModeViewModel(darkModeInteractor, logger)
        context = InstrumentationRegistry.getInstrumentation().targetContext
        colorUpdateViewModel =
            ColorUpdateViewModel(context, RetainedLifecycleImpl(), darkModeStateRepository)
        darkModeViewModel = DarkModeViewModel(colorUpdateViewModel, darkModeInteractor, logger)
    }

    @Test
@@ -141,10 +154,9 @@ class DarkModeViewModelTest {
            uiModeManager.setNightModeActivated(false)
            darkModeRepository.refreshIsDarkMode()
            val getToggleDarkMode = collectLastValue(darkModeViewModel.toggleDarkMode)
            val onApply = collectLastValue(darkModeViewModel.onApply)

            getToggleDarkMode()?.invoke()
            onApply()?.invoke()
            applyDarkMode()

            assertThat(logger.useDarkTheme).isTrue()
        }
@@ -156,12 +168,23 @@ class DarkModeViewModelTest {
            uiModeManager.setNightModeActivated(false)
            darkModeRepository.refreshIsDarkMode()
            val getToggleDarkMode = collectLastValue(darkModeViewModel.toggleDarkMode)
            val onApply = collectLastValue(darkModeViewModel.onApply)

            getToggleDarkMode()?.invoke()
            onApply()?.invoke()
            applyDarkMode()

            assertThat(uiModeManager.getIsNightModeActivated()).isTrue()
        }
    }

    /** Simulates a user applying the previewing dark mode, and the apply completes. */
    private fun TestScope.applyDarkMode() {
        val onApply = collectLastValue(darkModeViewModel.onApply)()
        testScope.launch { onApply?.invoke() }
        // Run coroutine launched in DarkModeViewModel#onApply
        runCurrent()
        // Simulate dark mode and color update config change
        colorUpdateViewModel.updateDarkModeAndColors()
        // Run coroutine launched in colorUpdateViewModel#updateColors
        runCurrent()
    }
}