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 com.android.systemui.plugins.clocks.ClockFontAxisSetting 27 import javax.inject.Inject 28 import javax.inject.Singleton 29 import kotlinx.coroutines.flow.Flow 30 import kotlinx.coroutines.flow.distinctUntilChanged 31 import kotlinx.coroutines.flow.firstOrNull 32 import kotlinx.coroutines.flow.map 33 34 /** 35 * Interactor for accessing application clock settings, as well as selecting and configuring custom 36 * clocks. 37 */ 38 @Singleton 39 class ClockPickerInteractor 40 @Inject 41 constructor( 42 private val repository: ClockPickerRepository, 43 private val snapshotRestorer: ClockPickerSnapshotRestorer, 44 ) { 45 46 val allClocks: Flow<List<ClockMetadataModel>> = repository.allClocks 47 48 val selectedClockId: Flow<String> = 49 repository.selectedClock.map { clock -> clock.clockId }.distinctUntilChanged() 50 51 val selectedClock: Flow<ClockMetadataModel> = repository.selectedClock 52 53 val selectedColorId: Flow<String?> = 54 repository.selectedClock.map { clock -> clock.selectedColorId }.distinctUntilChanged() 55 56 val colorToneProgress: Flow<Int> = 57 repository.selectedClock.map { clock -> clock.colorToneProgress } 58 59 val seedColor: Flow<Int?> = repository.selectedClock.map { clock -> clock.seedColor } 60 61 val axisSettings: Flow<List<ClockFontAxisSetting>?> = 62 repository.selectedClock.map { clock -> clock.fontAxes.map { it.toSetting() } } 63 64 val selectedClockSize: Flow<ClockSize> = repository.selectedClockSize 65 66 suspend fun setSelectedClock(clockId: String) { 67 // Use the [clockId] to override saved clock id, since it might not be updated in time 68 setClockOption(ClockSnapshotModel(clockId = clockId)) 69 } 70 71 suspend fun setClockColor( 72 selectedColorId: String?, 73 @IntRange(from = 0, to = 100) colorToneProgress: Int, 74 @ColorInt seedColor: Int?, 75 ) { 76 // Use the color to override saved color, since it might not be updated in time 77 setClockOption( 78 ClockSnapshotModel( 79 selectedColorId = selectedColorId, 80 colorToneProgress = colorToneProgress, 81 seedColor = seedColor, 82 ) 83 ) 84 } 85 86 suspend fun setClockSize(size: ClockSize) { 87 // Use the [ClockSize] to override saved clock size, since it might not be updated in time 88 setClockOption(ClockSnapshotModel(clockSize = size)) 89 } 90 91 suspend fun setClockFontAxes(axisSettings: List<ClockFontAxisSetting>) { 92 setClockOption(ClockSnapshotModel(axisSettings = axisSettings)) 93 } 94 95 suspend fun applyClock( 96 clockId: String?, 97 size: ClockSize?, 98 selectedColorId: String?, 99 @IntRange(from = 0, to = 100) colorToneProgress: Int?, 100 @ColorInt seedColor: Int?, 101 axisSettings: List<ClockFontAxisSetting>, 102 ) { 103 setClockOption( 104 ClockSnapshotModel( 105 clockId = clockId, 106 clockSize = size, 107 selectedColorId = selectedColorId, 108 colorToneProgress = colorToneProgress, 109 seedColor = seedColor, 110 axisSettings = axisSettings, 111 ) 112 ) 113 } 114 115 private suspend fun setClockOption(clockSnapshotModel: ClockSnapshotModel) { 116 // [ClockCarouselViewModel] is monitoring the [ClockPickerInteractor.setSelectedClock] job, 117 // so it needs to finish last. 118 storeCurrentClockOption(clockSnapshotModel) 119 120 clockSnapshotModel.clockSize?.let { repository.setClockSize(it) } 121 clockSnapshotModel.colorToneProgress?.let { 122 repository.setClockColor( 123 selectedColorId = clockSnapshotModel.selectedColorId, 124 colorToneProgress = clockSnapshotModel.colorToneProgress, 125 seedColor = clockSnapshotModel.seedColor, 126 ) 127 } 128 clockSnapshotModel.clockId?.let { repository.setSelectedClock(it) } 129 clockSnapshotModel.axisSettings?.let { repository.setClockFontAxes(it) } 130 } 131 132 private suspend fun storeCurrentClockOption(clockSnapshotModel: ClockSnapshotModel) { 133 val option = getCurrentClockToRestore(clockSnapshotModel) 134 snapshotRestorer.storeSnapshot(option) 135 } 136 137 /** 138 * Gets the [ClockSnapshotModel] from the storage and override with [latestOption]. 139 * 140 * The storage might be in the middle of a write, and not reflecting the user's options, always 141 * pass in a [ClockSnapshotModel] if we know it's the latest option from a user's point of view. 142 * 143 * [selectedColorId] and [seedColor] have null state collide with nullable type, but we know 144 * they are presented whenever there's a [colorToneProgress]. 145 */ 146 private suspend fun getCurrentClockToRestore(latestOption: ClockSnapshotModel) = 147 ClockSnapshotModel( 148 clockId = latestOption.clockId ?: selectedClockId.firstOrNull(), 149 clockSize = latestOption.clockSize ?: selectedClockSize.firstOrNull(), 150 colorToneProgress = latestOption.colorToneProgress ?: colorToneProgress.firstOrNull(), 151 selectedColorId = 152 latestOption.colorToneProgress?.let { latestOption.selectedColorId } 153 ?: selectedColorId.firstOrNull(), 154 seedColor = 155 latestOption.colorToneProgress?.let { latestOption.seedColor } 156 ?: seedColor.firstOrNull(), 157 axisSettings = latestOption.axisSettings ?: axisSettings.firstOrNull(), 158 ) 159 } 160