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.grid.domain.interactor 19 20 import com.android.customization.model.CustomizationManager 21 import com.android.customization.model.grid.GridOption 22 import com.android.customization.picker.grid.data.repository.GridRepository 23 import com.android.customization.picker.grid.shared.model.GridOptionItemModel 24 import com.android.customization.picker.grid.shared.model.GridOptionItemsModel 25 import javax.inject.Provider 26 import kotlinx.coroutines.CoroutineScope 27 import kotlinx.coroutines.flow.Flow 28 import kotlinx.coroutines.flow.SharingStarted 29 import kotlinx.coroutines.flow.emptyFlow 30 import kotlinx.coroutines.flow.flatMapLatest 31 import kotlinx.coroutines.flow.flow 32 import kotlinx.coroutines.flow.map 33 import kotlinx.coroutines.flow.onStart 34 import kotlinx.coroutines.flow.shareIn 35 36 class GridInteractor( 37 private val applicationScope: CoroutineScope, 38 private val repository: GridRepository, 39 private val snapshotRestorer: Provider<GridSnapshotRestorer>, 40 ) { 41 val options: Flow<GridOptionItemsModel> = 42 flow { emit(repository.isAvailable()) } 43 .flatMapLatest { isAvailable -> 44 if (isAvailable) { 45 // this upstream flow tells us each time the options are changed. 46 repository 47 .getOptionChanges() 48 // when we start, we pretend the options _just_ changed. This way, we load 49 // something as soon as possible into the flow so it's ready by the time the 50 // first observer starts to observe. 51 .onStart { emit(Unit) } 52 // each time the options changed, we load them. 53 .map { reload() } 54 // we place the loaded options in a SharedFlow so downstream observers all 55 // share the same flow and don't trigger a new one each time they want to 56 // start observing. 57 .shareIn( 58 scope = applicationScope, 59 started = SharingStarted.WhileSubscribed(), 60 replay = 1, 61 ) 62 } else { 63 emptyFlow() 64 } 65 } 66 67 suspend fun setSelectedOption(model: GridOptionItemModel) { 68 model.onSelected.invoke() 69 } 70 71 suspend fun getSelectedOption(): GridOptionItemModel? { 72 return (repository.getOptions() as? GridOptionItemsModel.Loaded)?.options?.firstOrNull { 73 optionItem -> 74 optionItem.isSelected.value 75 } 76 } 77 78 fun getSelectOptionNonSuspend(): GridOption? = repository.getSelectedOption() 79 80 fun clearSelectedOption() = repository.clearSelectedOption() 81 82 fun isSelectedOptionApplied() = repository.isSelectedOptionApplied() 83 84 fun applySelectedOption(callback: CustomizationManager.Callback) { 85 repository.applySelectedOption(callback) 86 } 87 88 private suspend fun reload(): GridOptionItemsModel { 89 val model = repository.getOptions() 90 return if (model is GridOptionItemsModel.Loaded) { 91 GridOptionItemsModel.Loaded( 92 options = 93 model.options.map { option -> 94 GridOptionItemModel( 95 name = option.name, 96 cols = option.cols, 97 rows = option.rows, 98 isSelected = option.isSelected, 99 onSelected = { 100 option.onSelected() 101 snapshotRestorer.get().store(option) 102 }, 103 ) 104 } 105 ) 106 } else { 107 model 108 } 109 } 110 } 111