Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ dependencies {
implementation(libs.androidx.navigation3.ui)
implementation(libs.compose.components.resources)
implementation(libs.compose.foundation)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.material3)
implementation(libs.compose.material3.adaptive.navigation.suite)
implementation(libs.compose.runtime)
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
Expand Down
1 change: 1 addition & 0 deletions androidApp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@
/>
</service>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
14 changes: 14 additions & 0 deletions androidApp/src/main/kotlin/app/icampuspass/android/AndroidApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package app.icampuspass.android

import android.app.Application
import app.icampuspass.shared.initKoin
import app.icampuspass.shared.viewmodels.ClassScheduleScreenViewModel
import app.icampuspass.shared.viewmodels.GreetingScreenViewModel
import app.icampuspass.shared.viewmodels.MainScreenViewModel
import app.icampuspass.shared.viewmodels.UserPickerScreenViewModel
import org.koin.dsl.module

class AndroidApp : Application() {
Expand All @@ -14,13 +16,25 @@ class AndroidApp : Application() {
androidContext = this@AndroidApp,
extraModules = listOf(
module {
factory<ClassScheduleScreenViewModel> {
ClassScheduleScreenViewModel(
tkuIlifeAccountRepository = get()
)
}

factory<GreetingScreenViewModel> {
GreetingScreenViewModel()
}

factory<MainScreenViewModel> {
MainScreenViewModel()
}

factory<UserPickerScreenViewModel> {
UserPickerScreenViewModel(
tkuIlifeAccountRepository = get()
)
}
}
)
)
Expand Down
99 changes: 97 additions & 2 deletions androidApp/src/main/kotlin/app/icampuspass/android/app/App.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,114 @@
package app.icampuspass.android.app

import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Surface
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import app.icampuspass.android.app.views.screens.GreetingScreen
import androidx.navigation3.runtime.rememberNavBackStack
import app.icampuspass.android.app.views.components.ModalDrawerSheet
import app.icampuspass.android.app.views.navigation.NavDisplay
import app.icampuspass.android.app.views.navigation.navkeys.AboutScreenNavKey
import app.icampuspass.android.app.views.navigation.navkeys.ClassScheduleScreenNavKey
import app.icampuspass.android.app.views.navigation.navkeys.MainScreenNavKey
import app.icampuspass.android.app.views.navigation.navkeys.SettingsScreenNavKey
import app.icampuspass.android.shared.views.theme.Theme
import kotlinx.coroutines.launch

@Composable
fun App() {
Theme {
Surface(color = Color.Black) {
Surface(modifier = Modifier.safeDrawingPadding()) {
GreetingScreen()
val scope = rememberCoroutineScope()

val backStack = rememberNavBackStack(MainScreenNavKey)

val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)

ModalNavigationDrawer(
drawerContent = {
ModalDrawerSheet(
backStack = backStack,
onMenuClose = {
scope.launch {
drawerState.close()
}
},
onNavigateMain = {
scope.launch {
drawerState.close()
}

while (backStack.lastOrNull() != MainScreenNavKey) {
backStack.removeLastOrNull()
}
},
onNavigateClassSchedule = {
scope.launch {
drawerState.close()
}

while (backStack.lastOrNull() != MainScreenNavKey) {
backStack.removeLastOrNull()
}

if (!backStack.contains(element = ClassScheduleScreenNavKey)) {
backStack.add(element = ClassScheduleScreenNavKey)
}
},
onNavigateSettings = {
scope.launch {
drawerState.close()
}

while (backStack.lastOrNull() != MainScreenNavKey) {
backStack.removeLastOrNull()
}

if (!backStack.contains(element = SettingsScreenNavKey)) {
backStack.add(element = SettingsScreenNavKey)
}
},
onNavigateAbout = {
scope.launch {
drawerState.close()
}

while (backStack.lastOrNull() != MainScreenNavKey) {
backStack.removeLastOrNull()
}

if (!backStack.contains(element = AboutScreenNavKey)) {
backStack.add(element = AboutScreenNavKey)
}
}
)
},
drawerState = drawerState,
gesturesEnabled = (
backStack.lastOrNull() == MainScreenNavKey ||
backStack.lastOrNull() == ClassScheduleScreenNavKey
),
content = {
NavDisplay(
backStack = backStack,
scope = scope,
drawerState = drawerState
)
}
)

BackHandler(enabled = drawerState.isOpen) {
scope.launch {
drawerState.close()
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package app.icampuspass.android.app.models

import java.util.Locale

object StringFormatter {
fun formatCourseNameZhHant(
name: String,
note: String
): String {
val courseNameZhHant = name
.removeSurrounding(delimiter = "\"").trim()

val courseNoteZhHant = note
.removeSurrounding(delimiter = "\"").trim()

return courseNameZhHant
.let {
when {
courseNoteZhHant.isNotBlank() -> it
.replace(oldValue = "(${courseNoteZhHant})", newValue = "")
.replace(oldValue = "(${courseNoteZhHant})", newValue = "")
else -> it
}
}
.replace(oldValue = "(", newValue = "(")
.replace(oldValue = ")", newValue = ")")
}

fun formatCourseNameEn(name: String): String {
return name
.removeSurrounding(delimiter = "\"").trim()
.replace(oldValue = "MILITARY TRAINING-", newValue = "MILITARY TRAINING - ")
.replace(oldValue = "PHYSICAL EDUCATION-", newValue = "PHYSICAL EDUCATION - ")
.replace(oldValue = "(", newValue = " (")
.replace(oldValue = " ", newValue = " ")
.lowercase()
.split(" ")
.joinToString(separator = " ") { word ->
when {
word == "of" -> word
word.matches(Regex("\\([ivx]+\\)")) -> word.uppercase()
else -> word.replaceFirstChar {
it.titlecase(Locale.forLanguageTag("en-US"))
}
}
}
}

fun formatInstructorNameEn(name: String): String {
val instructorNameEn = name
.removeSurrounding(delimiter = "\"").trim()
.lowercase()
.replace(oldValue = ",", newValue = "")
.split(" ")
.joinToString(separator = " ") { word ->
word.replaceFirstChar {
it.titlecase(Locale.forLanguageTag("en-US"))
}
}

return when (instructorNameEn) {
"Ming-hung Chang" -> "Chang Ming-hung"
"Chung-de Chen" -> "Chen Chung-de"
"Chen Hsin Liang" -> "Chen Hsin-liang"
"Po-hsiang Chen" -> "Chen Po-hsiang"
"Yu-jen Chi" -> "Chi Yu-jen"
"Chiu Sung Lan" -> "Chiu Sung-lan"
"Chien-hsiang Chou" -> "Chou Chien-hsiang"
"Wei-tsong Lee" -> "Lee Wei-tsong"
"Tai Chia Jwu" -> "Tai Chia-jwu"
"Hsien-wei Tseng" -> "Tseng Hsien-wei"
"Wei-chih Tseng" -> "Tseng Wei-chih"
"Yu-yin Tu" -> "Tu Yu-yin"
"Shu-chun Yang" -> "Yang Shu-chun"
"Chien-ho Yen" -> "Yen Chien-ho"
else -> instructorNameEn
}
}

fun formatCourseNoteZhHant(note: String): String {
val courseNoteZhHant = note
.removeSurrounding(delimiter = "\"").trim()

if (courseNoteZhHant.isBlank()) {
return courseNoteZhHant
}

return courseNoteZhHant
.replace(oldValue = "(", newValue = "(")
.replace(oldValue = ")", newValue = ")")
.replace(oldValue = ", ", newValue = ",")
.replace(oldValue = ",", newValue = ",")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package app.icampuspass.android.app.views.components

import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView

@Composable
fun AccountLogInWebView(
modifier: Modifier = Modifier,
onLogInPageFinished: (value: String) -> Unit = {}
) {
AndroidView(
factory = {
WebView(it).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
getSettings().javaScriptEnabled = true
setWebViewClient(AccountLogInWebViewClient(onLogInPageFinished = onLogInPageFinished))
}
},
modifier = modifier,
update = {
it.loadUrl("https://sso.tku.edu.tw/ilife/CoWork/AndroidSsoLogin.cshtml")
}
)
}

private class AccountLogInWebViewClient(
val onLogInPageFinished: (value: String) -> Unit = {}
) : WebViewClient() {
override fun onPageFinished(view: WebView, url: String?) {
view.evaluateJavascript(
"getSsoLoginToken();",
{ value ->
val result = value.removeSurrounding("\"")

if (result.isNotBlank() && result != "null") {
onLogInPageFinished(result)
}
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package app.icampuspass.android.app.views.components

import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView

@Composable
fun AccountLogOutWebView(
modifier: Modifier = Modifier,
onLogOutPageFinished: () -> Unit = {}
) {
AndroidView(
factory = {
WebView(it).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
getSettings().javaScriptEnabled = true
setWebViewClient(AccountLogOutWebViewClient(onLogOutPageFinished = onLogOutPageFinished))
}
},
modifier = modifier,
update = {
it.loadUrl("https://sso.tku.edu.tw/pkmslogout")
}
)
}

private class AccountLogOutWebViewClient(
val onLogOutPageFinished: () -> Unit = {}
) : WebViewClient() {
override fun onPageFinished(view: WebView, url: String?) {
onLogOutPageFinished()
}
}
Loading
Loading