• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.customization.picker.mode.ui.viewmodel
18 
19 import com.android.customization.module.logging.ThemesUserEventLogger
20 import com.android.customization.picker.mode.domain.interactor.DarkModeInteractor
21 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
22 import dagger.hilt.android.scopes.ViewModelScoped
23 import javax.inject.Inject
24 import kotlinx.coroutines.coroutineScope
25 import kotlinx.coroutines.flow.Flow
26 import kotlinx.coroutines.flow.MutableStateFlow
27 import kotlinx.coroutines.flow.asStateFlow
28 import kotlinx.coroutines.flow.combine
29 import kotlinx.coroutines.flow.drop
30 import kotlinx.coroutines.flow.take
31 import kotlinx.coroutines.launch
32 
33 @ViewModelScoped
34 class DarkModeViewModel
35 @Inject
36 constructor(
37     private val colorUpdateViewModel: ColorUpdateViewModel,
38     private val interactor: DarkModeInteractor,
39     private val logger: ThemesUserEventLogger,
40 ) {
41     private val isDarkMode = interactor.isDarkMode
42     val isEnabled = interactor.isEnabled
43 
44     private val _overridingIsDarkMode = MutableStateFlow<Boolean?>(null)
45     val overridingIsDarkMode = _overridingIsDarkMode.asStateFlow()
46     val previewingIsDarkMode =
47         combine(overridingIsDarkMode, isDarkMode, isEnabled) { override, current, isEnabled ->
48             if (isEnabled) {
49                 override ?: current
50             } else current
51         }
52 
53     val toggleDarkMode =
54         combine(overridingIsDarkMode, isDarkMode) { override, current ->
55             // Only set override if its value is different from current, else set to null
56             {
57                 _overridingIsDarkMode.value =
58                     if (override == null || override == current) !current else null
59             }
60         }
61 
62     val onApply: Flow<(suspend () -> Unit)?> =
63         combine(overridingIsDarkMode, isDarkMode, isEnabled) { override, current, isEnabled ->
64             if (override != null && override != current && isEnabled) {
65                 {
66                     coroutineScope {
67                         launch { interactor.setIsDarkMode(override) }
68                         // Dark mode change also invokes a color update. Suspend until both dark
69                         // mode and color are updated.
70                         combine(
71                                 // Omit the first value which is emitted on subscribe.
72                                 isDarkMode.drop(1).take(1),
73                                 colorUpdateViewModel.systemColorsUpdatedNoReplay.take(1),
74                                 ::Pair,
75                             )
76                             .collect { (_, _) ->
77                                 return@collect
78                             }
79                         logger.logDarkThemeApplied(override)
80                     }
81                 }
82             } else null
83         }
84 
85     fun resetPreview() {
86         _overridingIsDarkMode.value = null
87     }
88 }
89