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 */ 17 18 package com.android.customization.picker.clock.domain.interactor 19 20 import androidx.annotation.ColorInt 21 import androidx.annotation.IntRange 22 import com.android.customization.picker.clock.data.repository.ClockPickerRepository 23 import com.android.customization.picker.clock.shared.ClockSize 24 import com.android.customization.picker.clock.shared.model.ClockMetadataModel 25 import com.android.customization.picker.clock.shared.model.ClockSnapshotModel 26 import javax.inject.Provider 27 import kotlinx.coroutines.flow.Flow 28 import kotlinx.coroutines.flow.distinctUntilChanged 29 import kotlinx.coroutines.flow.firstOrNull 30 import kotlinx.coroutines.flow.map 31 32 /** 33 * Interactor for accessing application clock settings, as well as selecting and configuring custom 34 * clocks. 35 */ 36 class ClockPickerInteractor( 37 private val repository: ClockPickerRepository, 38 private val snapshotRestorer: Provider<ClockPickerSnapshotRestorer>, 39 ) { 40 41 val allClocks: Flow<List<ClockMetadataModel>> = repository.allClocks 42 43 val selectedClockId: Flow<String> = 44 repository.selectedClock.map { clock -> clock.clockId }.distinctUntilChanged() 45 46 val selectedColorId: Flow<String?> = 47 repository.selectedClock.map { clock -> clock.selectedColorId }.distinctUntilChanged() 48 49 val colorToneProgress: Flow<Int> = 50 repository.selectedClock.map { clock -> clock.colorToneProgress } 51 52 val seedColor: Flow<Int?> = repository.selectedClock.map { clock -> clock.seedColor } 53 54 val selectedClockSize: Flow<ClockSize> = repository.selectedClockSize 55 56 suspend fun setSelectedClock(clockId: String) { 57 // Use the [clockId] to override saved clock id, since it might not be updated in time 58 setClockOption(ClockSnapshotModel(clockId = clockId)) 59 } 60 61 suspend fun setClockColor( 62 selectedColorId: String?, 63 @IntRange(from = 0, to = 100) colorToneProgress: Int, 64 @ColorInt seedColor: Int?, 65 ) { 66 // Use the color to override saved color, since it might not be updated in time 67 setClockOption( 68 ClockSnapshotModel( 69 selectedColorId = selectedColorId, 70 colorToneProgress = colorToneProgress, 71 seedColor = seedColor, 72 ) 73 ) 74 } 75 76 suspend fun setClockSize(size: ClockSize) { 77 // Use the [ClockSize] to override saved clock size, since it might not be updated in time 78 setClockOption(ClockSnapshotModel(clockSize = size)) 79 } 80 81 suspend fun setClockOption(clockSnapshotModel: ClockSnapshotModel) { 82 // [ClockCarouselViewModel] is monitoring the [ClockPickerInteractor.setSelectedClock] job, 83 // so it needs to finish last. 84 storeCurrentClockOption(clockSnapshotModel) 85 86 clockSnapshotModel.clockSize?.let { repository.setClockSize(it) } 87 clockSnapshotModel.colorToneProgress?.let { 88 repository.setClockColor( 89 selectedColorId = clockSnapshotModel.selectedColorId, 90 colorToneProgress = clockSnapshotModel.colorToneProgress, 91 seedColor = clockSnapshotModel.seedColor 92 ) 93 } 94 clockSnapshotModel.clockId?.let { repository.setSelectedClock(it) } 95 } 96 97 /** 98 * Gets the [ClockSnapshotModel] from the storage and override with [latestOption]. 99 * 100 * The storage might be in the middle of a write, and not reflecting the user's options, always 101 * pass in a [ClockSnapshotModel] if we know it's the latest option from a user's point of view. 102 * 103 * [selectedColorId] and [seedColor] have null state collide with nullable type, but we know 104 * they are presented whenever there's a [colorToneProgress]. 105 */ 106 suspend fun getCurrentClockToRestore(latestOption: ClockSnapshotModel? = null) = 107 ClockSnapshotModel( 108 clockId = latestOption?.clockId ?: selectedClockId.firstOrNull(), 109 clockSize = latestOption?.clockSize ?: selectedClockSize.firstOrNull(), 110 colorToneProgress = latestOption?.colorToneProgress ?: colorToneProgress.firstOrNull(), 111 selectedColorId = latestOption?.colorToneProgress?.let { latestOption.selectedColorId } 112 ?: selectedColorId.firstOrNull(), 113 seedColor = latestOption?.colorToneProgress?.let { latestOption.seedColor } 114 ?: seedColor.firstOrNull(), 115 ) 116 117 private suspend fun storeCurrentClockOption(clockSnapshotModel: ClockSnapshotModel) { 118 val option = getCurrentClockToRestore(clockSnapshotModel) 119 snapshotRestorer.get().storeSnapshot(option) 120 } 121 } 122