Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d891a0e4 | ||
|
|
3bac016a8b | ||
|
|
81e875bea8 | ||
|
|
aed00c0694 | ||
|
|
6cbe12fb10 | ||
|
|
992c0b4a34 | ||
|
|
d8bda87b38 | ||
|
|
cca561f11b | ||
|
|
7907429aea | ||
|
|
4cb07126e2 | ||
|
|
1d885ade70 | ||
|
|
8615eee3a7 | ||
|
|
e3bb1b9cfa | ||
|
|
0109c7aa09 | ||
|
|
7800eeb61b | ||
|
|
4b7fbfd766 | ||
|
|
66f3ad791f | ||
|
|
910e968653 | ||
|
|
31197830d0 | ||
|
|
a619c8b92e | ||
|
|
04d8527434 | ||
|
|
bea50f7fe7 | ||
|
|
e9a422eda0 | ||
|
|
cc86373989 | ||
|
|
8b98d92c2b | ||
|
|
b10438c109 | ||
|
|
69f48750f1 | ||
|
|
81fb398ae2 | ||
|
|
1516a00841 | ||
|
|
6dae99d311 |
+2
-15
@@ -1,16 +1,3 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
mobile/app/build
|
||||
mobile/local.properties
|
||||
mobile/.gradle
|
||||
Generated
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
Generated
-12
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MaterialThemeProjectNewConfig">
|
||||
<option name="metadata">
|
||||
<MTProjectMetadataState>
|
||||
<option name="migrated" value="true" />
|
||||
<option name="pristineConfig" value="false" />
|
||||
<option name="userId" value="-239e49af:192a4be9597:-7ffe" />
|
||||
</MTProjectMetadataState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
-17
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,3 +1,6 @@
|
||||

