Skip to content

Commit

Permalink
finish wear os
Browse files Browse the repository at this point in the history
  • Loading branch information
Tiebe committed Feb 9, 2024
1 parent 84c29a1 commit 66504bc
Show file tree
Hide file tree
Showing 23 changed files with 280 additions and 74 deletions.
6 changes: 3 additions & 3 deletions libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ android_sdk-compile = "34"

ios_target = "11.0"

magister = "1.1.17"
magister = "1.2.0"

work-android = "2.9.0"

Expand All @@ -16,10 +16,10 @@ gradle = "8.2.2"
appcompat = "1.6.1"
material = "1.11.0"
compose = "1.5.11"
compose-android = "1.6.0"
compose-android = "1.6.1"
compose-compiler = "1.5.7"
compose-activity = "1.8.2"
material3 = "1.2.0-rc01"
material3 = "1.2.0"
ktor = "2.3.7"
decompose = "2.2.0-compose-experimental-alpha05"
moko = "0.24.0-alpha-2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import androidx.core.text.HtmlCompat

@Composable
actual fun HtmlView(html: String, textColor: Int, linkColor: Int, backgroundColor: Int, maxLines: Int) {
val color = if (textColor == 0) {
val color = if (textColor == -1) {
LocalTextStyle.current.color.takeOrElse { LocalContentColor.current }.toArgb()
} else textColor

Expand All @@ -25,7 +25,7 @@ actual fun HtmlView(html: String, textColor: Int, linkColor: Int, backgroundColo

setTextColor(color)
setLinkTextColor(linkColor)
setBackgroundColor(backgroundColor)
if (backgroundColor != -1) setBackgroundColor(backgroundColor)

if (maxLines > 0) {
setMaxLines(maxLines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
Expand All @@ -26,7 +28,12 @@ internal fun RecentGradesScreen(component: RecentGradesComponent) {
val grades = component.grades.subscribeAsState().value

val scrollState = rememberScrollState()
val reachedEnd = derivedStateOf { scrollState.value >= (scrollState.maxValue * 0.5).toInt() }
val reachedEnd by remember { derivedStateOf { scrollState.value >= (scrollState.maxValue * 0.5).toInt() } }

LaunchedEffect(reachedEnd) {
if (!component.refreshState.value)
component.loadNextGrades()
}

Column(
modifier = Modifier
Expand All @@ -36,13 +43,6 @@ internal fun RecentGradesScreen(component: RecentGradesComponent) {
grades.forEach {
GradeItem(component = component, grade = it)
}

if (reachedEnd.value) {
LaunchedEffect(Unit) {
if (!component.refreshState.value)
component.loadNextGrades()
}
}
}

PullRefreshIndicator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import androidx.compose.ui.graphics.toArgb
@Composable
expect fun HtmlView(
html: String,
textColor: Int = 0,
textColor: Int = -1,
linkColor: Int = Color(83, 155, 245).toArgb(),
backgroundColor: Int,
maxLines: Int = 0
Expand Down
10 changes: 7 additions & 3 deletions shared/src/commonMain/kotlin/nl/tiebe/otarium/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package nl.tiebe.otarium.utils

import androidx.compose.ui.graphics.ImageBitmap
import kotlinx.datetime.*
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
import nl.tiebe.otarium.Data
import nl.tiebe.otarium.magister.GradeWithGradeInfo
import nl.tiebe.otarium.magister.MagisterAccount
Expand Down Expand Up @@ -54,10 +58,10 @@ fun Instant.toFormattedStringDate(): String {
return "${dateTime.dayOfMonth.toFormattedString()}-${dateTime.monthNumber.toFormattedString()}-${dateTime.year.toFormattedString()}"
}

fun Instant.toFormattedStringTime(): String {
fun Instant.toFormattedStringTime(seconds: Boolean = true): String {
val dateTime = this.toLocalDateTime(TimeZone.of("Europe/Amsterdam"))

return "${dateTime.hour.toFormattedString()}:${dateTime.minute.toFormattedString()}:${dateTime.second.toFormattedString()}"
return "${dateTime.hour.toFormattedString()}:${dateTime.minute.toFormattedString()}" + if (seconds) ":${dateTime.second.toFormattedString()}" else ""
}

fun Int.toFormattedString(): String {
Expand Down
2 changes: 2 additions & 0 deletions wearApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ dependencies {
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)

implementation(libs.kotlin.datetime)

implementation(libs.decompose.core)
implementation(libs.decompose.compose)

Expand Down
3 changes: 1 addition & 2 deletions wearApp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:taskAffinity=""
android:theme="@style/MainActivityTheme.Starting">
android:taskAffinity="">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
6 changes: 4 additions & 2 deletions wearApp/src/main/java/nl/tiebe/otarium/wear/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.defaultComponentContext
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
import com.google.android.gms.wearable.MessageEvent
import nl.tiebe.otarium.ProvideComponentContext
import nl.tiebe.otarium.logic.default.DefaultRootComponent
import nl.tiebe.otarium.logic.root.RootComponent
import nl.tiebe.otarium.setup
import nl.tiebe.otarium.utils.ui.Android
import nl.tiebe.otarium.wear.theme.OtariumTheme
import nl.tiebe.otarium.wear.ui.home.HomeScreen
import nl.tiebe.otarium.wear.ui.login.LoginScreen

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()

super.onCreate(savedInstanceState)

Android.context = this
Android.window = this.window

setTheme(android.R.style.Theme_DeviceDefault)

val componentContext = defaultComponentContext()
Expand Down
17 changes: 10 additions & 7 deletions wearApp/src/main/java/nl/tiebe/otarium/wear/ui/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,30 @@ import androidx.compose.ui.Modifier
import androidx.wear.compose.material.Scaffold
import androidx.wear.compose.material3.ExperimentalWearMaterial3Api
import androidx.wear.compose.material3.HorizontalPageIndicator
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.rememberPageIndicatorState
import com.arkivanov.decompose.ComponentContext
import nl.tiebe.otarium.logic.default.home.children.grades.DefaultRecentGradesComponent
import nl.tiebe.otarium.logic.default.home.children.timetable.DefaultTimetableRootComponent
import nl.tiebe.otarium.wear.ui.home.grades.GradeScreen
import nl.tiebe.otarium.wear.ui.home.timetable.TimetableRootScreen

@OptIn(ExperimentalWearMaterial3Api::class)
@Composable
internal fun HomeScreen(componentContext: ComponentContext) {
val pagerState = rememberPagerState { 2 }
val pageIndicatorState = rememberPageIndicatorState(2) { pagerState.currentPageOffsetFraction + 0.5f }
val pageIndicatorState = rememberPageIndicatorState(2) {
pagerState.currentPage.toFloat() + pagerState.currentPageOffsetFraction
}

Scaffold(
pageIndicator = {
HorizontalPageIndicator(pageIndicatorState = pageIndicatorState)
}
) {

Text(text = "Success!")
HorizontalPager(state = pagerState, modifier = Modifier.fillMaxSize()) {
HorizontalPager(state = pagerState, modifier = Modifier.fillMaxSize(), outOfBoundsPageCount = pagerState.pageCount - 1) {
when (it) {
//1 -> TimetableRootScreen(DefaultTimetableRootComponent(componentContext))
//2 -> GradesScreen(DefaultGradesComponent(componentContext))
0 -> TimetableRootScreen(DefaultTimetableRootComponent(componentContext))
1 -> GradeScreen(DefaultRecentGradesComponent(componentContext))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package nl.tiebe.otarium.wear.ui.home.grades

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TitleCard
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
import dev.tiebe.magisterapi.response.general.year.grades.RecentGrade
import nl.tiebe.otarium.MR
import nl.tiebe.otarium.logic.root.home.children.grades.RecentGradesComponent
import nl.tiebe.otarium.utils.ui.getLocalizedString

@Composable
fun GradeScreen(component: RecentGradesComponent) {
component.refreshGrades()
val grades = component.grades.subscribeAsState().value

val listState = rememberScalingLazyListState()
val reachedEnd by remember { derivedStateOf { listState.centerItemIndex >= (grades.size * 0.5).toInt() } }

LaunchedEffect(reachedEnd) {
if (!component.refreshState.value)
component.loadNextGrades()
}

ScalingLazyColumn(modifier = Modifier.fillMaxSize(), state = listState) {
item { ListHeader {
Text(text = getLocalizedString(MR.strings.gradesItem))
} }
for (grade in grades) {
item { GradeItem(grade) }
}
}
}

@Composable
fun GradeItem(grade: RecentGrade) {
TitleCard(onClick = {}, title = {
Text(text = grade.subject.description + ": " + grade.description)
}, subtitle = {
Text(grade.grade, maxLines = 1)
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package nl.tiebe.otarium.wear.ui.home.timetable

import androidx.compose.runtime.Composable
import androidx.wear.compose.material3.Text
import nl.tiebe.otarium.magister.AgendaItemWithAbsence


//TODO: finish this, too lazy to do right now
@Composable
fun TimetableItemPopup(item: AgendaItemWithAbsence) {
Text(text = "Popup!")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package nl.tiebe.otarium.wear.ui.home.timetable

import androidx.compose.runtime.Composable
import nl.tiebe.otarium.logic.root.home.children.timetable.TimetableRootComponent
import nl.tiebe.otarium.wear.ui.utils.SwipeToDismissBox

@Composable
fun TimetableRootScreen(component: TimetableRootComponent) {
SwipeToDismissBox(stack = component.childStack, onDismissed = { component.back() }) {
when (val instance = it.instance) {
is TimetableRootComponent.Child.TimetableChild -> TimetableScreen(component.timetableComponent)
is TimetableRootComponent.Child.TimetablePopupChild -> TimetableItemPopup(instance.agendaItem)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package nl.tiebe.otarium.wear.ui.home.timetable

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TitleCard
import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
import kotlinx.datetime.Clock
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.TimeZone
import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime
import nl.tiebe.otarium.logic.root.home.children.timetable.children.timetable.TimetableComponent
import nl.tiebe.otarium.magister.AgendaItemWithAbsence
import nl.tiebe.otarium.ui.utils.HtmlView
import nl.tiebe.otarium.utils.toFormattedStringTime
import java.time.format.TextStyle
import java.util.Locale

@Composable
fun TimetableScreen(component: TimetableComponent) {
val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date

component.refreshTimetable(today, today + DatePeriod(days = 31))

val listState = rememberScalingLazyListState()
val items = component.timetable.subscribeAsState().value.groupBy { it.start.toLocalDateTime(TimeZone.currentSystemDefault()).date }

ScalingLazyColumn(modifier = Modifier.fillMaxSize(), state = listState) {
for ((day, timetableItems) in items) {
item { ListHeader {
Text(day.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.getDefault()) + " ${day.dayOfMonth}-${day.monthNumber}")
} }
for (item in timetableItems) item { TimetableItem(item = item) { /*component.parentComponent.navigate(TimetableRootComponent.Config.TimetablePopup(item))*/ } }
}
}
}

@Composable
fun TimetableItem(item: AgendaItemWithAbsence, onClick: () -> Unit) {
TitleCard(onClick = onClick, title = {
Text(text = item.agendaItem.subjects.firstOrNull()?.name?.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } ?: item.agendaItem.description ?: "No description")
}, subtitle = {
if (item.agendaItem.content != null) HtmlView(html = item.agendaItem.content!!, maxLines = 1, backgroundColor = -1, textColor = MaterialTheme.colorScheme.onSurface.toArgb())
}, time = { Text("${item.start.toFormattedStringTime(false)} - ${item.end.toFormattedStringTime(false)}") })
}
Loading

0 comments on commit 66504bc

Please sign in to comment.