diff --git a/mobile/app/src/main/java/com/odweta/solon/Global.kt b/mobile/app/src/main/java/com/odweta/solon/Global.kt index 295f395..283f27c 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Global.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Global.kt @@ -1,10 +1,18 @@ package com.odweta.solon +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith import androidx.compose.foundation.border +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,7 +42,8 @@ class Setting(auxId: SettingsId, auxName: String, auxChecked: Boolean) { enum class SettingsId { Svoboda, Amoled, - RefreshButton + RefreshButton, + Animations } class SettingsData { @@ -56,7 +65,13 @@ class SettingsData { true ) - override fun toString() = "$useSvobodaMode\n$useAmoledMode\n$showRefreshButton" + var useAnimations: Setting = Setting( + SettingsId.Animations, + "Používat animace v aplikaci", + true + ) + + override fun toString() = "$useSvobodaMode\n$useAmoledMode\n$showRefreshButton\n$useAnimations" } enum class LectureNoteType { @@ -184,7 +199,8 @@ enum class ScreenElement { Marks, Menu, About, - Settings + Settings, + SignOut } class Global { @@ -267,4 +283,23 @@ class Global { val global = Global() -var settings = SettingsData() \ No newline at end of file +var settings = SettingsData() + +@Composable +fun Animate(targetState: T, content: @Composable (target: T) -> Unit) { + if (settings.useAnimations.checked.value) { + AnimatedContent( + targetState = targetState, + transitionSpec = { + (slideInVertically(initialOffsetY = { it }) + fadeIn()).togetherWith( + slideOutVertically(targetOffsetY = { it }) + fadeOut() + ) + }, + modifier = Modifier.fillMaxSize(), label = "animated content" + ) { target -> + content(target) + } + } else { + content(targetState) + } +} \ No newline at end of file diff --git a/mobile/app/src/main/java/com/odweta/solon/Launch.kt b/mobile/app/src/main/java/com/odweta/solon/Launch.kt index 850f6b4..fdbc98c 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Launch.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Launch.kt @@ -28,10 +28,17 @@ fun remoteLoad(ctx: Context) { if (!global.isAPISetUp) { CoroutineScope(Dispatchers.IO) .launch { + // setup API setupAPI() + + // fetch remote data global.timetableJSON = networkRequestFactory.perform(NetworkRequestType.Timetable) global.marksJSON = networkRequestFactory.perform(NetworkRequestType.Marks) + // save to local for later local load + Util.saveStringToFile(global.filePaths.timetable, global.timetableJSON.toString()) + Util.saveStringToFile(global.filePaths.marks, global.marksJSON.toString()) + launchContinue( ctx, global.timetableJSON, @@ -76,9 +83,8 @@ fun launchContinue( global.marks = parsedMarks global.newMarks = Parsing.parseNewMarks(marks) - // load the settings - val settingsData = Util.fileToString(global.filePaths.settings).toSettingsData() - global.settings = settingsData +// // load the settings +// global.settings = Util.fileToString(global.filePaths.settings).toSettingsData() // load the lecture notes Util.loadLectureNotes() diff --git a/mobile/app/src/main/java/com/odweta/solon/MainActivity.kt b/mobile/app/src/main/java/com/odweta/solon/MainActivity.kt index 320b3c7..185164b 100644 --- a/mobile/app/src/main/java/com/odweta/solon/MainActivity.kt +++ b/mobile/app/src/main/java/com/odweta/solon/MainActivity.kt @@ -3,6 +3,16 @@ package com.odweta.solon import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.animation.with import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -19,17 +29,22 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + // for seemless switching window.statusBarColor = Color.Black.toArgb() + window.setBackgroundDrawable(resources.getDrawable(R.drawable.black)) + global.fileDir = applicationContext.filesDir launchStart(applicationContext) setContent { SolonTheme { - when (global.currentScreen) { - Screen.Main -> if (global.launched) MainScreen() - Screen.Splash -> SplashScreen() - Screen.SignIn -> SignInScreen() + Animate(global.currentScreen) { targetScreen -> + when (targetScreen) { + Screen.Main -> if (global.launched) MainScreen() + Screen.Splash -> SplashScreen() + Screen.SignIn -> SignInScreen() + } } } } @@ -54,12 +69,15 @@ class MainActivity : ComponentActivity() { .fillMaxWidth() .weight(1f) ) { - when (global.currentScreenElement) { - ScreenElement.Timetable -> TimetableScreen() - ScreenElement.Marks -> MarksScreen() - ScreenElement.Menu -> MenuScreen() - ScreenElement.About -> AboutScreen() - ScreenElement.Settings -> SettingsScreen() + Animate(global.currentScreenElement) { targetScreen -> + when (targetScreen) { + ScreenElement.Timetable -> TimetableScreen() + ScreenElement.Marks -> MarksScreen() + ScreenElement.Menu -> MenuScreen() + ScreenElement.About -> AboutScreen() + ScreenElement.Settings -> SettingsScreen() + ScreenElement.SignOut -> SignOutScreen() + } } } diff --git a/mobile/app/src/main/java/com/odweta/solon/Menu.kt b/mobile/app/src/main/java/com/odweta/solon/Menu.kt index 26a7dc6..04b8cca 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Menu.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Menu.kt @@ -1,5 +1,12 @@ package com.odweta.solon +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -36,7 +43,7 @@ fun MenuScreen() { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { - MenuButton({ showDialog = true }, "Odhlásit se") + MenuButton({ global.currentScreenElement = ScreenElement.SignOut }, "Odhlásit se") Spacer(modifier = Modifier.height(80.dp)) @@ -53,69 +60,6 @@ fun MenuScreen() { MenuButton({ global.currentScreenElement = ScreenElement.Settings }, "Nastavení") } - - // Dialog implementation - if (showDialog) { - AlertDialog( - onDismissRequest = { showDialog = false }, // Close the dialog on dismiss - title = { - Text(text = "Odhlásit se", color = Color.White) - }, - text = { - Text("Opravdu se chcete odhlásit? Tato akce zavře aplikaci.", color = Color.White) - }, - containerColor = Color.Black, - modifier = Modifier - .border( - width = global.dimens.borderWidth, - color = PastelYellow, - shape = RoundedCornerShape(global.dimens.roundedCorner)), // Yellow border - confirmButton = { - Button( - onClick = { - showDialog = false - Util.deleteFile(global.filePaths.userData) - Util.deleteFile(global.filePaths.timetable) - Util.deleteFile(global.filePaths.marks) - Util.deleteFile(global.filePaths.accountData) - Util.deleteFile(global.filePaths.lectureNotes) - // do not delete settings - exitProcess(0) - }, - colors = ButtonDefaults.buttonColors( - containerColor = Color.Black, // Black background for the button - contentColor = Color.White // Yellow text for the button - ), - modifier = Modifier - .border( - width = global.dimens.borderWidth, - color = PastelYellow, - shape = RoundedCornerShape(global.dimens.roundedCorner)) // Yellow border - ) { - Text("Ano") - } - }, - dismissButton = { - Button( - onClick = { - showDialog = false - }, - colors = ButtonDefaults.buttonColors( - containerColor = Color.Black, // Black background for the button - contentColor = Color.White, - // Yellow text for the button - ), - modifier = Modifier - .border( - width = global.dimens.borderWidth, - color = PastelYellow, - shape = RoundedCornerShape(global.dimens.roundedCorner)) // Yellow border - ) { - Text("Ne") - } - } - ) - } } @Composable diff --git a/mobile/app/src/main/java/com/odweta/solon/Settings.kt b/mobile/app/src/main/java/com/odweta/solon/Settings.kt index a3855e6..4f5aecd 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Settings.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Settings.kt @@ -1,5 +1,6 @@ package com.odweta.solon +import android.util.Log import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -16,6 +17,7 @@ import androidx.compose.material3.Switch import androidx.compose.material3.SwitchColors import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -29,6 +31,10 @@ fun SettingsScreen() { // handle the back gesture (back swipe form side or back button on bottom android navbar) BackHandler { global.currentScreenElement = ScreenElement.Menu } + // load the settings dat every time the settings screen is opened, + // maybe i can add a flag that determines if the settings are up to date? + settings = Util.fileToString(global.filePaths.settings).toSettingsData() + val scrollState = rememberScrollState() Column(modifier = Modifier @@ -38,22 +44,20 @@ fun SettingsScreen() { ) { backButtonFactory.get(BackButtonType.Menu)() - Spacer(Modifier.height(10.dp)) - SettingsTableRow(settings.useSvobodaMode) - Spacer(Modifier.height(10.dp)) - SettingsTableRow(settings.useAmoledMode) - Spacer(Modifier.height(10.dp)) - SettingsTableRow(settings.showRefreshButton) + + SettingsTableRow(settings.useAnimations) } } @Composable fun SettingsTableRow(setting: Setting) { + Spacer(Modifier.height(10.dp)) + Row( modifier = Modifier .padding(10.dp) @@ -78,7 +82,7 @@ fun SettingsTableRow(setting: Setting) { SettingsId.Svoboda -> settings.useSvobodaMode = setting SettingsId.Amoled -> settings.useAmoledMode = setting SettingsId.RefreshButton -> settings.showRefreshButton = setting - else -> {} + SettingsId.Animations -> settings.useAnimations = setting } Util.saveStringToFile(global.filePaths.settings, settings.toString()) }, @@ -113,6 +117,7 @@ fun String.toSettingsData(): SettingsData { val useSvobodaMode = settings[0].split("#") val useAmoledMode = settings[1].split("#") val showRefreshButton = settings[2].split("#") + val useAnimations = settings[3].split("#") auxSettingsData.useSvobodaMode.id = SettingsId.Svoboda auxSettingsData.useSvobodaMode.name = useSvobodaMode[1] @@ -126,5 +131,9 @@ fun String.toSettingsData(): SettingsData { auxSettingsData.showRefreshButton.name = showRefreshButton[1] auxSettingsData.showRefreshButton.checked.value = showRefreshButton[2].toBoolean() + auxSettingsData.useAnimations.id = SettingsId.Animations + auxSettingsData.useAnimations.name = useAnimations[1] + auxSettingsData.useAnimations.checked.value = useAnimations[2].toBoolean() + return auxSettingsData } \ No newline at end of file diff --git a/mobile/app/src/main/java/com/odweta/solon/SignOut.kt b/mobile/app/src/main/java/com/odweta/solon/SignOut.kt new file mode 100644 index 0000000..bc55008 --- /dev/null +++ b/mobile/app/src/main/java/com/odweta/solon/SignOut.kt @@ -0,0 +1,107 @@ +package com.odweta.solon + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +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.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.odweta.solon.ui.theme.PastelYellow +import kotlin.system.exitProcess + +@Composable +fun SignOutScreen() { + // handle the back gesture (back swipe form side or back button on bottom android navbar) + BackHandler { global.currentScreenElement = ScreenElement.Menu } + + Column( + modifier = Modifier + .fillMaxSize(), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start + ) { + backButtonFactory.get(BackButtonType.Menu)() + + Column( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Opravdu se chcete odhlásit? Tato akce zavře aplikaci.", + color = Color.White, + textAlign = TextAlign.Center, + fontSize = 27.sp, + lineHeight = 40.sp, + modifier = Modifier.padding(bottom = 10.dp) + ) + + Row { + SignOutButton( + onClick = { + Util.deleteFile(global.filePaths.userData) + Util.deleteFile(global.filePaths.timetable) + Util.deleteFile(global.filePaths.marks) + Util.deleteFile(global.filePaths.accountData) + Util.deleteFile(global.filePaths.lectureNotes) + // do not delete settings + exitProcess(0) + }, + label = "Ano" + ) + + Spacer(Modifier.width(10.dp)) + + SignOutButton( + onClick = { + global.currentScreenElement = ScreenElement.Menu + }, + label = "Ne" + ) + } + } + } +} + +@Composable +private fun SignOutButton(onClick: () -> Unit, label: String) { + Button( + onClick = onClick, + colors = ButtonDefaults.buttonColors( + containerColor = Color.Black, // Black background for the button + contentColor = Color.White // Yellow text for the button + ), + modifier = Modifier + .border( + width = global.dimens.borderWidth, + color = PastelYellow, + shape = RoundedCornerShape(global.dimens.roundedCorner) + ) // Yellow border + ) { + Text(label) + } +} \ No newline at end of file