mirror of https://github.com/openclaw/openclaw.git
feat(android): redesign onboarding flow UI
- Welcome: replace bullet list with icon+subtitle feature cards - Gateway: simplify to single instruction line, collapse advanced by default, remove verbose developer text - Permissions: group into System/Media/Personal Data sections, rewrite subtitles to plain English, style "Not granted" with warning color - Review: replace plain text fields with icon cards matching Welcome style, add colored status cards for connect/pairing states - Remove redundant "FIRST RUN" label, "Step X of 4" text, and StepRailWrap dividers
This commit is contained in:
parent
80e6701959
commit
aae75b5e57
|
|
@ -57,8 +57,16 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ChatBubble
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Cloud
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.Security
|
||||
import androidx.compose.material.icons.filled.Tune
|
||||
import androidx.compose.material.icons.filled.Wifi
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
|
@ -514,25 +522,20 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
"FIRST RUN",
|
||||
style = onboardingCaption1Style.copy(fontWeight = FontWeight.Bold, letterSpacing = 1.5.sp),
|
||||
color = onboardingAccent,
|
||||
)
|
||||
Text(
|
||||
"OpenClaw\nMobile Setup",
|
||||
style = onboardingDisplayStyle.copy(lineHeight = 38.sp),
|
||||
"OpenClaw",
|
||||
style = onboardingDisplayStyle,
|
||||
color = onboardingText,
|
||||
)
|
||||
Text(
|
||||
"Step ${step.index} of 4",
|
||||
style = onboardingCaption1Style,
|
||||
color = onboardingAccent,
|
||||
"Mobile Setup",
|
||||
style = onboardingTitle1Style,
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
}
|
||||
StepRailWrap(current = step)
|
||||
StepRail(current = step)
|
||||
|
||||
when (step) {
|
||||
OnboardingStep.Welcome -> WelcomeStep()
|
||||
|
|
@ -893,15 +896,6 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StepRailWrap(current: OnboardingStep) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
HorizontalDivider(color = onboardingBorder)
|
||||
StepRail(current = current)
|
||||
HorizontalDivider(color = onboardingBorder)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StepRail(current: OnboardingStep) {
|
||||
val steps = OnboardingStep.entries
|
||||
|
|
@ -943,11 +937,31 @@ private fun StepRail(current: OnboardingStep) {
|
|||
|
||||
@Composable
|
||||
private fun WelcomeStep() {
|
||||
StepShell(title = "What You Get") {
|
||||
Bullet("Control the gateway and operator chat from one mobile surface.")
|
||||
Bullet("Connect with setup code and recover pairing with CLI commands.")
|
||||
Bullet("Enable only the permissions and capabilities you want.")
|
||||
Bullet("Finish with a real connection check before entering the app.")
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
FeatureCard(
|
||||
icon = Icons.Default.Wifi,
|
||||
title = "Connect to your gateway",
|
||||
subtitle = "Scan a QR code or enter your host manually",
|
||||
accentColor = onboardingAccent,
|
||||
)
|
||||
FeatureCard(
|
||||
icon = Icons.Default.Tune,
|
||||
title = "Choose your permissions",
|
||||
subtitle = "Enable only what you need, change anytime",
|
||||
accentColor = Color(0xFF7C5AC7),
|
||||
)
|
||||
FeatureCard(
|
||||
icon = Icons.Default.ChatBubble,
|
||||
title = "Chat, voice, and screen",
|
||||
subtitle = "Full operator control from your phone",
|
||||
accentColor = onboardingSuccess,
|
||||
)
|
||||
FeatureCard(
|
||||
icon = Icons.Default.CheckCircle,
|
||||
title = "Verify your connection",
|
||||
subtitle = "Live check before you enter the app",
|
||||
accentColor = Color(0xFFC8841A),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -976,11 +990,12 @@ private fun GatewayStep(
|
|||
val manualResolvedEndpoint = remember(manualHost, manualPort, manualTls) { composeGatewayManualUrl(manualHost, manualPort, manualTls)?.let { parseGatewayEndpoint(it)?.displayUrl } }
|
||||
|
||||
StepShell(title = "Gateway Connection") {
|
||||
GuideBlock(title = "Scan onboarding QR") {
|
||||
Text("Run these on the gateway host:", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
CommandBlock("openclaw qr")
|
||||
Text("Then scan with this device.", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
}
|
||||
Text(
|
||||
"Run `openclaw qr` on your gateway host, then scan the code with this device.",
|
||||
style = onboardingCalloutStyle,
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
CommandBlock("openclaw qr")
|
||||
Button(
|
||||
onClick = onScanQrClick,
|
||||
modifier = Modifier.fillMaxWidth().height(48.dp),
|
||||
|
|
@ -1024,21 +1039,6 @@ private fun GatewayStep(
|
|||
|
||||
AnimatedVisibility(visible = advancedOpen) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
GuideBlock(title = "Manual setup commands") {
|
||||
Text("Run these on the gateway host:", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
CommandBlock("openclaw qr --setup-code-only")
|
||||
CommandBlock("openclaw qr --json")
|
||||
Text(
|
||||
"`--json` prints `setupCode` and `gatewayUrl`.",
|
||||
style = onboardingCalloutStyle,
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
Text(
|
||||
"Auto URL discovery is not wired yet. Android emulator uses `10.0.2.2`; real devices need LAN/Tailscale host.",
|
||||
style = onboardingCalloutStyle,
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
}
|
||||
GatewayModeToggle(inputMode = inputMode, onInputModeChange = onInputModeChange)
|
||||
|
||||
if (inputMode == GatewayInputMode.SetupCode) {
|
||||
|
|
@ -1307,13 +1307,9 @@ private fun StepShell(
|
|||
title: String,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(0.dp)) {
|
||||
HorizontalDivider(color = onboardingBorder)
|
||||
Column(modifier = Modifier.padding(vertical = 14.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Text(title, style = onboardingTitle1Style, color = onboardingText)
|
||||
content()
|
||||
}
|
||||
HorizontalDivider(color = onboardingBorder)
|
||||
Column(modifier = Modifier.padding(vertical = 4.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Text(title, style = onboardingTitle1Style, color = onboardingText)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1379,13 +1375,15 @@ private fun PermissionsStep(
|
|||
|
||||
StepShell(title = "Permissions") {
|
||||
Text(
|
||||
"Enable only what you need now. You can change everything later in Settings.",
|
||||
"Enable only what you need. You can change these anytime in Settings.",
|
||||
style = onboardingCalloutStyle,
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
|
||||
PermissionSectionHeader("System")
|
||||
PermissionToggleRow(
|
||||
title = "Gateway discovery",
|
||||
subtitle = if (Build.VERSION.SDK_INT >= 33) "Nearby devices" else "Location (for NSD)",
|
||||
subtitle = "Find gateways on your local network",
|
||||
checked = enableDiscovery,
|
||||
granted = isPermissionGranted(context, discoveryPermission),
|
||||
onCheckedChange = onDiscoveryChange,
|
||||
|
|
@ -1393,7 +1391,7 @@ private fun PermissionsStep(
|
|||
InlineDivider()
|
||||
PermissionToggleRow(
|
||||
title = "Location",
|
||||
subtitle = "location.get (while app is open)",
|
||||
subtitle = "Share device location while app is open",
|
||||
checked = enableLocation,
|
||||
granted = locationGranted,
|
||||
onCheckedChange = onLocationChange,
|
||||
|
|
@ -1402,7 +1400,7 @@ private fun PermissionsStep(
|
|||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
PermissionToggleRow(
|
||||
title = "Notifications",
|
||||
subtitle = "system.notify and foreground alerts",
|
||||
subtitle = "Alerts and foreground service notices",
|
||||
checked = enableNotifications,
|
||||
granted = isPermissionGranted(context, Manifest.permission.POST_NOTIFICATIONS),
|
||||
onCheckedChange = onNotificationsChange,
|
||||
|
|
@ -1411,15 +1409,16 @@ private fun PermissionsStep(
|
|||
}
|
||||
PermissionToggleRow(
|
||||
title = "Notification listener",
|
||||
subtitle = "notifications.list and notifications.actions (opens Android Settings)",
|
||||
subtitle = "Read and act on your notifications",
|
||||
checked = enableNotificationListener,
|
||||
granted = notificationListenerGranted,
|
||||
onCheckedChange = onNotificationListenerChange,
|
||||
)
|
||||
InlineDivider()
|
||||
|
||||
PermissionSectionHeader("Media")
|
||||
PermissionToggleRow(
|
||||
title = "Microphone",
|
||||
subtitle = "Foreground Voice tab transcription",
|
||||
subtitle = "Voice transcription in the Voice tab",
|
||||
checked = enableMicrophone,
|
||||
granted = isPermissionGranted(context, Manifest.permission.RECORD_AUDIO),
|
||||
onCheckedChange = onMicrophoneChange,
|
||||
|
|
@ -1427,7 +1426,7 @@ private fun PermissionsStep(
|
|||
InlineDivider()
|
||||
PermissionToggleRow(
|
||||
title = "Camera",
|
||||
subtitle = "camera.snap and camera.clip",
|
||||
subtitle = "Take photos and short video clips",
|
||||
checked = enableCamera,
|
||||
granted = isPermissionGranted(context, Manifest.permission.CAMERA),
|
||||
onCheckedChange = onCameraChange,
|
||||
|
|
@ -1435,15 +1434,16 @@ private fun PermissionsStep(
|
|||
InlineDivider()
|
||||
PermissionToggleRow(
|
||||
title = "Photos",
|
||||
subtitle = "photos.latest",
|
||||
subtitle = "Access your recent photos",
|
||||
checked = enablePhotos,
|
||||
granted = isPermissionGranted(context, photosPermission),
|
||||
onCheckedChange = onPhotosChange,
|
||||
)
|
||||
InlineDivider()
|
||||
|
||||
PermissionSectionHeader("Personal Data")
|
||||
PermissionToggleRow(
|
||||
title = "Contacts",
|
||||
subtitle = "contacts.search and contacts.add",
|
||||
subtitle = "Search and add contacts",
|
||||
checked = enableContacts,
|
||||
granted = contactsGranted,
|
||||
onCheckedChange = onContactsChange,
|
||||
|
|
@ -1451,7 +1451,7 @@ private fun PermissionsStep(
|
|||
InlineDivider()
|
||||
PermissionToggleRow(
|
||||
title = "Calendar",
|
||||
subtitle = "calendar.events and calendar.add",
|
||||
subtitle = "Read and create calendar events",
|
||||
checked = enableCalendar,
|
||||
granted = calendarGranted,
|
||||
onCheckedChange = onCalendarChange,
|
||||
|
|
@ -1459,7 +1459,7 @@ private fun PermissionsStep(
|
|||
InlineDivider()
|
||||
PermissionToggleRow(
|
||||
title = "Motion",
|
||||
subtitle = "motion.activity and motion.pedometer",
|
||||
subtitle = "Activity and step tracking",
|
||||
checked = enableMotion,
|
||||
granted = motionGranted,
|
||||
onCheckedChange = onMotionChange,
|
||||
|
|
@ -1470,16 +1470,25 @@ private fun PermissionsStep(
|
|||
InlineDivider()
|
||||
PermissionToggleRow(
|
||||
title = "SMS",
|
||||
subtitle = "Allow gateway-triggered SMS sending",
|
||||
subtitle = "Send text messages via the gateway",
|
||||
checked = enableSms,
|
||||
granted = isPermissionGranted(context, Manifest.permission.SEND_SMS),
|
||||
onCheckedChange = onSmsChange,
|
||||
)
|
||||
}
|
||||
Text("All settings can be changed later in Settings.", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermissionSectionHeader(title: String) {
|
||||
Text(
|
||||
title.uppercase(),
|
||||
style = onboardingCaption1Style.copy(fontWeight = FontWeight.Bold, letterSpacing = 1.2.sp),
|
||||
color = onboardingAccent,
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermissionToggleRow(
|
||||
title: String,
|
||||
|
|
@ -1490,6 +1499,12 @@ private fun PermissionToggleRow(
|
|||
statusOverride: String? = null,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
val statusText = statusOverride ?: if (granted) "Granted" else "Not granted"
|
||||
val statusColor = when {
|
||||
statusOverride != null -> onboardingTextTertiary
|
||||
granted -> onboardingSuccess
|
||||
else -> onboardingWarning
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().heightIn(min = 50.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -1498,11 +1513,7 @@ private fun PermissionToggleRow(
|
|||
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text(title, style = onboardingHeadlineStyle, color = onboardingText)
|
||||
Text(subtitle, style = onboardingCalloutStyle.copy(lineHeight = 18.sp), color = onboardingTextSecondary)
|
||||
Text(
|
||||
statusOverride ?: if (granted) "Granted" else "Not granted",
|
||||
style = onboardingCaption1Style,
|
||||
color = if (granted) onboardingSuccess else onboardingTextSecondary,
|
||||
)
|
||||
Text(statusText, style = onboardingCaption1Style, color = statusColor)
|
||||
}
|
||||
Switch(
|
||||
checked = checked,
|
||||
|
|
@ -1530,20 +1541,131 @@ private fun FinalStep(
|
|||
enabledPermissions: String,
|
||||
methodLabel: String,
|
||||
) {
|
||||
StepShell(title = "Review") {
|
||||
SummaryField(label = "Method", value = methodLabel)
|
||||
SummaryField(label = "Gateway", value = parsedGateway?.displayUrl ?: "Invalid gateway URL")
|
||||
SummaryField(label = "Enabled Permissions", value = enabledPermissions)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Text("Review", style = onboardingTitle1Style, color = onboardingText)
|
||||
|
||||
SummaryCard(
|
||||
icon = Icons.Default.Link,
|
||||
label = "Method",
|
||||
value = methodLabel,
|
||||
accentColor = onboardingAccent,
|
||||
)
|
||||
SummaryCard(
|
||||
icon = Icons.Default.Cloud,
|
||||
label = "Gateway",
|
||||
value = parsedGateway?.displayUrl ?: "Invalid gateway URL",
|
||||
accentColor = Color(0xFF7C5AC7),
|
||||
)
|
||||
SummaryCard(
|
||||
icon = Icons.Default.Security,
|
||||
label = "Permissions",
|
||||
value = enabledPermissions,
|
||||
accentColor = onboardingSuccess,
|
||||
)
|
||||
|
||||
if (!attemptedConnect) {
|
||||
Text("Press Connect to verify gateway reachability and auth.", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = onboardingAccentSoft,
|
||||
border = androidx.compose.foundation.BorderStroke(1.dp, onboardingAccent.copy(alpha = 0.2f)),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(42.dp)
|
||||
.background(onboardingAccent.copy(alpha = 0.1f), RoundedCornerShape(11.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Wifi,
|
||||
contentDescription = null,
|
||||
tint = onboardingAccent,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
"Tap Connect to verify your gateway is reachable.",
|
||||
style = onboardingCalloutStyle,
|
||||
color = onboardingAccent,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (isConnected) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = Color(0xFFEEF9F3),
|
||||
border = androidx.compose.foundation.BorderStroke(1.dp, onboardingSuccess.copy(alpha = 0.2f)),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(42.dp)
|
||||
.background(onboardingSuccess.copy(alpha = 0.1f), RoundedCornerShape(11.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CheckCircle,
|
||||
contentDescription = null,
|
||||
tint = onboardingSuccess,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text("Connected", style = onboardingHeadlineStyle, color = onboardingSuccess)
|
||||
Text(
|
||||
serverName ?: remoteAddress ?: "gateway",
|
||||
style = onboardingCalloutStyle,
|
||||
color = onboardingSuccess.copy(alpha = 0.8f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text("Status: $statusText", style = onboardingCalloutStyle, color = if (isConnected) onboardingSuccess else onboardingTextSecondary)
|
||||
if (isConnected) {
|
||||
Text("Connected to ${serverName ?: remoteAddress ?: "gateway"}", style = onboardingCalloutStyle, color = onboardingSuccess)
|
||||
} else {
|
||||
GuideBlock(title = "Pairing Required") {
|
||||
Text("Run these on the gateway host:", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = Color(0xFFFFF8EC),
|
||||
border = androidx.compose.foundation.BorderStroke(1.dp, onboardingWarning.copy(alpha = 0.2f)),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(14.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(42.dp)
|
||||
.background(onboardingWarning.copy(alpha = 0.1f), RoundedCornerShape(11.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Link,
|
||||
contentDescription = null,
|
||||
tint = onboardingWarning,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text("Pairing Required", style = onboardingHeadlineStyle, color = onboardingWarning)
|
||||
Text("Run these on your gateway host:", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
}
|
||||
}
|
||||
CommandBlock("openclaw devices list")
|
||||
CommandBlock("openclaw devices approve <requestId>")
|
||||
Text("Then tap Connect again.", style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
|
|
@ -1554,15 +1676,46 @@ private fun FinalStep(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun SummaryField(label: String, value: String) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
label,
|
||||
style = onboardingCaption2Style.copy(fontWeight = FontWeight.SemiBold, letterSpacing = 0.6.sp),
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
Text(value, style = onboardingHeadlineStyle, color = onboardingText)
|
||||
HorizontalDivider(color = onboardingBorder)
|
||||
private fun SummaryCard(
|
||||
icon: ImageVector,
|
||||
label: String,
|
||||
value: String,
|
||||
accentColor: Color,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = onboardingSurface,
|
||||
border = androidx.compose.foundation.BorderStroke(1.dp, onboardingBorder),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(42.dp)
|
||||
.background(accentColor.copy(alpha = 0.1f), RoundedCornerShape(11.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = accentColor,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text(
|
||||
label.uppercase(),
|
||||
style = onboardingCaption1Style.copy(fontWeight = FontWeight.Bold, letterSpacing = 0.8.sp),
|
||||
color = onboardingTextSecondary,
|
||||
)
|
||||
Text(value, style = onboardingHeadlineStyle, color = onboardingText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1589,23 +1742,42 @@ private fun CommandBlock(command: String) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun Bullet(text: String) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.Top) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(top = 7.dp)
|
||||
.size(8.dp)
|
||||
.background(onboardingAccentSoft, CircleShape),
|
||||
)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(top = 9.dp)
|
||||
.size(4.dp)
|
||||
.background(onboardingAccent, CircleShape),
|
||||
)
|
||||
Text(text, style = onboardingBodyStyle, color = onboardingTextSecondary, modifier = Modifier.weight(1f))
|
||||
private fun FeatureCard(
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
accentColor: Color,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
color = onboardingSurface,
|
||||
border = androidx.compose.foundation.BorderStroke(1.dp, onboardingBorder),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(42.dp)
|
||||
.background(accentColor.copy(alpha = 0.1f), RoundedCornerShape(11.dp)),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = accentColor,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
Text(title, style = onboardingHeadlineStyle, color = onboardingText)
|
||||
Text(subtitle, style = onboardingCalloutStyle, color = onboardingTextSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue