Merge "Add spinner options to "All apps""

This commit is contained in:
Chaohui Wang
2023-01-05 11:05:23 +00:00
committed by Android (Google) Code Review
4 changed files with 102 additions and 14 deletions

View File

@@ -16,10 +16,12 @@
package com.android.settings.spa.app
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import com.android.settings.R
@@ -28,9 +30,12 @@ import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.util.filterItem
import com.android.settingslib.spa.framework.util.mapItem
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.template.app.AppList
@@ -67,7 +72,7 @@ fun AllAppListPage(
val resetAppDialogPresenter = rememberResetAppDialogPresenter()
AppListPage(
title = stringResource(R.string.all_apps),
listModel = remember { AllAppListModel() },
listModel = rememberContext(::AllAppListModel),
showInstantApps = true,
moreOptions = { ResetAppPreferences(resetAppDialogPresenter::open) },
appList = appList,
@@ -79,17 +84,71 @@ data class AppRecordWithSize(
) : AppRecord
class AllAppListModel(
private val getSummary: @Composable ApplicationInfo.() -> State<String> = { getStorageSize() },
private val context: Context,
private val getStorageSummary: @Composable ApplicationInfo.() -> State<String> = {
getStorageSize()
},
) : AppListModel<AppRecordWithSize> {
override fun getSpinnerOptions(recordList: List<AppRecordWithSize>): List<SpinnerOption> {
val hasDisabled = recordList.any(isDisabled)
val hasInstant = recordList.any(isInstant)
if (!hasDisabled && !hasInstant) return emptyList()
val options = mutableListOf(SpinnerItem.All, SpinnerItem.Enabled)
if (hasDisabled) options += SpinnerItem.Disabled
if (hasInstant) options += SpinnerItem.Instant
return options.map {
SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
)
}
}
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
appListFlow.mapItem(::AppRecordWithSize)
override fun filter(
userIdFlow: Flow<Int>,
option: Int,
recordListFlow: Flow<List<AppRecordWithSize>>,
): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem(
when (SpinnerItem.values().getOrNull(option)) {
SpinnerItem.Enabled -> ({ it.app.enabled && !it.app.isInstantApp })
SpinnerItem.Disabled -> isDisabled
SpinnerItem.Instant -> isInstant
else -> ({ true })
}
)
private val isDisabled: (AppRecordWithSize) -> Boolean =
{ !it.app.enabled && !it.app.isInstantApp }
private val isInstant: (AppRecordWithSize) -> Boolean = { it.app.isInstantApp }
@Composable
override fun getSummary(option: Int, record: AppRecordWithSize) = record.app.getSummary()
override fun getSummary(option: Int, record: AppRecordWithSize): State<String> {
val storageSummary = record.app.getStorageSummary()
return remember {
derivedStateOf {
storageSummary.value +
when (isDisabled(record)) {
true -> System.lineSeparator() + context.getString(R.string.disabled)
else -> ""
}
}
}
}
@Composable
override fun AppListItemModel<AppRecordWithSize>.AppItem() {
AppListItem(onClick = AppInfoSettingsProvider.navigator(app = record.app))
}
}
private enum class SpinnerItem(val stringResId: Int) {
All(R.string.filter_all_apps),
Enabled(R.string.filter_enabled_apps),
Disabled(R.string.filter_apps_disabled),
Instant(R.string.filter_instant_apps);
}

View File

@@ -26,6 +26,7 @@ import androidx.compose.runtime.State
import com.android.settings.R
import com.android.settings.spa.development.UsageStatsListModel.SpinnerItem.Companion.toSpinnerItem
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.model.app.AppEntry
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
@@ -53,9 +54,13 @@ class UsageStatsListModel(private val context: Context) : AppListModel<UsageStat
appList.map { app -> UsageStatsAppRecord(app, usageStatsMap[app.packageName]) }
}
override fun getSpinnerOptions() = SpinnerItem.values().map {
context.getString(it.stringResId)
}
override fun getSpinnerOptions(recordList: List<UsageStatsAppRecord>): List<SpinnerOption> =
SpinnerItem.values().map {
SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
)
}
override fun filter(
userIdFlow: Flow<Int>,
@@ -75,7 +80,8 @@ class UsageStatsListModel(private val context: Context) : AppListModel<UsageStat
override fun getSummary(option: Int, record: UsageStatsAppRecord): State<String>? {
val usageStats = record.usageStats ?: return null
val lastTimeUsed = DateUtils.formatSameDayTime(
usageStats.lastTimeUsed, now, DateFormat.MEDIUM, DateFormat.MEDIUM)
usageStats.lastTimeUsed, now, DateFormat.MEDIUM, DateFormat.MEDIUM
)
val lastTimeUsedLine = "${context.getString(R.string.last_time_used_label)}: $lastTimeUsed"
val usageTime = DateUtils.formatElapsedTime(usageStats.totalTimeInForeground / 1000)
val usageTimeLine = "${context.getString(R.string.usage_time_label)}: $usageTime"

View File

@@ -30,6 +30,7 @@ import com.android.settings.spa.notification.SpinnerItem.Companion.toSpinnerItem
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.asyncFilter
import com.android.settingslib.spa.framework.util.asyncForEach
import com.android.settingslib.spa.widget.ui.SpinnerOption
import com.android.settingslib.spaprivileged.model.app.AppEntry
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
@@ -78,8 +79,9 @@ class AppNotificationsListModel(
}
}
override suspend fun onFirstLoaded(recordList: List<AppNotificationsRecord>) {
override suspend fun onFirstLoaded(recordList: List<AppNotificationsRecord>): Boolean {
recordList.asyncForEach { it.controller.getEnabled() }
return true
}
override fun getComparator(option: Int) = when (option.toSpinnerItem()) {
@@ -97,9 +99,13 @@ class AppNotificationsListModel(
}
}
override fun getSpinnerOptions() = SpinnerItem.values().map {
context.getString(it.stringResId)
}
override fun getSpinnerOptions(recordList: List<AppNotificationsRecord>): List<SpinnerOption> =
SpinnerItem.values().map {
SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
)
}
private fun formatLastSent(lastSent: Long) =
StringUtil.formatRelativeTime(

View File

@@ -121,7 +121,7 @@ class AllAppListTest {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun allAppListModel_transform() = runTest {
val listModel = AllAppListModel { stateOf(SUMMARY) }
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val recordListFlow = listModel.transform(flowOf(USER_ID), flowOf(listOf(APP)))
@@ -132,7 +132,7 @@ class AllAppListTest {
@Test
fun allAppListModel_getSummary() {
val listModel = AllAppListModel { stateOf(SUMMARY) }
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
lateinit var summaryState: State<String>
composeTestRule.setContent {
@@ -142,6 +142,23 @@ class AllAppListTest {
assertThat(summaryState.value).isEqualTo(SUMMARY)
}
@Test
fun allAppListModel_getSummaryWhenDisabled() {
val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
val disabledApp = ApplicationInfo().apply {
packageName = PACKAGE_NAME
enabled = false
}
lateinit var summaryState: State<String>
composeTestRule.setContent {
summaryState =
listModel.getSummary(option = 0, record = AppRecordWithSize(app = disabledApp))
}
assertThat(summaryState.value).isEqualTo("$SUMMARY${System.lineSeparator()}Disabled")
}
private fun getAppListInput(): AppListInput<AppRecordWithSize> {
lateinit var input: AppListInput<AppRecordWithSize>
composeTestRule.setContent {
@@ -157,7 +174,7 @@ class AllAppListTest {
private fun setItemContent() {
composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper {
with(AllAppListModel()) {
with(AllAppListModel(context)) {
AppListItemModel(
record = AppRecordWithSize(app = APP),
label = LABEL,