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