Skip to content

Commit 0bf1972

Browse files
authored
Merge pull request #200 from ryanw-mobile/feature/189-standing-charge-indicator
(#189) add standing charge ratio indicator
2 parents 1a8671a + cefbff6 commit 0bf1972

File tree

11 files changed

+135
-9
lines changed

11 files changed

+135
-9
lines changed

composeApp/composeApp.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |spec|
22
spec.name = 'composeApp'
3-
spec.version = '1.1.0'
3+
spec.version = '1.2.0'
44
spec.homepage = 'https://github.com/ryanw-mobile/OctoMeter/'
55
spec.source = { :http=> ''}
66
spec.authors = ''

composeApp/src/commonMain/composeResources/values/strings.xml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<string name="night_unit_rate">Night Unit Rate</string>
2929
<string name="unit_p_day">%1$d p/day</string>
3030
<string name="unit_p_kwh">%1$d p/kWh</string>
31+
<string name="unit_percent">%1$d %</string>
3132

3233
<string name="selected">Selected</string>
3334
<string name="loading">Loading...</string>
@@ -54,7 +55,8 @@
5455
<string name="usage_estimated_daily">Daily average: %1$s kWh / £%2$s</string>
5556
<string name="usage_annual_projection">Projected annual consumption</string>
5657
<string name="usage_demo_introduction">Demo Mode: The consumption figures shown are randomly generated.</string>
57-
58+
<string name="usage_consumption">Consumption</string>
59+
<string name="usage_standing_charge">Standing Charge</string>
5860

5961
<!-- Agile -->
6062
<string name="agile_demo_introduction">Demo Mode: The Agile Tariff shown for Retail Region A may not be available to you.</string>
@@ -109,7 +111,7 @@
109111

110112
<!-- Account Screen -->
111113
<string name="account_clear_credential_title">Clear Credentials and Switch to Demo Mode</string>
112-
<string name="account_clear_credential_description">Removing your API token and any account-specific data from local storage. Under demo mode, the application will utilise simulated local data to replicate authenticated access. Additionally, it will default to retail region A when retrieving tariffs.</string>
114+
<string name="account_clear_credential_description">Removing your API token and any account-specific data from local storage. Under demo mode, the application will utilise simulated local data to replicate authenticated access. Additionally, it will default to Eastern England when retrieving tariffs.</string>
113115
<string name="account_clear_credential_button_cta">Clear</string>
114116
<string name="account_version_api_disclaimer">OctoMeter Version %1$s (Build %2$d)\n%3$s\n\nThe APIs are provided by Octopus Energy Ltd. Usage subject to their terms of service.</string>
115117

@@ -136,7 +138,7 @@
136138

137139
<!-- Onboarding -->
138140
<string name="onboarding_welcome_aboard">Welcome Aboard!</string>
139-
<string name="onboarding_introduction_1">This app is currently running in demo mode, using simulated local data to replicate authenticated access. This means the electricity usage data displayed is not real. Additionally, the app will default to retail region A when retrieving tariffs.</string>
141+
<string name="onboarding_introduction_1">This app is currently running in demo mode, using simulated local data to replicate authenticated access. This means the electricity usage data displayed is not real. Additionally, the app will default to Eastern England when retrieving tariffs.</string>
140142
<string name="onboarding_question_1">Are you an Octopus Energy customer with a smart meter installed?</string>
141143
<string name="onboarding_introduction_2">If you are a current Octopus Energy customer with a smart meter and have access to your web account, you can generate an API key for this app to pull your smart meter data. Visit: https://octopus.energy/dashboard/new/accounts/personal-details/api-access</string>
142144
<string name="onboarding_reminder_1">Your API key is confidential, so don't share it with anyone. If you have concerns, regenerating a new key will invalidate all previously issued keys.</string>

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/ui/destinations/usage/components/AnnualProjectionCardAdaptive.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ private fun Preview() {
198198
insights = Insights(
199199
consumptionAggregateRounded = 42.290,
200200
consumptionTimeSpan = 8820,
201+
consumptionChargeRatio = 0.64,
201202
roughCost = 2.815,
202203
consumptionAnnualProjection = 48.504,
203204
costAnnualProjection = 24.868,
@@ -211,6 +212,7 @@ private fun Preview() {
211212
insights = Insights(
212213
consumptionAggregateRounded = 42.290,
213214
consumptionTimeSpan = 8820,
215+
consumptionChargeRatio = 0.64,
214216
roughCost = 2.815,
215217
consumptionAnnualProjection = 48.504,
216218
costAnnualProjection = 24.868,
@@ -224,6 +226,7 @@ private fun Preview() {
224226
insights = Insights(
225227
consumptionAggregateRounded = 42.290,
226228
consumptionTimeSpan = 8820,
229+
consumptionChargeRatio = 0.64,
227230
roughCost = 2.815,
228231
consumptionAnnualProjection = 48.504,
229232
costAnnualProjection = 24.868,

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/ui/destinations/usage/components/InsightsCard.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
package com.rwmobi.kunigami.ui.destinations.usage.components
99

1010
import androidx.compose.desktop.ui.tooling.preview.Preview
11+
import androidx.compose.foundation.layout.Arrangement
1112
import androidx.compose.foundation.layout.Column
1213
import androidx.compose.foundation.layout.IntrinsicSize
14+
import androidx.compose.foundation.layout.Row
1315
import androidx.compose.foundation.layout.Spacer
16+
import androidx.compose.foundation.layout.defaultMinSize
1417
import androidx.compose.foundation.layout.fillMaxSize
1518
import androidx.compose.foundation.layout.fillMaxWidth
1619
import androidx.compose.foundation.layout.height
1720
import androidx.compose.foundation.layout.padding
21+
import androidx.compose.foundation.layout.width
1822
import androidx.compose.material3.Card
1923
import androidx.compose.material3.MaterialTheme
2024
import androidx.compose.material3.Text
@@ -29,7 +33,9 @@ import com.rwmobi.kunigami.ui.model.consumption.Insights
2933
import com.rwmobi.kunigami.ui.theme.getDimension
3034
import io.github.koalaplot.core.util.toString
3135
import kunigami.composeapp.generated.resources.Res
36+
import kunigami.composeapp.generated.resources.standing_charge
3237
import kunigami.composeapp.generated.resources.unit_pound
38+
import kunigami.composeapp.generated.resources.usage_consumption
3339
import kunigami.composeapp.generated.resources.usage_estimated_cost
3440
import kunigami.composeapp.generated.resources.usage_estimated_daily
3541
import kunigami.composeapp.generated.resources.usage_insights_consumption
@@ -96,6 +102,31 @@ internal fun InsightsCard(
96102
),
97103
)
98104
}
105+
106+
Spacer(modifier = Modifier.weight(weight = 1f))
107+
108+
RatioBar(
109+
modifier = Modifier
110+
.padding(top = dimension.grid_1)
111+
.defaultMinSize(minHeight = dimension.grid_2)
112+
.fillMaxWidth(),
113+
consumptionRatio = insights.consumptionChargeRatio,
114+
)
115+
116+
Row(
117+
modifier = Modifier.fillMaxWidth(),
118+
horizontalArrangement = Arrangement.SpaceBetween,
119+
) {
120+
Text(
121+
style = MaterialTheme.typography.labelSmall,
122+
text = stringResource(resource = Res.string.usage_consumption),
123+
)
124+
Spacer(modifier = Modifier.width(dimension.grid_1))
125+
Text(
126+
style = MaterialTheme.typography.labelSmall,
127+
text = stringResource(resource = Res.string.standing_charge),
128+
)
129+
}
99130
}
100131
}
101132
}
@@ -111,6 +142,7 @@ private fun Preview() {
111142
insights = Insights(
112143
consumptionAggregateRounded = 86.693,
113144
consumptionTimeSpan = 2084,
145+
consumptionChargeRatio = 0.64,
114146
roughCost = 2880.027,
115147
consumptionDailyAverage = 71.227,
116148
costDailyAverage = 52.218,
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2024. Ryan Wong
3+
* https://github.com/ryanw-mobile
4+
* Sponsored by RW MobiMedia UK Limited
5+
*
6+
*/
7+
8+
package com.rwmobi.kunigami.ui.destinations.usage.components
9+
10+
import androidx.compose.desktop.ui.tooling.preview.Preview
11+
import androidx.compose.foundation.layout.Box
12+
import androidx.compose.foundation.layout.fillMaxWidth
13+
import androidx.compose.foundation.layout.height
14+
import androidx.compose.foundation.layout.padding
15+
import androidx.compose.material3.MaterialTheme
16+
import androidx.compose.material3.Text
17+
import androidx.compose.runtime.Composable
18+
import androidx.compose.ui.Alignment
19+
import androidx.compose.ui.Modifier
20+
import androidx.compose.ui.draw.clip
21+
import androidx.compose.ui.draw.drawBehind
22+
import androidx.compose.ui.graphics.Color
23+
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
24+
import androidx.compose.ui.unit.dp
25+
import com.rwmobi.kunigami.ui.components.CommonPreviewSetup
26+
import io.github.koalaplot.core.util.toString
27+
import kunigami.composeapp.generated.resources.Res
28+
import kunigami.composeapp.generated.resources.unit_percent
29+
import org.jetbrains.compose.resources.stringResource
30+
31+
@Composable
32+
internal fun RatioBar(
33+
modifier: Modifier = Modifier,
34+
consumptionRatio: Double, // Ratio between 0.0 and 1.0
35+
) {
36+
val consumptionColor = MaterialTheme.colorScheme.primary
37+
val standingChargeColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f)
38+
val effectiveRatio = consumptionRatio.coerceIn(minimumValue = 0.0, maximumValue = 1.0)
39+
40+
Box(
41+
modifier = modifier
42+
.clip(shape = MaterialTheme.shapes.large)
43+
.drawBehind {
44+
val consumptionWidth = size.width * effectiveRatio.toFloat()
45+
val standingChargeWidth = size.width - consumptionWidth
46+
47+
drawIntoCanvas {
48+
// Draw the consumption part
49+
drawRect(
50+
color = consumptionColor,
51+
size = size.copy(width = consumptionWidth),
52+
)
53+
54+
// Draw the standing charge part
55+
drawRect(
56+
color = standingChargeColor,
57+
topLeft = androidx.compose.ui.geometry.Offset(x = consumptionWidth, y = 0f),
58+
size = size.copy(width = standingChargeWidth),
59+
)
60+
}
61+
},
62+
) {
63+
Text(
64+
modifier = Modifier.align(alignment = Alignment.Center),
65+
style = MaterialTheme.typography.labelSmall,
66+
color = Color.White,
67+
text = stringResource(resource = Res.string.unit_percent, (consumptionRatio * 100).toString(precision = 0)),
68+
)
69+
}
70+
}
71+
72+
@Preview
73+
@Composable
74+
private fun Preview() {
75+
CommonPreviewSetup(
76+
modifier = Modifier.padding(all = 8.dp),
77+
) { dimension ->
78+
RatioBar(
79+
modifier = Modifier
80+
.fillMaxWidth()
81+
.height(dimension.grid_2),
82+
consumptionRatio = 0.64,
83+
)
84+
}
85+
}

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/ui/destinations/usage/components/TariffProjectionsCardAdaptive.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ private fun Preview() {
198198
val insights = Insights(
199199
consumptionAggregateRounded = 85.115,
200200
consumptionTimeSpan = 303,
201+
consumptionChargeRatio = 0.64,
201202
roughCost = 90.988,
202203
consumptionDailyAverage = 32.611,
203204
costDailyAverage = 12.434,

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/ui/model/consumption/Insights.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package com.rwmobi.kunigami.ui.model.consumption
1010
data class Insights(
1111
val consumptionAggregateRounded: Double,
1212
val consumptionTimeSpan: Int,
13+
val consumptionChargeRatio: Double,
1314
val roughCost: Double,
1415
val consumptionDailyAverage: Double,
1516
val costDailyAverage: Double,

composeApp/src/commonMain/kotlin/com/rwmobi/kunigami/ui/viewmodels/UsageViewModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ class UsageViewModel(
266266
val consumptionAggregateRounded = consumptions.sumOf { it.consumption }.roundToNearestEvenHundredth()
267267
val consumptionTimeSpan = consumptions.getConsumptionTimeSpan()
268268
val roughCost = ((consumptionTimeSpan * tariffSummary.vatInclusiveStandingCharge) + (consumptionAggregateRounded * tariffSummary.vatInclusiveUnitRate)) / 100.0
269+
val consumptionChargeRatio = (consumptionAggregateRounded * tariffSummary.vatInclusiveUnitRate / 100.0) / roughCost
269270
val consumptionDailyAverage = (consumptions.sumOf { it.consumption } / consumptions.getConsumptionTimeSpan()).roundToNearestEvenHundredth()
270271
val costDailyAverage = (tariffSummary.vatInclusiveStandingCharge + consumptionDailyAverage * tariffSummary.vatInclusiveUnitRate) / 100.0
271272
val consumptionAnnualProjection = (consumptions.sumOf { it.consumption } / consumptionTimeSpan * 365.25).roundToNearestEvenHundredth()
@@ -274,6 +275,7 @@ class UsageViewModel(
274275
Insights(
275276
consumptionAggregateRounded = consumptionAggregateRounded,
276277
consumptionTimeSpan = consumptionTimeSpan,
278+
consumptionChargeRatio = consumptionChargeRatio,
277279
roughCost = roughCost,
278280
consumptionDailyAverage = consumptionDailyAverage,
279281
costDailyAverage = costDailyAverage,

gradle/libs.versions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ baselineprofile = "1.2.4"
3737
profileinstaller = "1.3.1"
3838

3939
# App configurations, not dependencies
40-
versionCode = "6"
41-
versionName = "1.1.0"
40+
versionCode = "7"
41+
versionName = "1.2.0"
4242
android-minSdk = "26"
4343
android-targetSdk = "34"
4444
android-compileSdk = "34"

iosApp/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PODS:
2-
- composeApp (1.1.0)
2+
- composeApp (1.2.0)
33

44
DEPENDENCIES:
55
- composeApp (from `../composeApp/`)

0 commit comments

Comments
 (0)