|
||||
# Zorientujte se rychleji v této odlehčené verzi Školy OnLine
|
||||
# Odkaz na stažení aplikace pro Android [zde](https://gitlab.com/Odweta/solon/-/releases/2.3.0/downloads/app-release.apk) (verze 2.3.0)
|
||||
# Odkaz na stažení aplikace pro Android [zde](https://gitlab.com/Odweta/solon/-/releases/2.5/downloads/app-release.apk) (verze 2.5)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Nyní na F-Droidu"
|
||||
height="80">](https://f-droid.org/packages/com.odweta.solon/)
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo -n "version number: "
|
||||
read ver
|
||||
|
||||
echo -n "glpat: "
|
||||
echo -n "[glpat]: "
|
||||
read glpat
|
||||
|
||||
echo -n "[keystore password]: "
|
||||
read ks_pass
|
||||
|
||||
cd ./mobile
|
||||
|
||||
ver=$(./gradlew printVersionName -q)
|
||||
|
||||
# add, commit, push
|
||||
echo
|
||||
echo "[add, commit, push]"
|
||||
echo
|
||||
git add ..
|
||||
git commit -m "made new release $ver"
|
||||
git push -f
|
||||
|
||||
# build
|
||||
echo
|
||||
echo "[building]"
|
||||
echo
|
||||
./gradlew assembleRelease
|
||||
|
||||
# sign
|
||||
~/Android/Sdk/build-tools/35.0.0/apksigner sign --alignment-preserved --ks ~/Documents/android-keystore.jks app/build/outputs/apk/release/com.odweta.solon_$ver.apk
|
||||
|
||||
# add, commit, push
|
||||
git add ..
|
||||
git commit -m "made new release $ver"
|
||||
git push
|
||||
echo
|
||||
echo "[signing]"
|
||||
echo
|
||||
~/Android/Sdk/build-tools/35.0.1/apksigner sign --alignment-preserved --ks ~/Documents/android-keystore.jks --ks-pass pass:$ks_pass app/build/outputs/apk/release/com.odweta.solon_$ver.apk
|
||||
|
||||
# create the tag
|
||||
echo "\n[creating tag v$ver]"
|
||||
echo
|
||||
echo "[creating tag v$ver]"
|
||||
echo
|
||||
data_str='{ "tag_name": "'$ver'", "ref": "HEAD", "message": "Tagging release v'$ver'" }'
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: $glpat" \
|
||||
@@ -29,7 +42,9 @@ curl --request POST \
|
||||
"https://gitlab.com/api/v4/projects/47146187/repository/tags"
|
||||
|
||||
# make a release
|
||||
echo "\n[publishing release $ver]"
|
||||
echo
|
||||
echo "[publishing release $ver]"
|
||||
echo
|
||||
data_str='{ "name": "v'$ver'", "tag_name": "'$ver'", "ref": "HEAD" }'
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: $glpat" \
|
||||
@@ -38,14 +53,18 @@ curl --request POST \
|
||||
"https://gitlab.com/api/v4/projects/47146187/releases"
|
||||
|
||||
# attach a file
|
||||
echo "\n[attaching the release apk]"
|
||||
echo
|
||||
echo "[attaching the release apk]"
|
||||
echo
|
||||
asset_url="https://gitlab.com/-/project/47146187"$(curl --request POST \
|
||||
--header "PRIVATE-TOKEN: $glpat" \
|
||||
--form "file=@app/build/outputs/apk/release/com.odweta.solon_$ver.apk" \
|
||||
"https://gitlab.com/api/v4/projects/47146187/uploads" | jq .url -r)
|
||||
|
||||
# add the file as a stable release asset
|
||||
echo "\n[creating release asset]"
|
||||
echo
|
||||
echo "[creating release asset]"
|
||||
echo
|
||||
curl --request POST \
|
||||
--header "PRIVATE-TOKEN: $glpat" \
|
||||
--data name="app-release.apk" \
|
||||
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
|
||||
cd mobile
|
||||
|
||||
INTERNAL_STORAGE=/data/data/com.termux/files/home/storage/downloads/..
|
||||
|
||||
echo -n 'keystore key for signing: '
|
||||
read kskey
|
||||
|
||||
echo 'building the release bundle'
|
||||
|
||||
gradle bundleRelease
|
||||
|
||||
mkdir -p $INTERNAL_STORAGE/solon-bin/
|
||||
|
||||
cp $SOLON/mobile/app/build/outputs/bundle/release/app-release.aab $INTERNAL_STORAGE/solon-bin/
|
||||
|
||||
echo 'app-bundle.aab copied to \$INTERNAL_STORAGE/solon-bin'
|
||||
|
||||
apksigner sign --ks $INTERNAL_STORAGE/Documents/android-keystore.jks --ks-pass pass:$kskey --min-sdk-version=28 $INTERNAL_STORAGE/solon-bin/app-release.aab
|
||||
|
||||
echo 'aab signed with default android keystore'
|
||||
|
||||
cd ..
|
||||
Generated
+123
@@ -0,0 +1,123 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
-6
@@ -13,12 +13,6 @@
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="TimetablePreview">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="RBPreview">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
|
||||
Generated
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -4,6 +4,26 @@ plugins {
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
tasks.register("printVersionName") {
|
||||
doLast {
|
||||
println(android.defaultConfig.versionName)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("deploy") {
|
||||
dependsOn("assembleDebug")
|
||||
//dependsOn("installDebug")
|
||||
doLast {
|
||||
exec {
|
||||
commandLine("adb", "shell", "am", "start", "-n", "com.odweta.solon/.MainActivity")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("b") {
|
||||
dependsOn("assembleDebug")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.odweta.solon"
|
||||
compileSdk = 34
|
||||
@@ -12,8 +32,8 @@ android {
|
||||
applicationId = "com.odweta.solon"
|
||||
minSdk = 28
|
||||
targetSdk = 34
|
||||
versionCode = 208
|
||||
versionName = "2.3.0"
|
||||
versionCode = 213
|
||||
versionName = "2.5"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.odweta.solon",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "2.0",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/app-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/app-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
"minSdkVersionForDexing": 28
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.odweta.solon
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
@@ -8,17 +9,10 @@ import java.nio.charset.Charset
|
||||
|
||||
class Account(val username: String, val password: String, private val error: Int) {
|
||||
companion object {
|
||||
private fun parseAccountDataFile(file: File): Account {
|
||||
val lines: List<String> = file.readLines()
|
||||
|
||||
if (lines.size >= 2) {
|
||||
if (lines[0] != "" && lines[1] != "") {
|
||||
private fun parseAccountDataFile(): Account {
|
||||
val lines: List<String> = File(global.fileDir, global.filePaths.accountData).readLines()
|
||||
return Account(lines[0], lines[1], 0)
|
||||
}
|
||||
}
|
||||
|
||||
return Account("", "", 1)
|
||||
}
|
||||
|
||||
fun haveSaved(): Boolean {
|
||||
/* check if a file exists
|
||||
@@ -27,15 +21,17 @@ class Account(val username: String, val password: String, private val error: Int
|
||||
*/
|
||||
|
||||
// check if file exists
|
||||
val file = File(global.fileDir, global.filePaths.accountData)
|
||||
|
||||
return if (!file.exists()) {
|
||||
return if (
|
||||
!Util.fileExists(global.filePaths.accountData) ||
|
||||
Util.fileIsEmpty(global.filePaths.accountData)
|
||||
) {
|
||||
// it does not exist
|
||||
file.createNewFile()
|
||||
File(global.fileDir, global.filePaths.accountData).createNewFile()
|
||||
false
|
||||
} else {
|
||||
// it exists, so it shall be parsed
|
||||
val acc: Account = parseAccountDataFile(file)
|
||||
val acc: Account = parseAccountDataFile()
|
||||
global.username = acc.username
|
||||
global.password = acc.password
|
||||
acc.error == 0
|
||||
@@ -43,23 +39,20 @@ class Account(val username: String, val password: String, private val error: Int
|
||||
}
|
||||
|
||||
fun save(acc: Account): Boolean {
|
||||
return try {
|
||||
val file = File(global.fileDir, global.filePaths.accountData)
|
||||
file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8"))
|
||||
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
val payload = "${acc.username}\n${acc.password}"
|
||||
return Util.saveStringToFile(global.filePaths.accountData, payload)
|
||||
}
|
||||
|
||||
suspend fun isValid(acc: Account): Boolean {
|
||||
return withContext(Dispatchers.IO) {
|
||||
fun isValid(acc: Account): Boolean {
|
||||
global.username = acc.username
|
||||
global.password = acc.password
|
||||
|
||||
val token = networkRequestFactory.perform(NetworkRequestType.Token).getString("token")
|
||||
!JSONObject(token).has("error")
|
||||
val tokenJSON = networkRequestFactory.perform(NetworkRequestType.Token)
|
||||
return if (tokenJSON.has("token")) {
|
||||
val token = tokenJSON.getString("token")
|
||||
!token.contains("\"error\"")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,7 @@ interface BackButtonFactoryInterface {
|
||||
@Composable
|
||||
fun BackButton(onClick: () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 5.dp)
|
||||
modifier = Modifier.padding(start = 10.dp)
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
@@ -65,8 +63,14 @@ object BackButtonFactory: BackButtonFactoryInterface {
|
||||
@Composable
|
||||
override fun get(type: BackButtonType): @Composable () -> Unit {
|
||||
return when (type) {
|
||||
BackButtonType.Menu -> { { BackButton { global.currentScreenElement = ScreenElement.Menu } } }
|
||||
BackButtonType.MarksAll -> { { BackButton { global.marksDepthLevel = MarksDepthLevel.All } } }
|
||||
BackButtonType.Menu -> { { BackButton {
|
||||
global.currentScreenElement = ScreenElement.Menu
|
||||
global.currentScreen = Screen.Main
|
||||
} } }
|
||||
BackButtonType.MarksAll -> { { BackButton {
|
||||
global.currentScreenElement = ScreenElement.Marks
|
||||
global.marksDepthLevel = MarksDepthLevel.All
|
||||
} } }
|
||||
BackButtonType.MarksSubject -> { { BackButton { global.marksDepthLevel = MarksDepthLevel.Subject } } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,44 @@
|
||||
package com.odweta.solon
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
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.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.RecomposeScope
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.odweta.solon.ui.theme.PastelYellow
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
enum class Screen {
|
||||
Main,
|
||||
Splash,
|
||||
SignIn
|
||||
SignIn,
|
||||
Settings,
|
||||
Temp
|
||||
}
|
||||
|
||||
// default settings (will get overwritten by the saved settings)
|
||||
@@ -34,7 +52,8 @@ class Setting(auxId: SettingsId, auxName: String, auxChecked: Boolean) {
|
||||
enum class SettingsId {
|
||||
Svoboda,
|
||||
Amoled,
|
||||
RefreshButton
|
||||
RefreshButton,
|
||||
Animations
|
||||
}
|
||||
|
||||
class SettingsData {
|
||||
@@ -56,7 +75,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 {
|
||||
@@ -76,23 +101,13 @@ fun String.toLectureNoteType(): LectureNoteType {
|
||||
}
|
||||
}
|
||||
|
||||
class LectureNote(id: Int, note: MutableState<String>, type: MutableState<LectureNoteType>, number: Int, lectureName: String) {
|
||||
var id: Int = 0
|
||||
var note: MutableState<String> =
|
||||
mutableStateOf("")
|
||||
var type: MutableState<LectureNoteType> =
|
||||
mutableStateOf(LectureNoteType.Normal)
|
||||
var number: Int = 0
|
||||
var lectureName: String = ""
|
||||
|
||||
init {
|
||||
this.id = id
|
||||
this.note = note
|
||||
this.type = type
|
||||
this.number = number
|
||||
this.lectureName = lectureName
|
||||
}
|
||||
}
|
||||
data class LectureNote(
|
||||
var id: Int,
|
||||
var note: MutableState<String> = mutableStateOf(""),
|
||||
var type: MutableState<LectureNoteType> = mutableStateOf(LectureNoteType.Normal),
|
||||
var number: Int,
|
||||
var lectureName: String
|
||||
)
|
||||
|
||||
class Subject {
|
||||
lateinit var name: String
|
||||
@@ -174,16 +189,20 @@ class Mark {
|
||||
enum class ScreenElement {
|
||||
Timetable,
|
||||
Marks,
|
||||
NewMarks,
|
||||
FinalMarks,
|
||||
Menu,
|
||||
About,
|
||||
Settings
|
||||
SignOut
|
||||
}
|
||||
|
||||
class Global {
|
||||
lateinit var timetableJSON: JSONObject
|
||||
lateinit var marksJSON: JSONObject
|
||||
lateinit var finalMarksJSON: JSONObject
|
||||
lateinit var marks: MutableMap<String, MutableList<Mark>>
|
||||
lateinit var newMarks: MutableMap<String, MutableList<Mark>>
|
||||
lateinit var finalMarks: Map<String, Int>
|
||||
lateinit var settings: SettingsData
|
||||
lateinit var fileDir: File
|
||||
|
||||
@@ -205,11 +224,13 @@ class Global {
|
||||
var selectedLecture: Int = 0
|
||||
var currentLectureNumber: Int = 0
|
||||
|
||||
var tempScreen by mutableStateOf(Screen.Settings)
|
||||
var currentScreen by mutableStateOf(Screen.Main)
|
||||
var currentScreenElement by mutableStateOf(ScreenElement.Timetable)
|
||||
|
||||
var status by mutableStateOf("")
|
||||
|
||||
var returnToTempScreen by mutableStateOf(false)
|
||||
var isAPISetUp by mutableStateOf(false)
|
||||
var launched by mutableStateOf(false)
|
||||
var refreshed by mutableStateOf(false)
|
||||
@@ -218,6 +239,8 @@ class Global {
|
||||
var subjectToShow by mutableStateOf("")
|
||||
var markToShow by mutableStateOf(Mark())
|
||||
|
||||
var demoMode: Boolean = false
|
||||
|
||||
inner class FilePaths {
|
||||
val accountData: String = "account_data.txt"
|
||||
val timetable: String = "timetable.json"
|
||||
@@ -225,6 +248,7 @@ class Global {
|
||||
val marks: String = "marks.json"
|
||||
val settings: String = "settings.txt"
|
||||
val lectureNotes: String = "lecture_notes.csv"
|
||||
val finalMarks: String = "final_marks.txt"
|
||||
}
|
||||
val filePaths = FilePaths()
|
||||
|
||||
@@ -234,6 +258,7 @@ class Global {
|
||||
val userData = "$apiBase/v1/user"
|
||||
val timetable = "$apiBase/v1/timeTable"
|
||||
var marks: String = "$apiBase/v1/students/<student id>/marks/list"
|
||||
var finalMarks: String ="$apiBase/v1/students/<student id>/marks/final"
|
||||
}
|
||||
val apiEndPoints = APIEndPoints()
|
||||
|
||||
@@ -260,3 +285,34 @@ class Global {
|
||||
val global = Global()
|
||||
|
||||
var settings = SettingsData()
|
||||
|
||||
private fun <T> changeAndRevert(variable: T, newValue: T, revert: (T) -> Unit) {
|
||||
val orig = variable
|
||||
revert(newValue)
|
||||
revert(orig)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> Animate(targetState: T, content: @Composable (target: T) -> Unit) {
|
||||
if (settings.useAnimations.checked.value == true) {
|
||||
// When animations are enabled, apply AnimatedContent
|
||||
AnimatedContent(
|
||||
targetState = targetState,
|
||||
transitionSpec = {
|
||||
(slideInVertically(initialOffsetY = { it }) + fadeIn()).togetherWith(
|
||||
slideOutVertically(targetOffsetY = { it }) + fadeOut()
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
label = "animated content"
|
||||
) { target ->
|
||||
content(target)
|
||||
}
|
||||
} else {
|
||||
// When animations are disabled, just show content without animation
|
||||
content(targetState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,14 +28,24 @@ 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)
|
||||
global.finalMarksJSON = networkRequestFactory.perform(NetworkRequestType.FinalMarks)
|
||||
|
||||
// save to local for later local load
|
||||
Util.saveStringToFile(global.filePaths.timetable, global.timetableJSON.toString())
|
||||
Util.saveStringToFile(global.filePaths.marks, global.marksJSON.toString())
|
||||
Util.saveStringToFile(global.filePaths.finalMarks, global.finalMarksJSON.toString())
|
||||
|
||||
launchContinue(
|
||||
ctx,
|
||||
global.timetableJSON,
|
||||
global.marksJSON
|
||||
global.marksJSON,
|
||||
global.finalMarksJSON
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -43,7 +53,8 @@ fun remoteLoad(ctx: Context) {
|
||||
launchContinue(
|
||||
ctx,
|
||||
networkRequestFactory.perform(NetworkRequestType.Timetable),
|
||||
networkRequestFactory.perform(NetworkRequestType.Marks)
|
||||
networkRequestFactory.perform(NetworkRequestType.Marks),
|
||||
networkRequestFactory.perform(NetworkRequestType.FinalMarks)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -54,39 +65,43 @@ fun localLoad(ctx: Context) {
|
||||
global.studentId = Parsing.getStudentId()
|
||||
global.studentClass = Parsing.getStudentClass()
|
||||
global.apiEndPoints.marks = "${global.apiEndPoints.apiBase}/v1/students/${global.studentId}/marks/list"
|
||||
global.apiEndPoints.finalMarks = "${global.apiEndPoints.apiBase}/v1/students/${global.studentId}/marks/final"
|
||||
val localTimetable = JSONObject(Util.fileToString(global.filePaths.timetable))
|
||||
val localMarks = JSONObject(Util.fileToString(global.filePaths.marks))
|
||||
val localFinalMarks = JSONObject(Util.fileToString(global.filePaths.finalMarks))
|
||||
|
||||
launchContinue(
|
||||
ctx,
|
||||
localTimetable,
|
||||
localMarks
|
||||
localMarks,
|
||||
localFinalMarks
|
||||
)
|
||||
}
|
||||
|
||||
fun launchContinue(
|
||||
ctx: Context,
|
||||
timetable: JSONObject,
|
||||
marks: JSONObject
|
||||
marks: JSONObject,
|
||||
finalMarks: JSONObject
|
||||
) {
|
||||
// parse timetable and marks
|
||||
val parsedTimetable = Parsing.parseTimetable(timetable)
|
||||
global.timetable = parsedTimetable
|
||||
val parsedMarks = Parsing.parseMarks(marks)
|
||||
global.marks = parsedMarks
|
||||
global.timetable = Parsing.parseTimetable(timetable)
|
||||
global.marks = Parsing.parseMarks(marks)
|
||||
global.newMarks = Parsing.parseNewMarks(marks)
|
||||
|
||||
// load the settings
|
||||
val settingsData = Util.fileToString(global.filePaths.settings).toSettingsData()
|
||||
global.settings = settingsData
|
||||
global.finalMarks = Parsing.parseFinalMarks(finalMarks)
|
||||
|
||||
// load the lecture notes
|
||||
Util.csvToLectureNotes()
|
||||
//Util.loadLectureNotes()
|
||||
//Util.csvToLectureNotes()
|
||||
|
||||
// load the settings
|
||||
settings = Util.fileToString(global.filePaths.settings).toSettingsData()
|
||||
|
||||
// listen for internet availability in case of disconnection
|
||||
if (!global.internetAwaitListenerRunning)
|
||||
setOnInternetAvailListener(ctx)
|
||||
|
||||
// FIXME maybe? causes IndexOutOfBoundsException????? prob not tho
|
||||
if (!global.launched) {
|
||||
// show the default (main) screen
|
||||
global.currentScreen = Screen.Main
|
||||
|
||||
@@ -8,28 +8,45 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.currentRecomposeScope
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.odweta.solon.ui.theme.SolonTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// for seemless <currentScreen> switching
|
||||
window.statusBarColor = Color.Black.toArgb()
|
||||
window.setBackgroundDrawable(resources.getDrawable(R.drawable.black))
|
||||
|
||||
global.fileDir = applicationContext.filesDir
|
||||
|
||||
launchStart(applicationContext)
|
||||
|
||||
setContent {
|
||||
SolonTheme {
|
||||
when (global.currentScreen) {
|
||||
Animate(global.currentScreen) { targetScreen ->
|
||||
when (targetScreen) {
|
||||
Screen.Main -> if (global.launched) MainScreen()
|
||||
Screen.Splash -> SplashScreen()
|
||||
Screen.SignIn -> SignInScreen()
|
||||
Screen.Settings -> SettingsScreen()
|
||||
Screen.Temp -> TempScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,12 +71,16 @@ class MainActivity : ComponentActivity() {
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
when (global.currentScreenElement) {
|
||||
Animate(global.currentScreenElement) { targetScreen ->
|
||||
when (targetScreen) {
|
||||
ScreenElement.Timetable -> TimetableScreen()
|
||||
ScreenElement.Marks -> MarksScreen()
|
||||
ScreenElement.NewMarks -> NewMarksScreen()
|
||||
ScreenElement.FinalMarks -> FinalMarksScreen()
|
||||
ScreenElement.Menu -> MenuScreen()
|
||||
ScreenElement.About -> AboutScreen()
|
||||
ScreenElement.Settings -> SettingsScreen()
|
||||
ScreenElement.SignOut -> SignOutScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +92,3 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.border
|
||||
@@ -20,29 +21,27 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AutoAwesome
|
||||
import androidx.compose.material.icons.filled.WorkspacePremium
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.odweta.solon.ui.theme.PastelGreen
|
||||
import com.odweta.solon.ui.theme.PastelGreenTransparent
|
||||
import com.odweta.solon.ui.theme.PastelOrange
|
||||
@@ -53,10 +52,10 @@ import com.odweta.solon.ui.theme.PastelRed
|
||||
import com.odweta.solon.ui.theme.PastelRedTransparent
|
||||
import com.odweta.solon.ui.theme.PastelYellow
|
||||
import com.odweta.solon.ui.theme.PastelYellowTransparent
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
var showNewMarksDialog by mutableStateOf(false)
|
||||
var showFinalMarksDialog by mutableStateOf(false)
|
||||
|
||||
enum class MarksDepthLevel {
|
||||
All,
|
||||
@@ -66,46 +65,69 @@ enum class MarksDepthLevel {
|
||||
|
||||
@Composable
|
||||
fun MarksScreen() {
|
||||
when (global.marksDepthLevel) {
|
||||
Animate(global.marksDepthLevel) { targetDepthLevel ->
|
||||
when (targetDepthLevel) {
|
||||
MarksDepthLevel.All -> MarksAll()
|
||||
MarksDepthLevel.Subject -> MarksSubject()
|
||||
MarksDepthLevel.Detail -> MarksDetail()
|
||||
}
|
||||
if (showNewMarksDialog) {
|
||||
val scrollStateNewMarks = rememberScrollState()
|
||||
}
|
||||
}
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = { showNewMarksDialog = false },
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = true,
|
||||
dismissOnClickOutside = true
|
||||
)
|
||||
) {
|
||||
@Composable
|
||||
fun NewMarksScreen() {
|
||||
BackHandler { global.currentScreenElement = ScreenElement.Marks }
|
||||
|
||||
val ss = rememberScrollState()
|
||||
Column(
|
||||
Modifier.verticalScroll(scrollStateNewMarks).padding(vertical = 10.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
backButtonFactory.get(BackButtonType.MarksAll)()
|
||||
|
||||
Box(
|
||||
modifier = Modifier.weight(1f),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = Color.Black,
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.border(
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner),
|
||||
color = PastelYellow,
|
||||
width = global.dimens.borderWidth
|
||||
)
|
||||
.height(70.dp)
|
||||
.padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
"Nové známky",
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 30.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.background(
|
||||
color = Color.Black,
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
).border(
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner),
|
||||
color = PastelYellow,
|
||||
width = global.dimens.borderWidth
|
||||
).padding(10.dp)
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(ss)
|
||||
) {
|
||||
for ((k, v) in global.newMarks) {
|
||||
if (v.size > 0) {
|
||||
if (v.isNotEmpty()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.padding(horizontal = 10.dp)
|
||||
.padding(bottom = 10.dp)
|
||||
.border(
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner),
|
||||
color = PastelYellow,
|
||||
@@ -133,7 +155,8 @@ fun MarksScreen() {
|
||||
Text(
|
||||
i.theme,
|
||||
color = Color.White,
|
||||
modifier = Modifier.padding(end = 5.dp, top = 5.dp)
|
||||
modifier = Modifier
|
||||
.padding(end = 5.dp, top = 5.dp)
|
||||
.weight(1f)
|
||||
)
|
||||
Column(
|
||||
@@ -146,7 +169,8 @@ fun MarksScreen() {
|
||||
.background(
|
||||
color = getAvgColor(i.grade, true),
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
).padding(5.dp),
|
||||
)
|
||||
.padding(5.dp),
|
||||
style = TextStyle(
|
||||
shadow = Shadow(
|
||||
color = Color.Black,
|
||||
@@ -171,6 +195,67 @@ fun MarksScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FinalMarksScreen() {
|
||||
BackHandler { global.currentScreenElement = ScreenElement.Marks }
|
||||
|
||||
val ss = rememberScrollState()
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
backButtonFactory.get(BackButtonType.MarksAll)()
|
||||
|
||||
Box(
|
||||
modifier = Modifier.weight(1f),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = Color.Black,
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.border(
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner),
|
||||
color = PastelYellow,
|
||||
width = global.dimens.borderWidth
|
||||
)
|
||||
.height(70.dp)
|
||||
.padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
"Uzavřené známky",
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 30.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(ss)
|
||||
) {
|
||||
Text(Util.fileToString(global.filePaths.finalMarks), color = Color.White)
|
||||
for ((subject, mark) in global.finalMarks) {
|
||||
// LaunchedEffect(Unit) {
|
||||
// Log.d("debug", networkRequestFactory.perform(NetworkRequestType.Apologies).body.string?.toString())
|
||||
//}
|
||||
Row {
|
||||
Text(subject, color = Color.White)
|
||||
Text(mark.toString(), color = Color.White, modifier = Modifier.padding(10.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAvgColor(avg: String, isBorder: Boolean = false): Color {
|
||||
@@ -221,7 +306,7 @@ fun MarksAll() {
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
for ((subject, markList) in global.marks) {
|
||||
val amoledModifier = if (settings.useAmoledMode.checked.value) {
|
||||
val amoledModifier = if (settings.useAmoledMode.checked.value == true) {
|
||||
Modifier
|
||||
.width(150.dp)
|
||||
.fillMaxHeight()
|
||||
@@ -243,7 +328,7 @@ fun MarksAll() {
|
||||
)
|
||||
}
|
||||
|
||||
val amoledModifierBackground = if (settings.useAmoledMode.checked.value) {
|
||||
val amoledModifierBackground = if (settings.useAmoledMode.checked.value == true) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(70.dp)
|
||||
@@ -256,8 +341,10 @@ fun MarksAll() {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.clickable {
|
||||
if (markList[0].grade != "0") {
|
||||
global.marksDepthLevel = MarksDepthLevel.Subject
|
||||
global.subjectToShow = subject
|
||||
} // else { isInDemo == true }
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
@@ -268,12 +355,14 @@ fun MarksAll() {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.clickable {
|
||||
if (markList[0].grade != "0") {
|
||||
global.marksDepthLevel = MarksDepthLevel.Subject
|
||||
global.subjectToShow = subject
|
||||
} // else { isInDemo == true }
|
||||
}
|
||||
}
|
||||
|
||||
val amoledSpacerModifier = if (settings.useAmoledMode.checked.value) {
|
||||
val amoledSpacerModifier = if (settings.useAmoledMode.checked.value == true) {
|
||||
Modifier
|
||||
.width(10.dp)
|
||||
.fillMaxHeight()
|
||||
@@ -360,22 +449,46 @@ fun MarksAll() {
|
||||
}
|
||||
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
) {
|
||||
FloatingActionButton(
|
||||
onClick = { showNewMarksDialog = true },
|
||||
onClick = { global.currentScreenElement = ScreenElement.NewMarks },
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(10.dp)
|
||||
.background(Color.Black, shape = RoundedCornerShape(global.dimens.roundedCorner)),
|
||||
containerColor = Color.Black,
|
||||
contentColor = PastelYellow
|
||||
.background(
|
||||
Color.Black,
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
),
|
||||
contentColor = PastelYellow,
|
||||
containerColor = Color.Black
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(start = 4.dp, top = 4.dp),
|
||||
imageVector = Icons.Default.AutoAwesome,
|
||||
contentDescription = "New marks in subject",
|
||||
tint = PastelYellow // You can customize the icon color
|
||||
)
|
||||
}
|
||||
|
||||
/*FloatingActionButton(
|
||||
onClick = { global.currentScreenElement = ScreenElement.FinalMarks },
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.background(
|
||||
Color.Black,
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
),
|
||||
contentColor = PastelYellow,
|
||||
containerColor = Color.Black
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.WorkspacePremium,
|
||||
contentDescription = "Final marks",
|
||||
tint = PastelYellow // You can customize the icon color
|
||||
)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +526,7 @@ fun MarksSubject() {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.background(
|
||||
color = if (settings.useAmoledMode.checked.value) {
|
||||
color = if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
getAvgColor(
|
||||
@@ -454,7 +567,7 @@ fun MarksSubject() {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.background(
|
||||
color = if (settings.useAmoledMode.checked.value) {
|
||||
color = if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
PastelYellowTransparent
|
||||
@@ -481,7 +594,7 @@ fun MarksSubject() {
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 5.dp)
|
||||
.background(
|
||||
color = if (settings.useAmoledMode.checked.value) {
|
||||
color = if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
getAvgColor(mark.grade)
|
||||
@@ -561,7 +674,7 @@ fun MarksDetail() {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.background(
|
||||
if (settings.useAmoledMode.checked.value) {
|
||||
if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
getAvgColor(
|
||||
@@ -597,7 +710,7 @@ fun MarksDetail() {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner)
|
||||
)
|
||||
.background(
|
||||
if (settings.useAmoledMode.checked.value) {
|
||||
if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
PastelYellowTransparent
|
||||
@@ -614,7 +727,7 @@ fun MarksDetail() {
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 5.dp)
|
||||
.background(
|
||||
color = if (settings.useAmoledMode.checked.value) {
|
||||
color = if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
PastelYellowTransparent
|
||||
@@ -657,7 +770,7 @@ fun MarksDetail() {
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 5.dp)
|
||||
.background(
|
||||
color = if (settings.useAmoledMode.checked.value) {
|
||||
color = if (settings.useAmoledMode.checked.value == true) {
|
||||
Color.Black
|
||||
} else {
|
||||
PastelYellowTransparent
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
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
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ExitToApp
|
||||
import androidx.compose.material.icons.filled.AccountBox
|
||||
import androidx.compose.material.icons.filled.ArrowBackIosNew
|
||||
import androidx.compose.material.icons.filled.ExitToApp
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -19,8 +35,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.odweta.solon.ui.theme.PastelYellow
|
||||
@@ -28,7 +47,7 @@ import kotlin.system.exitProcess
|
||||
|
||||
@Composable
|
||||
fun MenuScreen() {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
val menuSeparatorDimen = 40.dp
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -36,87 +55,50 @@ fun MenuScreen() {
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
MenuButton({ showDialog = true }, "Odhlásit se")
|
||||
|
||||
Spacer(modifier = Modifier.height(80.dp))
|
||||
|
||||
MenuButton({ global.currentScreen = Screen.SignIn }, "Změnit přihlášení")
|
||||
|
||||
Spacer(modifier = Modifier.height(80.dp))
|
||||
|
||||
MenuButton({ global.currentScreenElement = ScreenElement.About }, "O aplikaci")
|
||||
|
||||
Spacer(modifier = Modifier.height(80.dp))
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
Row {
|
||||
MenuButton(
|
||||
{ global.currentScreenElement = ScreenElement.SignOut },
|
||||
Icons.AutoMirrored.Filled.ExitToApp,
|
||||
"Odhlásit se"
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(menuSeparatorDimen))
|
||||
|
||||
MenuButton(
|
||||
{
|
||||
global.noSignInBackHandle = false
|
||||
global.currentScreen = Screen.SignIn
|
||||
},
|
||||
Icons.Default.AccountBox,
|
||||
"Změnit přihlášení"
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(menuSeparatorDimen))
|
||||
|
||||
Row {
|
||||
MenuButton(
|
||||
{ global.currentScreenElement = ScreenElement.About },
|
||||
Icons.Default.Info,
|
||||
"O aplikaci"
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(menuSeparatorDimen))
|
||||
|
||||
MenuButton(
|
||||
{ global.currentScreen = Screen.Settings },
|
||||
Icons.Default.Settings,
|
||||
"Nastavení"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MenuButton(onClick: () -> Unit, text: String) {
|
||||
fun MenuButton(onClick: () -> Unit, icon: ImageVector, altText: String) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
@@ -129,12 +111,21 @@ fun MenuButton(onClick: () -> Unit, text: String) {
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner),
|
||||
color = PastelYellow
|
||||
)
|
||||
.width(230.dp)
|
||||
.height(80.dp)
|
||||
.size(80.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = altText,
|
||||
modifier = Modifier.size(40.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 23.sp
|
||||
text = altText,
|
||||
modifier = Modifier.padding(5.dp).width(80.dp),
|
||||
color = Color.White,
|
||||
fontSize = 17.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.odweta.solon
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun MessagesScreen() {
|
||||
Column {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ fun NavbarScreen() {
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
var spacerModifier = 30.dp
|
||||
if (settings.showRefreshButton.checked.value) {
|
||||
if (settings.showRefreshButton.checked.value == true) {
|
||||
spacerModifier = 15.dp
|
||||
Button(
|
||||
onClick = {
|
||||
|
||||
@@ -11,21 +11,28 @@ import org.json.JSONObject
|
||||
|
||||
fun setupAPI() {
|
||||
global.token = "NETWORK_UNREACHABLE"
|
||||
val token = networkRequestFactory.perform(NetworkRequestType.Token).getString("token")
|
||||
if (""""error": """ !in token) {
|
||||
|
||||
val tokenJSON = networkRequestFactory.perform(NetworkRequestType.Token)
|
||||
if (tokenJSON.has("token")) {
|
||||
val token = tokenJSON.getString("token")
|
||||
if ("\"error\": " !in token) {
|
||||
global.token = token
|
||||
}
|
||||
if (global.token == "NETWORK_UNREACHABLE" || """"error": """ in global.token) {
|
||||
}
|
||||
|
||||
if (global.token == "NETWORK_UNREACHABLE" || "\"error\":" in global.token) {
|
||||
global.userData = JSONObject()
|
||||
global.studentId = ""
|
||||
return
|
||||
}
|
||||
|
||||
global.userData = networkRequestFactory.perform(NetworkRequestType.UserData)
|
||||
Util.saveStringToFile(global.filePaths.userData, global.userData.toString())
|
||||
global.studentClass = Parsing.getStudentClass()
|
||||
global.studentId = Parsing.getStudentId()
|
||||
|
||||
global.apiEndPoints.marks = "${global.apiEndPoints.apiBase}/v1/students/${global.studentId}/marks/list"
|
||||
global.apiEndPoints.finalMarks = "${global.apiEndPoints.apiBase}/v1/students/${global.studentId}/marks/final"
|
||||
|
||||
global.isAPISetUp = true
|
||||
}
|
||||
@@ -34,6 +41,7 @@ enum class NetworkRequestType {
|
||||
Token,
|
||||
Timetable,
|
||||
Marks,
|
||||
FinalMarks,
|
||||
UserData
|
||||
}
|
||||
|
||||
@@ -65,6 +73,7 @@ object NetworkRequestFactory: NetworkRequestInterface {
|
||||
}
|
||||
NetworkRequestType.Timetable -> return getAPI(global.apiEndPoints.timetable)
|
||||
NetworkRequestType.Marks -> return getAPI(global.apiEndPoints.marks)
|
||||
NetworkRequestType.FinalMarks -> return getAPI(global.apiEndPoints.finalMarks)
|
||||
NetworkRequestType.UserData -> return getAPI(global.apiEndPoints.userData)
|
||||
}
|
||||
}
|
||||
@@ -76,7 +85,13 @@ object NetworkRequestFactory: NetworkRequestInterface {
|
||||
.get()
|
||||
.build()
|
||||
|
||||
return client.newCall(request).execute()
|
||||
return try {
|
||||
client.newCall(request).execute()
|
||||
} catch (e: Exception) {
|
||||
Response.Builder()
|
||||
.code(500)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun post(url: String, headers: Headers, body: FormBody): Response {
|
||||
@@ -86,11 +101,16 @@ object NetworkRequestFactory: NetworkRequestInterface {
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
return client.newCall(request).execute()
|
||||
return try {
|
||||
client.newCall(request).execute()
|
||||
} catch (e: Exception) {
|
||||
Response.Builder()
|
||||
.code(500)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAPI(url: String): JSONObject {
|
||||
Log.d("debug TOKEN", global.token)
|
||||
val response = get(url, headersOf(
|
||||
"Content-Type", "application/x-www-form-urlencoded",
|
||||
"Authorization", "Bearer ${global.token}"
|
||||
|
||||
@@ -153,9 +153,12 @@ class Parsing {
|
||||
subject.name = subj.getJSONObject("subject").getString("abbrev")
|
||||
subject.place =
|
||||
subj.getJSONArray("rooms").getJSONObject(0).getString("abbrev")
|
||||
subject.teacher = if (subj.getJSONArray("teachers").length() > 0) {
|
||||
subj.getJSONArray("teachers").getJSONObject(0)
|
||||
.getString("fullName") // TODO: keep in mind that fullName might not be a valid field (name and surname are!)
|
||||
subject.teacher =
|
||||
if (subj.getJSONArray("teachers").length() > 0) {
|
||||
subj.getJSONArray("teachers")
|
||||
.getJSONObject(0).getString("name") + " " +
|
||||
subj.getJSONArray("teachers")
|
||||
.getJSONObject(0).getString("surname")
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -196,15 +199,13 @@ class Parsing {
|
||||
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
newSubject.number =
|
||||
detailHours.getJSONObject(i).get("id").toString()
|
||||
detailHours.getJSONObject(i).getString("id")
|
||||
newSubject.start =
|
||||
detailHours.getJSONObject(i).get("timeFrom")
|
||||
.toString()
|
||||
detailHours.getJSONObject(i).getString("timeFrom")
|
||||
newSubject.start =
|
||||
newSubject.start.slice(0..newSubject.start.length - 4)
|
||||
newSubject.end =
|
||||
detailHours.getJSONObject(i).get("timeto")
|
||||
.toString()
|
||||
detailHours.getJSONObject(i).getString("timeto")
|
||||
newSubject.end =
|
||||
newSubject.end.slice(0..newSubject.end.length - 4)
|
||||
subjects.add(newSubject)
|
||||
@@ -214,7 +215,7 @@ class Parsing {
|
||||
}
|
||||
|
||||
val sCopy = subjects.toList()
|
||||
for (s in 0 until sCopy.size) {
|
||||
for (s in sCopy.indices) {
|
||||
if ("-" in subjects[s].number) {
|
||||
subjects[s].number = "1"
|
||||
subjects[s].start = "08:00"
|
||||
@@ -223,26 +224,6 @@ class Parsing {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// rust pseudocode
|
||||
|
||||
let mut res: Vec<i32> = vec![];
|
||||
for i in 0..v.len()-1 {
|
||||
if v[i] == v[i+1]-1 {
|
||||
res.push(v[i]);
|
||||
} else {
|
||||
res.push(v[i]);
|
||||
|
||||
while res[res.len()-1]+1 != v[i+1] {
|
||||
res.push(res[res.len()-1]+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.push(v[v.len()-1]);
|
||||
|
||||
res
|
||||
*/
|
||||
|
||||
if (subjects.size > 1) {
|
||||
for (i in 0..<subjects.size - 1) {
|
||||
if (subjects[i].number == ((subjects[i + 1].number.toInt() - 1).toString())) {
|
||||
@@ -322,7 +303,6 @@ class Parsing {
|
||||
timetable.add(day)
|
||||
}
|
||||
|
||||
Log.d("debug", "timetable: $timetable")
|
||||
return timetable
|
||||
}
|
||||
|
||||
@@ -426,6 +406,14 @@ class Parsing {
|
||||
return marks
|
||||
}
|
||||
|
||||
fun parseFinalMarks(json: JSONObject): Map<String, Int> {
|
||||
return mapOf(
|
||||
"AJ" to 1,
|
||||
"B" to 5,
|
||||
"Pr" to 1
|
||||
)
|
||||
}
|
||||
|
||||
fun getStudentClass(): String {
|
||||
return if (!userIsParent()) {
|
||||
global.userData
|
||||
|
||||
@@ -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,18 +17,37 @@ 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.runtime.currentComposer
|
||||
import androidx.compose.runtime.currentRecomposeScope
|
||||
import androidx.compose.runtime.getValue
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.liveData
|
||||
import com.odweta.solon.ui.theme.PastelYellow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen() {
|
||||
// handle the back gesture (back swipe form side or back button on bottom android navbar)
|
||||
BackHandler { global.currentScreenElement = ScreenElement.Menu }
|
||||
BackHandler {
|
||||
global.currentScreenElement = ScreenElement.Menu
|
||||
global.currentScreen = Screen.Main
|
||||
}
|
||||
|
||||
// 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?
|
||||
if (!global.demoMode) {
|
||||
settings = Util.fileToString(global.filePaths.settings).toSettingsData()
|
||||
}
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
@@ -38,22 +58,22 @@ 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))
|
||||
|
||||
if (!global.demoMode) {
|
||||
SettingsTableRow(settings.showRefreshButton)
|
||||
}
|
||||
|
||||
SettingsTableRow(settings.useAnimations)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsTableRow(setting: Setting) {
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
@@ -71,15 +91,10 @@ fun SettingsTableRow(setting: Setting) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Switch(
|
||||
checked = setting.checked.value,
|
||||
checked = setting.checked.value == true,
|
||||
onCheckedChange = {
|
||||
setting.checked.value = it
|
||||
when (setting.id) {
|
||||
SettingsId.Svoboda -> settings.useSvobodaMode = setting
|
||||
SettingsId.Amoled -> settings.useAmoledMode = setting
|
||||
SettingsId.RefreshButton -> settings.showRefreshButton = setting
|
||||
else -> {}
|
||||
}
|
||||
showTempScreen(Screen.Settings)
|
||||
Util.saveStringToFile(global.filePaths.settings, settings.toString())
|
||||
},
|
||||
colors = SwitchColors(
|
||||
@@ -113,6 +128,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 +142,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
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.odweta.solon
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.Image
|
||||
@@ -40,6 +41,7 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private var buttonText by mutableStateOf("Přihlásit se")
|
||||
|
||||
@@ -87,6 +89,10 @@ fun signInButtonCallback(ctx: Context, usernameText: String, passwordText: Strin
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
if (global.username == "demo" && global.password == "Demo1234") {
|
||||
// start the demo
|
||||
launchDemo(ctx)
|
||||
} else {
|
||||
val valid = Account.isValid(acc)
|
||||
|
||||
checked = true
|
||||
@@ -96,6 +102,7 @@ fun signInButtonCallback(ctx: Context, usernameText: String, passwordText: Strin
|
||||
global.currentScreen = Screen.Splash
|
||||
launchStart(ctx)
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
ctx,
|
||||
"Neplatné přihlašovací údaje nebo nejste připojeni k internetu, zkuste to znovu.",
|
||||
@@ -104,6 +111,32 @@ fun signInButtonCallback(ctx: Context, usernameText: String, passwordText: Strin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun launchDemo(ctx: Context) {
|
||||
global.demoMode = true
|
||||
|
||||
settings = Util.fileToString(global.filePaths.settings).toSettingsData()
|
||||
settings.showRefreshButton.checked.value = false
|
||||
|
||||
val d = Day()
|
||||
d.name = "demoday"
|
||||
d.date = "1. 1."
|
||||
d.subjects = listOf(Subject.empty())
|
||||
global.timetable = listOf(d)
|
||||
|
||||
global.marks = mutableMapOf("demomarks" to mutableListOf(Mark().none()))
|
||||
|
||||
global.newMarks = global.marks
|
||||
|
||||
global.status = "demo"
|
||||
|
||||
global.launched = true
|
||||
global.refreshed = true
|
||||
global.currentScreen = Screen.Main
|
||||
global.currentScreenElement = ScreenElement.Timetable
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SignInScreen() {
|
||||
@@ -115,7 +148,6 @@ fun SignInScreen() {
|
||||
var usernameText by remember { mutableStateOf("") }
|
||||
var passwordText by remember { mutableStateOf("") }
|
||||
val ctx = LocalContext.current
|
||||
val coscope = rememberCoroutineScope()
|
||||
var isPasswordVisible by remember { mutableStateOf(false) } // State for visibility toggle
|
||||
val visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation()
|
||||
|
||||
|
||||
@@ -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).padding(horizontal = 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, fontSize = 17.sp)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.odweta.solon
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
// temporary screens to artificially trigger recompositions basically at the root level
|
||||
|
||||
@Composable
|
||||
fun TempScreen() {
|
||||
when (global.tempScreen) {
|
||||
Screen.Main -> MainScreen()
|
||||
Screen.Splash -> SplashScreen()
|
||||
Screen.SignIn -> SignInScreen()
|
||||
Screen.Settings -> SettingsScreen()
|
||||
Screen.Temp -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun showTempScreen(screen: Screen) {
|
||||
global.returnToTempScreen = true
|
||||
global.tempScreen = screen
|
||||
global.currentScreen = Screen.Temp
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -49,6 +50,7 @@ import androidx.compose.ui.focus.focusModifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -64,8 +66,11 @@ import com.odweta.solon.ui.theme.PastelRed
|
||||
import com.odweta.solon.ui.theme.PastelRedTransparent
|
||||
import com.odweta.solon.ui.theme.PastelYellow
|
||||
import com.odweta.solon.ui.theme.PastelYellowTransparent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
@@ -73,16 +78,65 @@ import java.time.format.DateTimeFormatter
|
||||
|
||||
var showDetailDialog by mutableStateOf(false)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun TimetableScreen() {
|
||||
val pagerState = rememberPagerState(pageCount = { if (global.timetable.isNotEmpty()) global.timetable.size else 6 }) // FIXME
|
||||
Column {
|
||||
//QuickDays(pagerState)
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.BottomCenter
|
||||
) {
|
||||
DayPager()
|
||||
DayPager(pagerState)
|
||||
if (showDetailDialog) DetailDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun QuickDays(pagerState: PagerState) {
|
||||
@Composable
|
||||
fun QuickDay(day: Day, modifier: Modifier, idx: Int) {
|
||||
val scope = rememberCoroutineScope()
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = modifier.padding(5.dp).background(
|
||||
shape = RoundedCornerShape(global.dimens.roundedCorner),
|
||||
color = PastelYellowTransparent
|
||||
).clickable(onClick = {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.Main) {
|
||||
pagerState.animateScrollToPage(idx)
|
||||
}
|
||||
}
|
||||
})
|
||||
) {
|
||||
Text(
|
||||
text = "${day.name[0]}${day.name[1]}",
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(Modifier.height(5.dp))
|
||||
Text(
|
||||
text = day.date,
|
||||
color = Color.White,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(bottom = 15.dp).padding(horizontal = 5.dp)
|
||||
) {
|
||||
for ((idx, day) in global.timetable.withIndex()) {
|
||||
QuickDay(day, Modifier.weight(1f), idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -226,13 +280,28 @@ fun DetailDialog() {
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun DayPager() {
|
||||
val pagerState = rememberPagerState(pageCount = { if (global.timetable.isNotEmpty()) global.timetable.size else 6 }) // FIXME
|
||||
fun DayPager(pagerState: PagerState) {
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { pageIndex ->
|
||||
if (global.timetable.isNotEmpty()) {
|
||||
DayScreen(global.timetable[pageIndex])
|
||||
} else {
|
||||
NoDayScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NoDayScreen() {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
"Zatím žádný rozvrh.",
|
||||
color = Color.White,
|
||||
fontSize = 30.sp,
|
||||
fontStyle = FontStyle.Italic
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +326,7 @@ private fun getSubjectColor(s: Subject, transparent: Boolean): Color {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isTimeBetweenT(givenTime: LocalTime, startTime: LocalTime, endTime: LocalTime): Boolean {
|
||||
// Check if the given time is between startTime and endTime
|
||||
return givenTime.isAfter(startTime) && givenTime.isBefore(endTime)
|
||||
@@ -293,7 +363,7 @@ fun subjectModifier(d: Day, s: Subject, lectureNumber: Int): Modifier {
|
||||
global.currentLectureNumber = lectureNumber
|
||||
}
|
||||
|
||||
val m = if (settings.useAmoledMode.checked.value) {
|
||||
val m = if (settings.useAmoledMode.checked.value == true) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(70.dp)
|
||||
@@ -356,8 +426,10 @@ private fun DayScreen(day: Day) {
|
||||
) {
|
||||
for ((subjectIndex, subject) in day.subjects.withIndex()) {
|
||||
val lectureId = Util.getLectureId(day.date, subjectIndex)
|
||||
global.lectureNotes[lectureId]?.number = subject.number.toInt()
|
||||
global.lectureNotes[lectureId]?.lectureName = subject.name
|
||||
|
||||
val amoledModifier = if (settings.useAmoledMode.checked.value) {
|
||||
val amoledModifier = if (settings.useAmoledMode.checked.value == true) {
|
||||
Modifier
|
||||
.width(50.dp)
|
||||
.fillMaxHeight()
|
||||
@@ -438,8 +510,9 @@ private fun DayScreen(day: Day) {
|
||||
|
||||
Text(
|
||||
text = " supl ",
|
||||
color = Color.Black,
|
||||
modifier = Modifier.background(color = Color.White)
|
||||
color = Color.White,
|
||||
fontStyle = FontStyle.Italic,
|
||||
modifier = Modifier.background(color = PastelRedTransparent)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -482,10 +555,9 @@ private fun DayScreen(day: Day) {
|
||||
.padding(vertical = 5.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (settings.useSvobodaMode.checked.value) {
|
||||
if (settings.useSvobodaMode.checked.value == true) {
|
||||
if (
|
||||
subject.teacher == "Jindřich Svoboda" &&
|
||||
global.studentClass == "5. G"
|
||||
subject.teacher == "Jindřich Svoboda"
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.spock),
|
||||
@@ -531,10 +603,10 @@ private fun DayScreen(day: Day) {
|
||||
Box(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(99f)
|
||||
.clickable {
|
||||
/*.clickable {
|
||||
global.selectedLecture = lectureId
|
||||
showDetailDialog = true
|
||||
})
|
||||
}*/)
|
||||
|
||||
if (
|
||||
(global.lectureNotes[global.selectedLecture] != null &&
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
package com.odweta.solon
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.widget.ImageView
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Upload
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -12,16 +23,22 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONObject
|
||||
|
||||
private var showLoadingIcon by mutableStateOf(false)
|
||||
|
||||
@Composable
|
||||
fun ToolbarScreen() {
|
||||
// make the font size adaptive relative to the length of the user's name
|
||||
@@ -43,12 +60,28 @@ fun ToolbarScreen() {
|
||||
toolbarTextSizeAdaptive = 10.sp
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.padding(10.dp)) {
|
||||
Animate(global.status) { targetText ->
|
||||
Row(modifier = Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
|
||||
Text(
|
||||
text = global.status,
|
||||
text = targetText,
|
||||
fontSize = toolbarTextSizeAdaptive,
|
||||
color = Color.White
|
||||
)
|
||||
|
||||
if (settings.useAnimations.checked.value == true) {
|
||||
AnimatedVisibility(showLoadingIcon) {
|
||||
AndroidView(
|
||||
modifier = Modifier.size(50.dp).padding(start = 10.dp),
|
||||
factory = { context: Context -> ImageView(context) },
|
||||
update = { imageView ->
|
||||
Glide.with(imageView.context)
|
||||
.load(R.drawable.loading)
|
||||
.into(imageView)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,20 +113,14 @@ object StatusFactory: StatusFactoryInterface {
|
||||
StatusType.Loading -> {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
global.refreshed = false
|
||||
val anim = listOf(
|
||||
"Načítání nových dat",
|
||||
"Načítání nových dat.",
|
||||
"Načítání nových dat..",
|
||||
"Načítání nových dat...",
|
||||
"Načítání nových dat..",
|
||||
"Načítání nových dat."
|
||||
)
|
||||
var i = 0
|
||||
showStatus("Načítání nových dat")
|
||||
delay(100L)
|
||||
showLoadingIcon = true
|
||||
|
||||
while (!global.refreshed) {
|
||||
if (i == anim.size) i = 0
|
||||
showStatus(anim[i++])
|
||||
delay(300)
|
||||
showStatus("Načítání nových dat")
|
||||
}
|
||||
showLoadingIcon = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cp app/build/outputs/apk/release/com.odweta.solon_$1.apk ../release/
|
||||
Reference in New Issue
Block a user