• 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.wallpaper.customization.ui.viewmodel
18 
19 import com.android.customization.picker.mode.ui.viewmodel.DarkModeViewModel
20 import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil
21 import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationOptionsViewModel
22 import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationOptionsViewModelFactory
23 import com.android.wallpaper.picker.customization.ui.viewmodel.DefaultCustomizationOptionsViewModel
24 import dagger.assisted.Assisted
25 import dagger.assisted.AssistedFactory
26 import dagger.assisted.AssistedInject
27 import dagger.hilt.android.scopes.ViewModelScoped
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.ExperimentalCoroutinesApi
30 import kotlinx.coroutines.Job
31 import kotlinx.coroutines.flow.Flow
32 import kotlinx.coroutines.flow.SharingStarted
33 import kotlinx.coroutines.flow.StateFlow
34 import kotlinx.coroutines.flow.combine
35 import kotlinx.coroutines.flow.flatMapLatest
36 import kotlinx.coroutines.flow.flow
37 import kotlinx.coroutines.flow.map
38 import kotlinx.coroutines.flow.stateIn
39 import kotlinx.coroutines.launch
40 
41 class ThemePickerCustomizationOptionsViewModel
42 @AssistedInject
43 constructor(
44     defaultCustomizationOptionsViewModelFactory: DefaultCustomizationOptionsViewModel.Factory,
45     keyguardQuickAffordancePickerViewModel2Factory: KeyguardQuickAffordancePickerViewModel2.Factory,
46     colorPickerViewModel2Factory: ColorPickerViewModel2.Factory,
47     clockPickerViewModelFactory: ClockPickerViewModel.Factory,
48     shapeGridPickerViewModelFactory: ShapeGridPickerViewModel.Factory,
49     appIconPickerViewModelFactory: AppIconPickerViewModel.Factory,
50     val colorContrastSectionViewModel: ColorContrastSectionViewModel2,
51     val darkModeViewModel: DarkModeViewModel,
52     val themedIconViewModel: ThemedIconViewModel,
53     @Assisted private val viewModelScope: CoroutineScope,
54 ) : CustomizationOptionsViewModel {
55 
56     private val defaultCustomizationOptionsViewModel =
57         defaultCustomizationOptionsViewModelFactory.create(viewModelScope)
58 
59     override val wallpaperCarouselViewModel =
60         defaultCustomizationOptionsViewModel.wallpaperCarouselViewModel
61 
62     val clockPickerViewModel = clockPickerViewModelFactory.create(viewModelScope = viewModelScope)
63     val keyguardQuickAffordancePickerViewModel2 =
64         keyguardQuickAffordancePickerViewModel2Factory.create(viewModelScope = viewModelScope)
65     val colorPickerViewModel2 = colorPickerViewModel2Factory.create(viewModelScope = viewModelScope)
66     val shapeGridPickerViewModel =
67         shapeGridPickerViewModelFactory.create(viewModelScope = viewModelScope)
68     val appIconPickerViewModel =
69         appIconPickerViewModelFactory.create(viewModelScope = viewModelScope)
70 
71     private var onApplyJob: Job? = null
72 
73     override val selectedOption = defaultCustomizationOptionsViewModel.selectedOption
74 
75     override val discardChangesDialogViewModel =
76         defaultCustomizationOptionsViewModel.discardChangesDialogViewModel
77 
78     override fun handleBackPressed(): Boolean {
79         if (isApplyButtonEnabled.value) {
80             defaultCustomizationOptionsViewModel.showDiscardChangesDialogViewModel()
81             return true
82         }
83 
84         return defaultCustomizationOptionsViewModel.handleBackPressed()
85     }
86 
87     override fun resetPreview() {
88         defaultCustomizationOptionsViewModel.resetPreview()
89 
90         keyguardQuickAffordancePickerViewModel2.resetPreview()
91         shapeGridPickerViewModel.resetPreview()
92         clockPickerViewModel.resetPreview()
93         colorPickerViewModel2.resetPreview()
94         darkModeViewModel.resetPreview()
95     }
96 
97     val onCustomizeClockClicked: Flow<(() -> Unit)?> =
98         selectedOption.map {
99             if (it == null) {
100                 {
101                     defaultCustomizationOptionsViewModel.selectOption(
102                         ThemePickerCustomizationOptionUtil.ThemePickerLockCustomizationOption.CLOCK
103                     )
104                 }
105             } else {
106                 null
107             }
108         }
109 
110     val onCustomizeShortcutClicked: Flow<(() -> Unit)?> =
111         selectedOption.map {
112             if (it == null) {
113                 {
114                     defaultCustomizationOptionsViewModel.selectOption(
115                         ThemePickerCustomizationOptionUtil.ThemePickerLockCustomizationOption
116                             .SHORTCUTS
117                     )
118                 }
119             } else {
120                 null
121             }
122         }
123 
124     val onCustomizeColorsClicked: Flow<(() -> Unit)?> =
125         selectedOption.map {
126             if (it == null) {
127                 {
128                     defaultCustomizationOptionsViewModel.selectOption(
129                         ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.COLORS
130                     )
131                 }
132             } else {
133                 null
134             }
135         }
136 
137     val onCustomizeShapeGridClicked: Flow<(() -> Unit)?> =
138         selectedOption.map {
139             if (it == null) {
140                 {
141                     defaultCustomizationOptionsViewModel.selectOption(
142                         ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption
143                             .APP_SHAPE_GRID
144                     )
145                 }
146             } else {
147                 null
148             }
149         }
150 
151     @OptIn(ExperimentalCoroutinesApi::class)
152     val onApplyButtonClicked: Flow<((onComplete: () -> Unit) -> Unit)?> =
153         selectedOption
154             .flatMapLatest {
155                 when (it) {
156                     ThemePickerCustomizationOptionUtil.ThemePickerLockCustomizationOption.CLOCK ->
157                         clockPickerViewModel.onApply
158                     ThemePickerCustomizationOptionUtil.ThemePickerLockCustomizationOption
159                         .SHORTCUTS -> keyguardQuickAffordancePickerViewModel2.onApply
160                     ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption
161                         .APP_SHAPE_GRID -> shapeGridPickerViewModel.onApply
162                     ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.COLORS ->
163                         combine(colorPickerViewModel2.onApply, darkModeViewModel.onApply) {
164                             colorOnApply,
165                             darkModeOnApply ->
166                             if (colorOnApply == null && darkModeOnApply == null) {
167                                 null
168                             } else {
169                                 {
170                                     colorOnApply?.invoke()
171                                     darkModeOnApply?.invoke()
172                                 }
173                             }
174                         }
175                     else -> flow { emit(null) }
176                 }
177             }
178             .map { onApply ->
179                 if (onApply != null) {
180                     fun(onComplete: () -> Unit) {
181                         // Prevent double apply
182                         if (onApplyJob?.isActive != true) {
183                             onApplyJob =
184                                 viewModelScope.launch {
185                                     onApply()
186                                     onComplete()
187                                     onApplyJob = null
188                                 }
189                         }
190                     }
191                 } else {
192                     null
193                 }
194             }
195             .stateIn(viewModelScope, SharingStarted.Eagerly, null)
196 
197     val isApplyButtonEnabled: StateFlow<Boolean> =
198         onApplyButtonClicked
199             .map { it != null }
200             .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
201 
202     val isApplyButtonVisible: Flow<Boolean> = selectedOption.map { it != null }
203 
204     @ViewModelScoped
205     @AssistedFactory
206     interface Factory : CustomizationOptionsViewModelFactory {
207         override fun create(
208             viewModelScope: CoroutineScope
209         ): ThemePickerCustomizationOptionsViewModel
210     }
211 }
212