Skip to content

Commit 8a8e0fa

Browse files
hamenoclaude
andcommitted
Show dialog and filter button labels in sentence case
The package filter dialogs and the profile editor's add-filter buttons upper-cased their labels in code ("CANCEL", "ADD", "YES", "CLOSE", "PACKAGE NAME", "APP SIGNATURE"), while the rest of the app uses the string resources as written. They now use the strings unchanged, and add_package_name reads "Package name" instead of "package name". Compose UI tests also run as Robolectric unit tests now, so the labels are covered by DialogButtonLabelTest and FilterTypeLabelTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent fed64e4 commit 8a8e0fa

11 files changed

Lines changed: 201 additions & 26 deletions

File tree

‎app/build.gradle‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ dependencies {
159159
// Compose
160160
def composeBom = platform(libs.androidx.compose.bom)
161161
implementation(composeBom)
162+
testImplementation(composeBom)
162163

163164
// Material Design 3
164165
implementation libs.androidx.material3
@@ -167,6 +168,9 @@ dependencies {
167168
// Android Studio Preview support
168169
implementation libs.androidx.ui.tooling.preview
169170
debugImplementation libs.androidx.ui.tooling
171+
// UI tests, run as Robolectric unit tests
172+
testImplementation libs.androidx.ui.test.junit4
173+
debugImplementation libs.androidx.ui.test.manifest
170174
// Optional - Add full set of material icons
171175
implementation libs.androidx.material.icons.extended
172176
// Optional - Integration with activities

‎app/src/main/java/de/psdev/devdrawer/profiles/ui/editor/AddAppSignaturePackageFilterDialog.kt‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ import androidx.compose.ui.Modifier
3232
import androidx.compose.ui.platform.LocalResources
3333
import androidx.compose.ui.res.painterResource
3434
import androidx.compose.ui.res.stringResource
35-
import androidx.compose.ui.text.intl.Locale
36-
import androidx.compose.ui.text.toUpperCase
3735
import androidx.compose.ui.tooling.preview.Preview
3836
import androidx.compose.ui.unit.dp
3937
import androidx.core.graphics.createBitmap
@@ -66,7 +64,7 @@ fun AddAppSignaturePackageFilterDialog(
6664

6765
@OptIn(ExperimentalMaterial3Api::class)
6866
@Composable
69-
private fun AddAppSignaturePackageFilterDialog(
67+
internal fun AddAppSignaturePackageFilterDialog(
7068
viewState: ViewState,
7169
closeDialog: () -> Unit = {},
7270
appSelected: (AppInfo) -> Unit = {},
@@ -111,7 +109,7 @@ private fun AddAppSignaturePackageFilterDialog(
111109
modifier = Modifier
112110
.align(Alignment.End), onClick = closeDialog
113111
) {
114-
Text(text = stringResource(id = R.string.cancel).toUpperCase(Locale.current))
112+
Text(text = stringResource(id = R.string.cancel))
115113
}
116114
}
117115
) {

‎app/src/main/java/de/psdev/devdrawer/profiles/ui/editor/AddPackageNamePackageFilterDialog.kt‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import androidx.compose.ui.Alignment
2525
import androidx.compose.ui.Modifier
2626
import androidx.compose.ui.res.painterResource
2727
import androidx.compose.ui.res.stringResource
28-
import androidx.compose.ui.text.intl.Locale
29-
import androidx.compose.ui.text.toUpperCase
3028
import androidx.compose.ui.tooling.preview.Preview
3129
import androidx.compose.ui.unit.dp
3230
import androidx.compose.ui.window.Dialog
@@ -58,7 +56,7 @@ fun AddPackageNamePackageFilterDialog(
5856
}
5957

6058
@Composable
61-
private fun AddPackageNamePackageFilterDialog(
59+
internal fun AddPackageNamePackageFilterDialog(
6260
viewState: AddPackageNamePackageFilterDialogViewModel.ViewState,
6361
closeDialog: () -> Unit = {},
6462
addFilter: (String) -> Unit = {}
@@ -98,10 +96,10 @@ private fun AddPackageNamePackageFilterDialog(
9896
Spacer(modifier = Modifier.size(8.dp))
9997
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
10098
TextButton(onClick = closeDialog) {
101-
Text(text = stringResource(id = R.string.cancel).toUpperCase(Locale.current))
99+
Text(text = stringResource(id = R.string.cancel))
102100
}
103101
TextButton(onClick = { addFilter(text) }, enabled = text.isNotBlank()) {
104-
Text(text = stringResource(id = R.string.add).toUpperCase(Locale.current))
102+
Text(text = stringResource(id = R.string.add))
105103
}
106104
}
107105
}

‎app/src/main/java/de/psdev/devdrawer/profiles/ui/editor/DeletePackageFilterDialog.kt‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import androidx.compose.material3.Text
55
import androidx.compose.material3.TextButton
66
import androidx.compose.runtime.Composable
77
import androidx.compose.ui.res.stringResource
8-
import androidx.compose.ui.text.intl.Locale
9-
import androidx.compose.ui.text.toUpperCase
108
import de.psdev.devdrawer.R
119

1210
@Composable
@@ -21,12 +19,12 @@ fun DeletePackageFilterDialog(
2119
},
2220
dismissButton = {
2321
TextButton(onClick = onDismiss) {
24-
Text(text = stringResource(id = R.string.cancel).toUpperCase(Locale.current))
22+
Text(text = stringResource(id = R.string.cancel))
2523
}
2624
},
2725
confirmButton = {
2826
TextButton(onClick = onConfirm) {
29-
Text(text = stringResource(id = R.string.yes).toUpperCase(Locale.current))
27+
Text(text = stringResource(id = R.string.yes))
3028
}
3129
}
3230
)

‎app/src/main/java/de/psdev/devdrawer/profiles/ui/editor/PackageFilterInfoDialog.kt‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import androidx.compose.ui.Modifier
1919
import androidx.compose.ui.res.painterResource
2020
import androidx.compose.ui.res.stringResource
2121
import androidx.compose.ui.text.font.FontFamily
22-
import androidx.compose.ui.text.intl.Locale
23-
import androidx.compose.ui.text.toUpperCase
2422
import androidx.compose.ui.tooling.preview.Preview
2523
import androidx.compose.ui.unit.dp
2624
import de.psdev.devdrawer.R
@@ -65,7 +63,7 @@ fun PackageFilterInfoDialog(
6563
modifier = Modifier.align(Alignment.End),
6664
onClick = onDismiss
6765
) {
68-
Text(text = stringResource(id = R.string.close).toUpperCase(Locale.current))
66+
Text(text = stringResource(id = R.string.close))
6967
}
7068
}
7169
) {

‎app/src/main/java/de/psdev/devdrawer/profiles/ui/editor/PackageFilterPreviewDialog.kt‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import androidx.compose.ui.Modifier
2020
import androidx.compose.ui.platform.LocalContext
2121
import androidx.compose.ui.res.painterResource
2222
import androidx.compose.ui.res.stringResource
23-
import androidx.compose.ui.text.intl.Locale
24-
import androidx.compose.ui.text.toUpperCase
2523
import androidx.compose.ui.tooling.preview.Preview
2624
import androidx.compose.ui.unit.dp
2725
import androidx.core.content.res.ResourcesCompat
@@ -49,7 +47,7 @@ fun PackageFilterPreviewDialog(
4947
}
5048

5149
@Composable
52-
private fun PackageFilterPreviewDialog(
50+
internal fun PackageFilterPreviewDialog(
5351
viewState: PackageFilterPreviewDialogViewModel.ViewState,
5452
closeDialog: () -> Unit = {}
5553
) {
@@ -67,7 +65,7 @@ private fun PackageFilterPreviewDialog(
6765
},
6866
bottomContent = {
6967
TextButton(modifier = Modifier.align(Alignment.End), onClick = closeDialog) {
70-
Text(text = stringResource(id = R.string.close).toUpperCase(Locale.current))
68+
Text(text = stringResource(id = R.string.close))
7169
}
7270
}
7371
) {

‎app/src/main/java/de/psdev/devdrawer/profiles/ui/editor/WidgetProfileEditor.kt‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ import androidx.compose.ui.Alignment
4343
import androidx.compose.ui.Modifier
4444
import androidx.compose.ui.res.painterResource
4545
import androidx.compose.ui.res.stringResource
46-
import androidx.compose.ui.text.intl.Locale
47-
import androidx.compose.ui.text.toUpperCase
4846
import androidx.compose.ui.tooling.preview.Preview
4947
import androidx.compose.ui.unit.dp
5048
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
@@ -223,7 +221,7 @@ private sealed class WidgetProfileEditorDialogs {
223221

224222
@OptIn(ExperimentalAnimationApi::class)
225223
@Composable
226-
private fun WidgetProfileEditor(
224+
internal fun WidgetProfileEditor(
227225
viewState: WidgetProfileEditorViewState,
228226
modifier: Modifier = Modifier,
229227
onNameChange: (String) -> Unit = {},
@@ -266,7 +264,7 @@ private fun WidgetProfileEditor(
266264
contentDescription = stringResource(id = R.string.add_package_name)
267265
)
268266
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
269-
Text(text = stringResource(id = R.string.add_package_name).toUpperCase(Locale.current))
267+
Text(text = stringResource(id = R.string.add_package_name))
270268
}
271269
Button(
272270
modifier = Modifier.weight(1f),
@@ -278,7 +276,7 @@ private fun WidgetProfileEditor(
278276
contentDescription = stringResource(id = R.string.add_app_signature)
279277
)
280278
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
281-
Text(text = stringResource(id = R.string.add_app_signature).toUpperCase(Locale.current))
279+
Text(text = stringResource(id = R.string.add_app_signature))
282280
}
283281
}
284282
}

‎app/src/main/res/values/strings.xml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
<string name="uninstall">Uninstall</string>
6363
<string name="app_details">App details</string>
6464
<string name="add_app_signature">App signature</string>
65-
<string name="add_package_name">package name</string>
65+
<string name="add_package_name">Package name</string>
6666
<string name="app_icon">App icon</string>
6767
<string name="enter_package_name_filter">Enter package name filter</string>
6868
<string name="select_signature_from_app">Select signature from app</string>
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package de.psdev.devdrawer.profiles.ui.editor
2+
3+
import androidx.compose.ui.test.junit4.createComposeRule
4+
import androidx.compose.ui.test.onNodeWithText
5+
import de.psdev.devdrawer.database.FilterType
6+
import de.psdev.devdrawer.database.PackageFilter
7+
import de.psdev.devdrawer.ui.theme.DevDrawerTheme
8+
import org.junit.Rule
9+
import org.junit.Test
10+
import org.junit.runner.RunWith
11+
import org.robolectric.RobolectricTestRunner
12+
import org.robolectric.annotation.Config
13+
14+
/**
15+
* Dialog buttons use the string resources' sentence case ("Cancel"), not upper-cased labels ("CANCEL").
16+
*/
17+
@RunWith(RobolectricTestRunner::class)
18+
// SDK 36 requires Java 21; use SDK 33 which is compatible with the project's Java 17 toolchain.
19+
@Config(sdk = [33])
20+
class DialogButtonLabelTest {
21+
22+
@get:Rule
23+
val composeTestRule = createComposeRule()
24+
25+
private val packageFilter = PackageFilter(
26+
type = FilterType.SIGNATURE,
27+
filter = "4f8d2c9e",
28+
description = "Example App",
29+
profileId = "profile"
30+
)
31+
32+
@Test
33+
fun `given the delete filter dialog, when shown, then its buttons are in sentence case`() {
34+
// Given / When
35+
composeTestRule.setContent {
36+
DevDrawerTheme { DeletePackageFilterDialog(onDismiss = {}, onConfirm = {}) }
37+
}
38+
39+
// Then
40+
assertSentenceCase("Cancel", "Yes")
41+
}
42+
43+
@Test
44+
fun `given the add package name filter dialog, when loaded, then its buttons are in sentence case`() {
45+
// Given / When
46+
composeTestRule.setContent {
47+
DevDrawerTheme {
48+
AddPackageNamePackageFilterDialog(
49+
viewState = AddPackageNamePackageFilterDialogViewModel.ViewState.Loaded(listOf("com.example.app"))
50+
)
51+
}
52+
}
53+
54+
// Then
55+
assertSentenceCase("Cancel", "Add")
56+
}
57+
58+
@Test
59+
fun `given the add app signature filter dialog, when shown, then its button is in sentence case`() {
60+
// Given / When
61+
composeTestRule.setContent {
62+
DevDrawerTheme {
63+
AddAppSignaturePackageFilterDialog(
64+
viewState = AddAppSignaturePackageFilterDialogViewModel.ViewState.Loaded(
65+
data = emptyList(),
66+
showSystemApps = false
67+
)
68+
)
69+
}
70+
}
71+
72+
// Then
73+
assertSentenceCase("Cancel")
74+
}
75+
76+
@Test
77+
fun `given the filter info dialog, when shown, then its button is in sentence case`() {
78+
// Given / When
79+
composeTestRule.setContent {
80+
DevDrawerTheme { PackageFilterInfoDialog(packageFilter = packageFilter, onDismiss = {}) }
81+
}
82+
83+
// Then
84+
assertSentenceCase("Close")
85+
}
86+
87+
@Test
88+
fun `given the filter preview dialog, when loaded, then its button is in sentence case`() {
89+
// Given / When
90+
composeTestRule.setContent {
91+
DevDrawerTheme {
92+
PackageFilterPreviewDialog(
93+
viewState = PackageFilterPreviewDialogViewModel.ViewState.Loaded(emptyList())
94+
)
95+
}
96+
}
97+
98+
// Then
99+
assertSentenceCase("Close")
100+
}
101+
102+
private fun assertSentenceCase(vararg labels: String) {
103+
labels.forEach { label ->
104+
composeTestRule.onNodeWithText(label).assertExists()
105+
composeTestRule.onNodeWithText(label.uppercase()).assertDoesNotExist()
106+
}
107+
}
108+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package de.psdev.devdrawer.profiles.ui.editor
2+
3+
import androidx.compose.ui.test.junit4.createComposeRule
4+
import androidx.compose.ui.test.onNodeWithText
5+
import de.psdev.devdrawer.database.FilterType
6+
import de.psdev.devdrawer.database.PackageFilter
7+
import de.psdev.devdrawer.database.WidgetProfile
8+
import de.psdev.devdrawer.ui.theme.DevDrawerTheme
9+
import org.junit.Rule
10+
import org.junit.Test
11+
import org.junit.runner.RunWith
12+
import org.robolectric.RobolectricTestRunner
13+
import org.robolectric.annotation.Config
14+
15+
/**
16+
* The filter type labels ("Package name", "App signature") are shown in sentence case,
17+
* neither upper-cased ("PACKAGE NAME") nor lower-cased ("package name").
18+
*/
19+
@RunWith(RobolectricTestRunner::class)
20+
// SDK 36 requires Java 21; use SDK 33 which is compatible with the project's Java 17 toolchain.
21+
@Config(sdk = [33])
22+
class FilterTypeLabelTest {
23+
24+
@get:Rule
25+
val composeTestRule = createComposeRule()
26+
27+
@Test
28+
fun `given the profile editor, when loaded, then the add filter buttons are in sentence case`() {
29+
// Given
30+
val widgetProfile = WidgetProfile(name = "Test profile")
31+
32+
// When
33+
composeTestRule.setContent {
34+
DevDrawerTheme {
35+
WidgetProfileEditor(
36+
viewState = WidgetProfileEditorViewState(
37+
widgetProfile = widgetProfile,
38+
widgetName = widgetProfile.name
39+
)
40+
)
41+
}
42+
}
43+
44+
// Then
45+
assertSentenceCase("Package name", "App signature")
46+
}
47+
48+
@Test
49+
fun `given a package name filter, when showing its info, then the title is in sentence case`() {
50+
// Given
51+
val packageFilter = PackageFilter(
52+
type = FilterType.PACKAGE_NAME,
53+
filter = "com.example.*",
54+
profileId = "profile"
55+
)
56+
57+
// When
58+
composeTestRule.setContent {
59+
DevDrawerTheme { PackageFilterInfoDialog(packageFilter = packageFilter, onDismiss = {}) }
60+
}
61+
62+
// Then
63+
assertSentenceCase("Package name")
64+
}
65+
66+
private fun assertSentenceCase(vararg labels: String) {
67+
labels.forEach { label ->
68+
composeTestRule.onNodeWithText(label).assertExists()
69+
composeTestRule.onNodeWithText(label.uppercase()).assertDoesNotExist()
70+
composeTestRule.onNodeWithText(label.lowercase()).assertDoesNotExist()
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)