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