Refactor PrintSettings (2/n)

The "Add service" button.

Bug: 320076351
Flag: com.android.settings.flags.refactor_print_settings
Test: manual
Test: unit test
Change-Id: I624293edcbfe9ef8388d48759611aeac522352a4
This commit is contained in:
Chaohui Wang
2024-06-17 17:56:45 +08:00
parent 3576f4840d
commit 9e88efecdf
2 changed files with 149 additions and 60 deletions

View File

@@ -17,16 +17,32 @@
package com.android.settings.print
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Print
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
@@ -36,13 +52,18 @@ import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsOpacity
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.SettingsIcon
import com.android.settingslib.spaprivileged.settingsprovider.settingsSecureStringFlow
import com.android.settingslib.spaprivileged.template.common.UserProfilePager
import kotlinx.coroutines.flow.Flow
object PrintSettingsPageProvider : SettingsPageProvider {
override val name = "PrintSettings"
@@ -52,51 +73,101 @@ object PrintSettingsPageProvider : SettingsPageProvider {
RegularScaffold(title = stringResource(R.string.print_settings)) {
val context = LocalContext.current
val printRepository = remember(context) { PrintRepository(context) }
UserProfilePager {
PrintServices(printRepository)
}
UserProfilePager { PrintServices(printRepository) }
}
}
@Composable
private fun PrintServices(printRepository: PrintRepository) {
val printServiceDisplayInfos by remember {
printRepository.printServiceDisplayInfosFlow()
}.collectAsStateWithLifecycle(initialValue = emptyList())
Category(title = stringResource(R.string.print_settings_title)) {
for (printServiceDisplayInfo in printServiceDisplayInfos) {
PrintService(printServiceDisplayInfo)
val printServiceDisplayInfos by
remember { printRepository.printServiceDisplayInfosFlow() }
.collectAsStateWithLifecycle(initialValue = emptyList())
if (printServiceDisplayInfos.isEmpty()) {
NoServicesInstalled()
} else {
Category(title = stringResource(R.string.print_settings_title)) {
for (printServiceDisplayInfo in printServiceDisplayInfos) {
PrintService(printServiceDisplayInfo)
}
}
}
AddPrintService()
}
@Composable
private fun NoServicesInstalled() {
Column(
modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPaddingAround),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Outlined.Print,
contentDescription = null,
modifier =
Modifier.size(110.dp)
.padding(SettingsDimension.itemPaddingAround)
.alpha(SettingsOpacity.SurfaceTone),
)
Text(
text = stringResource(R.string.print_no_services_installed),
style = MaterialTheme.typography.titleLarge,
)
}
}
@VisibleForTesting
@Composable
fun PrintService(displayInfo: PrintServiceDisplayInfo) {
val context = LocalContext.current
Preference(model = object : PreferenceModel {
override val title = displayInfo.title
override val summary = { displayInfo.summary }
override val icon: @Composable () -> Unit = {
Image(
painter = rememberDrawablePainter(displayInfo.icon),
contentDescription = null,
modifier = Modifier.size(SettingsDimension.appIconItemSize),
)
}
override val onClick = {
SubSettingLauncher(context).apply {
setDestination(PrintServiceSettingsFragment::class.qualifiedName)
setArguments(
bundleOf(
EXTRA_CHECKED to displayInfo.isEnabled,
EXTRA_TITLE to displayInfo.title,
EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
)
Preference(
object : PreferenceModel {
override val title = displayInfo.title
override val summary = { displayInfo.summary }
override val icon: @Composable () -> Unit = {
Image(
painter = rememberDrawablePainter(displayInfo.icon),
contentDescription = null,
modifier = Modifier.size(SettingsDimension.appIconItemSize),
)
setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
}.launch()
}
override val onClick = { launchPrintServiceSettings(context, displayInfo) }
}
})
)
}
private fun launchPrintServiceSettings(context: Context, displayInfo: PrintServiceDisplayInfo) {
SubSettingLauncher(context)
.apply {
setDestination(PrintServiceSettingsFragment::class.qualifiedName)
setArguments(
bundleOf(
EXTRA_CHECKED to displayInfo.isEnabled,
EXTRA_TITLE to displayInfo.title,
EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
)
)
setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
}
.launch()
}
@Composable
fun AddPrintService(
searchUriFlow: Flow<String> = rememberContext { context ->
context.settingsSecureStringFlow(Settings.Secure.PRINT_SERVICE_SEARCH_URI)
},
) {
val context = LocalContext.current
val searchUri by searchUriFlow.collectAsStateWithLifecycle("")
if (searchUri.isEmpty()) return
Preference(
object : PreferenceModel {
override val title = stringResource(R.string.print_menu_item_add_service)
override val icon = @Composable { SettingsIcon(imageVector = Icons.Outlined.Add) }
override val onClick = {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)))
}
}
)
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.print
import android.content.Context
import android.net.Uri
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.isDisplayed
@@ -31,7 +32,9 @@ import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
import com.android.settings.print.PrintSettingsPageProvider.AddPrintService
import com.android.settings.print.PrintSettingsPageProvider.PrintService
import kotlinx.coroutines.flow.flowOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,35 +47,32 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class PrintSettingsPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
@get:Rule val composeTestRule = createComposeRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val context: Context =
spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val displayInfo = PrintServiceDisplayInfo(
title = TITLE,
isEnabled = true,
summary = SUMMARY,
icon = context.getDrawable(R.drawable.ic_settings_print)!!,
componentName = "ComponentName",
)
private val displayInfo =
PrintServiceDisplayInfo(
title = TITLE,
isEnabled = true,
summary = SUMMARY,
icon = context.getDrawable(R.drawable.ic_settings_print)!!,
componentName = "ComponentName",
)
@Test
fun printService_titleDisplayed() {
composeTestRule.setContent {
PrintService(displayInfo)
}
composeTestRule.setContent { PrintService(displayInfo) }
composeTestRule.onNodeWithText(TITLE).isDisplayed()
}
@Test
fun printService_summaryDisplayed() {
composeTestRule.setContent {
PrintService(displayInfo)
}
composeTestRule.setContent { PrintService(displayInfo) }
composeTestRule.onNodeWithText(SUMMARY).isDisplayed()
}
@@ -80,25 +80,43 @@ class PrintSettingsPageProviderTest {
@Test
fun printService_onClick() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
PrintService(displayInfo)
}
CompositionLocalProvider(LocalContext provides context) { PrintService(displayInfo) }
}
composeTestRule.onNodeWithText(TITLE).performClick()
verify(context).startActivity(argThat {
val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
fragment == PrintServiceSettingsFragment::class.qualifiedName &&
arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
arguments.getString(EXTRA_TITLE) == displayInfo.title &&
arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) == displayInfo.componentName
})
verify(context)
.startActivity(
argThat {
val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
fragment == PrintServiceSettingsFragment::class.qualifiedName &&
arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
arguments.getString(EXTRA_TITLE) == displayInfo.title &&
arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) ==
displayInfo.componentName
}
)
}
@Test
fun addPrintService_onClick() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AddPrintService(flowOf(SEARCH_URI))
}
}
composeTestRule
.onNodeWithText(context.getString(R.string.print_menu_item_add_service))
.performClick()
verify(context).startActivity(argThat { data == Uri.parse(SEARCH_URI) })
}
private companion object {
const val TITLE = "Title"
const val SUMMARY = "Summary"
const val SEARCH_URI = "search.uri"
}
}