Skip to content

Commit

Permalink
feat(card): query campus card info
Browse files Browse the repository at this point in the history
  • Loading branch information
I-Info committed Oct 30, 2023
1 parent 321c98d commit 76bdbd2
Show file tree
Hide file tree
Showing 20 changed files with 568 additions and 173 deletions.
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
# IJH Android
# IJH Android

[![Android CI](https://github.com/I-Info/IJH-Android/actions/workflows/ci.yml/badge.svg)](https://github.com/I-Info/IJH-Android/actions/workflows/ci.yml)

IJH app for Android, a **work in progress** currently.

# Features
## Features

- [ ] All features supported by **WeJH**.
- [ ] Notifications.
- [ ] More...

# Architecture
## Architecture

The app follows
the [official architecture guidance](https://developer.android.com/topic/architecture).

![module.png](https://s2.loli.net/2023/10/29/EUNtaGgBVqdfvJz.png)

- data (repository -> data source)
- network (Retrofit/OkHttp)
- datastore (Protobuf)
- database (Room)
- network (Retrofit/OkHttp)
- datastore (Protobuf)
- database (Room)

## UI

UI is built with [Jetpack Compose](https://developer.android.com/jetpack/compose) and
UI is built with [Jetpack Compose](https://developer.android.com/jetpack/compose) and follows
[Material Design 3](https://m3.material.io).

- Theme: IJH app uses the Dynamic color theme as possible, and provides a default theme for
- Theme: IJH app uses the Dynamic color theme (Material You), and provides a default theme for
fallbacks.

## Dependency injection
IJH app uses DI(Dependency injection) between layers and uses [Hilt](https://developer.android.com/training/dependency-injection/hilt-android)
to implement automatic DI.
## Dependency injection (DI)

IJH app uses [Hilt](https://developer.android.com/training/dependency-injection/hilt-android)
to implement automatic DI in modules and layers.
105 changes: 105 additions & 0 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/component/CampusCardInfoCard.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
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.CreditCard
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.ui.theme.IJhTheme
import com.zjutjh.ijh.util.toLocalizedString
import java.time.Duration

@Composable
fun CampusCardInfoCard(
modifier: Modifier = Modifier,
balance: String?,
lastSyncDuration: Duration?
) {
val context = LocalContext.current
val subtitle = remember(lastSyncDuration) {
prompt(context, lastSyncDuration)
}

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

else -> {
Text(
modifier = Modifier.padding(vertical = 8.dp),
color = MaterialTheme.colorScheme.primary,
text = "¥$it",
style = MaterialTheme.typography.displaySmall,
textAlign = TextAlign.Center,
maxLines = 1,
)
}
}
}
}
}

private fun prompt(context: Context, lastSyncDuration: Duration?) =
buildString {
val separator = ""
append(context.getString(R.string.balance))
append(separator)
if (lastSyncDuration != null) {
append(lastSyncDuration.toLocalizedString(context))
} else {
append(context.getString(R.string.never))
}
}


@Preview
@Composable
private fun CampusCardInfoCardPreview() {
IJhTheme {
CampusCardInfoCard(balance = "123", lastSyncDuration = Duration.ofDays(1))
}
}

@Preview
@Composable
private fun CampusCardInfoCardPreviewEmpty() {
IJhTheme {
CampusCardInfoCard(balance = null, lastSyncDuration = null)
}
}
101 changes: 101 additions & 0 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/component/GlanceCard.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.zjutjh.ijh.ui.component

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Image
import androidx.compose.material3.Card
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.zjutjh.ijh.R
import com.zjutjh.ijh.ui.theme.IJhTheme
import com.zjutjh.ijh.util.emptyFun

@Composable
internal fun GlanceCard(
modifier: Modifier,
title: String,
subtitle: String,
icon: ImageVector? = null,
onButtonClick: (() -> Unit)? = null,
content: @Composable (ColumnScope.() -> Unit),
) {
Card(
modifier = modifier,
) {
Row(
Modifier
.padding(top = 12.dp, start = 16.dp, end = 16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column {
// Title
if (icon != null)
IconText(
icon = icon,
text = " | $title",
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold,
)
else Text(
text = title,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold,
maxLines = 1,
)

// Subtitle
Text(
text = subtitle,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.outline,
maxLines = 1,
)
}
if (onButtonClick != null)
FilledTonalIconButton(
onClick = onButtonClick,
) {
Icon(
imageVector = Icons.Default.ChevronRight,
contentDescription = stringResource(id = R.string.more)
)
}
}

content()
}
}

@Preview
@Composable
private fun GlanceCardPreview() {
IJhTheme {
GlanceCard(
modifier = Modifier,
title = "Title",
subtitle = "Subtitle",
icon = Icons.Default.Image,
onButtonClick = ::emptyFun,
) {
Text(modifier = Modifier.padding(24.dp), text = "Content")
}
}
}
74 changes: 74 additions & 0 deletions app/src/main/kotlin/com/zjutjh/ijh/ui/component/IconText.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.zjutjh.ijh.ui.component

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Image
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp

@Composable
fun IconText(
icon: ImageVector,
text: String,
contentDescription: String? = null,
fontWeight: FontWeight? = null,
style: TextStyle = MaterialTheme.typography.bodyMedium,
) {
val id = "icon"
val annotatedString = buildAnnotatedString {
appendInlineContent(id, "[icon]")
append(text)
}
val inlineContent = mapOf(
id to InlineTextContent(
Placeholder(
width = style.fontSize,
height = style.fontSize,
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter,
)
) {
Icon(
modifier = Modifier.fillMaxSize(),
imageVector = icon,
contentDescription = contentDescription,
)
}
)

Text(
text = annotatedString,
inlineContent = inlineContent,
style = style,
fontWeight = fontWeight,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
}

@Preview
@Composable
fun IconTextPreview() {
Surface {
IconText(
icon = Icons.Default.Image,
text = "Hello World",
fontWeight = FontWeight.Bold,
style = TextStyle(fontSize = 30.sp)
)
}
}
Loading

0 comments on commit 76bdbd2

Please sign in to comment.