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 package com.android.settings.spa.app
import android.content.Context
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.os.Bundle import android.os.Bundle
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.android.settings.R 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.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator 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.framework.util.mapItem
import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel 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.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.template.app.AppList import com.android.settingslib.spaprivileged.template.app.AppList
@@ -67,7 +72,7 @@ fun AllAppListPage(
val resetAppDialogPresenter = rememberResetAppDialogPresenter() val resetAppDialogPresenter = rememberResetAppDialogPresenter()
AppListPage( AppListPage(
title = stringResource(R.string.all_apps), title = stringResource(R.string.all_apps),
listModel = remember { AllAppListModel() }, listModel = rememberContext(::AllAppListModel),
showInstantApps = true, showInstantApps = true,
moreOptions = { ResetAppPreferences(resetAppDialogPresenter::open) }, moreOptions = { ResetAppPreferences(resetAppDialogPresenter::open) },
appList = appList, appList = appList,
@@ -79,17 +84,71 @@ data class AppRecordWithSize(
) : AppRecord ) : AppRecord
class AllAppListModel( class AllAppListModel(
private val getSummary: @Composable ApplicationInfo.() -> State<String> = { getStorageSize() }, private val context: Context,
private val getStorageSummary: @Composable ApplicationInfo.() -> State<String> = {
getStorageSize()
},
) : AppListModel<AppRecordWithSize> { ) : 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>>) = override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
appListFlow.mapItem(::AppRecordWithSize) 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 @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 @Composable
override fun AppListItemModel<AppRecordWithSize>.AppItem() { override fun AppListItemModel<AppRecordWithSize>.AppItem() {
AppListItem(onClick = AppInfoSettingsProvider.navigator(app = record.app)) 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.R
import com.android.settings.spa.development.UsageStatsListModel.SpinnerItem.Companion.toSpinnerItem import com.android.settings.spa.development.UsageStatsListModel.SpinnerItem.Companion.toSpinnerItem
import com.android.settingslib.spa.framework.compose.stateOf 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.AppEntry
import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord 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]) } appList.map { app -> UsageStatsAppRecord(app, usageStatsMap[app.packageName]) }
} }
override fun getSpinnerOptions() = SpinnerItem.values().map { override fun getSpinnerOptions(recordList: List<UsageStatsAppRecord>): List<SpinnerOption> =
context.getString(it.stringResId) SpinnerItem.values().map {
} SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
)
}
override fun filter( override fun filter(
userIdFlow: Flow<Int>, userIdFlow: Flow<Int>,
@@ -75,7 +80,8 @@ class UsageStatsListModel(private val context: Context) : AppListModel<UsageStat
override fun getSummary(option: Int, record: UsageStatsAppRecord): State<String>? { override fun getSummary(option: Int, record: UsageStatsAppRecord): State<String>? {
val usageStats = record.usageStats ?: return null val usageStats = record.usageStats ?: return null
val lastTimeUsed = DateUtils.formatSameDayTime( 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 lastTimeUsedLine = "${context.getString(R.string.last_time_used_label)}: $lastTimeUsed"
val usageTime = DateUtils.formatElapsedTime(usageStats.totalTimeInForeground / 1000) val usageTime = DateUtils.formatElapsedTime(usageStats.totalTimeInForeground / 1000)
val usageTimeLine = "${context.getString(R.string.usage_time_label)}: $usageTime" 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.compose.stateOf
import com.android.settingslib.spa.framework.util.asyncFilter import com.android.settingslib.spa.framework.util.asyncFilter
import com.android.settingslib.spa.framework.util.asyncForEach 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.AppEntry
import com.android.settingslib.spaprivileged.model.app.AppListModel import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord 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() } recordList.asyncForEach { it.controller.getEnabled() }
return true
} }
override fun getComparator(option: Int) = when (option.toSpinnerItem()) { override fun getComparator(option: Int) = when (option.toSpinnerItem()) {
@@ -97,9 +99,13 @@ class AppNotificationsListModel(
} }
} }
override fun getSpinnerOptions() = SpinnerItem.values().map { override fun getSpinnerOptions(recordList: List<AppNotificationsRecord>): List<SpinnerOption> =
context.getString(it.stringResId) SpinnerItem.values().map {
} SpinnerOption(
id = it.ordinal,
text = context.getString(it.stringResId),
)
}
private fun formatLastSent(lastSent: Long) = private fun formatLastSent(lastSent: Long) =
StringUtil.formatRelativeTime( StringUtil.formatRelativeTime(

View File

@@ -121,7 +121,7 @@ class AllAppListTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun allAppListModel_transform() = runTest { 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))) val recordListFlow = listModel.transform(flowOf(USER_ID), flowOf(listOf(APP)))
@@ -132,7 +132,7 @@ class AllAppListTest {
@Test @Test
fun allAppListModel_getSummary() { fun allAppListModel_getSummary() {
val listModel = AllAppListModel { stateOf(SUMMARY) } val listModel = AllAppListModel(context) { stateOf(SUMMARY) }
lateinit var summaryState: State<String> lateinit var summaryState: State<String>
composeTestRule.setContent { composeTestRule.setContent {
@@ -142,6 +142,23 @@ class AllAppListTest {
assertThat(summaryState.value).isEqualTo(SUMMARY) 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> { private fun getAppListInput(): AppListInput<AppRecordWithSize> {
lateinit var input: AppListInput<AppRecordWithSize> lateinit var input: AppListInput<AppRecordWithSize>
composeTestRule.setContent { composeTestRule.setContent {
@@ -157,7 +174,7 @@ class AllAppListTest {
private fun setItemContent() { private fun setItemContent() {
composeTestRule.setContent { composeTestRule.setContent {
fakeNavControllerWrapper.Wrapper { fakeNavControllerWrapper.Wrapper {
with(AllAppListModel()) { with(AllAppListModel(context)) {
AppListItemModel( AppListItemModel(
record = AppRecordWithSize(app = APP), record = AppRecordWithSize(app = APP),
label = LABEL, label = LABEL,