43 Commits
Author SHA1 Message Date
Odweta bda937095c made new release 2.2 (205) 2024-12-15 00:20:17 +01:00
Odweta 67392ccb3d release 2.1 (pull) 2024-12-15 00:09:40 +01:00
Odweta a163d6b285 you can now add notes to every lecture and they will automatically be saved locally 2024-12-15 00:05:44 +01:00
Odweta e67f87864c WIP lecture notes 2024-12-14 20:21:49 +01:00
Odweta 8e9693bfe6 fixed colors in MarksSubject and tagged new marks 2024-12-14 18:44:21 +01:00
Odweta c6646e554f made new release 2.1 (204) 2024-12-08 09:35:46 +01:00
Odweta 98b6b72e81 add generic release app name 2024-12-03 19:19:20 +01:00
Odweta c6a5c50a7d build and sign release 2.1 2024-12-03 16:28:14 +01:00
Odweta 09bddaac3c made new logo 2024-12-02 13:48:35 +01:00
Odweta bb39f10a0b made new logo 2024-12-02 13:48:27 +01:00
Odweta 7e44cb585b sign release again 2024-12-02 11:06:11 +01:00
Odweta 91ba977a86 made release 2.1.1 2024-12-01 15:21:00 +01:00
Odweta 5d9b70f3a1 moved the refresh button to the navbar 2024-12-01 15:17:55 +01:00
Odweta 75a766ca7d signed new release 2.1 (204) 2024-12-01 08:41:55 +01:00
Odweta 23607f93b3 hotfix debug residue 2024-11-30 23:03:12 +01:00
Odweta 887c7d58b5 made new release 2.1 2024-11-30 12:33:08 +01:00
Odweta 4d96707a51 fixed settings file read error 2024-11-30 12:15:36 +01:00
Odweta 5a98379d3f made the AMOLED mode work 2024-11-30 12:06:51 +01:00
Odweta 80e5581f45 made the Svoboda mode work and improved the toolbar ux 2024-11-30 11:38:50 +01:00
Odweta 5200e3ba07 improved menu layout 2024-11-30 11:23:04 +01:00
Odweta f67e28b8f6 made the showRefreshButton setting work 2024-11-30 11:15:08 +01:00
Odweta 597971288e improved the displaying of averages with marks 2024-11-30 11:10:23 +01:00
Odweta 0a4ea395fd added a settings screen skeleton 2024-11-30 09:52:32 +01:00
Odweta 509f7f33a7 added a mark to substituted lectures 2024-11-29 18:41:48 +01:00
Odweta 87470effcd improved the About screen and added version number 2024-11-29 18:33:36 +01:00
Odweta 079872dae2 locked the screen orientation in portrait mode 2024-11-29 10:25:28 +01:00
Odweta 26317052cc added prepending free subjects to timetable when the day does not start with subject number 1 2024-11-26 18:52:03 +01:00
Odweta ed981f89de added a refresh button to the timetable 2024-11-26 18:04:05 +01:00
Odweta fd788195c5 added icon and screenshots 2024-11-20 13:10:18 +01:00
Odweta ed1f67f3e6 signed the apk 2024-11-20 11:20:50 +01:00
Odweta 980e51e8f1 removed dependency info block and made new release 2.0.3 2024-11-19 19:09:21 +01:00
Odweta ebfc3c1c23 still trying to fix it 2024-11-09 10:04:50 +01:00
Odweta 07716e1ae6 fix build error (perhaps) again 2024-11-09 09:57:19 +01:00
Odweta 1049401429 fix build error (perhaps) 2024-11-09 09:50:58 +01:00
Odweta 83a825f4b7 naming output files based on versionName 2024-11-08 23:48:38 +01:00
Odweta 66dc2338fb added fastlane folders 2024-11-08 23:34:36 +01:00
Odweta 7c585709ed versioncode bump thing 2024-11-08 22:54:46 +01:00
Odweta f448420e03 update TODO.md 2024-11-08 19:07:52 +01:00
Odweta ec989d6ae0 add TODO.md 2024-11-08 19:07:18 +01:00
Odweta db91df4d22 bump version to 2.0.2 2024-11-08 19:04:10 +01:00
Odweta e020ae5f17 made the sign in layout responsive to the keyboard 2024-11-08 18:59:25 +01:00
Odweta d3197111c3 quality of life fixes
made the toolbar font size adaptively large relative to the user's name length, shrunk down the height of subjects and marks and made back gestures work properly - not close the app but show the previous screen
2024-11-08 18:09:55 +01:00
Odweta 8a4956e6c6 hotfix app crash, bump to 2.0.1 2024-10-25 16:44:05 +02:00
73 changed files with 1415 additions and 606 deletions
+10
View File
@@ -0,0 +1,10 @@
<?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>
+12
View File
@@ -0,0 +1,12 @@
<?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>
+17
View File
@@ -0,0 +1,17 @@
<?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
-2
View File
@@ -2,7 +2,5 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/help" vcs="Git" />
<mapping directory="$PROJECT_DIR$/mobile/help" vcs="Git" />
</component> </component>
</project> </project>
+1 -1
View File
@@ -1,3 +1,3 @@
![solon-logo](./solon_logo.png) ![solon-logo](./solon_logo.png)
# Zorientujte se rychleji v této odlehčené verzi Školy OnLine # Zorientujte se rychleji v této odlehčené verzi Školy OnLine
# Odkaz na stažení aplikace pro Android [zde](https://gitlab.com/Odweta/solon/-/raw/master/release/solon.apk?ref_type=heads&inline=false) (verze 2.0) # Odkaz na stažení aplikace pro Android [zde](https://gitlab.com/Odweta/solon/-/raw/master/release/com.odweta.solon_2.2.apk?ref_type=heads&inline=false) (verze 2.2)
+4
View File
@@ -0,0 +1,4 @@
- add settings
- color schemes
- modes
- new logo
@@ -0,0 +1 @@
Optimalizovana a zbavena nesmyslu verze skolniho online systemu SkolaOnLine
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

