Skip to content

Commit

Permalink
feat: query electricity balance
Browse files Browse the repository at this point in the history
  • Loading branch information
I-Info committed Nov 3, 2023
1 parent 1825b6f commit 9e1508e
Show file tree
Hide file tree
Showing 13 changed files with 255 additions and 29 deletions.
8 changes: 4 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {
minSdk min_sdk
targetSdk target_sdk

versionCode 2
versionName '0.1.0'
versionCode 3
versionName '0.2.0-alpha'

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -78,7 +78,7 @@ dependencies {
implementation 'androidx.compose.material3:material3:1.1.2'
implementation 'androidx.compose.material:material-icons-extended:1.5.4'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation "androidx.navigation:navigation-compose:2.7.4"
implementation 'androidx.navigation:navigation-compose:2.7.5'
implementation 'androidx.profileinstaller:profileinstaller:1.3.1'
implementation 'com.google.android.material:material:1.10.0'
implementation "com.google.accompanist:accompanist-systemuicontroller:0.32.0"
Expand All @@ -101,7 +101,7 @@ dependencies {
// Dependency injection (Hilt)
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
implementation 'androidx.hilt:hilt-navigation-compose:1.1.0'

testImplementation 'junit:junit:4.13.2'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.zjutjh.ijh.ui.component

import android.content.Context
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ElectricBolt
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.zjutjh.ijh.R
import com.zjutjh.ijh.model.ElectricityBalance
import com.zjutjh.ijh.ui.theme.IJhTheme
import com.zjutjh.ijh.util.LoadResult
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle

@Composable
fun ElectricityStatusCard(
modifier: Modifier = Modifier,
balance: LoadResult<ElectricityBalance?>,
) {
val context = LocalContext.current
val subtitle = remember(balance) {
prompt(context, if (balance is LoadResult.Loading) null else LocalDateTime.now())
}

GlanceCard(
modifier = modifier,
title = stringResource(id = R.string.dorm_electricity),
subtitle = subtitle,
icon = Icons.Default.ElectricBolt
) {
AnimatedContent(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
targetState = balance,
contentAlignment = Alignment.Center,
label = "Loading",
) {
when (it) {
is LoadResult.Loading -> Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
CircularProgressIndicator()
Text(
text = stringResource(id = R.string.loading),
textAlign = TextAlign.Center
)
}

is LoadResult.Ready -> {
val text = if (it.data == null) {
"N/A"
} else {
"${it.data.total} KW•h"
}

Text(
modifier = Modifier.padding(vertical = 8.dp),
color = MaterialTheme.colorScheme.primary,
text = text,
style = MaterialTheme.typography.displaySmall,
textAlign = TextAlign.Center,
)
}
}
}
}
}

private fun prompt(context: Context, syncTime: LocalDateTime? = null): String = buildString {
val divider = ""
append(context.getString(R.string.balance))
append(divider)
if (syncTime == null) {
append(context.getString(R.string.unknown))
} else {
append(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).format(syncTime))
}
}


@Preview
@Composable
private fun ElectricityStatusCardPreview() {
IJhTheme {
ElectricityStatusCard(
balance = LoadResult.Ready(
ElectricityBalance(
total = 200f,
totalAmount = 100f,
subsidy = 100f,
subsidyAmount = 50f,
surplus = 100f,
surplusAmount = 50f
)
)
)
}
}
15 changes: 15 additions & 0 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/screen/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.zjutjh.ijh.R
import com.zjutjh.ijh.data.mock.CourseRepositoryMock
import com.zjutjh.ijh.model.Course
import com.zjutjh.ijh.model.ElectricityBalance
import com.zjutjh.ijh.model.Term
import com.zjutjh.ijh.ui.component.CampusCardInfoCard
import com.zjutjh.ijh.ui.component.ElectricityStatusCard
import com.zjutjh.ijh.ui.component.IJhScaffold
import com.zjutjh.ijh.ui.component.ScheduleCard
import com.zjutjh.ijh.ui.model.TermDayState
Expand All @@ -79,6 +81,7 @@ fun HomeRoute(
val coursesLastSyncState by viewModel.courseLastSyncState.collectAsStateWithLifecycle()
val cardBalanceState by viewModel.cardBalanceState.collectAsStateWithLifecycle()
val cardBalanceLastSyncState by viewModel.cardBalanceLastSyncState.collectAsStateWithLifecycle()
val electricityBalance by viewModel.electricityState.collectAsStateWithLifecycle()

val isLoggedIn = when (loginState) {
null -> false
Expand All @@ -103,6 +106,7 @@ fun HomeRoute(
coursesLastSync = coursesLastSyncState,
cardBalance = cardBalanceState,
cardBalanceLastSync = cardBalanceLastSyncState,
electricityBalance = electricityBalance,
onRefresh = viewModel::refreshAll,
onNavigateToLogin = onNavigateToLogin,
onNavigateToProfile = onNavigateToProfile,
Expand All @@ -120,6 +124,7 @@ private fun HomeScreen(
coursesLastSync: Duration?,
cardBalance: LoadResult<String?>,
cardBalanceLastSync: LoadResult<Duration?>,
electricityBalance: LoadResult<ElectricityBalance?>,
onRefresh: () -> Unit,
onNavigateToLogin: () -> Unit,
onNavigateToProfile: () -> Unit,
Expand Down Expand Up @@ -161,6 +166,7 @@ private fun HomeScreen(
balance = cardBalance,
lastSync = cardBalanceLastSync,
)
ElectricityStatusCard(modifier = modifier, balance = electricityBalance)
}

PullRefreshIndicator(
Expand Down Expand Up @@ -347,6 +353,14 @@ private fun NavigationDrawerPreview() {
private fun HomeScreenPreview() {
val courses = CourseRepositoryMock.getCourses()
val termDay = TermDayState(2023, Term.FIRST, 1, true, DayOfWeek.MONDAY)
val electricityBalance = ElectricityBalance(
total = 200f,
totalAmount = 100f,
subsidy = 100f,
subsidyAmount = 50f,
surplus = 100f,
surplusAmount = 50f
)
IJhTheme {
HomeScreen(
refreshing = false,
Expand All @@ -356,6 +370,7 @@ private fun HomeScreenPreview() {
coursesLastSync = Duration.ofDays(1),
cardBalance = LoadResult.Ready("123"),
cardBalanceLastSync = LoadResult.Ready(Duration.ofDays(2)),
electricityBalance = LoadResult.Ready(electricityBalance),
onRefresh = ::emptyFun,
onNavigateToAbout = ::emptyFun,
onNavigateToCalendar = ::emptyFun,
Expand Down
26 changes: 24 additions & 2 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/viewmodel/HomeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import androidx.lifecycle.viewModelScope
import com.zjutjh.ijh.data.CampusRepository
import com.zjutjh.ijh.data.CardRepository
import com.zjutjh.ijh.data.CourseRepository
import com.zjutjh.ijh.data.ElectricityRepository
import com.zjutjh.ijh.data.WeJhUserRepository
import com.zjutjh.ijh.model.Course
import com.zjutjh.ijh.model.ElectricityBalance
import com.zjutjh.ijh.model.Session
import com.zjutjh.ijh.model.Term
import com.zjutjh.ijh.ui.model.TermDayState
Expand All @@ -29,6 +31,7 @@ class HomeViewModel @Inject constructor(
private val courseRepository: CourseRepository,
private val campusRepository: CampusRepository,
private val cardRepository: CardRepository,
private val electricityRepository: ElectricityRepository,
) : ViewModel() {

private val timerFlow: Flow<Unit> = flow {
Expand Down Expand Up @@ -79,7 +82,7 @@ class HomeViewModel @Inject constructor(
@OptIn(ExperimentalCoroutinesApi::class)
val coursesState: StateFlow<LoadResult<List<Course>>> = termDayState
.dropWhile(LoadResult<*>::isLoading)
.distinctUntilChanged(LoadResult<*>::isEqual)
.distinctUntilChanged()
.flatMapLatest { state ->
if (state is LoadResult.Ready && state.data != null) {
val day = state.data
Expand Down Expand Up @@ -112,6 +115,11 @@ class HomeViewModel @Inject constructor(
started = SharingStarted.WhileSubscribed(5_000)
)

private val _electricityState: MutableStateFlow<LoadResult<ElectricityBalance?>> =
MutableStateFlow(LoadResult.Loading)
val electricityState: StateFlow<LoadResult<ElectricityBalance?>> =
_electricityState.asStateFlow()

init {
viewModelScope.launch(Dispatchers.Default) {
// Check session state, renew if needed. TODO: move to application scope
Expand Down Expand Up @@ -166,7 +174,8 @@ class HomeViewModel @Inject constructor(
val task2 = scope.async {
refreshCard()
}
mutableListOf(task1, task2)
val task3 = scope.async { refreshElectricity() }
mutableListOf(task1, task2, task3)
} else {
mutableListOf()
}
Expand Down Expand Up @@ -216,4 +225,17 @@ class HomeViewModel @Inject constructor(
}
}

private suspend fun refreshElectricity() {
runCatching {
electricityRepository.getBalance()
}.fold({ balance ->
_electricityState.emit(LoadResult.Ready(balance))
Log.i("Home", "Sync Electricity succeed.")
}) {
_electricityState.update { state ->
if (state is LoadResult.Loading) LoadResult.Ready(null) else state
}
Log.e("Home", "Sync Electricity failed.", it)
}
}
}
31 changes: 19 additions & 12 deletions app/src/main/kotlin/com/zjutjh/ijh/util/LoadResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,32 @@ import kotlinx.coroutines.flow.stateIn
import kotlin.coroutines.CoroutineContext

@Stable
sealed interface LoadResult<out T> {
data object Loading : LoadResult<Nothing>
class Ready<T>(val data: T) : LoadResult<T>
sealed class LoadResult<out T> {
data object Loading : LoadResult<Nothing>()
class Ready<T>(val data: T) : LoadResult<T>()

fun <T> isEqual(v: LoadResult<T>): Boolean =
if (this is Ready && v is Ready) {
this.data == v.data
} else this is Loading && v is Loading

fun <R> isEqual(v: LoadResult<R>, areEquivalent: (left: T, right: R) -> Boolean): Boolean =
if (this is Ready && v is Ready) {
areEquivalent(this.data, v.data)
} else this is Loading && v is Loading
override fun equals(other: Any?): Boolean {
if (other !is LoadResult<*>) {
return false
}
return when (this) {
is Loading -> other is Loading
is Ready<*> -> other is Ready<*> && this.data == other.data
}
}

fun <R> map(transform: (T) -> R): LoadResult<R> =
when (this) {
is Loading -> Loading
is Ready -> Ready(transform(data))
}

override fun hashCode(): Int {
return when (this) {
is Loading -> javaClass.hashCode()
is Ready<*> -> data.hashCode()
}
}
}

fun <T> LoadResult<T>.isLoading(): Boolean = this is LoadResult.Loading
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,6 @@
<string name="more">更多</string>
<string name="balance">余额</string>
<string name="campus_card">校园卡</string>
<string name="dorm_electricity">寝室电费</string>
<string name="status">状态</string>
</resources>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,6 @@
<string name="more">More</string>
<string name="balance">Balance</string>
<string name="campus_card">Campus Card</string>
<string name="dorm_electricity">Dorm Electricity</string>
<string name="status">Satus</string>
</resources>
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
compose_version = '1.5.4'
hilt_version = '2.48.1'
retrofit_version = '2.9.0'
protobuf_version = '3.24.4'
protobuf_version = '3.25.0'
room_version = '2.6.0'
work_version = '2.8.1'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.zjutjh.ijh.model

import androidx.compose.runtime.Stable

@Stable
data class ElectricityBalance(
val total: Float,
val totalAmount: Float,
val subsidy: Float,
val subsidyAmount: Float,
val surplus: Float,
val surplusAmount: Float
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.zjutjh.ijh.data

import com.zjutjh.ijh.model.ElectricityBalance

interface ElectricityRepository {

suspend fun getBalance(): ElectricityBalance
}
Loading

0 comments on commit 9e1508e

Please sign in to comment.