Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit de7fae78 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Fix the selected option not saved for App List

The available options will be refreshed each time the app list is
re-entered, which caused the selected option is cleared before.

Only reset the selected option if it is not available any more to fix
this issue.

Bug: 264228237
Test: Manually with Settings
Test: Unit test
Change-Id: I6d7b65d0a944bf1ab392500d17966c697971ea64
parent 5975f9dc
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ internal data class AppListData<T : AppRecord>(
}

internal interface IAppListViewModel<T : AppRecord> {
    val option: StateFlowBridge<Int?>
    val optionFlow: MutableStateFlow<Int?>
    val spinnerOptionsFlow: Flow<List<SpinnerOption>>
    val appListDataFlow: Flow<AppListData<T>>
}
@@ -69,7 +69,7 @@ internal open class AppListViewModelImpl<T : AppRecord>(
    val appListConfig = StateFlowBridge<AppListConfig>()
    val listModel = StateFlowBridge<AppListModel<T>>()
    val showSystem = StateFlowBridge<Boolean>()
    final override val option = StateFlowBridge<Int?>()
    final override val optionFlow = MutableStateFlow<Int?>(null)
    val searchQuery = StateFlowBridge<String>()

    private val appListRepository = appListRepositoryFactory(application)
@@ -97,7 +97,7 @@ internal open class AppListViewModelImpl<T : AppRecord>(
            listModel.getSpinnerOptions(recordList)
        }

    override val appListDataFlow = option.flow.flatMapLatest(::filterAndSort)
    override val appListDataFlow = optionFlow.filterNotNull().flatMapLatest(::filterAndSort)
        .combine(searchQuery.flow) { appListData, searchQuery ->
            appListData.filter {
                it.label.contains(other = searchQuery, ignoreCase = true)
+11 −9
Original line number Diff line number Diff line
@@ -24,11 +24,10 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
@@ -37,7 +36,6 @@ import com.android.settingslib.spa.framework.compose.LogCompositions
import com.android.settingslib.spa.framework.compose.TimeMeasurer.Companion.rememberTimeMeasurer
import com.android.settingslib.spa.framework.compose.rememberLazyListStateAndHideKeyboardWhenStartScroll
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.util.StateFlowBridge
import com.android.settingslib.spa.widget.ui.CategoryTitle
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
import com.android.settingslib.spa.widget.ui.Spinner
@@ -52,6 +50,7 @@ import com.android.settingslib.spaprivileged.model.app.AppListViewModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.IAppListViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow

private const val TAG = "AppList"
private const val CONTENT_TYPE_HEADER = "header"
@@ -88,7 +87,7 @@ internal fun <T : AppRecord> AppListInput<T>.AppListImpl(
    val viewModel = viewModelSupplier()
    Column(Modifier.fillMaxSize()) {
        val optionsState = viewModel.spinnerOptionsFlow.collectAsState(null, Dispatchers.IO)
        SpinnerOptions(optionsState, viewModel.option)
        SpinnerOptions(optionsState, viewModel.optionFlow)
        val appListData = viewModel.appListDataFlow.collectAsState(null, Dispatchers.IO)
        listModel.AppListWidget(appListData, header, bottomPadding, noItemMessage)
    }
@@ -97,15 +96,18 @@ internal fun <T : AppRecord> AppListInput<T>.AppListImpl(
@Composable
private fun SpinnerOptions(
    optionsState: State<List<SpinnerOption>?>,
    optionBridge: StateFlowBridge<Int?>,
    optionFlow: MutableStateFlow<Int?>,
) {
    val options = optionsState.value
    val selectedOption = rememberSaveable(options) {
        mutableStateOf(options?.let { it.firstOrNull()?.id ?: -1 })
    LaunchedEffect(options) {
        if (options != null && !options.any { it.id == optionFlow.value }) {
            // Reset to first option if the available options changed, and the current selected one
            // does not in the new options.
            optionFlow.value = options.let { it.firstOrNull()?.id ?: -1 }
        }
    }
    optionBridge.Sync(selectedOption)
    if (options != null) {
        Spinner(options, selectedOption.value) { selectedOption.value = it }
        Spinner(options, optionFlow.collectAsState().value) { optionFlow.value = it }
    }
}

+1 −1
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ class AppListViewModelTest {
        viewModel.appListConfig.setIfAbsent(CONFIG)
        viewModel.listModel.setIfAbsent(listModel)
        viewModel.showSystem.setIfAbsent(false)
        viewModel.option.setIfAbsent(0)
        viewModel.optionFlow.value = 0
        viewModel.searchQuery.setIfAbsent("")
        viewModel.reloadApps()
        return viewModel
+2 −2
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import androidx.compose.ui.unit.dp
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.util.StateFlowBridge
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.AppEntry
@@ -38,6 +37,7 @@ import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.IAppListViewModel
import com.android.settingslib.spaprivileged.tests.testutils.TestAppListModel
import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import org.junit.Rule
import org.junit.Test
@@ -125,7 +125,7 @@ class AppListTest {
                bottomPadding = 0.dp,
            ).AppListImpl {
                object : IAppListViewModel<TestAppRecord> {
                    override val option: StateFlowBridge<Int?> = StateFlowBridge()
                    override val optionFlow: MutableStateFlow<Int?> = MutableStateFlow(null)
                    override val spinnerOptionsFlow = flowOf(options.mapIndexed { index, option ->
                        SpinnerOption(id = index, text = option)
                    })