Refine Compose manage UI parity and button behavior.

Apply legacy-accurate manage header/CTA styling, add reusable button icon support with previews, and include related map/navigation polish updates in this working tree.

Made-with: Cursor
This commit is contained in:
2026-03-01 19:02:13 +01:00
parent 9885162c4e
commit 7c00163304
9 changed files with 324 additions and 109 deletions

View File

@@ -5,12 +5,21 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.BorderStroke
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -22,8 +31,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.db3.airmq.R
import org.db3.airmq.ui.theme.AirMQTheme
import org.db3.airmq.ui.theme.LegacyButtonContained
import org.db3.airmq.ui.theme.LegacyButtonGradientEnd
import org.db3.airmq.ui.theme.LegacyButtonGradientStart
@@ -32,7 +46,7 @@ import org.db3.airmq.ui.theme.LegacyButtonOnOutlined
import org.db3.airmq.ui.theme.LegacyButtonOnText
import org.db3.airmq.ui.theme.LegacyOutlineLight
enum class AirMqButtonStyle {
enum class AirMQButtonStyle {
Contained,
Outlined,
Text,
@@ -40,52 +54,58 @@ enum class AirMqButtonStyle {
}
private val LegacyButtonShape = RoundedCornerShape(18.dp)
private val LegacyButtonHeight = 36.dp
private val LegacyButtonHeight = 48.dp
private val LegacyDisabledContainer = Color(0xFFE0E0E0)
private val LegacyDisabledContent = Color(0x61000000)
@Composable
fun AirMqButton(
fun AirMQButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
style: AirMqButtonStyle = AirMqButtonStyle.Contained
style: AirMQButtonStyle = AirMQButtonStyle.Contained,
leadingIconRes: Int? = null
) {
when (style) {
AirMqButtonStyle.Contained -> AirMqContainedButton(
AirMQButtonStyle.Contained -> AirMQContainedButton(
text = text,
onClick = onClick,
modifier = modifier,
enabled = enabled
enabled = enabled,
leadingIconRes = leadingIconRes
)
AirMqButtonStyle.Outlined -> AirMqOutlinedButton(
AirMQButtonStyle.Outlined -> AirMQOutlinedButton(
text = text,
onClick = onClick,
modifier = modifier,
enabled = enabled
enabled = enabled,
leadingIconRes = leadingIconRes
)
AirMqButtonStyle.Text -> AirMqTextButton(
AirMQButtonStyle.Text -> AirMQTextButton(
text = text,
onClick = onClick,
modifier = modifier,
enabled = enabled
enabled = enabled,
leadingIconRes = leadingIconRes
)
AirMqButtonStyle.Gradient -> AirMqGradientButton(
AirMQButtonStyle.Gradient -> AirMQGradientButton(
text = text,
onClick = onClick,
modifier = modifier,
enabled = enabled
enabled = enabled,
leadingIconRes = leadingIconRes
)
}
}
@Composable
fun AirMqContainedButton(
fun AirMQContainedButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true
enabled: Boolean = true,
leadingIconRes: Int? = null
) {
Button(
onClick = onClick,
@@ -99,16 +119,21 @@ fun AirMqContainedButton(
disabledContentColor = LegacyDisabledContent
)
) {
Text(text = text, fontWeight = FontWeight.Medium)
AirMQButtonLabel(
text = text,
leadingIconRes = leadingIconRes,
iconTint = if (enabled) LegacyButtonOnContained else LegacyDisabledContent
)
}
}
@Composable
fun AirMqOutlinedButton(
fun AirMQOutlinedButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true
enabled: Boolean = true,
leadingIconRes: Int? = null
) {
OutlinedButton(
onClick = onClick,
@@ -124,16 +149,21 @@ fun AirMqOutlinedButton(
disabledContentColor = LegacyDisabledContent
)
) {
Text(text = text, fontWeight = FontWeight.Medium)
AirMQButtonLabel(
text = text,
leadingIconRes = leadingIconRes,
iconTint = if (enabled) LegacyButtonOnOutlined else LegacyDisabledContent
)
}
}
@Composable
fun AirMqTextButton(
fun AirMQTextButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true
enabled: Boolean = true,
leadingIconRes: Int? = null
) {
TextButton(
onClick = onClick,
@@ -145,16 +175,21 @@ fun AirMqTextButton(
disabledContentColor = LegacyDisabledContent
)
) {
Text(text = text, fontWeight = FontWeight.Medium)
AirMQButtonLabel(
text = text,
leadingIconRes = leadingIconRes,
iconTint = if (enabled) LegacyButtonOnText else LegacyDisabledContent
)
}
}
@Composable
fun AirMqGradientButton(
fun AirMQGradientButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true
enabled: Boolean = true,
leadingIconRes: Int? = null
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
@@ -183,6 +218,7 @@ fun AirMqGradientButton(
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = LegacyButtonHeight)
.clip(LegacyButtonShape)
.background(
brush = Brush.horizontalGradient(
@@ -192,10 +228,104 @@ fun AirMqGradientButton(
.background(overlay),
contentAlignment = Alignment.Center
) {
Text(
AirMQButtonLabel(
text = text,
fontWeight = FontWeight.Medium,
color = if (enabled) LegacyButtonOnContained else LegacyDisabledContent
leadingIconRes = leadingIconRes,
iconTint = if (enabled) LegacyButtonOnContained else LegacyDisabledContent,
textColor = if (enabled) LegacyButtonOnContained else LegacyDisabledContent
)
}
}
}
@Composable
private fun AirMQButtonLabel(
text: String,
leadingIconRes: Int?,
iconTint: Color,
textColor: Color = Color.Unspecified
) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (leadingIconRes != null) {
Icon(
painter = painterResource(id = leadingIconRes),
contentDescription = null,
tint = iconTint,
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(6.dp))
}
Text(
text = text.uppercase(),
fontWeight = FontWeight.Medium,
color = textColor,
textAlign = TextAlign.Start
)
}
}
@Preview(name = "Buttons - All Styles", showBackground = true)
@Composable
private fun AirMQButtonsPreviewAllStyles() {
AirMQTheme {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
AirMQButton(
text = "Contained",
onClick = {},
style = AirMQButtonStyle.Contained,
modifier = Modifier.fillMaxWidth()
)
AirMQButton(
text = "Outlined",
onClick = {},
style = AirMQButtonStyle.Outlined,
modifier = Modifier.fillMaxWidth()
)
AirMQButton(
text = "Text",
onClick = {},
style = AirMQButtonStyle.Text,
modifier = Modifier.fillMaxWidth()
)
AirMQButton(
text = "Gradient",
onClick = {},
style = AirMQButtonStyle.Gradient,
modifier = Modifier.fillMaxWidth()
)
}
}
}
@Preview(name = "Buttons - Gradient With Icon", showBackground = true)
@Composable
private fun AirMQButtonsPreviewGradientWithIcon() {
AirMQTheme {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
AirMQButton(
text = "Sign In",
onClick = {},
style = AirMQButtonStyle.Gradient,
leadingIconRes = R.drawable.ic_account,
modifier = Modifier.fillMaxWidth()
)
AirMQButton(
text = "Sign In",
onClick = {},
style = AirMQButtonStyle.Gradient,
leadingIconRes = R.drawable.ic_account,
enabled = false,
modifier = Modifier.fillMaxWidth()
)
}
}

View File

@@ -18,7 +18,7 @@ import androidx.compose.ui.unit.dp
data class ScreenAction(
val label: String,
val onClick: () -> Unit,
val style: AirMqButtonStyle = AirMqButtonStyle.Contained
val style: AirMQButtonStyle = AirMQButtonStyle.Contained
)
@Composable
@@ -61,7 +61,7 @@ private fun ScreenContent(
}
content?.invoke()
actions.forEach { action ->
AirMqButton(
AirMQButton(
text = action.label,
onClick = action.onClick,
style = action.style,

View File

@@ -1,6 +1,7 @@
package org.db3.airmq.features.manage
import androidx.compose.foundation.background
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -10,12 +11,17 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -27,6 +33,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
@@ -34,8 +41,8 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.flow.collectLatest
import org.db3.airmq.R
import org.db3.airmq.features.common.AirMqButton
import org.db3.airmq.features.common.AirMqButtonStyle
import org.db3.airmq.features.common.AirMQButton
import org.db3.airmq.features.common.AirMQButtonStyle
import org.db3.airmq.features.manage.ManageScreenContract.Action
import org.db3.airmq.features.manage.ManageScreenContract.DeviceItem
import org.db3.airmq.features.manage.ManageScreenContract.Event
@@ -92,10 +99,12 @@ private fun ManageScreenContent(
ProfileHeader(
name = uiState.userName,
email = uiState.userEmail,
isAnonymous = uiState.userMode == UserMode.ANONYMOUS,
onSettingsClick = { onEvent(Event.SettingsClicked) }
)
when (uiState.userMode) {
UserMode.ANONYMOUS -> AnonymousContent(
modifier = Modifier.weight(1f),
devicesLabel = uiState.devicesLabel,
onSignIn = { onEvent(Event.SignInClicked) }
)
@@ -107,22 +116,24 @@ private fun ManageScreenContent(
onOpenLocation = { onEvent(Event.DeviceLocationClicked(it)) }
)
}
Spacer(modifier = Modifier.height(8.dp))
AirMqButton(
text = stringResource(id = R.string.manage_open_widget_constructor),
onClick = onOpenWidgetConstructor,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
)
AirMqButton(
text = stringResource(id = R.string.back_to_dashboard),
onClick = onBackToDashboard,
style = AirMqButtonStyle.Outlined,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
)
if (uiState.userMode == UserMode.AUTHORIZED) {
Spacer(modifier = Modifier.height(8.dp))
AirMQButton(
text = stringResource(id = R.string.manage_open_widget_constructor),
onClick = onOpenWidgetConstructor,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
)
AirMQButton(
text = stringResource(id = R.string.back_to_dashboard),
onClick = onBackToDashboard,
style = AirMQButtonStyle.Outlined,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
}
}
@@ -131,6 +142,7 @@ private fun ManageScreenContent(
private fun ProfileHeader(
name: String,
email: String,
isAnonymous: Boolean,
onSettingsClick: () -> Unit
) {
Column(
@@ -141,39 +153,44 @@ private fun ProfileHeader(
colors = listOf(LegacyNavGradientEnd, LegacyNavGradientStart)
)
)
.padding(16.dp)
.statusBarsPadding()
.padding(horizontal = 16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.Top
) {
Text(
text = stringResource(id = R.string.title_manage),
style = MaterialTheme.typography.headlineSmall,
color = Color.White
)
AirMqButton(
text = stringResource(id = R.string.title_settings),
onClick = onSettingsClick,
style = AirMqButtonStyle.Text
)
}
Spacer(modifier = Modifier.height(12.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.background(color = Color.White.copy(alpha = 0.25f), shape = CircleShape)
.padding(horizontal = 14.dp, vertical = 10.dp)
) {
Text(
text = name.firstOrNull()?.uppercase() ?: "A",
color = Color.White,
style = MaterialTheme.typography.titleMedium
if (isAnonymous) {
Image(
painter = painterResource(id = R.drawable.placeholder_avatar_round),
contentDescription = stringResource(id = R.string.content_desc_user_pic),
modifier = Modifier
.padding(top = 24.dp)
.size(96.dp)
)
} else {
Box(
modifier = Modifier
.padding(top = 24.dp)
.size(96.dp)
.background(color = Color.White.copy(alpha = 0.25f), shape = CircleShape),
contentAlignment = Alignment.Center
) {
Text(
text = name.firstOrNull()?.uppercase() ?: "A",
color = Color.White,
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Medium
)
}
}
Spacer(modifier = Modifier.padding(horizontal = 8.dp))
Column {
Spacer(modifier = Modifier.width(12.dp))
Column(
modifier = Modifier
.weight(1f)
.padding(top = 24.dp)
) {
Text(
text = name,
color = Color.White,
@@ -186,31 +203,50 @@ private fun ProfileHeader(
style = MaterialTheme.typography.bodyMedium
)
}
IconButton(
onClick = onSettingsClick,
modifier = Modifier
.padding(top = 24.dp)
.size(36.dp)
) {
Icon(
painter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.content_settings),
tint = Color.White
)
}
}
Spacer(modifier = Modifier.height(24.dp))
}
}
@Composable
private fun AnonymousContent(
modifier: Modifier = Modifier,
devicesLabel: String,
onSignIn: () -> Unit
) {
Column(
modifier = Modifier
Box(
modifier = modifier
.fillMaxWidth()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
Text(
text = devicesLabel,
style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Light),
color = Color(0xFFBDBDBD),
modifier = Modifier.align(Alignment.Center)
)
Spacer(modifier = Modifier.height(16.dp))
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.button_sign_in),
onClick = onSignIn,
style = AirMqButtonStyle.Gradient,
modifier = Modifier.fillMaxWidth()
style = AirMQButtonStyle.Gradient,
leadingIconRes = R.drawable.ic_account,
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth(0.46f)
.height(48.dp)
.padding(bottom = 16.dp)
)
}
}
@@ -230,10 +266,10 @@ private fun AuthorizedContent(
) {
Text(text = devicesLabel, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(12.dp))
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.button_setup),
onClick = onOpenSetup,
style = AirMqButtonStyle.Gradient,
style = AirMQButtonStyle.Gradient,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
@@ -273,20 +309,20 @@ private fun DeviceRow(
)
Spacer(modifier = Modifier.height(8.dp))
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.button_open),
onClick = onOpenDevice,
style = AirMqButtonStyle.Contained,
style = AirMQButtonStyle.Contained,
modifier = Modifier.weight(1f)
)
AirMqButton(
AirMQButton(
text = if (item.hasLocation) {
stringResource(id = R.string.button_view_on_map)
} else {
stringResource(id = R.string.manage_set_location)
},
onClick = onOpenLocation,
style = AirMqButtonStyle.Outlined,
style = AirMQButtonStyle.Outlined,
modifier = Modifier.weight(1f)
)
}
@@ -300,7 +336,10 @@ private fun ManageScreenAnonymousPreview() {
AirMQTheme {
ManageScreenContent(
uiState = State(
userMode = UserMode.ANONYMOUS
userMode = UserMode.ANONYMOUS,
userName = "Anonymous user",
userEmail = "Your preferences are not being synced, please sign in",
devicesLabel = "Sign in to add devices"
),
onEvent = {},
onOpenWidgetConstructor = {},

View File

@@ -41,8 +41,8 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.db3.airmq.R
import org.db3.airmq.features.common.AirMqButton
import org.db3.airmq.features.common.AirMqButtonStyle
import org.db3.airmq.features.common.AirMQButton
import org.db3.airmq.features.common.AirMQButtonStyle
import org.db3.airmq.features.map.MapScreenContract.DevicePanelState
import org.db3.airmq.features.map.MapScreenContract.DeviceSensorType
import org.db3.airmq.features.map.MapScreenContract.SearchResult
@@ -279,10 +279,10 @@ fun MapSearchOverlay(
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.content_back),
onClick = onClose,
style = AirMqButtonStyle.Outlined
style = AirMQButtonStyle.Outlined
)
OutlinedTextField(
value = query,
@@ -356,10 +356,10 @@ fun MapDevicePanel(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.button_close),
onClick = onClose,
style = AirMqButtonStyle.Text
style = AirMQButtonStyle.Text
)
Column(
modifier = Modifier.weight(1f),
@@ -372,10 +372,10 @@ fun MapDevicePanel(
color = Color.Gray
)
}
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.button_open),
onClick = onOpenDevice,
style = AirMqButtonStyle.Outlined
style = AirMQButtonStyle.Outlined
)
}
@@ -386,19 +386,19 @@ fun MapDevicePanel(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.map_arrow_left),
onClick = onDateBack,
style = AirMqButtonStyle.Text
style = AirMQButtonStyle.Text
)
Text(
text = data.displayedDateRange,
style = MaterialTheme.typography.bodyMedium
)
AirMqButton(
AirMQButton(
text = stringResource(id = R.string.map_arrow_right),
onClick = onDateForward,
style = AirMqButtonStyle.Text
style = AirMQButtonStyle.Text
)
}

View File

@@ -42,6 +42,8 @@ class MapViewModel @Inject constructor(
private val _actions = MutableSharedFlow<Action>(extraBufferCapacity = 1)
val actions: SharedFlow<Action> = _actions.asSharedFlow()
private var showOfflineDevices = false
init {
refreshMapItems()
}
@@ -158,14 +160,15 @@ class MapViewModel @Inject constructor(
_uiState.value = _uiState.value.copy(isLoading = true)
viewModelScope.launch(Dispatchers.IO) {
val result = runCatching {
val showOfflineDevices = settingsService.getOfflineDevicesVisible()
showOfflineDevices = settingsService.getOfflineDevicesVisible()
mapService.fetchMapItems(showOfflineDevices = showOfflineDevices)
}
_uiState.value = result.fold(
onSuccess = { items ->
domainItems = items
val searchPanelState = _uiState.value.searchPanelState
val markers = items.toMarkers(_uiState.value.selectedTopSensor)
val selectedSensorType = _uiState.value.selectedTopSensor
val markers = items.toMarkers(selectedSensorType)
_uiState.value.copy(
isLoading = false,
items = markers,
@@ -223,7 +226,17 @@ class MapViewModel @Inject constructor(
}
private fun List<MapItem>.toMarkers(sensorType: SensorType): List<MapMarker> {
return map { item ->
return mapNotNull { item ->
val value = when (sensorType) {
SensorType.DUST -> item.dustValue
SensorType.RADIOACTIVITY -> item.radioactivityValue
}
// Return null if device is offline or value is missing
if (!showOfflineDevices && (!item.isOnline || value == null )) {
return@mapNotNull null
}
MapMarker(
id = item.id,
title = item.title,
@@ -232,10 +245,7 @@ class MapViewModel @Inject constructor(
longitude = item.longitude,
isOnline = item.isOnline,
sensorType = sensorType,
value = when (sensorType) {
SensorType.DUST -> item.dustValue
SensorType.RADIOACTIVITY -> item.radioactivityValue
},
value = value,
isOwned = false // TODO: derive from authenticated user when ownership/auth is implemented.
)
}

View File

@@ -1,6 +1,7 @@
package org.db3.airmq.features.navigation
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Icon
@@ -64,6 +65,7 @@ fun AirMQNavGraph(modifier: Modifier = Modifier) {
Scaffold(
modifier = modifier,
contentWindowInsets = WindowInsets(0, 0, 0, 0),
bottomBar = {
if (showBottomBar) {
Box(

View File

@@ -0,0 +1,10 @@
<vector android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:tools="http://schemas.android.com/tools"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"
tools:ignore="VectorPath" />
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="96dp"
android:height="96dp"
android:viewportWidth="96"
android:viewportHeight="96">
<path
android:pathData="M48,96L48,96C21.49,96 0,74.51 0,48v0C0,21.49 21.49,0 48,0h0c26.51,0 48,21.49 48,48v0C96,74.51 74.51,96 48,96z"
android:fillColor="#E6E6E6" />
<path
android:pathData="M46.79,83.76c-5.16,-0.07 -11.1,-1.5 -16.25,-5.39c-2.98,-2.26 -4.96,-5.17 -5.51,-8.96c-0.37,-2.58 0.36,-4.1 2.65,-5.32c0.23,-0.12 0.36,-0.5 0.47,-0.78c0.45,-1.22 0.9,-2.43 1.29,-3.67c0.15,-0.46 0.33,-1.02 0.19,-1.43c-0.66,-2.06 -1.49,-4.06 -2.13,-6.13c-0.99,-3.2 -0.94,-6.48 -0.51,-9.78c0.44,-3.45 0.7,-6.93 1.19,-10.38c0.46,-3.21 0.93,-6.44 1.73,-9.58c0.88,-3.48 3.38,-5.83 6.52,-7.34c7.65,-3.68 15.4,-3.67 23.06,-0.01c4.51,2.15 6.78,5.95 7.34,10.83c0.48,4.15 0.92,8.31 1.42,12.46c0.24,2 0.69,3.97 0.89,5.97c0.37,3.65 -0.27,7.17 -1.65,10.56c-0.26,0.64 -0.42,1.33 -0.72,1.95c-0.64,1.31 -0.99,2.57 0.25,3.75c0.1,0.1 0.17,0.27 0.2,0.42c0.3,1.77 0.98,3.12 2.59,4.3c1.72,1.26 1.32,3.48 0.85,5.41c-1.03,4.25 -3.84,7.09 -7.46,9.22C58.5,82.61 53.38,83.75 46.79,83.76zM45.49,38.67c-3.49,-2.2 -6.81,-4.38 -11,-4.36c-1.49,0 -2.5,1.18 -2.56,2.76c-0.07,1.69 1.08,3.52 2.6,4.2C37.59,42.63 42.66,41.29 45.49,38.67zM50.27,38.81c3.31,2.18 6.45,3.6 10.17,2.73c2.12,-0.49 3.29,-1.93 3.54,-4.06c0.27,-2.3 -1.64,-3.68 -4.32,-3.11c-3.11,0.66 -5.86,2.11 -8.49,3.82C50.94,38.34 50.73,38.5 50.27,38.81zM48.23,58.86c2,-0.09 3.9,-0.78 5.13,-1.85c0.97,-0.84 0.93,-1.6 -0.17,-2.18c-3.46,-1.82 -6.95,-1.83 -10.42,-0.02c-1.13,0.59 -1.21,1.36 -0.24,2.17C44.18,58.34 46.11,58.92 48.23,58.86z"
android:fillColor="#808080" />
</vector>