blob: 4f534ccfb0e05e80d13370306d8b32da41553e23 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.customization.model.grid.data.repository
import android.content.Context
import androidx.lifecycle.asFlow
import com.android.customization.model.CustomizationManager
import com.android.customization.model.grid.GridOption
import com.android.customization.model.grid.GridOptionsManager
import com.android.customization.model.grid.shared.model.GridOptionItemModel
import com.android.customization.model.grid.shared.model.GridOptionItemsModel
import com.android.wallpaper.config.BaseFlags
import kotlin.coroutines.resume
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
interface GridRepository {
suspend fun isAvailable(): Boolean
fun getOptionChanges(): Flow<Unit>
suspend fun getOptions(): GridOptionItemsModel
}
class GridRepositoryImpl(
private val context: Context,
private val applicationScope: CoroutineScope,
private val manager: GridOptionsManager,
private val backgroundDispatcher: CoroutineDispatcher,
) : GridRepository {
override suspend fun isAvailable(): Boolean {
return withContext(backgroundDispatcher) { manager.isAvailable }
}
override fun getOptionChanges(): Flow<Unit> =
manager.getOptionChangeObservable(/* handler= */ null).asFlow().map {}
private val selectedOption = MutableStateFlow<GridOption?>(null)
override suspend fun getOptions(): GridOptionItemsModel {
return withContext(backgroundDispatcher) {
suspendCancellableCoroutine { continuation ->
manager.fetchOptions(
object : CustomizationManager.OptionsFetchedListener<GridOption> {
override fun onOptionsLoaded(options: MutableList<GridOption>?) {
val optionsOrEmpty = options ?: emptyList()
selectedOption.value = optionsOrEmpty.find { it.isActive(manager) }
continuation.resume(
GridOptionItemsModel.Loaded(
optionsOrEmpty.map { option -> toModel(option) }
)
)
}
override fun onError(throwable: Throwable?) {
continuation.resume(
GridOptionItemsModel.Error(
throwable ?: Exception("Failed to load grid options!")
),
)
}
},
/* reload= */ true,
)
}
}
}
private fun toModel(option: GridOption): GridOptionItemModel {
return GridOptionItemModel(
name = option.title,
rows = option.rows,
cols = option.cols,
isSelected =
selectedOption
.map { it.key() }
.map { selectedOptionKey -> option.key() == selectedOptionKey }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
initialValue = false,
),
onSelected = { onSelected(option) },
)
}
private suspend fun onSelected(option: GridOption) {
withContext(backgroundDispatcher) {
suspendCancellableCoroutine { continuation ->
if (BaseFlags.get().isGridApplyButtonEnabled(context)) {
manager.preview(option)
} else {
manager.apply(
option,
object : CustomizationManager.Callback {
override fun onSuccess() {
continuation.resume(true)
}
override fun onError(throwable: Throwable?) {
continuation.resume(false)
}
},
)
}
}
}
}
private fun GridOption?.key(): String? {
return if (this != null) "${cols}x${rows}" else null
}
}