1 /* <lambda>null2 * Copyright (C) 2023 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 package com.android.customization.picker.clock.ui.viewmodel 17 18 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor 19 import kotlinx.coroutines.ExperimentalCoroutinesApi 20 import kotlinx.coroutines.delay 21 import kotlinx.coroutines.flow.Flow 22 import kotlinx.coroutines.flow.MutableStateFlow 23 import kotlinx.coroutines.flow.combine 24 import kotlinx.coroutines.flow.distinctUntilChanged 25 import kotlinx.coroutines.flow.flatMapLatest 26 import kotlinx.coroutines.flow.map 27 import kotlinx.coroutines.flow.mapLatest 28 import kotlinx.coroutines.flow.mapNotNull 29 30 /** 31 * Clock carousel view model that provides data for the carousel of clock previews. When there is 32 * only one item, we should show a single clock preview instead of a carousel. 33 */ 34 class ClockCarouselViewModel( 35 private val interactor: ClockPickerInteractor, 36 ) { 37 @OptIn(ExperimentalCoroutinesApi::class) 38 val allClockIds: Flow<List<String>> = 39 interactor.allClocks.mapLatest { allClocks -> 40 // Delay to avoid the case that the full list of clocks is not initiated. 41 delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS) 42 allClocks.map { it.clockId } 43 } 44 45 val seedColor: Flow<Int?> = interactor.seedColor 46 47 private val shouldShowCarousel = MutableStateFlow(false) 48 val isCarouselVisible: Flow<Boolean> = 49 combine(allClockIds.map { it.size > 1 }.distinctUntilChanged(), shouldShowCarousel) { 50 hasMoreThanOneClock, 51 shouldShowCarousel -> 52 hasMoreThanOneClock && shouldShowCarousel 53 } 54 .distinctUntilChanged() 55 56 @OptIn(ExperimentalCoroutinesApi::class) 57 val selectedIndex: Flow<Int> = 58 allClockIds 59 .flatMapLatest { allClockIds -> 60 interactor.selectedClockId.map { selectedClockId -> 61 val index = allClockIds.indexOf(selectedClockId) 62 if (index >= 0) { 63 index 64 } else { 65 null 66 } 67 } 68 } 69 .mapNotNull { it } 70 71 // Handle the case when there is only one clock in the carousel 72 private val shouldShowSingleClock = MutableStateFlow(false) 73 val isSingleClockViewVisible: Flow<Boolean> = 74 combine(allClockIds.map { it.size == 1 }.distinctUntilChanged(), shouldShowSingleClock) { 75 hasOneClock, 76 shouldShowSingleClock -> 77 hasOneClock && shouldShowSingleClock 78 } 79 .distinctUntilChanged() 80 81 val clockId: Flow<String> = 82 allClockIds 83 .map { allClockIds -> if (allClockIds.size == 1) allClockIds[0] else null } 84 .mapNotNull { it } 85 86 fun setSelectedClock(clockId: String) { 87 interactor.setSelectedClock(clockId) 88 } 89 90 fun showClockCarousel(shouldShow: Boolean) { 91 shouldShowCarousel.value = shouldShow 92 shouldShowSingleClock.value = shouldShow 93 } 94 95 companion object { 96 const val CLOCKS_EVENT_UPDATE_DELAY_MILLIS: Long = 100 97 } 98 } 99