@@ -0,0 +1 @@
Odlehcena verze aplikace SkolaOnLine
+1
View File
@@ -0,0 +1 @@
Solon
@@ -0,0 +1 @@
A more optimized and debloated version of the SkolaOnLine online school system
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1 @@
A light-weight Android frontend to SkolaOnLine
@@ -0,0 +1 @@
Solon
+12
View File
@@ -62,6 +62,18 @@
<PersistentState> <PersistentState>
<option name="children"> <option name="children">
<map> <map>
<entry key="backgroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="imagePath" value="$PROJECT_DIR$/app/src/main/res/drawable/black.png" />
<entry key="scalingPercent" value="172" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundClipArt"> <entry key="foregroundClipArt">
<value> <value>
<PersistentState> <PersistentState>
+6 -3
View File
@@ -4,16 +4,19 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-10-20T07:27:51.115175643Z"> <DropdownSelection timestamp="2024-11-29T17:18:42.354545820Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/thinker/.android/avd/Motorola_Moto_e22_API_31.avd" /> <DeviceId pluginId="LocalEmulator" identifier="path=/home/thinker/.config/.android/avd/Medium_Phone_API_35.avd" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>
<DialogSelection /> <DialogSelection />
</SelectionState> </SelectionState>
<SelectionState runConfigName="DefaultPreview"> <SelectionState runConfigName="TimetablePreview">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="RBPreview">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
+10
View File
@@ -3,15 +3,19 @@
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
@@ -27,21 +31,27 @@
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true"> <inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" /> <option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool> </inspection_tool>
</profile> </profile>
</component> </component>
+1 -2
View File
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/help" vcs="Git" />
</component> </component>
</project> </project>
+24 -5
View File
@@ -12,12 +12,25 @@ android {
applicationId = "com.odweta.solon" applicationId = "com.odweta.solon"
minSdk = 28 minSdk = 28
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 205
versionName = "2.0" versionName = "2.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
applicationVariants.all {
this.outputs
.map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl }
.forEach { output ->
val apkName = "com.odweta.solon_" + this.versionName + ".apk"
output.outputFileName = apkName
}
}
buildFeatures {
compose = true
}
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = false
@@ -27,15 +40,21 @@ android {
) )
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
} }
buildFeatures {
compose = true dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
} }
} }
@@ -51,4 +70,4 @@ dependencies {
implementation(libs.okhttp) implementation(libs.okhttp)
implementation(libs.glide) implementation(libs.glide)
implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material.icons.extended)
} }
+4 -1
View File
@@ -18,9 +18,12 @@
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:screenOrientation="portrait"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.Solon"> android:theme="@style/Theme.Solon"
android:windowSoftInputMode="adjustResize"
tools:ignore="DiscouragedApi">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@@ -1,6 +1,8 @@
package com.odweta.solon package com.odweta.solon
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -8,6 +10,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -24,6 +27,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import com.odweta.solon.ui.theme.PastelYellow import com.odweta.solon.ui.theme.PastelYellow
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -32,10 +36,14 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import java.time.format.TextStyle
@Composable @Composable
fun AboutScreen(modifier: Modifier = Modifier) { fun AboutScreen(modifier: Modifier = Modifier) {
// handle the back gesture (back swipe form side or back button on bottom android navbar)
BackHandler {
isShowing.value = IsShowing.Menu
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
@@ -45,6 +53,7 @@ fun AboutScreen(modifier: Modifier = Modifier) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = 5.dp)
) { ) {
Button( Button(
onClick = { onClick = {
@@ -60,13 +69,13 @@ fun AboutScreen(modifier: Modifier = Modifier) {
shape = RoundedCornerShape(roundedCornerDimen), shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow color = PastelYellow
) )
.width(100.dp) .width(70.dp)
.height(100.dp) .height(70.dp)
) { ) {
Icon( Icon(
imageVector = Icons.Default.ArrowBackIosNew, // Replace with your desired icon imageVector = Icons.Default.ArrowBackIosNew,
contentDescription = "Back icon", contentDescription = "Back icon",
modifier = Modifier.size(35.dp) // Adjust size if needed modifier = Modifier.size(40.dp)
) )
} }
} }
@@ -100,30 +109,71 @@ fun AboutScreen(modifier: Modifier = Modifier) {
.padding(10.dp) .padding(10.dp)
) )
Row( HorizontalLine()
modifier = Modifier
.padding(10.dp)
) {
val uriHandler = LocalUriHandler.current
Text( AboutTableRow(
text = "Vývoj: ", "Vývoj",
textAlign = TextAlign.Start, "@Odweta",
color = Color.White, "https://www.gitlab.com/Odweta/solon"
fontSize = 30.sp )
)
Text( AboutTableRow(
text = "@Odweta", "Verze aplikace",
color = PastelYellow, Util.getAppVersion(LocalContext.current)?.versionName ?: "N/A"
fontSize = 30.sp, )
textAlign = TextAlign.End, }
textDecoration = TextDecoration.Underline, }
modifier = Modifier.clickable { }
uriHandler.openUri("https://www.gitlab.com/Odweta/solon")
} @Composable
) fun HorizontalLine(modifier: Modifier = Modifier) {
} Spacer(modifier = Modifier.height(20.dp))
Spacer(modifier = Modifier
.height(2.dp)
.background(color = PastelYellow)
.fillMaxWidth(fraction = 0.8f)
)
Spacer(modifier = Modifier.height(20.dp))
}
@Composable
fun AboutTableRow(key: String, value: String, href: String = "") {
Row(
modifier = Modifier
.padding(10.dp)
.background(color = Color.Black, shape = RoundedCornerShape(roundedCornerDimen))
.fillMaxWidth()
) {
Text(
text = key,
textAlign = TextAlign.Start,
color = Color.White,
fontSize = 20.sp
)
Spacer(modifier = Modifier.weight(1f))
if (href != "") {
val uriHandler = LocalUriHandler.current
Text(
text = value,
color = PastelYellow,
fontSize = 20.sp,
textAlign = TextAlign.End,
textDecoration = TextDecoration.Underline,
modifier = Modifier.clickable {
uriHandler.openUri(href)
}
)
} else {
Text(
text = value,
color = Color.White,
fontSize = 20.sp,
textAlign = TextAlign.End
)
} }
} }
} }
@@ -1,22 +1,18 @@
package com.odweta.solon package com.odweta.solon
import android.view.Window import android.view.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import com.odweta.solon.ui.theme.PastelYellow import com.odweta.solon.ui.theme.PastelYellow
import com.odweta.solon.ui.theme.PastelYellowTransparent
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.io.File import java.io.File
@@ -44,6 +40,8 @@ const val accountDataFilePath: String = "account_data.txt"
const val timeTableFilePath: String = "timetable.json" const val timeTableFilePath: String = "timetable.json"
const val userDataFilePath: String = "userdata.json" const val userDataFilePath: String = "userdata.json"
const val marksFilePath: String = "marks.json" const val marksFilePath: String = "marks.json"
const val settingsDataFilePath: String = "settings.txt"
const val lectureNotesFilePath: String = "lecture_notes.csv"
lateinit var fileDir: File lateinit var fileDir: File
var width: Int = 0 var width: Int = 0
var height: Int = 0 var height: Int = 0
@@ -59,7 +57,8 @@ enum class IsShowing {
Timetable, Timetable,
Marks, Marks,
Menu, Menu,
About About,
Settings
} }
enum class Screen { enum class Screen {
@@ -76,8 +75,48 @@ var isShowing: MutableState<IsShowing> = mutableStateOf(IsShowing.Timetable)
var toolbarText: MutableState<String> = mutableStateOf("") var toolbarText: MutableState<String> = mutableStateOf("")
// default settings (will get overwritten by the saved settings)
class Setting(auxId: SettingsId, auxName: String, auxChecked: Boolean) {
var id: SettingsId = auxId
var name: String = auxName
var checked: MutableState<Boolean> = mutableStateOf(auxChecked)
override fun toString() = "$id#$name#${checked.value}"
}
enum class SettingsId {
Svoboda,
Amoled,
RefreshButton
}
class SettingsData {
var useSvobodaMode: Setting = Setting(
SettingsId.Svoboda,
"Používat Svoboda mód",
false
)
var useAmoledMode: Setting = Setting(
SettingsId.Amoled,
"Používat AMOLED mód",
false
)
var showRefreshButton: Setting = Setting(
SettingsId.RefreshButton,
"Zobrazovat tlačítko obnovení dat",
true
)
override fun toString() = "$useSvobodaMode\n$useAmoledMode\n$showRefreshButton"
}
var settings : SettingsData = SettingsData()
var done: Boolean = false var done: Boolean = false
var internetAwaitListenerRunning: Boolean = false
var timeTableAvailOffline: Boolean = false var timeTableAvailOffline: Boolean = false
var marksAvailOffline: Boolean = false var marksAvailOffline: Boolean = false
@@ -99,6 +138,9 @@ var resetNav: Boolean = false
var isAPISetUp: Boolean = false var isAPISetUp: Boolean = false
var lectureNotes: HashMap<Int, MutableState<String>> = hashMapOf()
var selectedLecture: Int = 0
var selectedDay: Int = 0 var selectedDay: Int = 0
var dayEndString: String = "" var dayEndString: String = ""
@@ -1,15 +1,12 @@
package com.odweta.solon package com.odweta.solon
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.widget.Toast import android.widget.Toast
import androidx.compose.ui.graphics.Color
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.json.JSONObject import org.json.JSONObject
import kotlin.math.cos
private var week: List<Day> = mutableListOf() private var week: List<Day> = mutableListOf()
private var toastShowing: Boolean = false private var toastShowing: Boolean = false
@@ -19,6 +16,7 @@ private var toast2Shown: Boolean = false
private lateinit var coscope_: CoroutineScope private lateinit var coscope_: CoroutineScope
private fun setOnInternetAvailListener(ctx: Context, coscope: CoroutineScope) { private fun setOnInternetAvailListener(ctx: Context, coscope: CoroutineScope) {
internetAwaitListenerRunning = true
coscope.launch { coscope.launch {
while (true) { while (true) {
if (Util.internetAvail(ctx) && !internetAvail) { if (Util.internetAvail(ctx) && !internetAvail) {
@@ -37,7 +35,7 @@ private fun setOnInternetAvailListener(ctx: Context, coscope: CoroutineScope) {
} }
} }
private fun fetchNewData(ctx: Context, coscope: CoroutineScope) { fun fetchNewData(ctx: Context, coscope: CoroutineScope) {
coscope.launch { coscope.launch {
done = false done = false
@@ -46,23 +44,19 @@ private fun fetchNewData(ctx: Context, coscope: CoroutineScope) {
"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..",
"Načítání nových dat..." "Načítání nových dat...",
"Načítání nových dat..",
"Načítání nových dat."
) )
val delayTime: Long = 300 // delay in ms val delayTime: Long = 300 // delay in ms
// show a loading animation // show a loading animation
while (!done && Util.internetAvail(ctx)) { while (!done && Util.internetAvail(ctx)) {
for (text in loadingArray) { for (text in loadingArray) {
if (done) break
toolbarText.value = text toolbarText.value = text
delay(delayTime) delay(delayTime)
} }
for (text in loadingArray.reversedArray().slice(1..<loadingArray.size)) {
toolbarText.value = text
delay(delayTime)
}
delay(delayTime)
} }
// determine the role of the student // determine the role of the student
@@ -119,6 +113,7 @@ fun launchPhase0(ctx: Context, coscope: CoroutineScope) {
} }
private suspend fun launchPhase1(ctx: Context) { private suspend fun launchPhase1(ctx: Context) {
// TODO: CONVERT TO SNACKBARS
val toast: Toast = Toast.makeText( val toast: Toast = Toast.makeText(
ctx, ctx,
"Nelze načíst data, zkuste se připojit k internetu.", "Nelze načíst data, zkuste se připojit k internetu.",
@@ -144,9 +139,11 @@ private suspend fun launchPhase1(ctx: Context) {
toast2.show() toast2.show()
} }
if (!timeTableAvailOffline || internetAvail) { if (!timeTableAvailOffline || !marksAvailOffline || internetAvail) {
// set up the API (required for later uses of it) // set up the API (required for later uses of it)
Network.setupAPI() if (!isAPISetUp) {
Network.setupAPI()
}
if (internetAvail) { if (internetAvail) {
// cancel a toast, if any // cancel a toast, if any
@@ -155,23 +152,31 @@ private suspend fun launchPhase1(ctx: Context) {
} }
// get the JSON data // get the JSON data
val timeTableJSON = Network.getTimeTableJSON() var timeTableJSON = Network.getTimeTableJSON()
if (timeTableJSON == """{"error": "network"}""") {
// try to save them into a file for later use timeTableJSON = Util.fileToString(timeTableFilePath)
timeTableAvailOffline = }
Util.saveStringToFile(timeTableFilePath, timeTableJSON)
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
// fetch and parse marks // fetch and parse marks
marksJSON = Network.getMarksJSON() marksJSON = Network.getMarksJSON()
if (marksJSON == """{"error": "network"}""") {
marksJSON = Util.fileToString(marksFilePath)
}
timeTableAvailOffline =
Util.saveStringToFile(timeTableFilePath, timeTableJSON)
marksAvailOffline = marksAvailOffline =
Util.saveStringToFile(marksFilePath, marksJSON) Util.saveStringToFile(marksFilePath, marksJSON)
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
marks = Parsing.parseMarksJSON(marksJSON) marks = Parsing.parseMarksJSON(marksJSON)
// set the settings
if (Util.fileExists(settingsDataFilePath) && !Util.fileIsEmpty(settingsDataFilePath)) {
settings = Util.stringToSettingsData(Util.fileToString(settingsDataFilePath))
} // else load the default settings
// continue to launch phase 2 // continue to launch phase 2
done = true done = true
launchPhase2(ctx) launchPhase2(ctx)
@@ -270,10 +275,14 @@ private fun launchPhase2(ctx: Context) {
dayList.add(day) dayList.add(day)
} }
Util.loadLectureIds()
loaded = true loaded = true
coscope_.launch { coscope_.launch {
setOnInternetAvailListener(ctx, coscope_) if (!internetAwaitListenerRunning) {
setOnInternetAvailListener(ctx, coscope_)
}
} }
val role = if (Parsing.userIsParent()) { val role = if (Parsing.userIsParent()) {
@@ -287,8 +296,13 @@ private fun launchPhase2(ctx: Context) {
Util.fileToString( Util.fileToString(
userDataFilePath userDataFilePath
) )
).getString("fullName")}$role" ).getString("fullName")
}$role"
// set the main content view // set the main content view
screenToShow.value = Screen.Main screenToShow.value = Screen.Main
if (settings.showRefreshButton.checked.value) {
refreshButtonContentColor.value = Color.White
}
} }
@@ -15,6 +15,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.core.view.WindowCompat
import com.odweta.solon.ui.theme.SolonTheme import com.odweta.solon.ui.theme.SolonTheme
import kotlin.math.sign import kotlin.math.sign
@@ -68,6 +69,7 @@ class MainActivity : ComponentActivity() {
IsShowing.Marks -> MarksScreen() IsShowing.Marks -> MarksScreen()
IsShowing.Menu -> MenuScreen() IsShowing.Menu -> MenuScreen()
IsShowing.About -> AboutScreen() IsShowing.About -> AboutScreen()
IsShowing.Settings -> SettingsScreen()
} }
} }
+342 -198
View File
@@ -1,5 +1,6 @@
package com.odweta.solon package com.odweta.solon
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -20,6 +21,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBackIosNew import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.AutoAwesome
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -37,11 +39,15 @@ import androidx.compose.ui.unit.sp
import com.odweta.solon.ui.theme.PastelGreen import com.odweta.solon.ui.theme.PastelGreen
import com.odweta.solon.ui.theme.PastelGreenTransparent import com.odweta.solon.ui.theme.PastelGreenTransparent
import com.odweta.solon.ui.theme.PastelOrange import com.odweta.solon.ui.theme.PastelOrange
import com.odweta.solon.ui.theme.PastelOrangeDark
import com.odweta.solon.ui.theme.PastelOrangeDarkTransparent
import com.odweta.solon.ui.theme.PastelOrangeTransparent import com.odweta.solon.ui.theme.PastelOrangeTransparent
import com.odweta.solon.ui.theme.PastelRed import com.odweta.solon.ui.theme.PastelRed
import com.odweta.solon.ui.theme.PastelRedTransparent import com.odweta.solon.ui.theme.PastelRedTransparent
import com.odweta.solon.ui.theme.PastelYellow import com.odweta.solon.ui.theme.PastelYellow
import com.odweta.solon.ui.theme.PastelYellowTransparent import com.odweta.solon.ui.theme.PastelYellowTransparent
import java.time.LocalDate
import java.time.format.DateTimeFormatter
enum class MarksShow { enum class MarksShow {
All, All,
@@ -63,25 +69,31 @@ fun MarksScreen() {
} }
private fun getAvgColor(avg: String, isBorder: Boolean = false): Color { private fun getAvgColor(avg: String, isBorder: Boolean = false): Color {
val aux = avg.replace(",", ".").toFloat().toInt() val aux = avg.replace(",", ".").toFloat()
return if (aux in 1..<2) { return if (aux in 1f..<1.5f) {
if (isBorder) { if (isBorder) {
PastelGreen PastelGreen
} else { } else {
PastelGreenTransparent PastelGreenTransparent
} }
} else if (aux in 2..<3) { } else if (aux in 1.5f..<2.5f) {
if (isBorder) { if (isBorder) {
PastelYellow PastelYellow
} else { } else {
PastelYellowTransparent PastelYellowTransparent
} }
} else if (aux in 3..<4) { } else if (aux in 2.5f..<3.5f) {
if (isBorder) { if (isBorder) {
PastelOrange PastelOrange
} else { } else {
PastelOrangeTransparent PastelOrangeTransparent
} }
} else if (aux in 3.5f..<4.5f) {
if (isBorder) {
PastelOrangeDark
} else {
PastelOrangeDarkTransparent
}
} else { } else {
if (isBorder) { if (isBorder) {
PastelRed PastelRed
@@ -104,6 +116,64 @@ fun MarksAll() {
.verticalScroll(scrollState) .verticalScroll(scrollState)
) { ) {
for ((subject, markList) in marks) { for ((subject, markList) in marks) {
val amoledModifier = if (settings.useAmoledMode.checked.value) {
Modifier
.width(150.dp)
.fillMaxHeight()
.border(
width = borderWidthDimen,
color = getAvgColor(Util.avg(markList), true), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
} else {
Modifier
.width(150.dp)
.fillMaxHeight()
.background(
color = getAvgColor(Util.avg(markList)), // decide on the color (not transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
}
val amoledModifierBackground = if (settings.useAmoledMode.checked.value) {
Modifier
.fillMaxWidth()
.height(70.dp)
.border(
width = borderWidthDimen,
color = getAvgColor(Util.avg(markList), true), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
.clickable {
marksShow.value = MarksShow.Subject
subjectToShow = subject
}
} else {
Modifier
.fillMaxWidth()
.height(70.dp)
.background(
color = getAvgColor(Util.avg(markList)), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
.clickable {
marksShow.value = MarksShow.Subject
subjectToShow = subject
}
}
val amoledSpacerModifier = if (settings.useAmoledMode.checked.value) {
Modifier
.width(10.dp)
.fillMaxHeight()
.background(getAvgColor(Util.avg(markList)))
} else {
Modifier
.width(10.dp)
.fillMaxHeight()
.background(Color.Black)
}
var marksString = "" var marksString = ""
for (m in 0 until markList.size-1) { for (m in 0 until markList.size-1) {
if (markList[m].grade in "0123456789") { if (markList[m].grade in "0123456789") {
@@ -116,68 +186,78 @@ fun MarksAll() {
Box(modifier = Modifier.height(10.dp)) Box(modifier = Modifier.height(10.dp))
} }
// only add if mark date is not older than one week
var subjectContainsNewMark = false
for (mark in markList) {
if (LocalDate.parse(mark.date, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.isAfter(LocalDate.now().minusWeeks(1))
) {
subjectContainsNewMark = true
}
}
Row( Row(
modifier = Modifier modifier = amoledModifierBackground
.fillMaxWidth()
.height(100.dp)
.background(
color = getAvgColor(Util.avg(markList)), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
.clickable {
marksShow.value = MarksShow.Subject
subjectToShow = subject
}
) { ) {
Row( Row(
modifier = Modifier modifier = amoledModifier,
.width(210.dp)
.fillMaxHeight()
.background(
color = getAvgColor(Util.avg(markList)), // decide on the color (not transparent)
shape = RoundedCornerShape(roundedCornerDimen)
),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
Text( Text(
text = subject.uppercase(), text = subject.uppercase(),
fontSize = 35.sp, fontSize = 30.sp,
color = Color.White, color = Color.White,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier modifier = Modifier
.width(100.dp) .width(70.dp)
) )
Box( Box(
modifier = Modifier modifier = amoledSpacerModifier
.width(10.dp)
.fillMaxHeight()
.background(Color.Black)
) )
val currAvg = Util.avg(markList).replace(".", ",")
var textToShow = currAvg
if (currAvg.length == 3 && currAvg[2] == '0') {
textToShow = currAvg[0].toString()
}
Text( Text(
text = Util.avg(markList).replace(".", ","), text = textToShow,
fontSize = 30.sp, fontSize = 28.sp,
color = Color.White, color = Color.White,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier modifier = Modifier
.width(100.dp) .width(70.dp)
) )
} }
Box( Box(
modifier = Modifier modifier = Modifier.fillMaxSize().weight(1f),
.weight(1f) contentAlignment = Alignment.TopStart
.fillMaxHeight(),
contentAlignment = Alignment.Center
) { ) {
Text( if (subjectContainsNewMark) {
text = marksString, Icon(
fontSize = 22.sp, modifier = Modifier.padding(start = 4.dp, top = 4.dp),
color = Color.White imageVector = Icons.Default.AutoAwesome,
) contentDescription = "New marks in subject",
tint = Color.White // You can customize the icon color
)
}
Box(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 10.dp),
contentAlignment = Alignment.Center
) {
Text(
text = marksString,
fontSize = 22.sp,
color = Color.White
)
}
} }
} }
} }
@@ -186,6 +266,11 @@ fun MarksAll() {
@Composable @Composable
fun MarksSubject() { fun MarksSubject() {
// handle the back gesture (back swipe form side or back button on bottom android navbar)
BackHandler {
marksShow.value = MarksShow.All
}
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
Column( Column(
@@ -195,62 +280,39 @@ fun MarksSubject() {
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 5.dp) .padding(horizontal = 5.dp)
) { ) {
Button( Row(
onClick = { modifier = Modifier.fillMaxWidth()
marksShow.value = MarksShow.All
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(100.dp)
.height(100.dp)
) { ) {
Icon( Button(
imageVector = Icons.Default.ArrowBackIosNew, // Replace with your desired icon onClick = {
contentDescription = "Back icon", marksShow.value = MarksShow.All
modifier = Modifier.size(35.dp) // Adjust size if needed },
) colors = ButtonDefaults.buttonColors(
} contentColor = Color.White,
containerColor = Color.Black
Spacer(modifier = Modifier.height(20.dp)) ),
Box(modifier = Modifier.height(100.dp)) {
Text(
text = subjectNameMap[subjectToShow]!!,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier modifier = Modifier
.fillMaxWidth()
.border( .border(
width = 5.dp, width = 5.dp,
color = PastelYellow, shape = RoundedCornerShape(roundedCornerDimen),
shape = RoundedCornerShape(roundedCornerDimen) color = PastelYellow
) )
.background(PastelYellowTransparent) .width(70.dp)
.padding(vertical = 20.dp) .height(70.dp)
) // subject ) {
} Icon(
imageVector = Icons.Default.ArrowBackIosNew,
contentDescription = "Back icon",
modifier = Modifier.size(40.dp)
)
}
Box( Box(modifier = Modifier.weight(1f))
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center Box(
) {
Text(
text = Util.avg(marks[subjectToShow]!!).replace(".", ","),
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier modifier = Modifier
.width(100.dp) .width(70.dp)
.height(100.dp) .height(70.dp)
.border( .border(
width = 5.dp, width = 5.dp,
color = getAvgColor( color = getAvgColor(
@@ -259,61 +321,120 @@ fun MarksSubject() {
), ),
shape = RoundedCornerShape(roundedCornerDimen) shape = RoundedCornerShape(roundedCornerDimen)
) )
.background( .background(color = if (settings.useAmoledMode.checked.value) {
getAvgColor( Color.Black
Util.avg(marks[subjectToShow]!!) } else {
) getAvgColor(
Util.avg(marks[subjectToShow]!!)
)
},
shape = RoundedCornerShape(roundedCornerDimen)
) )
.padding(vertical = 32.5.dp) .padding(vertical = 15.dp),
) // average contentAlignment = Alignment.Center
) {
val currAvg = Util.avg(marks[subjectToShow]!!).replace(".", ",")
var textToShow = currAvg
if (currAvg.length == 3 && currAvg[2] == '0') {
textToShow = currAvg[0].toString()
}
Text(
text = textToShow,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 25.sp
) // average
}
} }
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
Text(
text = subjectNameMap[subjectToShow]!!,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier
.fillMaxWidth()
.border(
width = 5.dp,
color = PastelYellow,
shape = RoundedCornerShape(roundedCornerDimen)
)
.background(color = if (settings.useAmoledMode.checked.value) {
Color.Black
} else {
PastelYellowTransparent
},
shape = RoundedCornerShape(roundedCornerDimen)
)
.padding(vertical = 20.dp)
) // subject
Spacer(modifier = Modifier.height(20.dp))
Column(modifier = Modifier.verticalScroll(scrollState)) { Column(modifier = Modifier.verticalScroll(scrollState)) {
for ((i, mark) in marks[subjectToShow]!!.withIndex()) { for ((i, mark) in marks[subjectToShow]!!.withIndex()) {
if (i > 0) { if (i > 0) {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
} }
Row( Box(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth() contentAlignment = Alignment.TopStart
.padding(horizontal = 5.dp)
.background(
color = getAvgColor(mark.grade),
shape = RoundedCornerShape(roundedCornerDimen)
)
.padding(vertical = 20.dp)
.clickable {
markToShow = mark
marksShow.value = MarksShow.Detail
}
) { ) {
Box( Row(
modifier = Modifier modifier = Modifier
.width(100.dp) .fillMaxWidth()
.align(Alignment.CenterVertically), .padding(horizontal = 5.dp)
contentAlignment = Alignment.Center .background(color = if (settings.useAmoledMode.checked.value) {
Color.Black
} else {
getAvgColor(mark.grade)
},
shape = RoundedCornerShape(roundedCornerDimen)
)
.padding(vertical = 10.dp)
.clickable {
markToShow = mark
marksShow.value = MarksShow.Detail
}
) { ) {
Text( Box(
text = mark.grade, modifier = Modifier
color = Color.White, .width(70.dp)
fontSize = 30.sp .align(Alignment.CenterVertically),
) // grade contentAlignment = Alignment.Center
) {
Text(
text = mark.grade,
color = Color.White,
fontSize = 30.sp
) // grade
}
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.weight(1f),
contentAlignment = Alignment.CenterStart
) {
Text(
text = mark.theme,
color = Color.White,
fontSize = 20.sp
) // theme
}
} }
Box( // only add if mark date is not older than one week
modifier = Modifier if (LocalDate.parse(mark.date, DateTimeFormatter.ISO_LOCAL_DATE_TIME).isAfter(LocalDate.now().minusWeeks(1))) {
.align(Alignment.CenterVertically) Icon(
.weight(1f), modifier = Modifier.padding(start = 7.dp, top = 4.dp),
contentAlignment = Alignment.CenterStart imageVector = Icons.Default.AutoAwesome,
) { contentDescription = "A new mark",
Text( tint = Color.White // You can customize the icon color
text = mark.theme, )
color = Color.White,
fontSize = 23.sp
) // theme
} }
} }
} }
@@ -323,6 +444,11 @@ fun MarksSubject() {
@Composable @Composable
fun MarksDetail() { fun MarksDetail() {
// handle the back gesture (back swipe form side or back button on bottom android navbar)
BackHandler {
marksShow.value = MarksShow.Subject
}
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
Column( Column(
@@ -333,82 +459,92 @@ fun MarksDetail() {
.padding(horizontal = 5.dp) .padding(horizontal = 5.dp)
.verticalScroll(scrollState) .verticalScroll(scrollState)
) { ) {
Button( Row(
onClick = { modifier = Modifier.fillMaxWidth()
marksShow.value = MarksShow.Subject
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(100.dp)
.height(100.dp)
) { ) {
Icon( Button(
imageVector = Icons.Default.ArrowBackIosNew, // Replace with your desired icon onClick = {
contentDescription = "Back icon", marksShow.value = MarksShow.Subject
modifier = Modifier.size(35.dp) // Adjust size if needed },
) colors = ButtonDefaults.buttonColors(
} contentColor = Color.White,
containerColor = Color.Black
Spacer(modifier = Modifier.height(20.dp)) ),
modifier = Modifier
Row { .border(
Box(modifier = Modifier.height(100.dp)) { width = 5.dp,
Text( shape = RoundedCornerShape(roundedCornerDimen),
text = markToShow.theme, color = PastelYellow
color = Color.White, )
textAlign = TextAlign.Center, .width(70.dp)
fontSize = 30.sp, .height(70.dp)
modifier = Modifier ) {
.fillMaxWidth() Icon(
.border( imageVector = Icons.Default.ArrowBackIosNew,
width = 5.dp, contentDescription = "Back icon",
color = PastelYellow, modifier = Modifier.size(40.dp)
shape = RoundedCornerShape(roundedCornerDimen) )
)
.background(PastelYellowTransparent)
.padding(vertical = 20.dp)
) // description
} }
} // description
Row { Box(modifier = Modifier.weight(1f))
Box( Box(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.width(70.dp)
.height(70.dp)
.border(
width = 5.dp,
color = getAvgColor(
Util.avg(marks[subjectToShow]!!),
true
),
shape = RoundedCornerShape(roundedCornerDimen)
)
.background(
if (settings.useAmoledMode.checked.value) {
Color.Black
} else {
getAvgColor(
Util.avg(marks[subjectToShow]!!)
)
}
)
.padding(vertical = 15.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(
text = markToShow.grade, text = markToShow.grade,
color = Color.White, color = Color.White,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
fontSize = 35.sp, fontSize = 25.sp
modifier = Modifier
.width(100.dp)
.height(100.dp)
.border(
width = 5.dp,
color = getAvgColor(
Util.avg(mutableListOf(markToShow)),
true
),
shape = RoundedCornerShape(roundedCornerDimen)
)
.background(
getAvgColor(
Util.avg(mutableListOf(markToShow))
)
)
.padding(vertical = 32.5.dp)
) // average ) // average
} }
} // grade }
Spacer(modifier = Modifier.height(20.dp))
Row {
Text(
text = markToShow.theme,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier
.fillMaxWidth()
.border(
width = 5.dp,
color = PastelYellow,
shape = RoundedCornerShape(roundedCornerDimen)
)
.background(if (settings.useAmoledMode.checked.value) {
Color.Black
} else {
PastelYellowTransparent
}
)
.padding(vertical = 20.dp)
) // description
} // description
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
@@ -417,7 +553,11 @@ fun MarksDetail() {
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 5.dp) .padding(horizontal = 5.dp)
.background( .background(
color = PastelYellowTransparent, color = if (settings.useAmoledMode.checked.value) {
Color.Black
} else {
PastelYellowTransparent
},
shape = RoundedCornerShape(roundedCornerDimen) shape = RoundedCornerShape(roundedCornerDimen)
) )
.padding(vertical = 20.dp, horizontal = 10.dp) .padding(vertical = 20.dp, horizontal = 10.dp)
@@ -431,7 +571,7 @@ fun MarksDetail() {
Text( Text(
text = "Váha:", text = "Váha:",
color = Color.White, color = Color.White,
fontSize = 30.sp fontSize = 20.sp
) )
} }
@@ -444,7 +584,7 @@ fun MarksDetail() {
Text( Text(
text = markToShow.weight.replace(".", ","), text = markToShow.weight.replace(".", ","),
color = Color.White, color = Color.White,
fontSize = 23.sp fontSize = 20.sp
) )
} }
} // weight } // weight
@@ -456,7 +596,11 @@ fun MarksDetail() {
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 5.dp) .padding(horizontal = 5.dp)
.background( .background(
color = PastelYellowTransparent, color = if (settings.useAmoledMode.checked.value) {
Color.Black
} else {
PastelYellowTransparent
},
shape = RoundedCornerShape(roundedCornerDimen) shape = RoundedCornerShape(roundedCornerDimen)
) )
.padding(vertical = 20.dp, horizontal = 10.dp) .padding(vertical = 20.dp, horizontal = 10.dp)
@@ -470,7 +614,7 @@ fun MarksDetail() {
Text( Text(
text = "Datum:", text = "Datum:",
color = Color.White, color = Color.White,
fontSize = 30.sp fontSize = 20.sp
) )
} }
@@ -489,7 +633,7 @@ fun MarksDetail() {
Text( Text(
text = date, text = date,
color = Color.White, color = Color.White,
fontSize = 23.sp fontSize = 20.sp
) )
} }
} // date } // date
+87 -119
View File
@@ -36,129 +36,97 @@ fun MenuScreen(modifier: Modifier = Modifier) {
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
// Button to trigger the dialog MenuButton({ showDialog = true }, "Odhlásit se")
Button(
onClick = { showDialog = true }, Spacer(modifier = Modifier.height(80.dp))
colors = ButtonDefaults.buttonColors(
contentColor = Color.White, MenuButton({ screenToShow.value = Screen.SignIn }, "Změnit přihlášení")
containerColor = Color.Black
), Spacer(modifier = Modifier.height(80.dp))
MenuButton({ isShowing.value = IsShowing.About }, "O aplikaci")
Spacer(modifier = Modifier.height(80.dp))
MenuButton({ isShowing.value = IsShowing.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 modifier = Modifier
.border( .border(width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
width = 5.dp, roundedCornerDimen)), // Yellow border
shape = RoundedCornerShape(roundedCornerDimen), confirmButton = {
color = PastelYellow Button(
) onClick = {
.width(230.dp) showDialog = false
.height(80.dp) Util.deleteFile(userDataFilePath)
) { Util.deleteFile(timeTableFilePath)
Text( Util.deleteFile(marksFilePath)
text = "Odhlásit se", Util.deleteFile(accountDataFilePath)
fontSize = 23.sp exitProcess(0)
) },
} colors = ButtonDefaults.buttonColors(
containerColor = Color.Black, // Black background for the button
Spacer(modifier = Modifier.height(100.dp)) contentColor = Color.White // Yellow text for the button
),
// Dialog implementation modifier = Modifier
if (showDialog) { .border(width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
AlertDialog( roundedCornerDimen)) // Yellow border
onDismissRequest = { showDialog = false }, // Close the dialog on dismiss ) {
title = { Text("Ano")
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 = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen)), // Yellow border
confirmButton = {
Button(
onClick = {
showDialog = false
Util.deleteFile(userDataFilePath)
Util.deleteFile(timeTableFilePath)
Util.deleteFile(marksFilePath)
Util.deleteFile(accountDataFilePath)
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 = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen)) // 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 = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen)) // Yellow border
) {
Text("Ne")
}
} }
) // TODO: THEME DIALOG
}
Button(onClick = {
screenToShow.value = Screen.SignIn
}, },
colors = ButtonDefaults.buttonColors( dismissButton = {
contentColor = Color.White, Button(
containerColor = Color.Black onClick = {
), showDialog = false
modifier = Modifier },
.border( colors = ButtonDefaults.buttonColors(
width = 5.dp, containerColor = Color.Black, // Black background for the button
shape = RoundedCornerShape(roundedCornerDimen), contentColor = Color.White,
color = PastelYellow // Yellow text for the button
) ),
.width(230.dp) modifier = Modifier
.height(80.dp) .border(width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
) { roundedCornerDimen)) // Yellow border
Text( ) {
text = "Změnit přihlášení", Text("Ne")
fontSize = 23.sp }
) }
} )
}
}
Spacer(modifier = Modifier.height(100.dp)) @Composable
fun MenuButton(onClick: () -> Unit, text: String) {
Button(onClick = { Button(
isShowing.value = IsShowing.About onClick = onClick,
}, colors = ButtonDefaults.buttonColors(
colors = ButtonDefaults.buttonColors( contentColor = Color.White,
contentColor = Color.White, containerColor = Color.Black
containerColor = Color.Black ),
), modifier = Modifier
modifier = Modifier .border(
.border( width = 5.dp,
width = 5.dp, shape = RoundedCornerShape(roundedCornerDimen),
shape = RoundedCornerShape(roundedCornerDimen), color = PastelYellow
color = PastelYellow
)
.width(230.dp)
.height(80.dp)
) {
Text(
text = "O aplikaci",
fontSize = 23.sp
) )
} .width(230.dp)
.height(80.dp)
) {
Text(
text = text,
fontSize = 23.sp
)
} }
} }
@@ -4,10 +4,12 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
@@ -15,24 +17,54 @@ import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.odweta.solon.ui.theme.PastelYellow import com.odweta.solon.ui.theme.PastelYellow
import kotlinx.coroutines.CoroutineScope
private var timetableColor: MutableState<Color> = mutableStateOf(PastelYellow) private var timetableColor: MutableState<Color> = mutableStateOf(PastelYellow)
private var marksColor: MutableState<Color> = mutableStateOf(Color.White) private var marksColor: MutableState<Color> = mutableStateOf(Color.White)
private var menuColor: MutableState<Color> = mutableStateOf(Color.White) private var menuColor: MutableState<Color> = mutableStateOf(Color.White)
var refreshButtonContentColor: MutableState<Color> = mutableStateOf(Color.White)
@Composable @Composable
fun NavbarScreen(modifier: Modifier = Modifier) { fun NavbarScreen(modifier: Modifier = Modifier) {
val ctx = LocalContext.current
val coscope = rememberCoroutineScope()
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
var spacerModifier = 30.dp
if (settings.showRefreshButton.checked.value) {
spacerModifier = 15.dp
Button(
onClick = {
fetchNewData(ctx, coscope)
refreshButtonContentColor.value = PastelYellow
},
colors = ButtonDefaults.buttonColors(
contentColor = refreshButtonContentColor.value,
containerColor = Color.Black
)
) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Refresh Button",
modifier = Modifier.size(25.dp)
)
}
Spacer(modifier = Modifier.width(spacerModifier))
}
Button( Button(
onClick = { onClick = {
isShowing.value = IsShowing.Timetable isShowing.value = IsShowing.Timetable
@@ -52,7 +84,7 @@ fun NavbarScreen(modifier: Modifier = Modifier) {
) )
} }
Spacer(modifier = Modifier.width(30.dp)) Spacer(modifier = Modifier.width(spacerModifier))
Button( Button(
onClick = { onClick = {
@@ -74,7 +106,7 @@ fun NavbarScreen(modifier: Modifier = Modifier) {
) )
} }
Spacer(modifier = Modifier.width(30.dp)) Spacer(modifier = Modifier.width(spacerModifier))
Button( Button(
onClick = { onClick = {
@@ -58,10 +58,15 @@ class Network {
internetAvail = true internetAvail = true
userData = getUserDataJSON() userData = getUserDataJSON()
Util.saveStringToFile(userDataFilePath, userData) Util.saveStringToFile(userDataFilePath, userData)
if (!Parsing.userIsParent()) { studentClass = if (!Parsing.userIsParent()) {
studentClass = JSONObject( JSONObject(
Util.fileToString(userDataFilePath) Util.fileToString(userDataFilePath)
).getJSONObject("class").get("name").toString() ).getJSONObject("class").get("name").toString()
} else {
// if the user is a parent, the class will be of their first child
JSONObject(
Util.fileToString(userDataFilePath)
).getJSONArray("children").getJSONObject(0).getString("className")
} }
studentId = getStudentId() studentId = getStudentId()
//syid = getSYID() //syid = getSYID()
@@ -269,6 +269,23 @@ class Parsing {
auxSubjects.add(Subject.empty()) auxSubjects.add(Subject.empty())
} }
// prepend free subjects when day does not start with subject number 1
val newSubjects: MutableList<Subject> = mutableListOf()
var n = auxSubjects[0].number.toInt()
while (n > 1) {
newSubjects.add(
Subject.freeSubject(
number = (n - 1).toString(),
start = "00:00",
end = "00:00"
)
)
n -= 1
}
val finalSubjects: MutableList<Subject> = mutableListOf()
finalSubjects.addAll(newSubjects.reversed())
finalSubjects.addAll(auxSubjects)
for ((i, s) in auxSubjects.withIndex()) { for ((i, s) in auxSubjects.withIndex()) {
if (s.free) { if (s.free) {
s.start = auxSubjects[i-1].end s.start = auxSubjects[i-1].end
@@ -279,7 +296,7 @@ class Parsing {
val day = Day() val day = Day()
day.name = name day.name = name
day.date = date day.date = date
day.subjects = auxSubjects day.subjects = finalSubjects
// get rid of duplicates // get rid of duplicates
val nextSubjects: MutableList<Subject> = mutableListOf() val nextSubjects: MutableList<Subject> = mutableListOf()
@@ -0,0 +1,143 @@
package com.odweta.solon
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchColors
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.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.odweta.solon.ui.theme.PastelYellow
@Composable
fun SettingsScreen(modifier: Modifier = Modifier) {
// handle the back gesture (back swipe form side or back button on bottom android navbar)
BackHandler {
isShowing.value = IsShowing.Menu
}
val scrollState = rememberScrollState()
Column(modifier = Modifier
.fillMaxSize()
.background(color = Color.Black)
.verticalScroll(scrollState)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(start = 5.dp)
) {
Button(
onClick = {
isShowing.value = IsShowing.Menu
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(70.dp)
.height(70.dp)
) {
Icon(
imageVector = Icons.Default.ArrowBackIosNew,
contentDescription = "Back icon",
modifier = Modifier.size(40.dp)
)
}
} // back button
Spacer(Modifier.height(10.dp))
SettingsTableRow(settings.useSvobodaMode)
Spacer(Modifier.height(10.dp))
SettingsTableRow(settings.useAmoledMode)
Spacer(Modifier.height(10.dp))
SettingsTableRow(settings.showRefreshButton)
}
}
@Composable
fun SettingsTableRow(setting: Setting) {
Row(
modifier = Modifier
.padding(10.dp)
.background(color = Color.Black, shape = RoundedCornerShape(roundedCornerDimen))
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = setting.name,
textAlign = TextAlign.Start,
color = Color.White,
fontSize = 20.sp
)
Spacer(modifier = Modifier.weight(1f))
Switch(
checked = setting.checked.value,
onCheckedChange = {
setting.checked.value = it
when (setting.id) {
SettingsId.Svoboda -> settings.useSvobodaMode = setting
SettingsId.Amoled -> settings.useAmoledMode = setting
SettingsId.RefreshButton -> settings.showRefreshButton = setting
}
Util.saveStringToFile(settingsDataFilePath, settings.toString())
},
colors = SwitchColors(
uncheckedThumbColor = Color.Black,
uncheckedTrackColor = Color.DarkGray,
uncheckedBorderColor = Color.DarkGray,
checkedThumbColor = Color.Black,
checkedTrackColor = PastelYellow,
checkedBorderColor = PastelYellow,
checkedIconColor = Color.White,
uncheckedIconColor = Color.White,
disabledCheckedThumbColor = Color.Black,
disabledCheckedTrackColor = Color.Black,
disabledCheckedBorderColor = Color.Black,
disabledCheckedIconColor = Color.Black,
disabledUncheckedThumbColor = Color.Black,
disabledUncheckedTrackColor = Color.Black,
disabledUncheckedBorderColor = Color.Black,
disabledUncheckedIconColor = Color.Black
)
)
}
}
@@ -2,20 +2,18 @@ package com.odweta.solon
import android.content.Context import android.content.Context
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material.icons.filled.VisibilityOff
@@ -27,8 +25,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -40,6 +36,9 @@ import com.odweta.solon.ui.theme.PastelYellow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
private var buttonText: MutableState<String> = mutableStateOf("Přihlásit se") private var buttonText: MutableState<String> = mutableStateOf("Přihlásit se")
@@ -96,6 +95,7 @@ fun signInButtonCallback(ctx: Context, coscope: CoroutineScope, buttonText: Stri
Account.save(acc) Account.save(acc)
signedInAndReadyToLaunch.value = true signedInAndReadyToLaunch.value = true
screenToShow.value = Screen.Splash screenToShow.value = Screen.Splash
fetchNewData(ctx, coscope)
} else { } else {
Toast.makeText( Toast.makeText(
ctx, ctx,
@@ -106,41 +106,48 @@ fun signInButtonCallback(ctx: Context, coscope: CoroutineScope, buttonText: Stri
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SignInScreen() { fun SignInScreen() {
// handle the back gesture (back swipe form side or back button on bottom android navbar)
BackHandler {
screenToShow.value = Screen.Main
}
var usernameText by remember { mutableStateOf("") } var usernameText by remember { mutableStateOf("") }
var passwordText by remember { mutableStateOf("") } var passwordText by remember { mutableStateOf("") }
val ctx = LocalContext.current val ctx = LocalContext.current
val coscope = rememberCoroutineScope() val coscope = rememberCoroutineScope()
var isPasswordVisible by remember { mutableStateOf(false) } // State for visibility toggle var isPasswordVisible by remember { mutableStateOf(false) } // State for visibility toggle
val visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation() val visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation()
val scrollState = rememberScrollState()
Box( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier
contentAlignment = Alignment.Center .fillMaxSize()
.background(Color.Black)
.imePadding(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .wrapContentSize(),
.background(Color.Black) horizontalAlignment = Alignment.CenterHorizontally
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) { ) {
Image( Image(
painter = painterResource(R.drawable.solon_just_yellow), painter = painterResource(R.drawable.solon_just_yellow),
contentDescription = "Solon logo", contentDescription = "Solon logo",
modifier = Modifier.size(230.dp) modifier = Modifier.size(200.dp)
) )
TextField( TextField(
value = usernameText, value = usernameText,
onValueChange = { input -> usernameText = input }, onValueChange = { input -> usernameText = input },
modifier = Modifier modifier = Modifier
.border(width = 5.dp, color = PastelYellow, shape = RoundedCornerShape( .border(
roundedCornerDimen)) width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen
)
)
.width(230.dp), .width(230.dp),
colors = TextFieldDefaults.colors( colors = TextFieldDefaults.colors(
focusedTextColor = Color.White, // White text focusedTextColor = Color.White, // White text
@@ -214,4 +221,4 @@ fun SignInScreen() {
} }
} }
} }
} }
@@ -1,7 +1,7 @@
package com.odweta.solon package com.odweta.solon
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.text.format.Time import android.util.Log
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -13,16 +13,29 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -31,22 +44,89 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp 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.PastelGreen
import com.odweta.solon.ui.theme.PastelGreenTransparent import com.odweta.solon.ui.theme.PastelGreenTransparent
import com.odweta.solon.ui.theme.PastelRed import com.odweta.solon.ui.theme.PastelRed
import com.odweta.solon.ui.theme.PastelRedTransparent import com.odweta.solon.ui.theme.PastelRedTransparent
import com.odweta.solon.ui.theme.PastelYellow import com.odweta.solon.ui.theme.PastelYellow
import com.odweta.solon.ui.theme.PastelYellowTransparent import com.odweta.solon.ui.theme.PastelYellowTransparent
import kotlinx.coroutines.delay import java.security.MessageDigest
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime import java.time.LocalTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import kotlin.math.abs
var showDetailDialog = mutableStateOf(false)
@Composable
fun TimetableScreen() {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.BottomCenter
) {
DayPager()
if (showDetailDialog.value) {
DetailDialog()
}
}
}
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DetailDialog(modifier: Modifier = Modifier) {
Dialog(
onDismissRequest = { showDetailDialog.value = false }, // Close the dialog on dismiss
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true
),
content = {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.background(
color = Color.Black,
shape = RoundedCornerShape(roundedCornerDimen)
)
) {
if (lectureNotes[selectedLecture] == null) {
lectureNotes[selectedLecture] = mutableStateOf("")
}
TextField(
value = lectureNotes[selectedLecture]!!.value,
onValueChange = { newText: String ->
lectureNotes[selectedLecture]!!.value = newText
Util.saveLectureIds()
},
placeholder = { Text("Sem můžete napsat poznámky k\u00A0této hodině.") },
colors = TextFieldDefaults.textFieldColors(
focusedTextColor = Color.White,
containerColor = Color.Black,
selectionColors = TextSelectionColors(
handleColor = PastelYellow,
backgroundColor = PastelYellowTransparent
),
cursorColor = PastelYellow,
focusedIndicatorColor = PastelYellow,
unfocusedIndicatorColor = PastelYellowTransparent
)
)
}
}
)
}
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun TimetableScreen() { fun DayPager(modifier: Modifier = Modifier) {
val pagerState = rememberPagerState( pageCount = { days.size } ) val pagerState = rememberPagerState(pageCount = { days.size })
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
@@ -85,9 +165,19 @@ fun isTimeBetween(givenTime: LocalTime, startTime: LocalTime, endTime: LocalTime
@SuppressLint("ModifierFactoryExtensionFunction") @SuppressLint("ModifierFactoryExtensionFunction")
@Composable @Composable
fun subjectModifier(d: Day, s: Subject, lectureNumber: Int): Modifier { fun subjectModifier(d: Day, s: Subject, lectureNumber: Int): Modifier {
var day = d.date.split(".")[0].replace(" ", "")
var month = d.date.split(".")[1].replace(" ", "")
if (day.length < 2) {
day = "0$day"
}
if (month.length < 2) {
month = "0$month"
}
val dayFormatter = DateTimeFormatter.ofPattern("dd. MM. yyyy") val dayFormatter = DateTimeFormatter.ofPattern("dd. MM. yyyy")
val hourFormatter = DateTimeFormatter.ofPattern("HH:mm:ss") val hourFormatter = DateTimeFormatter.ofPattern("HH:mm:ss")
val dateParsed = LocalDate.parse(d.date + " " + LocalDate.now().year, dayFormatter) val dateParsed = LocalDate.parse("$day. $month." + " " + LocalDate.now().year, dayFormatter)
val startTimeParsed = LocalTime.parse(s.start + ":00", hourFormatter) val startTimeParsed = LocalTime.parse(s.start + ":00", hourFormatter)
val endTimeParsed = LocalTime.parse(s.end + ":00", hourFormatter) val endTimeParsed = LocalTime.parse(s.end + ":00", hourFormatter)
@@ -98,28 +188,34 @@ fun subjectModifier(d: Day, s: Subject, lectureNumber: Int): Modifier {
currentLectureNumber = lectureNumber currentLectureNumber = lectureNumber
} }
return if (lectureNumber == currentLectureNumber && dateParsed == todayDate) { val m = if (settings.useAmoledMode.checked.value) {
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.height(100.dp) .height(70.dp)
.background(
color = getSubjectColor(s, true), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
.border( .border(
width = 5.dp, width = borderWidthDimen,
color = getSubjectColor(s, false), color = getSubjectColor(s, false), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen) shape = RoundedCornerShape(roundedCornerDimen)
) )
} else { } else {
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.height(100.dp) .height(70.dp)
.background( .background(
color = getSubjectColor(s, true), // decide on the color (transparent) color = getSubjectColor(s, true), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen) shape = RoundedCornerShape(roundedCornerDimen)
) )
} }
return if (lectureNumber == currentLectureNumber && dateParsed == todayDate) {
m.border(
width = borderWidthDimen,
color = getSubjectColor(s, false),
shape = RoundedCornerShape(roundedCornerDimen)
)
} else {
m
}
} }
@Composable @Composable
@@ -138,7 +234,7 @@ private fun DayScreen(day: Day) {
Text( Text(
text = "${day.name} ${day.date}", text = "${day.name} ${day.date}",
color = Color.White, color = Color.White,
fontSize = 30.sp, fontSize = 26.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontStyle = FontStyle.Italic fontStyle = FontStyle.Italic
) )
@@ -152,12 +248,42 @@ private fun DayScreen(day: Day) {
.padding(horizontal = 5.dp) .padding(horizontal = 5.dp)
.verticalScroll(scrollState) .verticalScroll(scrollState)
) { ) {
for (subject in day.subjects) { for ((subjectIndex, subject) in day.subjects.withIndex()) {
val lectureId = Util.getLectureId(day.date, subjectIndex)
val amoledModifier = if (settings.useAmoledMode.checked.value) {
Modifier
.width(50.dp)
.fillMaxHeight()
.border(
width = borderWidthDimen,
color = getSubjectColor(
subject,
false
),
shape = RoundedCornerShape(roundedCornerDimen)
)
} else {
Modifier
.width(50.dp)
.fillMaxHeight()
.background(
color = getSubjectColor(
subject,
true
),
shape = RoundedCornerShape(roundedCornerDimen)
)
}
if (day.subjects.indexOf(subject) > 0) { if (day.subjects.indexOf(subject) > 0) {
Box(modifier = Modifier.height(10.dp)) { Box(modifier = Modifier.height(10.dp)) {
if (subject.name == day.subjects[day.subjects.indexOf(subject) - 1].name) { if (subject.name == day.subjects[day.subjects.indexOf(subject) - 1].name) {
val drawable =
if (subject.free) { R.drawable.squiggly_line_green }
else { R.drawable.squiggly_line_yellow }
Image( Image(
painter = painterResource(R.drawable.squiggly_line_yellow), painter = painterResource(drawable),
contentDescription = "Spojovač stejných předmětů", contentDescription = "Spojovač stejných předmětů",
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
@@ -165,115 +291,140 @@ private fun DayScreen(day: Day) {
} }
} }
Row( Box (modifier = subjectModifier(day, subject, subject.number.toInt())) {
modifier = subjectModifier(day, subject, subject.number.toInt()) Row {
) { if (subject.substitute != "2") {
if (subject.substitute != "2") { Box(
Box( modifier = amoledModifier,
modifier = Modifier contentAlignment = Alignment.Center
.width(50.dp) ) {
.fillMaxHeight()
.background(
color = getSubjectColor(
subject,
true
), // decide on the color (not transparent)
shape = RoundedCornerShape(roundedCornerDimen)
),
contentAlignment = Alignment.Center
) {
Text(
text = subject.number,
fontSize = 30.sp,
color = Color.White
)
}
}
if (subject.substitute != "2") {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(start = 20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start
) {
if (!subject.free) {
Text( Text(
text = subject.name.uppercase(), text = subject.number,
fontSize = 30.sp,
color = Color.White
)
}
}
if (subject.substitute != "2") {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(start = 20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.Start
) {
if (!subject.free) {
if (subject.substitute == "1") {
Row {
Text(
text = subject.name.uppercase(),
color = Color.White,
fontSize = 25.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = " supl ",
color = Color.Black,
modifier = Modifier.background(color = Color.White)
)
}
} else {
Text(
text = subject.name.uppercase(),
color = Color.White,
fontSize = 25.sp,
fontWeight = FontWeight.Bold
)
}
}
Text(
text = subject.place,
color = Color.White, color = Color.White,
fontSize = 25.sp, fontSize = 21.sp
)
}
} else {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(start = 20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = subject.name,
color = Color.White,
fontSize = 20.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
} }
Text(
text = subject.place,
color = Color.White,
fontSize = 21.sp
)
} }
} else {
Column( Box(
modifier = Modifier modifier = Modifier
.weight(1f)
.fillMaxHeight() .fillMaxHeight()
.padding(start = 20.dp), .padding(vertical = 5.dp),
verticalArrangement = Arrangement.Center, contentAlignment = Alignment.Center
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( if (settings.useSvobodaMode.checked.value) {
text = subject.name, if (
color = Color.White, subject.teacher == "Jindřich Svoboda" &&
fontSize = 20.sp, studentClass == "5. G"
fontWeight = FontWeight.Bold ) {
) Image(
painter = painterResource(R.drawable.spock),
contentDescription = "Spock",
modifier = Modifier.fillMaxSize()
)
}
}
} }
}
Box( if (subject.substitute != "2") {
modifier = Modifier Column(
.weight(1f) modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.padding(vertical = 5.dp), .padding(end = 10.dp),
contentAlignment = Alignment.Center verticalArrangement = Arrangement.Center,
) { horizontalAlignment = Alignment.End
if (!Parsing.userIsParent()) {
if (
subject.teacher == "Jindřich Svoboda" &&
studentClass == "5. G"
) { ) {
Image( // show only for non prepended free lectures
painter = painterResource(R.drawable.spock), if ((subject.free && subject.start != "00:00" && subject.end != "00:00") || !subject.free) {
contentDescription = "Spock", Text(
modifier = Modifier.fillMaxSize() text = "${subject.start}${subject.end}",
) color = Color.White,
fontSize = 21.sp
)
}
if (!subject.free) {
Box (
contentAlignment = Alignment.BottomEnd
) {
Text(
text = subject.teacher,
color = Color.White,
fontSize = 12.sp
)
}
}
} }
} }
} }
if (subject.substitute != "2") { Box(modifier = Modifier
Column( .fillMaxSize()
modifier = Modifier .zIndex(99f)
.fillMaxHeight() .clickable {
.padding(end = 20.dp), selectedLecture = lectureId
verticalArrangement = Arrangement.Center, showDetailDialog.value = true
horizontalAlignment = Alignment.End })
) {
Text(
text = "${subject.start}${subject.end}",
color = Color.White,
fontSize = 21.sp
)
if (!subject.free) {
Text(
text = subject.teacher,
color = Color.White,
fontSize = 12.sp
)
}
}
}
} }
} }
} }
@@ -1,15 +1,46 @@
package com.odweta.solon package com.odweta.solon
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@Composable @Composable
fun ToolbarScreen() { fun ToolbarScreen() {
Text( // make the font size adaptive relative to the length of the user's name
text = toolbarText.value, var toolbarTextSizeAdaptive by remember { mutableStateOf(25.sp) }
fontSize = 25.sp,
color = Color.White if (toolbarText.value.length > 28) {
) toolbarTextSizeAdaptive = 20.sp
}
if (toolbarText.value.length > 32) {
toolbarTextSizeAdaptive = 16.sp
}
if (toolbarText.value.length > 40) {
toolbarTextSizeAdaptive = 12.sp
}
if (toolbarText.value.length > 45) {
toolbarTextSizeAdaptive = 10.sp
}
Box(modifier = Modifier.padding(10.dp)) {
Text(
text = toolbarText.value,
fontSize = toolbarTextSizeAdaptive,
color = Color.White
)
}
} }
+109 -26
View File
@@ -1,33 +1,22 @@
package com.odweta.solon package com.odweta.solon
import android.animation.Animator
import android.animation.AnimatorInflater
import android.animation.AnimatorListenerAdapter
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.app.ActivityManager import android.app.ActivityManager
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.pm.PackageManager
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.Handler import android.os.Build
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.AnimationUtils import androidx.compose.runtime.MutableState
import android.widget.Button import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.Color import androidx.core.content.pm.PackageInfoCompat
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.odweta.solon.ui.theme.PastelGreenTransparent
import com.odweta.solon.ui.theme.PastelOrangeTransparent
import com.odweta.solon.ui.theme.PastelRedTransparent
import com.odweta.solon.ui.theme.PastelYellowTransparent
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.charset.Charset import java.nio.charset.Charset
import java.security.MessageDigest
import kotlin.math.abs
import kotlin.math.round
class Util { class Util {
companion object { companion object {
@@ -130,22 +119,20 @@ class Util {
} }
fun isAppInForeground(context: Context, packageName: String): Boolean { fun isAppInForeground(context: Context, packageName: String): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val activityManager =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val runningAppProcesses = activityManager.runningAppProcesses ?: return false val runningAppProcesses = activityManager.runningAppProcesses ?: return false
for (processInfo in runningAppProcesses) { for (processInfo in runningAppProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
processInfo.processName == packageName) { processInfo.processName == packageName
) {
return true return true
} }
} }
return false return false
} }
fun pixelsToDp(pixels: Int, resources: Resources): Int {
return (pixels * resources.displayMetrics.density).toInt()
}
fun avg(marks: MutableList<Mark>): String { fun avg(marks: MutableList<Mark>): String {
var sum = 0f var sum = 0f
var len = 0 var len = 0
@@ -166,7 +153,8 @@ class Util {
weights.add(mark.weight.toFloat()) weights.add(mark.weight.toFloat())
} }
return String.format("%.2f", (sum / sum(weights))).replace(',', '.') return "${Math.round(sum / sum(weights) * 100f) / 100f}".replace(",", ".")
//return String.format("%.2f", (sum / sum(weights))).replace(',', '.')
} }
private fun sum(nums: List<Float>): Float { private fun sum(nums: List<Float>): Float {
@@ -220,5 +208,100 @@ class Util {
return d return d
}*/ }*/
data class AppVersion(
val versionName: String,
val versionNumber: Long,
)
fun getAppVersion(
context: Context,
): AppVersion? {
return try {
val packageManager = context.packageManager
val packageName = context.packageName
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(0)
)
} else {
packageManager.getPackageInfo(packageName, 0)
}
AppVersion(
versionName = packageInfo.versionName,
versionNumber = PackageInfoCompat.getLongVersionCode(packageInfo),
)
} catch (e: Exception) {
null
}
}
fun stringToSettingsData(s: String): SettingsData {
val settings = s.split("\n")
val auxSettingsData = SettingsData()
val useSvobodaMode = settings[0].split("#")
val useAmoledMode = settings[1].split("#")
val showRefreshButton = settings[2].split("#")
auxSettingsData.useSvobodaMode.id = SettingsId.Svoboda
auxSettingsData.useSvobodaMode.name = useSvobodaMode[1]
auxSettingsData.useSvobodaMode.checked.value = useSvobodaMode[2].toBoolean()
auxSettingsData.useAmoledMode.id = SettingsId.Amoled
auxSettingsData.useAmoledMode.name = useAmoledMode[1]
auxSettingsData.useAmoledMode.checked.value = useAmoledMode[2].toBoolean()
auxSettingsData.showRefreshButton.id = SettingsId.RefreshButton
auxSettingsData.showRefreshButton.name = showRefreshButton[1]
auxSettingsData.showRefreshButton.checked.value = showRefreshButton[2].toBoolean()
return auxSettingsData
}
fun getLectureId(date: String, subjectIndex: Int): Int {
val concat = "$date[$subjectIndex]"
val bytes = concat.toByteArray()
val md5AsBytes = MessageDigest.getInstance("MD5").digest(bytes)
return abs(md5AsBytes.take(4).fold(0) { acc, byte ->
(acc shl 8) or (byte.toInt() and 0xff)
})
}
fun csvToHashMap(filePath: String): HashMap<Int, MutableState<String>> {
val hashMap = HashMap<Int, MutableState<String>>()
val lines = fileToString(filePath).lines()
for (line in lines) {
if (line.isBlank()) continue
val parts = line.split(",")
if (parts.size == 2) {
val key = parts[0].trim().toInt()
val value = parts[1].trim()
hashMap[key] = mutableStateOf("")
hashMap[key]!!.value = value
}
}
return hashMap
}
fun hashMapToCsv(hashMap: HashMap<Int, MutableState<String>>): String {
var result = ""
for ((key, value) in hashMap) {
result += "$key,${value.value}\n"
}
return result
}
fun loadLectureIds() {
lectureNotes = csvToHashMap(lectureNotesFilePath)
}
fun saveLectureIds() {
val toSave = hashMapToCsv(lectureNotes)
saveStringToFile(lectureNotesFilePath, toSave)
}
} }
} }
@@ -19,11 +19,13 @@ val White = Color(0xFFFFFFFF)
val Transparent = Color(0x00000000) val Transparent = Color(0x00000000)
val PastelRed = Color(0xFFFF6666) val PastelRed = Color(0xFFFF6666)
val PastelOrangeDark = Color(0xFFDC6421)
val PastelGreen = Color(0xFF00CC99) val PastelGreen = Color(0xFF00CC99)
val PastelOrange = Color(0xFFFF9933) val PastelOrange = Color(0xFFFF9933)
val PastelYellow = Color(0xFFFFCC33) val PastelYellow = Color(0xFFFFCC33)
val PastelRedTransparent = Color(0x50FF6666) val PastelRedTransparent = Color(0x50FF6666)
val PastelOrangeDarkTransparent = Color(0x50DC6421)
val PastelGreenTransparent = Color(0x5000CC99) val PastelGreenTransparent = Color(0x5000CC99)
val PastelOrangeTransparent = Color(0x50FF9933) val PastelOrangeTransparent = Color(0x50FF9933)
val PastelYellowTransparent = Color(0x50FFCC33) val PastelYellowTransparent = Color(0x50FFCC33)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

@@ -1,53 +1,74 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector <vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp" android:height="108dp"
android:width="108dp" android:width="108dp"
android:viewportHeight="108" android:viewportHeight="108"
android:viewportWidth="108"> android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background Color --> <path android:fillColor="#3DDC84"
<path android:fillColor="#000000" android:pathData="M0,0h108v108h-108z"/>
android:pathData="M0,0h108v108h-108z"/> <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<!-- Vertical Grid Lines --> <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
<path android:fillColor="#00000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:pathData="M9,0L9,108" <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeWidth="0.8"/> <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
<path android:fillColor="#00000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:pathData="M19,0L19,108" <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeWidth="0.8"/> <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
<path android:fillColor="#00000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:pathData="M29,0L29,108" <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeWidth="0.8"/> <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
<!-- Repeat the lines for remaining grid --> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
<!-- Horizontal Grid Lines --> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:pathData="M0,9L108,9" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeColor="#000000" <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:pathData="M0,19L108,19" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeColor="#000000" <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:pathData="M0,29L108,29" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeColor="#000000" <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeWidth="0.8"/> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<!-- Repeat the lines for remaining grid --> <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<!-- Additional Grid Lines within the Central Area --> <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
<path android:fillColor="#00000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:pathData="M19,29L89,29" <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeWidth="0.8"/> <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
<path android:fillColor="#00000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:pathData="M19,39L89,39" <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#000000" android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
android:strokeWidth="0.8"/> <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
<!-- Repeat similar paths for the remaining inner lines --> android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector> </vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/launcher_background"/> <background android:drawable="@mipmap/launcher_background"/>
<foreground android:drawable="@mipmap/launcher_foreground"/> <foreground android:drawable="@mipmap/launcher_foreground"/>
</adaptive-icon> </adaptive-icon>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/launcher_background"/> <background android:drawable="@mipmap/launcher_background"/>
<foreground android:drawable="@mipmap/launcher_foreground"/> <foreground android:drawable="@mipmap/launcher_foreground"/>
</adaptive-icon> </adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -5,7 +5,7 @@
--> -->
<data-extraction-rules> <data-extraction-rules>
<cloud-backup> <cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up. <!-- xTODO: Use <include> and <exclude> to control what is backed up.
<include .../> <include .../>
<exclude .../> <exclude .../>
--> -->
+2 -1
View File
@@ -20,4 +20,5 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the # Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
sdk.dir=~/Android/Sdk
+1 -1
View File
@@ -1,6 +1,6 @@
[versions] [versions]
accompanistCoil = "0.30.1" accompanistCoil = "0.30.1"
agp = "8.7.1" agp = "8.7.2"
coilCompose = "2.4.0" coilCompose = "2.4.0"
glide = "4.16.0" glide = "4.16.0"
kotlin = "2.0.0" kotlin = "2.0.0"
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
cp app/build/outputs/apk/release/com.odweta.solon_$1.apk ../release/
Binary file not shown.
Binary file not shown.