mirror of
https://github.com/vide/matedroid.git
synced 2026-01-20 00:03:17 +08:00
feat(stats): improve AC/DC ratio bar visualization
- Change colors: green for AC, yellow/amber for DC - Make bar thicker (20dp height with rounded corners) - Calculate ratios based on charged kWh instead of charge count - Show energy values (kWh) in the stats row - Show charge counts in small text below each bar side - Remove percentage numbers from display Added new DAO queries to sum energy by AC/DC type: - sumAcChargeEnergy / sumAcChargeEnergyInRange - sumDcChargeEnergy / sumDcChargeEnergyInRange Added acChargeEnergyKwh and dcChargeEnergyKwh fields to DeepStats. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
4a3e015af7
commit
49e8eb25f1
@@ -279,6 +279,38 @@ interface AggregateDao {
|
||||
@Query("SELECT chargeId FROM charge_detail_aggregates WHERE carId = :carId AND isFastCharger = 1")
|
||||
suspend fun getDcChargeIds(carId: Int): List<Int>
|
||||
|
||||
// Sum of energy added for AC charges (join with summary to get energyAdded)
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(c.energyAdded), 0.0) FROM charge_detail_aggregates a
|
||||
JOIN charges_summary c ON a.chargeId = c.chargeId
|
||||
WHERE a.carId = :carId AND a.isFastCharger = 0
|
||||
""")
|
||||
suspend fun sumAcChargeEnergy(carId: Int): Double
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(c.energyAdded), 0.0) FROM charge_detail_aggregates a
|
||||
JOIN charges_summary c ON a.chargeId = c.chargeId
|
||||
WHERE a.carId = :carId AND a.isFastCharger = 0
|
||||
AND c.startDate >= :startDate AND c.startDate < :endDate
|
||||
""")
|
||||
suspend fun sumAcChargeEnergyInRange(carId: Int, startDate: String, endDate: String): Double
|
||||
|
||||
// Sum of energy added for DC charges
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(c.energyAdded), 0.0) FROM charge_detail_aggregates a
|
||||
JOIN charges_summary c ON a.chargeId = c.chargeId
|
||||
WHERE a.carId = :carId AND a.isFastCharger = 1
|
||||
""")
|
||||
suspend fun sumDcChargeEnergy(carId: Int): Double
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(c.energyAdded), 0.0) FROM charge_detail_aggregates a
|
||||
JOIN charges_summary c ON a.chargeId = c.chargeId
|
||||
WHERE a.carId = :carId AND a.isFastCharger = 1
|
||||
AND c.startDate >= :startDate AND c.startDate < :endDate
|
||||
""")
|
||||
suspend fun sumDcChargeEnergyInRange(carId: Int, startDate: String, endDate: String): Double
|
||||
|
||||
// Get all processed charge IDs (for checking if we have aggregate data)
|
||||
@Query("SELECT chargeId FROM charge_detail_aggregates WHERE carId = :carId")
|
||||
suspend fun getAllProcessedChargeIds(carId: Int): List<Int>
|
||||
|
||||
@@ -238,6 +238,8 @@ class StatsRepository @Inject constructor(
|
||||
|
||||
acChargeCount = aggregateDao.countAcCharges(carId),
|
||||
dcChargeCount = aggregateDao.countDcCharges(carId),
|
||||
acChargeEnergyKwh = aggregateDao.sumAcChargeEnergy(carId),
|
||||
dcChargeEnergyKwh = aggregateDao.sumDcChargeEnergy(carId),
|
||||
|
||||
driveDetailsProcessed = driveCount,
|
||||
chargeDetailsProcessed = chargeCount
|
||||
@@ -341,6 +343,8 @@ class StatsRepository @Inject constructor(
|
||||
|
||||
acChargeCount = aggregateDao.countAcChargesInRange(carId, startDate, endDate),
|
||||
dcChargeCount = aggregateDao.countDcChargesInRange(carId, startDate, endDate),
|
||||
acChargeEnergyKwh = aggregateDao.sumAcChargeEnergyInRange(carId, startDate, endDate),
|
||||
dcChargeEnergyKwh = aggregateDao.sumDcChargeEnergyInRange(carId, startDate, endDate),
|
||||
|
||||
driveDetailsProcessed = driveCount,
|
||||
chargeDetailsProcessed = chargeCount
|
||||
|
||||
@@ -98,6 +98,8 @@ data class DeepStats(
|
||||
// === AC/DC Ratio ===
|
||||
val acChargeCount: Int,
|
||||
val dcChargeCount: Int,
|
||||
val acChargeEnergyKwh: Double,
|
||||
val dcChargeEnergyKwh: Double,
|
||||
|
||||
// === Sync Progress ===
|
||||
val driveDetailsProcessed: Int,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.matedroid.ui.screens.stats
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -16,6 +18,7 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
@@ -46,6 +49,8 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.foundation.lazy.LazyColumn as LogLazyColumn
|
||||
import androidx.compose.foundation.lazy.items as logItems
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
@@ -714,53 +719,85 @@ private fun ChargingPowerCard(deepStats: DeepStats, palette: CarColorPalette) {
|
||||
|
||||
@Composable
|
||||
private fun AcDcRatioCard(deepStats: DeepStats, palette: CarColorPalette) {
|
||||
val total = deepStats.acChargeCount + deepStats.dcChargeCount
|
||||
if (total == 0) {
|
||||
val totalEnergy = deepStats.acChargeEnergyKwh + deepStats.dcChargeEnergyKwh
|
||||
if (totalEnergy <= 0) {
|
||||
return // No charge data
|
||||
}
|
||||
|
||||
val acPercent = (deepStats.acChargeCount * 100f / total).toInt()
|
||||
val dcPercent = (deepStats.dcChargeCount * 100f / total).toInt()
|
||||
val acRatio = (deepStats.acChargeEnergyKwh / totalEnergy).toFloat()
|
||||
val acColor = Color(0xFF4CAF50) // Green
|
||||
val dcColor = Color(0xFFFFC107) // Yellow/Amber
|
||||
|
||||
StatsCard(
|
||||
title = "AC/DC Charging Ratio",
|
||||
icon = Icons.Default.BatteryChargingFull,
|
||||
palette = palette
|
||||
) {
|
||||
// Energy stats row
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
StatItem(
|
||||
label = "AC Charges",
|
||||
value = "${deepStats.acChargeCount} ($acPercent%)",
|
||||
label = "AC Energy",
|
||||
value = "%.1f kWh".format(deepStats.acChargeEnergyKwh),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
StatItem(
|
||||
label = "DC Charges",
|
||||
value = "${deepStats.dcChargeCount} ($dcPercent%)",
|
||||
label = "DC Energy",
|
||||
value = "%.1f kWh".format(deepStats.dcChargeEnergyKwh),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
// Progress bar showing ratio
|
||||
LinearProgressIndicator(
|
||||
progress = { acPercent / 100f },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
trackColor = MaterialTheme.colorScheme.tertiary
|
||||
)
|
||||
|
||||
// Custom ratio bar (thicker, green/yellow)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.background(dcColor)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth(acRatio)
|
||||
.background(acColor)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
// Labels with counts below
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = "AC (Home/Destination)",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = "DC (Supercharger)",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.tertiary
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = "AC",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = acColor
|
||||
)
|
||||
Text(
|
||||
text = "${deepStats.acChargeCount} charges",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = palette.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = "DC",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = dcColor
|
||||
)
|
||||
Text(
|
||||
text = "${deepStats.dcChargeCount} charges",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = palette.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user