Skip to content

Commit

Permalink
chore: optimize code
Browse files Browse the repository at this point in the history
  • Loading branch information
I-Info committed Oct 24, 2023
1 parent db83288 commit a8b894a
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 56 deletions.
6 changes: 4 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ dependencies {
implementation 'com.google.android.material:material:1.10.0'
implementation "com.google.accompanist:accompanist-systemuicontroller:0.32.0"

// For building appwidgets with Glance
implementation 'androidx.glance:glance-appwidget:1.0.0'
// For building AppWidgets with Glance
def glance_version = "1.0.0"
implementation "androidx.glance:glance-appwidget:$glance_version"
implementation "androidx.glance:glance-material3:$glance_version"

implementation 'org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6'

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
android:supportsRtl="true"
android:theme="@style/Theme.IJh"
tools:targetApi="tiramisu">

<receiver
android:name=".widget.ScheduleWidgetReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>

<meta-data
Expand Down
16 changes: 13 additions & 3 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/screen/AboutScreen.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
package com.zjutjh.ijh.ui.screen

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.UrlAnnotation
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
Expand Down
50 changes: 38 additions & 12 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/screen/ClassScheduleScreen.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package com.zjutjh.ijh.ui.screen

import androidx.compose.animation.*
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
Expand All @@ -14,11 +24,27 @@ import androidx.compose.material.icons.filled.Undo
import androidx.compose.material.icons.filled.UnfoldMore
import androidx.compose.material.icons.outlined.CalendarViewWeek
import androidx.compose.material.icons.outlined.DateRange
import androidx.compose.material3.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.pullrefresh.PullRefreshIndicator
import androidx.compose.material3.pullrefresh.pullRefresh
import androidx.compose.material3.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
Expand Down Expand Up @@ -66,7 +92,7 @@ fun ClassScheduleRoute(
selectedTermWeek = termState.second,
refreshing = refreshing,
switchTermView = viewModel::switchTermView,
onUnselect = viewModel::unselectTerm,
onUnselect = viewModel::clearSelection,
onSelectTerm = viewModel::selectTerm,
onRefresh = viewModel::refresh,
onNavigateBack = onNavigateBack
Expand Down Expand Up @@ -340,8 +366,8 @@ fun TermPicker(

var selectedYear by remember(currentTermWeek) {
if (termWeek != null)
mutableStateOf(termWeek.year)
else mutableStateOf(java.time.Year.now().value)
mutableIntStateOf(termWeek.year)
else mutableIntStateOf(java.time.Year.now().value)
}
var selectedTerm by remember(currentTermWeek) {
if (termWeek != null)
Expand Down Expand Up @@ -391,15 +417,15 @@ fun TermPicker(
val currentYear = remember {
currentTermWeek?.year ?: java.time.Year.now().value
}
val count = currentYear - startYear + 2
val count = currentYear - startYear + 1
val yearScrollState = rememberLazyListState()
LazyColumn(
Modifier.weight(1f),
state = yearScrollState,
horizontalAlignment = Alignment.CenterHorizontally
) {
items(count) {
val year = currentYear + 1 - it
val year = currentYear - it
Text(
text = year.toString(),
modifier = Modifier
Expand All @@ -418,7 +444,7 @@ fun TermPicker(
Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Term.values().forEach { term ->
Term.entries.forEach { term ->
Text(
text = term.value,
modifier = Modifier
Expand Down Expand Up @@ -446,8 +472,8 @@ fun WeekPicker(
val scope = rememberCoroutineScope()
val termWeek = selectedTermWeek ?: currentTermWeek
var selectedWeek by remember(currentTermWeek) {
if (termWeek == null || !termWeek.isInTerm) mutableStateOf(1)
else mutableStateOf(termWeek.week)
if (termWeek == null || !termWeek.isInTerm) mutableIntStateOf(1)
else mutableIntStateOf(termWeek.week)
}
AlertDialog(
modifier = Modifier.widthIn(max = 275.dp),
Expand Down
10 changes: 7 additions & 3 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package com.zjutjh.ijh.ui.theme
import android.os.Build
import android.util.Log
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext

private val LightColorScheme = lightColorScheme(
val LightColorScheme = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
Expand Down Expand Up @@ -39,7 +43,7 @@ private val LightColorScheme = lightColorScheme(
scrim = md_theme_light_scrim,
)

private val DarkColorScheme = darkColorScheme(
val DarkColorScheme = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,25 @@ import com.zjutjh.ijh.ui.model.toTermDayState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
Expand Down Expand Up @@ -44,10 +61,15 @@ class ClassScheduleViewModel @Inject constructor(

private val _selectedTermDayState = MutableStateFlow<TermWeekState?>(null)

// Switch between term view and week view
/**
* State of term-week view switcher
*/
private val _termView = MutableStateFlow(false)
val termView = _termView.asStateFlow()

/**
* Term state pair, first is current term day, second is selected term week
*/
val termState: StateFlow<Pair<TermDayState?, TermWeekState?>> = localTermDayState
.distinctUntilChanged()
.combine(_selectedTermDayState) { localTermDayState, selectedTermDayState ->
Expand All @@ -62,19 +84,21 @@ class ClassScheduleViewModel @Inject constructor(

@OptIn(ExperimentalCoroutinesApi::class)
val coursesState: StateFlow<ImmutableList<Course>?> = termState
.dropWhile { (l, r) -> l == null && r == null }
.dropWhile { (l, r) -> l == null && r == null } // Drop initial null value
.flatMapLatest { state ->
val termState = state.second ?: state.first
val termState = state.second ?: state.first // Selected state have higher priority
if (termState != null) {
courseRepository.getCourses(termState.year, termState.term)
.map { it to termState.week }
.map { it to termState.week } // Save week number for further filtering
} else flowOf(Pair(emptyList(), 1))
}
.combine(_termView) { courses, termView ->
.combine(_termView) { pair, termView ->
// Switch between term view and week view
if (termView) {
courses.first
pair.first
} else {
courses.first.filter { course -> course.weeks.contains(courses.second) }
// Week view
pair.first.filter { pair.second in it.weeks }
}.toImmutableList()
}
.flowOn(Dispatchers.Default)
Expand Down Expand Up @@ -126,7 +150,7 @@ class ClassScheduleViewModel @Inject constructor(
if (refresh) refresh(termWeekState.year, termWeekState.term)
}

fun unselectTerm() {
fun clearSelection() {
_selectedTermDayState.value = null
}

Expand Down
22 changes: 17 additions & 5 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/viewmodel/LoginViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ import com.zjutjh.ijh.R
import com.zjutjh.ijh.data.repository.WeJhUserRepository
import com.zjutjh.ijh.exception.ApiResponseException
import com.zjutjh.ijh.exception.HttpStatusException
import com.zjutjh.ijh.exception.UnauthorizedException
import com.zjutjh.ijh.exception.WeJhApiExceptions
import com.zjutjh.ijh.ui.model.CancellableLoadingState
import com.zjutjh.ijh.ui.util.DismissibleSnackbarVisuals
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.job
import kotlinx.coroutines.launch
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject
Expand Down Expand Up @@ -73,12 +78,14 @@ class LoginViewModel @Inject constructor(private val weJhUserRepository: WeJhUse
_uiState.loading = CancellableLoadingState.READY
}
}

CancellableLoadingState.CANCELLABLE -> {
viewModelScope.launch {
currentJob?.cancelAndJoin()
_uiState.loading = CancellableLoadingState.READY
}
}

else -> Unit
}
}
Expand All @@ -92,36 +99,41 @@ class LoginViewModel @Inject constructor(private val weJhUserRepository: WeJhUse
_uiState.passwordUiState = PasswordUiState.INVALID
showDismissibleSnackbar(context.getString(R.string.invalid_inputs))
}

WeJhApiExceptions.USER_NOT_FOUND -> {
_uiState.usernameUiState = UsernameUiState.UNKNOWN
showDismissibleSnackbar(context.getString(R.string.unknown_user))
}

WeJhApiExceptions.WRONG_PASSWORD -> {
_uiState.passwordUiState = PasswordUiState.WRONG
showDismissibleSnackbar(context.getString(R.string.wrong_password))
}

else -> {
Log.w("Login", "code: ${t.code}, msg: ${t.message}")
showDismissibleSnackbar("${t.message} (${t.code})")
}
}
}

is JsonDataException -> {
Log.e("JsonParsing", t.localizedMessage ?: t.toString())
showDismissibleSnackbar(context.getString(R.string.error_response))
}

is HttpStatusException -> {
showDismissibleSnackbar(context.getString(R.string.unexpected_http_status, t.code))
}
is UnauthorizedException -> {
showDismissibleSnackbar(context.getString(R.string.authentication_exception))
}

is SocketTimeoutException -> {
showDismissibleSnackbar(context.getString(R.string.request_timeout))
}

is UnknownHostException -> {
showDismissibleSnackbar(context.getString(R.string.network_error))
}

else -> {
Log.e("Login", "Error: $t")
showDismissibleSnackbar(context.getString(R.string.unknown_error))
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/kotlin/com/zjutjh/ijh/widget/Components.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Compose components for Glance
*/
package com.zjutjh.ijh.widget

import androidx.compose.runtime.Composable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider

/**
* Simple text component wrapper for Glance
*/
@Composable
fun GText(
text: String,
color: ColorProvider = GlanceTheme.colors.onSurface,
modifier: GlanceModifier = GlanceModifier
) = Text(
text = text,
modifier = modifier,
style = TextStyle(color),
maxLines = 1,
)
Loading

0 comments on commit a8b894a

Please sign in to comment.