feat(metric): reimplement RoundedDiagram/RingDiagram in Compose
- Add MetricGauge, RingGauge, MetricGaugeRow composables - Add MetricGaugeContract with SensorType enum and AQI color mapping - Add AQI colors (SensorGreen, SensorYellow, etc.) to theme - Copy ic_temperature, ic_humidity, ic_pressure drawables from legacy - Integrate MetricGaugeRow into DashboardScreen - Add 9 preview composables for portfolio readiness Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
package org.db3.airmq.features.common.metric
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import org.db3.airmq.features.common.metric.getColorFromMetrics
|
||||
import org.db3.airmq.features.common.metric.getSensorIconRes
|
||||
import org.db3.airmq.ui.theme.AirMQTheme
|
||||
import org.db3.airmq.ui.theme.LegacyNavGradientEnd
|
||||
import org.db3.airmq.ui.theme.LegacyNavGradientStart
|
||||
|
||||
/**
|
||||
* Single metric gauge with ring, value, units, and icon.
|
||||
* Mirrors legacy [RoundedDiagram] layout and behavior.
|
||||
*
|
||||
* @param value Current value; null shows "?"
|
||||
* @param sensorType Sensor type for icon, units, and progress logic
|
||||
* @param selected When true, shows frosted background
|
||||
* @param onClick Called when gauge is tapped
|
||||
* @param modifier Modifier
|
||||
*/
|
||||
@Composable
|
||||
fun MetricGauge(
|
||||
value: Float?,
|
||||
sensorType: SensorType,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val (progress, progressColor) = when (sensorType) {
|
||||
SensorType.HUMIDITY -> {
|
||||
val p = value?.coerceIn(0f, 100f) ?: 0f
|
||||
p to Color.White
|
||||
}
|
||||
SensorType.DUST -> {
|
||||
val p = value?.coerceIn(0f, 100f) ?: 0f
|
||||
val color = value?.let { getColorFromMetrics(it, sensorType) } ?: Color.White
|
||||
p to color
|
||||
}
|
||||
else -> 0f to Color.White
|
||||
}
|
||||
|
||||
val valueText = when {
|
||||
value == null -> "?"
|
||||
sensorType == SensorType.RADIOACTIVITY -> "%.2f".format(value)
|
||||
else -> kotlin.math.round(value).toInt().toString()
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.widthIn(min = 96.dp)
|
||||
.padding(horizontal = 4.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null
|
||||
) { onClick() }
|
||||
) {
|
||||
if (selected) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.background(Color.White.copy(alpha = 0.1f))
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(vertical = 8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
RingGauge(
|
||||
progress = progress,
|
||||
progressColor = progressColor,
|
||||
modifier = Modifier.size(77.dp)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = valueText,
|
||||
color = Color.White,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Text(
|
||||
text = sensorType.units(),
|
||||
color = Color.White.copy(alpha = 0.54f),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(id = getSensorIconRes(sensorType)),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.offset(y = 38.dp)
|
||||
.size(24.dp),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Default sensor order for dashboard gauge row. */
|
||||
private val DashboardGaugeOrder = listOf(
|
||||
SensorType.DUST,
|
||||
SensorType.RADIOACTIVITY,
|
||||
SensorType.TEMPERATURE
|
||||
)
|
||||
|
||||
/**
|
||||
* Horizontal row of metric gauges (dust, radioactivity, temperature).
|
||||
*
|
||||
* @param selectedSensor Currently selected sensor; null if none
|
||||
* @param values Map of sensor type to value
|
||||
* @param onGaugeSelected Called when a gauge is tapped
|
||||
* @param modifier Modifier
|
||||
*/
|
||||
@Composable
|
||||
fun MetricGaugeRow(
|
||||
selectedSensor: SensorType?,
|
||||
values: Map<SensorType, Float?>,
|
||||
onGaugeSelected: (SensorType) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
for (sensorType in DashboardGaugeOrder) {
|
||||
MetricGauge(
|
||||
value = values[sensorType],
|
||||
sensorType = sensorType,
|
||||
selected = selectedSensor == sensorType,
|
||||
onClick = { onGaugeSelected(sensorType) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Dust – value 6")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeDust() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = 6f,
|
||||
sensorType = SensorType.DUST,
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Radiation – value 0")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeRadiation() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = 0f,
|
||||
sensorType = SensorType.RADIOACTIVITY,
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Temperature – value 3")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeTemperature() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = 3f,
|
||||
sensorType = SensorType.TEMPERATURE,
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Humidity – value 65")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeHumidity() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = 65f,
|
||||
sensorType = SensorType.HUMIDITY,
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "No data")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeNoData() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = null,
|
||||
sensorType = SensorType.DUST,
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Selected")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeSelected() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = 6f,
|
||||
sensorType = SensorType.DUST,
|
||||
selected = true,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Gauge row")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeRow() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGaugeRow(
|
||||
selectedSensor = SensorType.DUST,
|
||||
values = mapOf(
|
||||
SensorType.DUST to 6f,
|
||||
SensorType.RADIOACTIVITY to 0f,
|
||||
SensorType.TEMPERATURE to 3f
|
||||
),
|
||||
onGaugeSelected = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Dust orange band")
|
||||
@Composable
|
||||
private fun PreviewMetricGaugeDustOrange() {
|
||||
AirMQTheme {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
MetricGauge(
|
||||
value = 40f,
|
||||
sensorType = SensorType.DUST,
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.db3.airmq.features.common.metric
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import org.db3.airmq.R
|
||||
import org.db3.airmq.features.common.chart.getUnitsForSensor
|
||||
import org.db3.airmq.ui.theme.SensorGreen
|
||||
import org.db3.airmq.ui.theme.SensorOrange
|
||||
import org.db3.airmq.ui.theme.SensorPink
|
||||
import org.db3.airmq.ui.theme.SensorPurple
|
||||
import org.db3.airmq.ui.theme.SensorRed
|
||||
import org.db3.airmq.ui.theme.SensorYellow
|
||||
|
||||
/**
|
||||
* Sensor types supported by the metric gauge.
|
||||
* Maps to legacy Chart sensor constants.
|
||||
*/
|
||||
enum class SensorType(val legacyKey: String) {
|
||||
DUST("sensor_dust"),
|
||||
RADIOACTIVITY("sensor_radioactivity"),
|
||||
TEMPERATURE("sensor_temperature"),
|
||||
HUMIDITY("sensor_humidity"),
|
||||
PRESSURE("sensor_pressure"),
|
||||
CO2("sensor_co2"),
|
||||
VOC("sensor_voc");
|
||||
|
||||
fun units(): String = getUnitsForSensor(legacyKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* AQI color bands for dust (PM2.5 µg/m³) per EPA scale.
|
||||
* Returns progress ring color based on value.
|
||||
*/
|
||||
fun getColorFromMetrics(value: Float, sensorType: SensorType): Color = when (sensorType) {
|
||||
SensorType.DUST -> when {
|
||||
value <= 12f -> SensorGreen
|
||||
value <= 35.4f -> SensorYellow
|
||||
value <= 55.4f -> SensorOrange
|
||||
value <= 150.4f -> SensorRed
|
||||
value <= 250.4f -> SensorPink
|
||||
else -> SensorPurple
|
||||
}
|
||||
else -> Color.White
|
||||
}
|
||||
|
||||
/**
|
||||
* Drawable resource ID for the sensor icon.
|
||||
*/
|
||||
fun getSensorIconRes(sensorType: SensorType): Int = when (sensorType) {
|
||||
SensorType.TEMPERATURE -> R.drawable.ic_temperature
|
||||
SensorType.HUMIDITY -> R.drawable.ic_humidity
|
||||
SensorType.PRESSURE -> R.drawable.ic_pressure
|
||||
SensorType.RADIOACTIVITY -> R.drawable.ic_radiation
|
||||
else -> R.drawable.ic_dust
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.db3.airmq.features.common.metric
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.db3.airmq.ui.theme.SensorGreen
|
||||
|
||||
private const val StartAngle = 135f
|
||||
private const val FullSweepAngle = 270f
|
||||
|
||||
/**
|
||||
* Circular progress gauge drawn as a 270° arc (7 o'clock to 4 o'clock).
|
||||
* Mirrors legacy [RingDiagram] behavior.
|
||||
*
|
||||
* @param progress Progress 0–100, maps to arc sweep
|
||||
* @param progressColor Color of the progress arc
|
||||
* @param modifier Modifier
|
||||
* @param size Total size of the gauge
|
||||
* @param strokeWidth Stroke width of the arc
|
||||
* @param backgroundColor Background ring color (default white38)
|
||||
*/
|
||||
@Composable
|
||||
fun RingGauge(
|
||||
progress: Float,
|
||||
progressColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
size: Dp = 77.dp,
|
||||
strokeWidth: Dp = 5.dp,
|
||||
backgroundColor: Color = Color.White.copy(alpha = 0.38f)
|
||||
) {
|
||||
val animatedProgress by animateFloatAsState(
|
||||
targetValue = progress.coerceIn(0f, 100f),
|
||||
animationSpec = tween(durationMillis = 400),
|
||||
label = "ring_progress"
|
||||
)
|
||||
val sweepDegrees = (FullSweepAngle / 100f) * animatedProgress
|
||||
|
||||
Canvas(
|
||||
modifier = modifier.size(size)
|
||||
) {
|
||||
val strokePx = strokeWidth.toPx()
|
||||
val halfStroke = strokePx / 2f
|
||||
val topLeft = Offset(halfStroke, halfStroke)
|
||||
val arcSize = androidx.compose.ui.geometry.Size(size.toPx() - strokePx, size.toPx() - strokePx)
|
||||
|
||||
// Background ring
|
||||
drawArc(
|
||||
color = backgroundColor,
|
||||
startAngle = StartAngle,
|
||||
sweepAngle = FullSweepAngle,
|
||||
useCenter = false,
|
||||
topLeft = topLeft,
|
||||
size = arcSize,
|
||||
style = Stroke(width = strokePx, cap = StrokeCap.Round)
|
||||
)
|
||||
// Progress ring
|
||||
if (sweepDegrees > 0f) {
|
||||
drawArc(
|
||||
color = progressColor,
|
||||
startAngle = StartAngle,
|
||||
sweepAngle = sweepDegrees,
|
||||
useCenter = false,
|
||||
topLeft = topLeft,
|
||||
size = arcSize,
|
||||
style = Stroke(width = strokePx, cap = StrokeCap.Round)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "RingGauge – 40% progress")
|
||||
@Composable
|
||||
private fun PreviewRingGauge() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(100.dp)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
RingGauge(
|
||||
progress = 40f,
|
||||
progressColor = SensorGreen
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,16 @@ package org.db3.airmq.features.dashboard
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -13,6 +19,8 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.db3.airmq.R
|
||||
import org.db3.airmq.features.common.MockScreenScaffold
|
||||
import org.db3.airmq.features.common.metric.MetricGaugeRow
|
||||
import org.db3.airmq.features.common.metric.SensorType
|
||||
import org.db3.airmq.features.common.ScreenAction
|
||||
import org.db3.airmq.features.common.chart.AirMQChart
|
||||
import org.db3.airmq.features.common.chart.ChartConfig
|
||||
@@ -36,30 +44,46 @@ fun DashboardScreen(
|
||||
title = stringResource(id = R.string.title_dashboard),
|
||||
subtitle = stringResource(id = R.string.dashboard_subtitle),
|
||||
content = {
|
||||
Box(
|
||||
var selectedSensor by remember { mutableStateOf<SensorType?>(SensorType.DUST) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(144.dp)
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(LegacyNavGradientStart, LegacyNavGradientEnd)
|
||||
)
|
||||
)
|
||||
) {
|
||||
AirMQChart(
|
||||
data = ChartDataset.Single(generateSineWaveData()),
|
||||
config = ChartConfig(
|
||||
lineColor = Color.White,
|
||||
fillColor = ChartFill,
|
||||
backgroundColor = ChartBackground,
|
||||
labelColor = Color.White,
|
||||
leftTimeLabel = stringResource(R.string.text_yesterday),
|
||||
rightTimeLabel = stringResource(R.string.text_now),
|
||||
unit = "°C"
|
||||
),
|
||||
sensorType = "sensor_temperature",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
MetricGaugeRow(
|
||||
selectedSensor = selectedSensor,
|
||||
values = mapOf(
|
||||
SensorType.DUST to 6f,
|
||||
SensorType.RADIOACTIVITY to 0f,
|
||||
SensorType.TEMPERATURE to 3f
|
||||
),
|
||||
onGaugeSelected = { selectedSensor = it },
|
||||
modifier = Modifier.padding(vertical = 8.dp)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(144.dp)
|
||||
) {
|
||||
AirMQChart(
|
||||
data = ChartDataset.Single(generateSineWaveData()),
|
||||
config = ChartConfig(
|
||||
lineColor = Color.White,
|
||||
fillColor = ChartFill,
|
||||
backgroundColor = ChartBackground,
|
||||
labelColor = Color.White,
|
||||
leftTimeLabel = stringResource(R.string.text_yesterday),
|
||||
rightTimeLabel = stringResource(R.string.text_now),
|
||||
unit = "°C"
|
||||
),
|
||||
sensorType = "sensor_temperature",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = listOf(
|
||||
|
||||
@@ -43,4 +43,13 @@ val SensorPressure = Color(0xFF4FC3F7)
|
||||
val SensorDust25 = Color(0xFF4A90D9)
|
||||
val SensorDust10 = Color(0xFF81C784)
|
||||
val SensorDust1 = Color(0xFF90A4AE)
|
||||
val SensorRadioactivity = Color(0xFFE57373)
|
||||
val SensorRadioactivity = Color(0xFFE57373)
|
||||
|
||||
// AQI / metric gauge colors (EPA scale for dust)
|
||||
val SensorGreen = Color(0xFF00FF1E)
|
||||
val SensorYellow = Color(0xFFFFBF00)
|
||||
val SensorOrange = Color(0xFFFF6F00)
|
||||
val SensorRed = Color(0xFFFF0000)
|
||||
val SensorPink = Color(0xFFFF006A)
|
||||
val SensorPurple = Color(0xFF6200FF)
|
||||
val SensorGrey = Color(0xFF858585)
|
||||
15
app/src/main/res/drawable/ic_humidity.xml
Normal file
15
app/src/main/res/drawable/ic_humidity.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9.5,12.1c-0.11,-0.13 -0.27,-0.19 -0.46,-0.19c-0.2,0 -0.35,0.06 -0.46,0.19c-0.12,0.12 -0.17,0.29 -0.17,0.52v0.43c0,0.21 0.05,0.37 0.17,0.5c0.11,0.12 0.27,0.19 0.47,0.19c0.41,0 0.62,-0.27 0.62,-0.79V12.6C9.67,12.4 9.61,12.23 9.5,12.1zM9.5,12.1c-0.11,-0.13 -0.27,-0.19 -0.46,-0.19c-0.2,0 -0.35,0.06 -0.46,0.19c-0.12,0.12 -0.17,0.29 -0.17,0.52v0.43c0,0.21 0.05,0.37 0.17,0.5c0.11,0.12 0.27,0.19 0.47,0.19c0.41,0 0.62,-0.27 0.62,-0.79V12.6C9.67,12.4 9.61,12.23 9.5,12.1zM18.71,13.23c-0.43,-1.84 -1.3,-3.46 -2.29,-5.03C14.77,5.59 13,3.06 12.01,0c-0.24,0.61 -0.44,1.15 -0.66,1.68c-0.86,2.03 -2.07,3.86 -3.26,5.71C7.04,9 6.03,10.63 5.48,12.49c-0.55,1.9 -0.6,3.79 0.24,5.63c1.65,3.62 6.61,5.18 10.37,2.59C18.44,19.08 19.4,16.2 18.71,13.23zM7.65,14.33c-0.35,-0.32 -0.52,-0.76 -0.52,-1.31V12.6c0,-0.53 0.17,-0.96 0.52,-1.28c0.34,-0.33 0.81,-0.49 1.39,-0.49c0.59,0 1.05,0.16 1.39,0.48c0.34,0.33 0.51,0.77 0.51,1.32v0.42c0,0.53 -0.17,0.96 -0.51,1.28c-0.34,0.32 -0.8,0.48 -1.38,0.48S8.01,14.65 7.65,14.33zM10.5,18.34L9.31,17.6l4.19,-6.69l1.18,0.74L10.5,18.34zM16.88,16.58c0,0.53 -0.18,0.96 -0.52,1.28c-0.34,0.32 -0.79,0.48 -1.39,0.48c-0.57,0 -1.04,-0.16 -1.39,-0.48c-0.35,-0.32 -0.53,-0.76 -0.53,-1.31v-0.43c0,-0.53 0.18,-0.95 0.52,-1.28c0.35,-0.32 0.81,-0.48 1.39,-0.48c0.58,0 1.05,0.16 1.39,0.48c0.35,0.31 0.53,0.76 0.53,1.32V16.58zM9.04,11.91c-0.2,0 -0.35,0.06 -0.46,0.19c-0.12,0.12 -0.17,0.29 -0.17,0.52v0.43c0,0.21 0.05,0.37 0.17,0.5c0.11,0.12 0.27,0.19 0.47,0.19c0.41,0 0.62,-0.27 0.62,-0.79V12.6c0,-0.2 -0.06,-0.37 -0.17,-0.5S9.23,11.91 9.04,11.91zM9.5,12.1c-0.11,-0.13 -0.27,-0.19 -0.46,-0.19c-0.2,0 -0.35,0.06 -0.46,0.19c-0.12,0.12 -0.17,0.29 -0.17,0.52v0.43c0,0.21 0.05,0.37 0.17,0.5c0.11,0.12 0.27,0.19 0.47,0.19c0.41,0 0.62,-0.27 0.62,-0.79V12.6C9.67,12.4 9.61,12.23 9.5,12.1z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M15.6,16.12l-0.01,0.6c-0.02,0.37 -0.24,0.54 -0.62,0.54c-0.04,0 -0.09,-0.01 -0.13,-0.02c-0.01,0 -0.01,0 -0.02,0c-0.12,-0.02 -0.22,-0.08 -0.31,-0.18c-0.12,-0.14 -0.19,-0.3 -0.19,-0.48V16.1c0.02,-0.21 0.08,-0.37 0.18,-0.49c0.09,-0.08 0.2,-0.14 0.32,-0.16c0.05,-0.01 0.09,-0.01 0.14,-0.01c0.2,0 0.36,0.06 0.46,0.18C15.54,15.75 15.6,15.92 15.6,16.12z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9.67,12.6v0.35c0,0.52 -0.21,0.79 -0.62,0.79c-0.2,0 -0.36,-0.07 -0.47,-0.19c-0.12,-0.13 -0.17,-0.29 -0.17,-0.5v-0.43c0,-0.23 0.05,-0.4 0.17,-0.52c0.11,-0.13 0.26,-0.19 0.46,-0.19c0.19,0 0.35,0.06 0.46,0.19S9.67,12.4 9.67,12.6z"/>
|
||||
</vector>
|
||||
36
app/src/main/res/drawable/ic_pressure.xml
Normal file
36
app/src/main/res/drawable/ic_pressure.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M4.59,19.15c-0.33,0 -0.65,-0.15 -0.87,-0.43c-1.54,-2.02 -2.29,-4.52 -2.11,-7.05C2.03,5.94 7.01,1.62 12.74,2.03c2.77,0.2 5.3,1.46 7.12,3.56c1.82,2.1 2.71,4.78 2.52,7.56c-0.14,2 -0.85,3.9 -2.05,5.5c-0.36,0.48 -1.04,0.58 -1.52,0.22c-0.48,-0.36 -0.58,-1.04 -0.22,-1.52c0.95,-1.26 1.51,-2.76 1.62,-4.35c0.16,-2.19 -0.55,-4.32 -1.99,-5.98c-1.44,-1.66 -3.44,-2.66 -5.63,-2.82c-4.52,-0.32 -8.47,3.1 -8.8,7.63c-0.14,2 0.45,3.98 1.67,5.58c0.36,0.48 0.27,1.16 -0.2,1.52C5.05,19.07 4.82,19.15 4.59,19.15z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9.63,20.32v1.62H8.68v-4.59h1.79c0.34,0 0.65,0.06 0.91,0.19c0.26,0.13 0.46,0.31 0.6,0.54c0.14,0.23 0.21,0.5 0.21,0.79c0,0.45 -0.15,0.8 -0.46,1.06c-0.31,0.26 -0.73,0.39 -1.28,0.39H9.63zM9.63,19.55h0.85c0.25,0 0.44,-0.06 0.57,-0.18c0.13,-0.12 0.2,-0.29 0.2,-0.5c0,-0.22 -0.07,-0.41 -0.2,-0.55c-0.13,-0.14 -0.32,-0.21 -0.55,-0.21H9.63V19.55z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.73,21.94c-0.04,-0.08 -0.07,-0.18 -0.09,-0.31C14.42,21.88 14.13,22 13.78,22c-0.33,0 -0.61,-0.1 -0.83,-0.29c-0.22,-0.19 -0.33,-0.44 -0.33,-0.73c0,-0.36 0.13,-0.64 0.4,-0.83c0.27,-0.19 0.66,-0.29 1.16,-0.29h0.42v-0.2c0,-0.16 -0.04,-0.28 -0.12,-0.38c-0.08,-0.09 -0.21,-0.14 -0.38,-0.14c-0.15,0 -0.27,0.04 -0.36,0.11c-0.09,0.07 -0.13,0.17 -0.13,0.3h-0.91c0,-0.2 0.06,-0.38 0.18,-0.55c0.12,-0.17 0.29,-0.3 0.52,-0.4c0.22,-0.1 0.47,-0.14 0.75,-0.14c0.42,0 0.75,0.11 1,0.32c0.25,0.21 0.37,0.51 0.37,0.89v1.48c0,0.32 0.05,0.57 0.14,0.73v0.05H14.73zM13.98,21.3c0.13,0 0.26,-0.03 0.37,-0.09c0.11,-0.06 0.2,-0.14 0.25,-0.24v-0.59h-0.34c-0.46,0 -0.7,0.16 -0.73,0.47l0,0.05c0,0.11 0.04,0.21 0.12,0.28C13.73,21.27 13.84,21.3 13.98,21.3z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16.75,7.72l-2.58,3.37c0.04,0.11 0.08,0.23 0.1,0.35c0.24,1.2 -0.54,2.37 -1.75,2.61c-0.22,0.04 -0.43,0.05 -0.64,0.03l-1.01,1.32c-0.08,0.11 -0.2,0.18 -0.34,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.18,0.01 -0.36,-0.05 -0.5,-0.18l-1.34,-1.28c-0.14,-0.13 -0.21,-0.31 -0.2,-0.49c0.01,-0.17 0.08,-0.32 0.21,-0.42l1.27,-1.06c-0.16,-1.16 0.61,-2.26 1.77,-2.49c0.39,-0.08 0.77,-0.05 1.12,0.07l3.25,-2.72c0.2,-0.16 0.47,-0.15 0.66,0.02C16.88,7.24 16.91,7.52 16.75,7.72z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7.04,16.22m-0.64,0a0.64,0.64 0,1 1,1.28 0a0.64,0.64 0,1 1,-1.28 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M5.75,11.91m-0.64,0a0.64,0.64 0,1 1,1.28 0a0.64,0.64 0,1 1,-1.28 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7.74,7.83m-0.64,0a0.64,0.64 0,1 1,1.28 0a0.64,0.64 0,1 1,-1.28 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.98,6.14m-0.64,0a0.64,0.64 0,1 1,1.28 0a0.64,0.64 0,1 1,-1.28 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16.95,16.22m-0.64,0a0.64,0.64 0,1 1,1.28 0a0.64,0.64 0,1 1,-1.28 0"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18.25,11.91m-0.64,0a0.64,0.64 0,1 1,1.28 0a0.64,0.64 0,1 1,-1.28 0"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_temperature.xml
Normal file
9
app/src/main/res/drawable/ic_temperature.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="36dp"
|
||||
android:viewportWidth="36"
|
||||
android:viewportHeight="36">
|
||||
<path
|
||||
android:pathData="M22.68,8h-3.52V7.83c0,-0.7 -0.28,-1.33 -0.74,-1.79c-0.46,-0.46 -1.09,-0.74 -1.79,-0.74c-1.4,0 -2.53,1.13 -2.53,2.53v15.74c-0.9,0.74 -1.48,1.86 -1.48,3.11c0,2.21 1.8,4.01 4.01,4.01c2.21,0 4.01,-1.79 4.01,-4.01c0,-1.26 -0.58,-2.38 -1.48,-3.11v-1.41h1.96c0.39,0 0.7,-0.31 0.7,-0.7c0,-0.39 -0.31,-0.7 -0.7,-0.7h-1.96v-1.79h1.96c0.39,0 0.7,-0.31 0.7,-0.7c0,-0.39 -0.31,-0.7 -0.7,-0.7h-1.96v-1.79h1.96c0.39,0 0.7,-0.31 0.7,-0.7c0,-0.39 -0.31,-0.7 -0.7,-0.7h-1.96v-1.79h1.96c0.39,0 0.7,-0.31 0.7,-0.7c0,-0.39 -0.31,-0.7 -0.7,-0.7h-1.96V9.4h3.52c0.39,0 0.7,-0.31 0.7,-0.7S23.07,8 22.68,8zM16.63,28.43c-0.96,0 -1.74,-0.78 -1.74,-1.74c0,-0.21 0.04,-0.41 0.11,-0.6c0.16,-0.44 0.5,-0.8 0.93,-0.99V14.9c0,-0.19 0.08,-0.37 0.2,-0.49c0.13,-0.13 0.3,-0.2 0.49,-0.2c0.39,0 0.7,0.31 0.7,0.7v10.2c0.43,0.19 0.77,0.55 0.93,0.99c0.07,0.19 0.11,0.39 0.11,0.6C18.37,27.65 17.59,28.43 16.63,28.43z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
||||
Reference in New Issue
Block a user