Skip to content

Commit 6464b1e

Browse files
authored
UI Improvement (#177)
* UI improvements on the home view * UI improvements in profiles * Migration, deprecations fixed * Paddings fixed on Profile screen * Final touches * Nordic Version Catalog updated to 2025.12.00
1 parent 161a230 commit 6464b1e

116 files changed

Lines changed: 3334 additions & 4018 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/src/main/java/no/nordicsemi/android/nrftoolbox/ScannerDestination.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package no.nordicsemi.android.nrftoolbox
22

3-
import androidx.hilt.navigation.compose.hiltViewModel
3+
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
44
import no.nordicsemi.android.common.navigation.createDestination
55
import no.nordicsemi.android.common.navigation.defineDestination
66
import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel
Lines changed: 89 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,129 @@
11
package no.nordicsemi.android.nrftoolbox.view
22

3-
import androidx.annotation.DrawableRes
4-
import androidx.annotation.StringRes
5-
import androidx.compose.foundation.Image
63
import androidx.compose.foundation.layout.Arrangement
74
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.FlowRow
86
import androidx.compose.foundation.layout.Row
97
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.height
109
import androidx.compose.foundation.layout.padding
1110
import androidx.compose.foundation.layout.size
11+
import androidx.compose.foundation.layout.wrapContentHeight
12+
import androidx.compose.material3.Badge
13+
import androidx.compose.material3.ElevatedAssistChip
14+
import androidx.compose.material3.FilterChip
15+
import androidx.compose.material3.Icon
16+
import androidx.compose.material3.ListItem
17+
import androidx.compose.material3.LocalContentColor
1218
import androidx.compose.material3.MaterialTheme
1319
import androidx.compose.material3.OutlinedCard
20+
import androidx.compose.material3.SuggestionChip
1421
import androidx.compose.material3.Text
1522
import androidx.compose.runtime.Composable
1623
import androidx.compose.ui.Alignment
1724
import androidx.compose.ui.Modifier
18-
import androidx.compose.ui.graphics.ColorFilter
19-
import androidx.compose.ui.graphics.vector.ImageVector
25+
import androidx.compose.ui.graphics.Color
26+
import androidx.compose.ui.graphics.painter.Painter
2027
import androidx.compose.ui.res.painterResource
2128
import androidx.compose.ui.res.stringResource
2229
import androidx.compose.ui.tooling.preview.Preview
2330
import androidx.compose.ui.unit.dp
31+
import no.nordicsemi.android.common.theme.NordicTheme
2432
import no.nordicsemi.android.nrftoolbox.R
33+
import kotlin.math.absoluteValue
2534

2635
@Composable
2736
internal fun FeatureButton(
28-
@DrawableRes iconId: Int,
29-
@StringRes description: Int,
30-
profileNames: List<String> = listOf(stringResource(description)),
37+
icon: Painter,
38+
description: String,
39+
profileNames: List<String> = listOf(description),
3140
deviceName: String?,
3241
deviceAddress: String,
3342
onClick: () -> Unit
3443
) {
35-
OutlinedCard(onClick = onClick, modifier = Modifier.fillMaxWidth()) {
36-
Row(
37-
modifier = Modifier
38-
.padding(16.dp)
39-
.fillMaxWidth(),
40-
verticalAlignment = Alignment.CenterVertically,
41-
horizontalArrangement = Arrangement.spacedBy(8.dp)
42-
) {
43-
Image(
44-
painter = painterResource(iconId),
45-
contentDescription = stringResource(id = description),
46-
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
47-
modifier = Modifier
48-
.size(40.dp)
49-
)
50-
51-
Column(
52-
verticalArrangement = Arrangement.spacedBy(8.dp),
53-
horizontalAlignment = Alignment.CenterHorizontally,
54-
) {
55-
Text(
56-
modifier = Modifier.fillMaxWidth(),
57-
text = deviceName ?: stringResource(R.string.unknown_device),
58-
style = MaterialTheme.typography.titleMedium,
59-
)
60-
Text(
61-
modifier = Modifier.fillMaxWidth(),
62-
text = profileNames.joinToString(", "),
63-
style = MaterialTheme.typography.labelMedium,
64-
color = MaterialTheme.colorScheme.onSurfaceVariant
65-
)
66-
67-
68-
Text(
69-
modifier = Modifier.fillMaxWidth(),
70-
text = deviceAddress,
71-
style = MaterialTheme.typography.labelMedium,
72-
color = MaterialTheme.colorScheme.onSurfaceVariant
44+
OutlinedCard(
45+
onClick = onClick,
46+
) {
47+
ListItem(
48+
headlineContent = { Text(deviceName ?: stringResource(R.string.unknown_device)) },
49+
supportingContent = {
50+
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
51+
Text(deviceAddress)
52+
FlowRow(
53+
horizontalArrangement = Arrangement.spacedBy(4.dp),
54+
maxLines = 1,
55+
) {
56+
profileNames.forEach {
57+
Badge(
58+
containerColor = vibrantColorFromString(it),
59+
contentColor = MaterialTheme.colorScheme.onPrimary,
60+
) {
61+
Text(
62+
text = it,
63+
modifier = Modifier.padding(1.dp),
64+
)
65+
}
66+
}
67+
}
68+
}
69+
},
70+
leadingContent = {
71+
Icon(
72+
painter = icon,
73+
contentDescription = description,
74+
tint = MaterialTheme.colorScheme.primary,
75+
modifier = Modifier.size(24.dp),
7376
)
7477
}
75-
}
78+
)
7679
}
7780
}
7881

79-
@Preview
82+
@Preview(heightDp = 100)
8083
@Composable
8184
private fun FeatureButtonPreview() {
82-
FeatureButton(
83-
R.drawable.ic_csc,
84-
R.string.csc_module_full,
85-
listOf("Cycling Speed and Cadence", "Cycling Speed Sensor"),
86-
"Testing peripheral",
87-
deviceAddress = "AA:BB:CC:DD:EE:FF",
88-
) { }
85+
NordicTheme {
86+
FeatureButton(
87+
icon = painterResource(R.drawable.ic_csc),
88+
description = stringResource(R.string.csc_module_full),
89+
profileNames = listOf("Cycling Speed and Cadence", "Battery", "Heart Rate", "Blood Pressure"),
90+
deviceName = "Testing peripheral",
91+
deviceAddress = "AA:BB:CC:DD:EE:FF",
92+
onClick = {}
93+
)
94+
}
8995
}
9096

91-
@Composable
92-
internal fun FeatureButton(
93-
iconId: ImageVector,
94-
@StringRes description: Int,
95-
profileNames: List<String> = listOf(stringResource(description)),
96-
deviceName: String?,
97-
deviceAddress: String,
98-
onClick: () -> Unit
99-
) {
100-
OutlinedCard(onClick = onClick, modifier = Modifier.fillMaxWidth()) {
101-
Row(
102-
modifier = Modifier
103-
.padding(16.dp)
104-
.fillMaxWidth(),
105-
verticalAlignment = Alignment.Top,
106-
horizontalArrangement = Arrangement.spacedBy(8.dp)
107-
) {
108-
Image(
109-
imageVector = iconId,
110-
contentDescription = deviceAddress,
111-
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
112-
modifier = Modifier
113-
.size(40.dp)
114-
)
97+
private fun vibrantColorFromString(input: String): Color {
98+
// Hash → 0..360 for hue
99+
val hue = (input.hashCode().absoluteValue % 360).toFloat()
115100

116-
Column(
117-
verticalArrangement = Arrangement.spacedBy(8.dp),
118-
horizontalAlignment = Alignment.CenterHorizontally,
119-
) {
120-
Text(
121-
modifier = Modifier.fillMaxWidth(),
122-
text = deviceName ?: stringResource(R.string.unknown_device),
123-
style = MaterialTheme.typography.titleMedium,
124-
)
101+
val saturation = 0.65f // vibrant
102+
val lightness = 0.45f // not too dark/light
125103

126-
Text(
127-
modifier = Modifier.fillMaxWidth(),
128-
text = profileNames.joinToString(", "),
129-
style = MaterialTheme.typography.labelMedium,
130-
color = MaterialTheme.colorScheme.onSurfaceVariant
131-
)
104+
return hslToColor(hue, saturation, lightness)
105+
}
132106

133-
Text(
134-
modifier = Modifier.fillMaxWidth(),
135-
text = deviceAddress,
136-
style = MaterialTheme.typography.labelMedium,
137-
color = MaterialTheme.colorScheme.onSurfaceVariant
138-
)
139-
}
140-
}
107+
/**
108+
* HSL → Color conversion for Jetpack Compose.
109+
*/
110+
private fun hslToColor(h: Float, s: Float, l: Float): Color {
111+
val c = (1 - kotlin.math.abs(2 * l - 1)) * s
112+
val x = c * (1 - kotlin.math.abs((h / 60) % 2 - 1))
113+
val m = l - c / 2
114+
115+
val (r1, g1, b1) = when {
116+
h < 60 -> Triple(c, x, 0f)
117+
h < 120 -> Triple(x, c, 0f)
118+
h < 180 -> Triple(0f, c, x)
119+
h < 240 -> Triple(0f, x, c)
120+
h < 300 -> Triple(x, 0f, c)
121+
else -> Triple(c, 0f, x)
141122
}
123+
124+
return Color(
125+
red = r1 + m,
126+
green = g1 + m,
127+
blue = b1 + m
128+
)
142129
}

0 commit comments

Comments
 (0)