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:
@@ -5,12 +5,21 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
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.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@@ -22,8 +31,13 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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 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.LegacyButtonContained
|
||||||
import org.db3.airmq.ui.theme.LegacyButtonGradientEnd
|
import org.db3.airmq.ui.theme.LegacyButtonGradientEnd
|
||||||
import org.db3.airmq.ui.theme.LegacyButtonGradientStart
|
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.LegacyButtonOnText
|
||||||
import org.db3.airmq.ui.theme.LegacyOutlineLight
|
import org.db3.airmq.ui.theme.LegacyOutlineLight
|
||||||
|
|
||||||
enum class AirMqButtonStyle {
|
enum class AirMQButtonStyle {
|
||||||
Contained,
|
Contained,
|
||||||
Outlined,
|
Outlined,
|
||||||
Text,
|
Text,
|
||||||
@@ -40,52 +54,58 @@ enum class AirMqButtonStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val LegacyButtonShape = RoundedCornerShape(18.dp)
|
private val LegacyButtonShape = RoundedCornerShape(18.dp)
|
||||||
private val LegacyButtonHeight = 36.dp
|
private val LegacyButtonHeight = 48.dp
|
||||||
private val LegacyDisabledContainer = Color(0xFFE0E0E0)
|
private val LegacyDisabledContainer = Color(0xFFE0E0E0)
|
||||||
private val LegacyDisabledContent = Color(0x61000000)
|
private val LegacyDisabledContent = Color(0x61000000)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AirMqButton(
|
fun AirMQButton(
|
||||||
text: String,
|
text: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
style: AirMqButtonStyle = AirMqButtonStyle.Contained
|
style: AirMQButtonStyle = AirMQButtonStyle.Contained,
|
||||||
|
leadingIconRes: Int? = null
|
||||||
) {
|
) {
|
||||||
when (style) {
|
when (style) {
|
||||||
AirMqButtonStyle.Contained -> AirMqContainedButton(
|
AirMQButtonStyle.Contained -> AirMQContainedButton(
|
||||||
text = text,
|
text = text,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled
|
enabled = enabled,
|
||||||
|
leadingIconRes = leadingIconRes
|
||||||
)
|
)
|
||||||
AirMqButtonStyle.Outlined -> AirMqOutlinedButton(
|
AirMQButtonStyle.Outlined -> AirMQOutlinedButton(
|
||||||
text = text,
|
text = text,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled
|
enabled = enabled,
|
||||||
|
leadingIconRes = leadingIconRes
|
||||||
)
|
)
|
||||||
AirMqButtonStyle.Text -> AirMqTextButton(
|
AirMQButtonStyle.Text -> AirMQTextButton(
|
||||||
text = text,
|
text = text,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled
|
enabled = enabled,
|
||||||
|
leadingIconRes = leadingIconRes
|
||||||
)
|
)
|
||||||
AirMqButtonStyle.Gradient -> AirMqGradientButton(
|
AirMQButtonStyle.Gradient -> AirMQGradientButton(
|
||||||
text = text,
|
text = text,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
enabled = enabled
|
enabled = enabled,
|
||||||
|
leadingIconRes = leadingIconRes
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AirMqContainedButton(
|
fun AirMQContainedButton(
|
||||||
text: String,
|
text: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
leadingIconRes: Int? = null
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -99,16 +119,21 @@ fun AirMqContainedButton(
|
|||||||
disabledContentColor = LegacyDisabledContent
|
disabledContentColor = LegacyDisabledContent
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(text = text, fontWeight = FontWeight.Medium)
|
AirMQButtonLabel(
|
||||||
|
text = text,
|
||||||
|
leadingIconRes = leadingIconRes,
|
||||||
|
iconTint = if (enabled) LegacyButtonOnContained else LegacyDisabledContent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AirMqOutlinedButton(
|
fun AirMQOutlinedButton(
|
||||||
text: String,
|
text: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
leadingIconRes: Int? = null
|
||||||
) {
|
) {
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -124,16 +149,21 @@ fun AirMqOutlinedButton(
|
|||||||
disabledContentColor = LegacyDisabledContent
|
disabledContentColor = LegacyDisabledContent
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(text = text, fontWeight = FontWeight.Medium)
|
AirMQButtonLabel(
|
||||||
|
text = text,
|
||||||
|
leadingIconRes = leadingIconRes,
|
||||||
|
iconTint = if (enabled) LegacyButtonOnOutlined else LegacyDisabledContent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AirMqTextButton(
|
fun AirMQTextButton(
|
||||||
text: String,
|
text: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
leadingIconRes: Int? = null
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -145,16 +175,21 @@ fun AirMqTextButton(
|
|||||||
disabledContentColor = LegacyDisabledContent
|
disabledContentColor = LegacyDisabledContent
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(text = text, fontWeight = FontWeight.Medium)
|
AirMQButtonLabel(
|
||||||
|
text = text,
|
||||||
|
leadingIconRes = leadingIconRes,
|
||||||
|
iconTint = if (enabled) LegacyButtonOnText else LegacyDisabledContent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AirMqGradientButton(
|
fun AirMQGradientButton(
|
||||||
text: String,
|
text: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true
|
enabled: Boolean = true,
|
||||||
|
leadingIconRes: Int? = null
|
||||||
) {
|
) {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val isPressed by interactionSource.collectIsPressedAsState()
|
val isPressed by interactionSource.collectIsPressedAsState()
|
||||||
@@ -183,6 +218,7 @@ fun AirMqGradientButton(
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.heightIn(min = LegacyButtonHeight)
|
||||||
.clip(LegacyButtonShape)
|
.clip(LegacyButtonShape)
|
||||||
.background(
|
.background(
|
||||||
brush = Brush.horizontalGradient(
|
brush = Brush.horizontalGradient(
|
||||||
@@ -192,10 +228,104 @@ fun AirMqGradientButton(
|
|||||||
.background(overlay),
|
.background(overlay),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
AirMQButtonLabel(
|
||||||
text = text,
|
text = text,
|
||||||
fontWeight = FontWeight.Medium,
|
leadingIconRes = leadingIconRes,
|
||||||
color = if (enabled) LegacyButtonOnContained else LegacyDisabledContent
|
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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
data class ScreenAction(
|
data class ScreenAction(
|
||||||
val label: String,
|
val label: String,
|
||||||
val onClick: () -> Unit,
|
val onClick: () -> Unit,
|
||||||
val style: AirMqButtonStyle = AirMqButtonStyle.Contained
|
val style: AirMQButtonStyle = AirMQButtonStyle.Contained
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -61,7 +61,7 @@ private fun ScreenContent(
|
|||||||
}
|
}
|
||||||
content?.invoke()
|
content?.invoke()
|
||||||
actions.forEach { action ->
|
actions.forEach { action ->
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = action.label,
|
text = action.label,
|
||||||
onClick = action.onClick,
|
onClick = action.onClick,
|
||||||
style = action.style,
|
style = action.style,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.db3.airmq.features.manage
|
package org.db3.airmq.features.manage
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -27,6 +33,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -34,8 +41,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import org.db3.airmq.R
|
import org.db3.airmq.R
|
||||||
import org.db3.airmq.features.common.AirMqButton
|
import org.db3.airmq.features.common.AirMQButton
|
||||||
import org.db3.airmq.features.common.AirMqButtonStyle
|
import org.db3.airmq.features.common.AirMQButtonStyle
|
||||||
import org.db3.airmq.features.manage.ManageScreenContract.Action
|
import org.db3.airmq.features.manage.ManageScreenContract.Action
|
||||||
import org.db3.airmq.features.manage.ManageScreenContract.DeviceItem
|
import org.db3.airmq.features.manage.ManageScreenContract.DeviceItem
|
||||||
import org.db3.airmq.features.manage.ManageScreenContract.Event
|
import org.db3.airmq.features.manage.ManageScreenContract.Event
|
||||||
@@ -92,10 +99,12 @@ private fun ManageScreenContent(
|
|||||||
ProfileHeader(
|
ProfileHeader(
|
||||||
name = uiState.userName,
|
name = uiState.userName,
|
||||||
email = uiState.userEmail,
|
email = uiState.userEmail,
|
||||||
|
isAnonymous = uiState.userMode == UserMode.ANONYMOUS,
|
||||||
onSettingsClick = { onEvent(Event.SettingsClicked) }
|
onSettingsClick = { onEvent(Event.SettingsClicked) }
|
||||||
)
|
)
|
||||||
when (uiState.userMode) {
|
when (uiState.userMode) {
|
||||||
UserMode.ANONYMOUS -> AnonymousContent(
|
UserMode.ANONYMOUS -> AnonymousContent(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
devicesLabel = uiState.devicesLabel,
|
devicesLabel = uiState.devicesLabel,
|
||||||
onSignIn = { onEvent(Event.SignInClicked) }
|
onSignIn = { onEvent(Event.SignInClicked) }
|
||||||
)
|
)
|
||||||
@@ -107,18 +116,19 @@ private fun ManageScreenContent(
|
|||||||
onOpenLocation = { onEvent(Event.DeviceLocationClicked(it)) }
|
onOpenLocation = { onEvent(Event.DeviceLocationClicked(it)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (uiState.userMode == UserMode.AUTHORIZED) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.manage_open_widget_constructor),
|
text = stringResource(id = R.string.manage_open_widget_constructor),
|
||||||
onClick = onOpenWidgetConstructor,
|
onClick = onOpenWidgetConstructor,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
)
|
)
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.back_to_dashboard),
|
text = stringResource(id = R.string.back_to_dashboard),
|
||||||
onClick = onBackToDashboard,
|
onClick = onBackToDashboard,
|
||||||
style = AirMqButtonStyle.Outlined,
|
style = AirMQButtonStyle.Outlined,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
@@ -126,11 +136,13 @@ private fun ManageScreenContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ProfileHeader(
|
private fun ProfileHeader(
|
||||||
name: String,
|
name: String,
|
||||||
email: String,
|
email: String,
|
||||||
|
isAnonymous: Boolean,
|
||||||
onSettingsClick: () -> Unit
|
onSettingsClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -141,39 +153,44 @@ private fun ProfileHeader(
|
|||||||
colors = listOf(LegacyNavGradientEnd, LegacyNavGradientStart)
|
colors = listOf(LegacyNavGradientEnd, LegacyNavGradientStart)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.padding(16.dp)
|
.statusBarsPadding()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.Top
|
||||||
) {
|
) {
|
||||||
Text(
|
if (isAnonymous) {
|
||||||
text = stringResource(id = R.string.title_manage),
|
Image(
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
painter = painterResource(id = R.drawable.placeholder_avatar_round),
|
||||||
color = Color.White
|
contentDescription = stringResource(id = R.string.content_desc_user_pic),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 24.dp)
|
||||||
|
.size(96.dp)
|
||||||
)
|
)
|
||||||
AirMqButton(
|
} else {
|
||||||
text = stringResource(id = R.string.title_settings),
|
|
||||||
onClick = onSettingsClick,
|
|
||||||
style = AirMqButtonStyle.Text
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(color = Color.White.copy(alpha = 0.25f), shape = CircleShape)
|
.padding(top = 24.dp)
|
||||||
.padding(horizontal = 14.dp, vertical = 10.dp)
|
.size(96.dp)
|
||||||
|
.background(color = Color.White.copy(alpha = 0.25f), shape = CircleShape),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = name.firstOrNull()?.uppercase() ?: "A",
|
text = name.firstOrNull()?.uppercase() ?: "A",
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
style = MaterialTheme.typography.titleMedium
|
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(
|
||||||
text = name,
|
text = name,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
@@ -186,31 +203,50 @@ private fun ProfileHeader(
|
|||||||
style = MaterialTheme.typography.bodyMedium
|
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
|
@Composable
|
||||||
private fun AnonymousContent(
|
private fun AnonymousContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
devicesLabel: String,
|
devicesLabel: String,
|
||||||
onSignIn: () -> Unit
|
onSignIn: () -> Unit
|
||||||
) {
|
) {
|
||||||
Column(
|
Box(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp),
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = devicesLabel,
|
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),
|
text = stringResource(id = R.string.button_sign_in),
|
||||||
onClick = onSignIn,
|
onClick = onSignIn,
|
||||||
style = AirMqButtonStyle.Gradient,
|
style = AirMQButtonStyle.Gradient,
|
||||||
modifier = Modifier.fillMaxWidth()
|
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)
|
Text(text = devicesLabel, style = MaterialTheme.typography.titleMedium)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.button_setup),
|
text = stringResource(id = R.string.button_setup),
|
||||||
onClick = onOpenSetup,
|
onClick = onOpenSetup,
|
||||||
style = AirMqButtonStyle.Gradient,
|
style = AirMQButtonStyle.Gradient,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
@@ -273,20 +309,20 @@ private fun DeviceRow(
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.button_open),
|
text = stringResource(id = R.string.button_open),
|
||||||
onClick = onOpenDevice,
|
onClick = onOpenDevice,
|
||||||
style = AirMqButtonStyle.Contained,
|
style = AirMQButtonStyle.Contained,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = if (item.hasLocation) {
|
text = if (item.hasLocation) {
|
||||||
stringResource(id = R.string.button_view_on_map)
|
stringResource(id = R.string.button_view_on_map)
|
||||||
} else {
|
} else {
|
||||||
stringResource(id = R.string.manage_set_location)
|
stringResource(id = R.string.manage_set_location)
|
||||||
},
|
},
|
||||||
onClick = onOpenLocation,
|
onClick = onOpenLocation,
|
||||||
style = AirMqButtonStyle.Outlined,
|
style = AirMQButtonStyle.Outlined,
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -300,7 +336,10 @@ private fun ManageScreenAnonymousPreview() {
|
|||||||
AirMQTheme {
|
AirMQTheme {
|
||||||
ManageScreenContent(
|
ManageScreenContent(
|
||||||
uiState = State(
|
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 = {},
|
onEvent = {},
|
||||||
onOpenWidgetConstructor = {},
|
onOpenWidgetConstructor = {},
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import org.db3.airmq.R
|
import org.db3.airmq.R
|
||||||
import org.db3.airmq.features.common.AirMqButton
|
import org.db3.airmq.features.common.AirMQButton
|
||||||
import org.db3.airmq.features.common.AirMqButtonStyle
|
import org.db3.airmq.features.common.AirMQButtonStyle
|
||||||
import org.db3.airmq.features.map.MapScreenContract.DevicePanelState
|
import org.db3.airmq.features.map.MapScreenContract.DevicePanelState
|
||||||
import org.db3.airmq.features.map.MapScreenContract.DeviceSensorType
|
import org.db3.airmq.features.map.MapScreenContract.DeviceSensorType
|
||||||
import org.db3.airmq.features.map.MapScreenContract.SearchResult
|
import org.db3.airmq.features.map.MapScreenContract.SearchResult
|
||||||
@@ -279,10 +279,10 @@ fun MapSearchOverlay(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.content_back),
|
text = stringResource(id = R.string.content_back),
|
||||||
onClick = onClose,
|
onClick = onClose,
|
||||||
style = AirMqButtonStyle.Outlined
|
style = AirMQButtonStyle.Outlined
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = query,
|
value = query,
|
||||||
@@ -356,10 +356,10 @@ fun MapDevicePanel(
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.button_close),
|
text = stringResource(id = R.string.button_close),
|
||||||
onClick = onClose,
|
onClick = onClose,
|
||||||
style = AirMqButtonStyle.Text
|
style = AirMQButtonStyle.Text
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
@@ -372,10 +372,10 @@ fun MapDevicePanel(
|
|||||||
color = Color.Gray
|
color = Color.Gray
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.button_open),
|
text = stringResource(id = R.string.button_open),
|
||||||
onClick = onOpenDevice,
|
onClick = onOpenDevice,
|
||||||
style = AirMqButtonStyle.Outlined
|
style = AirMQButtonStyle.Outlined
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,19 +386,19 @@ fun MapDevicePanel(
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.map_arrow_left),
|
text = stringResource(id = R.string.map_arrow_left),
|
||||||
onClick = onDateBack,
|
onClick = onDateBack,
|
||||||
style = AirMqButtonStyle.Text
|
style = AirMQButtonStyle.Text
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = data.displayedDateRange,
|
text = data.displayedDateRange,
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
AirMqButton(
|
AirMQButton(
|
||||||
text = stringResource(id = R.string.map_arrow_right),
|
text = stringResource(id = R.string.map_arrow_right),
|
||||||
onClick = onDateForward,
|
onClick = onDateForward,
|
||||||
style = AirMqButtonStyle.Text
|
style = AirMQButtonStyle.Text
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class MapViewModel @Inject constructor(
|
|||||||
private val _actions = MutableSharedFlow<Action>(extraBufferCapacity = 1)
|
private val _actions = MutableSharedFlow<Action>(extraBufferCapacity = 1)
|
||||||
val actions: SharedFlow<Action> = _actions.asSharedFlow()
|
val actions: SharedFlow<Action> = _actions.asSharedFlow()
|
||||||
|
|
||||||
|
private var showOfflineDevices = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
refreshMapItems()
|
refreshMapItems()
|
||||||
}
|
}
|
||||||
@@ -158,14 +160,15 @@ class MapViewModel @Inject constructor(
|
|||||||
_uiState.value = _uiState.value.copy(isLoading = true)
|
_uiState.value = _uiState.value.copy(isLoading = true)
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val result = runCatching {
|
val result = runCatching {
|
||||||
val showOfflineDevices = settingsService.getOfflineDevicesVisible()
|
showOfflineDevices = settingsService.getOfflineDevicesVisible()
|
||||||
mapService.fetchMapItems(showOfflineDevices = showOfflineDevices)
|
mapService.fetchMapItems(showOfflineDevices = showOfflineDevices)
|
||||||
}
|
}
|
||||||
_uiState.value = result.fold(
|
_uiState.value = result.fold(
|
||||||
onSuccess = { items ->
|
onSuccess = { items ->
|
||||||
domainItems = items
|
domainItems = items
|
||||||
val searchPanelState = _uiState.value.searchPanelState
|
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(
|
_uiState.value.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
items = markers,
|
items = markers,
|
||||||
@@ -223,7 +226,17 @@ class MapViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun List<MapItem>.toMarkers(sensorType: SensorType): List<MapMarker> {
|
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(
|
MapMarker(
|
||||||
id = item.id,
|
id = item.id,
|
||||||
title = item.title,
|
title = item.title,
|
||||||
@@ -232,10 +245,7 @@ class MapViewModel @Inject constructor(
|
|||||||
longitude = item.longitude,
|
longitude = item.longitude,
|
||||||
isOnline = item.isOnline,
|
isOnline = item.isOnline,
|
||||||
sensorType = sensorType,
|
sensorType = sensorType,
|
||||||
value = when (sensorType) {
|
value = value,
|
||||||
SensorType.DUST -> item.dustValue
|
|
||||||
SensorType.RADIOACTIVITY -> item.radioactivityValue
|
|
||||||
},
|
|
||||||
isOwned = false // TODO: derive from authenticated user when ownership/auth is implemented.
|
isOwned = false // TODO: derive from authenticated user when ownership/auth is implemented.
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.db3.airmq.features.navigation
|
package org.db3.airmq.features.navigation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -64,6 +65,7 @@ fun AirMQNavGraph(modifier: Modifier = Modifier) {
|
|||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
if (showBottomBar) {
|
if (showBottomBar) {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
10
app/src/main/res/drawable/ic_account.xml
Normal file
10
app/src/main/res/drawable/ic_account.xml
Normal 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>
|
||||||
12
app/src/main/res/drawable/ic_settings.xml
Normal file
12
app/src/main/res/drawable/ic_settings.xml
Normal 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>
|
||||||
12
app/src/main/res/drawable/placeholder_avatar_round.xml
Normal file
12
app/src/main/res/drawable/placeholder_avatar_round.xml
Normal 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>
|
||||||
Reference in New Issue
Block a user