made solon 2.0

This commit is contained in:
Odweta
2024-10-22 18:18:13 +02:00
parent c008330aa5
commit eab717ad69
153 changed files with 2863 additions and 3991 deletions
-15
View File
@@ -1,15 +0,0 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+1 -1
View File
@@ -1 +1 @@
Solon
solon
+297
View File
@@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="imageAssetPanel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="actionbar">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="/tmp/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="launcher">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundClipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="imagePath" value="/tmp/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="$PROJECT_DIR$/app/src/main/res/drawable/solon_500_yellow.png" />
<entry key="scalingPercent" value="70" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundTextAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="outputName" value="launcher" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="launcherLegacy">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="/tmp/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="notification">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="/tmp/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="tvBanner">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="tvChannel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundClipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="imagePath" value="/tmp/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundTextAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>
-123
View File
@@ -1,123 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>
-5
View File
@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>
+5 -2
View File
@@ -4,15 +4,18 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-09-22T16:40:27.288755501Z">
<DropdownSelection timestamp="2024-10-20T07:27:51.115175643Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/thinker/.android/avd/5_inch_API_31.avd" />
<DeviceId pluginId="LocalEmulator" identifier="path=/home/thinker/.android/avd/Motorola_Moto_e22_API_31.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="DefaultPreview">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>
+47
View File
@@ -0,0 +1,47 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
</inspection_tool>
</profile>
</component>
+11 -1
View File
@@ -1,6 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.0" />
<option name="languageVersion" value="2.0" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.22" />
<option name="version" value="2.0.0" />
</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>
+3 -1
View File
@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
+2 -1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/help" vcs="Git" />
</component>
</project>
+25 -21
View File
@@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
android {
@@ -12,39 +13,42 @@ android {
minSdk = 28
targetSdk = 34
versionCode = 1
versionName = "1.0.9"
versionName = "2.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
buildFeatures {
viewBinding = true
compose = true
}
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("androidx.viewpager2:viewpager2:1.0.0")
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("com.google.android.material:material:1.11.0")
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.okhttp)
implementation(libs.glide)
implementation(libs.androidx.material.icons.extended)
}
Binary file not shown.
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.odweta.solon",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "2.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 28
}
+6 -12
View File
@@ -10,29 +10,23 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:icon="@mipmap/launcher"
android:label="Solon"
android:roundIcon="@mipmap/launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Solon"
tools:targetApi="tiramisu"
android:usesCleartextTraffic="true"
android:enableOnBackInvokedCallback="true">
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.Solon"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait">
android:label="@string/app_name"
android:theme="@style/Theme.Solon">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

@@ -0,0 +1,129 @@
package com.odweta.solon
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Row
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.shape.RoundedCornerShape
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.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import com.odweta.solon.ui.theme.PastelYellow
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import java.time.format.TextStyle
@Composable
fun AboutScreen(modifier: Modifier = Modifier) {
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start
) {
Box(
modifier = Modifier
.fillMaxWidth()
) {
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(100.dp)
.height(100.dp)
) {
Icon(
imageVector = Icons.Default.ArrowBackIosNew, // Replace with your desired icon
contentDescription = "Back icon",
modifier = Modifier.size(35.dp) // Adjust size if needed
)
}
}
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.solon_just_yellow),
contentDescription = "Solon logo",
modifier = Modifier.size(200.dp)
)
Text(
text = "Přehlednost je podstata",
color = Color.White,
fontSize = 30.sp,
fontStyle = FontStyle.Italic,
modifier = Modifier
.padding(10.dp)
)
Text(
text = "Odlehčená verze mobilní aplikace Škola OnLine",
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 25.sp,
modifier = Modifier
.padding(10.dp)
)
Row(
modifier = Modifier
.padding(10.dp)
) {
val uriHandler = LocalUriHandler.current
Text(
text = "Vývoj: ",
textAlign = TextAlign.Start,
color = Color.White,
fontSize = 30.sp
)
Text(
text = "@Odweta",
color = PastelYellow,
fontSize = 30.sp,
textAlign = TextAlign.End,
textDecoration = TextDecoration.Underline,
modifier = Modifier.clickable {
uriHandler.openUri("https://www.gitlab.com/Odweta/solon")
}
)
}
}
}
}
@@ -1,145 +0,0 @@
package com.odweta.solon
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.text.style.UnderlineSpan
import android.view.GestureDetector
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import kotlin.math.abs
class AboutFragment : DialogFragment() {
private lateinit var gestureDetector: GestureDetector
private var initialTouchY: Float = 0f
private val swipeThreshold = 100
private val swipeVelocityThreshold = 100
private lateinit var dialog: Dialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.Theme_Solon)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_about, container, false)
// Set up GestureDetector
gestureDetector = GestureDetector(requireContext(), SwipeGestureListener())
// Attach the gesture detector to the root layout
view.setOnTouchListener { _, event ->
view.performClick()
handleTouchEvent(event)
}
val link = view.findViewById<TextView>(R.id.link)
val underlinedText = SpannableString(link.text)
underlinedText.setSpan(UnderlineSpan(), 8, link.text.length, 0)
link.text = underlinedText
makeTextViewClickable(link, "https://gitlab.com/Odweta", 8)
val srcLink = view.findViewById<TextView>(R.id.sourceCode)
val underlinedText2 = SpannableString(srcLink.text)
underlinedText2.setSpan(UnderlineSpan(), 1, srcLink.text.length, 0)
srcLink.text = underlinedText2
makeTextViewClickable(srcLink, "https://gitlab.com/Odweta/solon")
return view
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog.setCancelable(true)
return dialog
}
private fun makeTextViewClickable(link: TextView, url: String, offset: Int = 0) {
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
if (Vars.internetAvail) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
} else {
Toast.makeText(
requireContext(),
"Bez připojení k internetu.",
Toast.LENGTH_SHORT
).show()
}
}
}
val spannable = Spannable.Factory.getInstance().newSpannable(link.text)
spannable.setSpan(clickableSpan, offset, link.text.length, Spannable.SPAN_COMPOSING)
link.text = spannable
link.movementMethod = LinkMovementMethod.getInstance()
}
inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffY = e1?.let { e2.y.minus(it.y) } ?: 0f
if (diffY > swipeThreshold && abs(velocityY) > swipeVelocityThreshold) {
// Swiped down
dismiss()
return true
}
return false
}
}
@Suppress("SameReturnValue", "SameReturnValue", "SameReturnValue")
private fun handleTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialTouchY = event.rawY
}
MotionEvent.ACTION_MOVE -> {
val deltaY = event.rawY - initialTouchY
dialog?.window?.attributes?.y = deltaY.toInt()
dialog?.window?.attributes?.windowAnimations = 0
dialog?.show()
// Check if the dialog has been dragged beyond a threshold and dismiss if necessary
if (deltaY > 200) {
dismiss()
}
}
MotionEvent.ACTION_UP -> {
initialTouchY = 0f
}
}
return true
}
}
@@ -27,7 +27,7 @@ class Account(val username: String, val password: String, private val error: Int
*/
// check if file exists
val file = File(Vars.fileDir, Vars.accountDataFilePath)
val file = File(fileDir, accountDataFilePath)
return if (!file.exists()) {
// it does not exist
@@ -36,15 +36,15 @@ class Account(val username: String, val password: String, private val error: Int
} else {
// it exists, so it shall be parsed
val acc: Account = parseAccountDataFile(file)
Vars.username = acc.username
Vars.password = acc.password
username = acc.username
password = acc.password
acc.error == 0
}
}
fun save(acc: Account): Boolean {
return try {
val file = File(Vars.fileDir, Vars.accountDataFilePath)
val file = File(fileDir, accountDataFilePath)
file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8"))
true
@@ -55,8 +55,8 @@ class Account(val username: String, val password: String, private val error: Int
suspend fun isValid(acc: Account): Boolean {
return withContext(Dispatchers.IO) {
Vars.username = acc.username
Vars.password = acc.password
username = acc.username
password = acc.password
val token = Network.getToken()
!JSONObject(token).has("error")
@@ -1,751 +0,0 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.Space
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.marginLeft
import androidx.core.view.setMargins
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import java.time.LocalDate
import java.time.LocalTime
class CalWeekFragment : Fragment() {
private lateinit var viewPager: ViewPager2
private lateinit var adapter: DayPagerAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_cal_week, container, false)
viewPager = view.findViewById(R.id.dayPager)
adapter = DayPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
Vars.dayCounter = 0
for (i in 0 until Vars.days.size) {
adapter.addFragment(DayFragment())
}
viewPager.viewTreeObserver.addOnPreDrawListener {
viewPager.currentItem = Vars.selectedDay
true
}
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (viewPager.isLaidOut) {
Vars.selectedDay = position
}
}
})
viewPager.adapter = adapter
viewPager.offscreenPageLimit = adapter.itemCount
return view
}
}
class DayPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
private val fragmentList = mutableListOf<Fragment>()
fun addFragment(fragment: Fragment) {
fragmentList.add(fragment)
}
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
class DayFragment : Fragment() {
private val handler = Handler(Looper.getMainLooper())
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_day, container, false)
val dayLayout: LinearLayout = view.findViewById(R.id.layoutDayMain)
createDayView(Vars.dayList[Vars.dayCounter], dayLayout, resources)
Vars.dayCounter += 1
return view
}
private fun highlightCurrent(
subj: Subject,
day: Day,
subjectDrawable: GradientDrawable,
subjectStroke: Int,
i: Int
) {
handler.postDelayed(object : Runnable {
override fun run() {
subjectDrawable.setStroke(0, 0)
val now = LocalTime.now()
val nowD = LocalDate.now().dayOfMonth
val nowH = now.hour
val nowM = now.minute
val nowT = LocalTime.of(nowH, nowM)
lateinit var subjST: LocalTime
lateinit var subjET: LocalTime
val subjD = day.date.split(".")[0].toInt()
if ("T" !in subj.start && "T" !in subj.end) {
val subjSH = subj.start.split(":")[0].toInt()
val subjSM = subj.start.split(":")[1].toInt()
val subjEH = subj.end.split(":")[0].toInt()
val subjEM = subj.end.split(":")[1].toInt()
subjST = LocalTime.of(subjSH, subjSM)
subjET = LocalTime.of(subjEH, subjEM)
} else {
val subjSH = subj.start
.split("T")[1]
.split(":")[0]
.toInt()
val subjSM = subj.start
.split("T")[1]
.split(":")[1]
.toInt()
val subjEH = subj.end
.split("T")[1]
.split(":")[0]
.toInt()
val subjEM = subj.end
.split("T")[1]
.split(":")[1]
.toInt()
subjST = LocalTime.of(subjSH, subjSM)
subjET = LocalTime.of(subjEH, subjEM)
}
if (
nowT in subjST..subjET &&
nowD == subjD
) {
if (i < day.subjects.size - 1 && !day.subjects[i + 1].free && !subj.free) {
subjectDrawable.setStroke(
10,
ContextCompat.getColor(
requireContext(),
subjectStroke
)
)
} else if (i < day.subjects.size - 1 && day.subjects[i + 1].free && nowT != subjET) {
subjectDrawable.setStroke(
10,
ContextCompat.getColor(
requireContext(),
subjectStroke
)
)
} else if (i < day.subjects.size - 1 && subj.free && !day.subjects[i + 1].free && nowT != subjET) {
subjectDrawable.setStroke(
10,
ContextCompat.getColor(
requireContext(),
subjectStroke
)
)
} else if (i == day.subjects.size - 1) {
subjectDrawable.setStroke(
10,
ContextCompat.getColor(
requireContext(),
subjectStroke
)
)
}
} else if (
nowD == subjD &&
i > 0
) {
lateinit var prevET: LocalTime
val prev = day.subjects[i - 1]
if ("T" !in prev.start && "T" !in prev.end) {
val prevEH = prev.end
.split(":")[0]
.toInt()
val prevEM = prev.end
.split(":")[1]
.toInt()
prevET = LocalTime.of(prevEH, prevEM)
} else {
val prevEH = prev.end
.split("T")[1]
.split(":")[0]
.toInt()
val prevEM = prev.end
.split("T")[1]
.split(":")[1]
.toInt()
prevET = LocalTime.of(prevEH, prevEM)
}
if (
nowT < subjST &&
nowT > prevET
) {
subjectDrawable.setStroke(
10,
ContextCompat.getColor(
requireContext(),
subjectStroke
)
)
}
}
requireView().postInvalidate()
// repeat every second
handler.postDelayed(this, 1000L)
}
}, 0L)
}
override fun onDestroyView() {
super.onDestroyView()
// Stop updating UI when the view is destroyed
handler.removeCallbacksAndMessages(null)
}
@SuppressLint("UseCompatLoadingForDrawables")
private fun createDayView(day: Day, rootLayout: LinearLayout, resources: Resources) {
val customFont = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
val scrollView = ScrollView(requireContext())
scrollView.scrollBarStyle = R.style.Theme_Solon_Scrollbar
val dayLayout = LinearLayout(requireContext())
dayLayout.orientation = LinearLayout.VERTICAL
val nameDateTextView = TextView(requireContext())
nameDateTextView.text = String.format("%s %s", day.name, day.date)
nameDateTextView.setTypeface(customFont, Typeface.BOLD_ITALIC)
//nameDateTextView.textSize = 28f
nameDateTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
nameDateTextView.setPadding(10, 0, 10, 40)
nameDateTextView.gravity = Gravity.CENTER_HORIZONTAL
rootLayout.addView(nameDateTextView)
var prevSubj = Subject()
prevSubj.name = ""
prevSubj.place = ""
prevSubj.substitute = ""
prevSubj.teacher = ""
prevSubj.number = ""
prevSubj.start = ""
prevSubj.end = ""
prevSubj.free = false
for ((i, subj) in day.subjects.withIndex()) {
val subjectBackground = when (subj.substitute) {
"0" -> if (subj.place != "Volno") {
R.color.primary_transparent_bg
} else {
R.color.cal_week_free_transparent_bg
}
"1" -> if (subj.place != "Volno") {
R.color.cal_week_substitute_transparent_bg
} else {
R.color.cal_week_free_transparent_bg
}
"2" -> if (subj.place != "Volno") {
R.color.cal_week_event_transparent_bg
} else {
R.color.cal_week_free_transparent_bg
}
else -> R.color.primary_transparent_bg
}
val subjectStroke = when (subj.substitute) {
"0" -> if (subj.place != "Volno") {
R.color.primary
} else {
R.color.cal_week_free
}
"1" -> if (subj.place != "Volno") {
R.color.cal_week_substitute
} else {
R.color.cal_week_free
}
"2" -> if (subj.place != "Volno") {
R.color.cal_week_event
} else {
R.color.cal_week_free
}
else -> R.color.primary
}
val subjectDrawable = GradientDrawable()
subjectDrawable.shape = GradientDrawable.RECTANGLE
subjectDrawable.cornerRadius =
resources.getDimension(R.dimen.corner_radius) // Specify corner radius in resources
subjectDrawable.setColor(
ContextCompat.getColor(
requireContext(),
subjectBackground
)
)
// launch a periodic check for highlighting the current lecture
highlightCurrent(subj, day, subjectDrawable, subjectStroke, i)
val subjectLayout = LinearLayout(requireContext())
subjectLayout.orientation = LinearLayout.HORIZONTAL
subjectLayout.gravity = Gravity.CENTER_VERTICAL
subjectLayout.background = subjectDrawable
// height specifier
val heightSpecifier = View(requireContext())
heightSpecifier.layoutParams = LinearLayout.LayoutParams(
0,
160
)
subjectLayout.addView(heightSpecifier)
val subjectNumberDrawable = GradientDrawable()
subjectNumberDrawable.shape = GradientDrawable.RECTANGLE
subjectNumberDrawable.cornerRadius = resources.getDimension(R.dimen.corner_radius)
subjectNumberDrawable.setColor(
ContextCompat.getColor(
requireContext(),
subjectBackground
)
)
val subjectNumberTextView = TextView(requireContext())
subjectNumberTextView.typeface = customFont
subjectNumberTextView.text = subj.number
//subjectNumberTextView.textSize = 33f
subjectNumberTextView.setPadding(20, 20, 20, 20)
subjectNumberTextView.gravity = Gravity.CENTER // Center text vertically
subjectNumberTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
subjectNumberTextView.background = subjectNumberDrawable
val frameLayout = FrameLayout(requireContext())
val layoutParams = LinearLayout.LayoutParams(
120,
FrameLayout.LayoutParams.MATCH_PARENT,
)
frameLayout.foregroundGravity = Gravity.CENTER
frameLayout.layoutParams = layoutParams
frameLayout.addView(subjectNumberTextView)
if (subj.substitute != "2") {
subjectLayout.addView(frameLayout)
}
val namePlaceLL = LinearLayout(requireContext())
namePlaceLL.orientation = LinearLayout.VERTICAL
val subjectNameTextView = TextView(requireContext())
if (subj.name != "Volno") {
subjectNameTextView.text = subj.name.uppercase()
}
subjectNameTextView.setTypeface(customFont, Typeface.BOLD)
//subjectNameTextView.textSize = 29f
subjectNameTextView.setPadding(0, 0, 0, 0)
subjectNameTextView.gravity = Gravity.CENTER_VERTICAL
subjectNameTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
if (!subj.free) {
namePlaceLL.addView(subjectNameTextView)
}
val subjectPlaceTextView = TextView(requireContext())
subjectPlaceTextView.typeface = customFont
subjectPlaceTextView.text = subj.place
subjectPlaceTextView.setPadding(0, 0, 0, 0)
if (subj.free) {
// italic
subjectPlaceTextView.setTypeface(customFont, Typeface.ITALIC)
subjectPlaceTextView.text = "Volno "
}
//subjectPlaceTextView.textSize = 22f
subjectPlaceTextView.gravity = Gravity.CENTER_VERTICAL
subjectPlaceTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
if (subj.substitute != "2") {
namePlaceLL.addView(subjectPlaceTextView)
}
namePlaceLL.setPadding(30, 15, 20, 15)
subjectLayout.addView(namePlaceLL)
if (subj.teacher == "Jindřich Svoboda") {
val img = ImageView(requireContext())
img.setImageDrawable(resources.getDrawable(R.drawable.spock))
val frameLayout2 = FrameLayout(requireContext())
val layoutParams2 = LinearLayout.LayoutParams(
100,
120,
)
frameLayout2.foregroundGravity = Gravity.CENTER
frameLayout2.layoutParams = layoutParams2
frameLayout2.addView(img)
subjectLayout.addView(frameLayout2)
}
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
params.gravity = Gravity.END
val timeTeacherLayout = LinearLayout(requireContext())
timeTeacherLayout.orientation = LinearLayout.VERTICAL
timeTeacherLayout.layoutParams = params
timeTeacherLayout.gravity = Gravity.CENTER_VERTICAL
val subjectTimeTextView = TextView(requireContext())
val subjectTimeString = subj.start + "" + subj.end
subjectTimeTextView.typeface = customFont
subjectTimeTextView.text = subjectTimeString
//subjectTimeTextView.textSize = 20f
subjectTimeTextView.setPadding(10, 0, 20, 0)
subjectTimeTextView.gravity = Gravity.END
subjectTimeTextView.isSingleLine = true
subjectTimeTextView.textAlignment = View.TEXT_ALIGNMENT_TEXT_END
subjectTimeTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
val subjectTeacherTextView = TextView(requireContext())
subjectTeacherTextView.text = subj.teacher
subjectTeacherTextView.typeface = customFont
//subjectTeacherTextView.textSize = 15f
subjectTeacherTextView.setPadding(0, 10, 20, 10)
subjectTeacherTextView.textAlignment = View.TEXT_ALIGNMENT_TEXT_END
subjectTeacherTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
timeTeacherLayout.addView(subjectTimeTextView)
if (subj.name != "Volno") {
subjectTimeTextView.setPadding(0, 10, 20, 0)
timeTeacherLayout.addView(subjectTeacherTextView)
}
timeTeacherLayout.setPadding(0, 0, 10, 0)
if (subj.substitute != "2") {
subjectLayout.addView(timeTeacherLayout)
}
// drawables
val substituteDrawable = GradientDrawable()
substituteDrawable.shape = GradientDrawable.RECTANGLE
substituteDrawable.cornerRadius = resources.getDimension(R.dimen.corner_radius)
substituteDrawable.setColor(
ContextCompat.getColor(
requireContext(),
R.color.cal_week_substitute
)
)
val eventDrawable = GradientDrawable()
eventDrawable.shape = GradientDrawable.RECTANGLE
eventDrawable.cornerRadius = resources.getDimension(R.dimen.corner_radius)
eventDrawable.setColor(
ContextCompat.getColor(
requireContext(),
R.color.cal_week_event
)
)
// Attach an OnPreDrawListener to the rootLayout's ViewTreeObserver
rootLayout.viewTreeObserver.addOnPreDrawListener(object :
ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
// Remove the listener to avoid multiple callbacks
rootLayout.viewTreeObserver.removeOnPreDrawListener(this)
// Call your function or perform an action here
if (subj.free) {
subjectPlaceTextView.text = "Volno "
}
nameDateTextView.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
if (subj.substitute == "2") {
namePlaceLL.textAlignment = View.TEXT_ALIGNMENT_CENTER
namePlaceLL.setPadding(0, 0, 0, 0)
subjectNameTextView.layoutParams = LinearLayout.LayoutParams(
subjectLayout.width,
LinearLayout.LayoutParams.MATCH_PARENT
)
subjectNameTextView.textAlignment = View.TEXT_ALIGNMENT_CENTER
subjectNameTextView.gravity = Gravity.CENTER_VERTICAL
}
// Return true to continue with the drawing, or false to cancel
return true
}
})
val subjectLayoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
subjectLayoutParams.setMargins(
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4,
0,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4,
0
)
subjectLayout.layoutParams = subjectLayoutParams
if (prevSubj.name != subj.name && i != 0) {
val subjectSeparator = Space(requireContext())
subjectSeparator.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height) // Specify separator height in resources
)
dayLayout.addView(subjectSeparator)
} else if (i != 0) {
val subjectSeparator = ImageView(requireContext())
subjectSeparator.setImageResource(R.drawable.squiggly_line_100_yellow_transparent_one_color_edge)
subjectSeparator.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)
)
dayLayout.addView(subjectSeparator)
}
subjectNumberTextView.textSize = resources.getDimension(R.dimen.text_size_subject_number) / resources.displayMetrics.scaledDensity
subjectNameTextView.textSize = resources.getDimension(R.dimen.text_size_subject_name) / resources.displayMetrics.scaledDensity
if (subj.free) {
subjectPlaceTextView.textSize =
resources.getDimension(R.dimen.text_size_subject_place_free) / resources.displayMetrics.scaledDensity
} else {
subjectPlaceTextView.textSize =
resources.getDimension(R.dimen.text_size_subject_place) / resources.displayMetrics.scaledDensity
}
subjectTimeTextView.textSize = resources.getDimension(R.dimen.text_size_subject_time) / resources.displayMetrics.scaledDensity
subjectTeacherTextView.textSize = resources.getDimension(R.dimen.text_size_subject_teacher) / resources.displayMetrics.scaledDensity
dayLayout.addView(subjectLayout)
prevSubj = subj
}
val subjectSeparator = Space(requireContext())
subjectSeparator.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height) // Specify separator height in resources
)
dayLayout.addView(subjectSeparator)
val dayEndTextView = TextView(requireContext())
val endTime = day.subjects[day.subjects.size - 1].end
Vars.dayEndString = "Den končí v $endTime"
dayEndTextView.text = Vars.dayEndString
dayEndTextView.typeface = customFont
//dayEndTextView.setAutoSizeTextTypeUniformWithConfiguration(55, 85, 1, 0)
dayEndTextView.setPadding(0, 25, 0, 25)
dayEndTextView.textAlignment = View.TEXT_ALIGNMENT_CENTER
dayEndTextView.gravity = Gravity.CENTER
dayEndTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
val dayEndDrawable = GradientDrawable()
dayEndDrawable.shape = GradientDrawable.RECTANGLE
dayEndDrawable.cornerRadius = resources.getDimension((R.dimen.corner_radius))
dayEndDrawable.setColor(
ContextCompat.getColor(
requireContext(),
R.color.primary_transparent_bg
)
)
dayEndDrawable.setStroke(
10,
ContextCompat.getColor(
requireContext(),
R.color.primary
)
)
dayEndTextView.background = dayEndDrawable
val dayEndSeparator = Space(requireContext())
dayEndSeparator.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height) // Specify separator height in resources
)
val dayEndSeparator2 = Space(requireContext())
dayEndSeparator2.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height) // Specify separator height in resources
)
val endParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
130
)
endParams.setMargins(
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4,
0,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4,
0
)
dayEndTextView.layoutParams = endParams
dayLayout.addView(dayEndSeparator)
dayLayout.addView(dayEndTextView)
dayLayout.addView(dayEndSeparator2)
scrollView.addView(dayLayout)
rootLayout.addView(scrollView)
// fill in the rest with empty space
val sepr = Space(requireContext())
sepr.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
Vars.height - rootLayout.height
)
rootLayout.addView(sepr)
nameDateTextView.textSize = resources.getDimension(R.dimen.text_size_name_date) / resources.displayMetrics.scaledDensity
dayEndTextView.textSize = resources.getDimension(R.dimen.text_size_day_end) / resources.displayMetrics.scaledDensity
}
}
class Subject {
lateinit var name: String
lateinit var place: String
lateinit var teacher: String
lateinit var substitute: String
lateinit var number: String
lateinit var start: String
lateinit var end: String
var free: Boolean = false
companion object {
fun freeSubject(number: String, start: String, end: String): Subject {
val s = Subject()
s.name = "Volno"
s.place = "Volno"
s.teacher = "Volno"
s.substitute = "0"
s.number = number
s.start = start
s.end = end
s.free = true
return s
}
fun empty(): Subject {
val s = Subject()
s.name = "err"
s.place = "error"
s.teacher = "error"
s.substitute = "0"
s.number = "0"
s.start = "00:00"
s.end = "00:00"
s.free = false
return s
}
}
override fun toString(): String {
return "subject ${this.name} at ${this.place} from ${this.start} to ${this.end}; free? ${this.free}"
}
}
class Day {
var name: String = ""
var date: String = ""
var subjects: List<Subject> = mutableListOf()
}
@@ -1,48 +0,0 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.view.LayoutInflater
import androidx.core.content.ContextCompat
class Dialogs {
@SuppressLint("InflateParams")
class LanguageChange(context: Context) : AlertDialog(context) {
init {
// Initialize your dialog properties
setTitle("Změnit jazyk")
setCancelable(true)
// Inflate the layout for the dialog
val view = LayoutInflater.from(context).inflate(R.layout.radio_group_language, null)
window?.setBackgroundDrawableResource(R.drawable.alert_dialog_background)
setView(view)
// Set positive button (e.g., "OK" button)
setButton(
BUTTON_POSITIVE,
"OK"
) { _, _ ->
// functionality here
dismiss()
}
// Set negative button (e.g., "Cancel" button)
setButton(
BUTTON_NEGATIVE,
"Zrušit"
) { _, _ ->
dismiss()
}
}
override fun onStart() {
super.onStart()
val okButton = getButton(BUTTON_POSITIVE)
okButton?.setTextColor(ContextCompat.getColor(context, R.color.white))
val cancelButton = getButton(BUTTON_NEGATIVE)
cancelButton?.setTextColor(ContextCompat.getColor(context, R.color.white))
}
}
}
@@ -0,0 +1,294 @@
package com.odweta.solon
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.widget.Toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay
import org.json.JSONObject
import kotlin.math.cos
private var week: List<Day> = mutableListOf()
private var toastShowing: Boolean = false
private var toastShown: Boolean = false
private var toast2Shown: Boolean = false
private lateinit var coscope_: CoroutineScope
private fun setOnInternetAvailListener(ctx: Context, coscope: CoroutineScope) {
coscope.launch {
while (true) {
if (Util.internetAvail(ctx) && !internetAvail) {
internetAvail = Util.internetAvail(ctx)
coscope.launch {
fetchNewData(ctx, coscope)
}
} else if (!internetAvail) {
internetAvail = Util.internetAvail(ctx)
toolbarText.value = "Bez připojení k internetu."
} else {
internetAvail = Util.internetAvail(ctx)
}
delay(1000) // Check for internet availability every second
}
}
}
private fun fetchNewData(ctx: Context, coscope: CoroutineScope) {
coscope.launch {
done = false
// variables for the loading animation
val loadingArray: Array<String> = arrayOf(
"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
// show a loading animation
while (!done && Util.internetAvail(ctx)) {
for (text in loadingArray) {
toolbarText.value = text
delay(delayTime)
}
for (text in loadingArray.reversedArray().slice(1..<loadingArray.size)) {
toolbarText.value = text
delay(delayTime)
}
delay(delayTime)
}
// determine the role of the student
val role = if (Parsing.userIsParent()) {
" Rodič"
} else {
" Student"
}
// display the full name of the user in the toolbar
toolbarText.value =
"${
JSONObject(
Util.fileToString(
userDataFilePath
)
).getString("fullName")}$role"
}
coscope.launch {
launchPhase1(ctx)
}
}
fun launchPhase0(ctx: Context, coscope: CoroutineScope) {
// start a sign in activity
// gather input and put it into global state (if login is needed)
// otherwise show the splash screen and load the app
/* check if there is a saved user account
*
* if yes, use that
* otherwise show the login activity
*/
coscope_ = coscope
fileDir = ctx.filesDir
width = Util.getDisplayWidth(ctx)
height = Util.getDisplayHeight(ctx)
val accSaved: Boolean = Account.haveSaved()
if (!accSaved) {
// show the sign in screen
screenToShow.value = Screen.SignIn
} else {
// show the splash/loading screen
screenToShow.value = Screen.Splash
coscope.launch {
// launch!
launchPhase1(ctx)
}
}
}
private suspend fun launchPhase1(ctx: Context) {
val toast: Toast = Toast.makeText(
ctx,
"Nelze načíst data, zkuste se připojit k internetu.",
Toast.LENGTH_LONG
)
val toast2: Toast = Toast.makeText(
ctx,
"Připojeno k internetu.",
Toast.LENGTH_SHORT
)
// check if the file is available offline,
// if yes, load the data and don't connect to the internet
internetAvail = Util.internetAvail(ctx)
timeTableAvailOffline =
Util.fileExists(timeTableFilePath) && !Util.fileIsEmpty(timeTableFilePath)
marksAvailOffline =
Util.fileExists(marksFilePath) && !Util.fileIsEmpty(marksFilePath)
if (toastShown && internetAvail && !toast2Shown) {
toast2Shown = true
toast2.show()
}
if (!timeTableAvailOffline || internetAvail) {
// set up the API (required for later uses of it)
Network.setupAPI()
if (internetAvail) {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// get the JSON data
val timeTableJSON = Network.getTimeTableJSON()
// try to save them into a file for later use
timeTableAvailOffline =
Util.saveStringToFile(timeTableFilePath, timeTableJSON)
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
// fetch and parse marks
marksJSON = Network.getMarksJSON()
marksAvailOffline =
Util.saveStringToFile(marksFilePath, marksJSON)
marks = Parsing.parseMarksJSON(marksJSON)
// continue to launch phase 2
done = true
launchPhase2(ctx)
} else {
if (timeTableAvailOffline) {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// load the offline data
val timeTableJSON = Util.fileToString(timeTableFilePath)
// parse the data
if (timeTableJSON != "") {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
// continue launching
done = true
launchPhase2(ctx)
} else {
// make a toast or something
toast.show()
// try to reconnect to the internet
launchPhase1(ctx)
}
} else {
// make a toast or something
if (!toastShown) {
toast.show()
toastShowing = true
toastShown = true
}
// try to reconnect to the internet
launchPhase1(ctx)
}
}
} else {
// load offline
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// load the offline data
val timeTableJSON = Util.fileToString(timeTableFilePath)
marksJSON = Util.fileToString(marksFilePath)
if (marksJSON == "") {
marksJSON =
"""{"message": "Zatím žádné známky, zkuste aktualizovat data."}"""
}
// parse the data
if (timeTableJSON != "") {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
marks = Parsing.parseMarksJSON(marksJSON)
// continue launching
done = true
launchPhase2(ctx)
} else {
// make a toast or something
if (!toastShown) {
toast.show()
toastShowing = true
toastShown = true
}
// try to reconnect to the internet
launchPhase1(ctx)
}
}
}
private fun launchPhase2(ctx: Context) {
days = week
for ((i, day) in week.withIndex()) {
dayMap[i] = day
dayList.add(day)
}
loaded = true
coscope_.launch {
setOnInternetAvailListener(ctx, coscope_)
}
val role = if (Parsing.userIsParent()) {
" Rodič"
} else {
" Student"
}
toolbarText.value = "${
JSONObject(
Util.fileToString(
userDataFilePath
)
).getString("fullName")}$role"
// set the main content view
screenToShow.value = Screen.Main
}
@@ -1,338 +1,82 @@
package com.odweta.solon
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.odweta.solon.ui.theme.SolonTheme
import kotlin.math.sign
interface MainActivityListener {
fun onSignIn(view: View, wantDelete: Boolean, dialog: Dialog?)
fun launchPhase1()
}
class MainActivity : AppCompatActivity(), MainActivityListener {
private var week: List<Day> = mutableListOf()
private var toastShowing: Boolean = false
private var toastShown: Boolean = false
private var toast2Shown: Boolean = false
// @section COMPANION
companion object {
private var intfc: MainActivityListener? = null
fun setListener(context: Context) {
if (context is MainActivityListener) {
intfc = context
}
}
fun callOnSignIn(view: View, wantDelete: Boolean, dialog: Dialog?) {
intfc?.onSignIn(view, wantDelete, dialog)
}
fun callLaunchPhase1() {
intfc?.launchPhase1()
}
}
// @section LISTENERS
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set variables
Vars.fileDir = applicationContext.filesDir
Vars.width = Util.getDisplayWidth(this)
Vars.height = Util.getDisplayHeight(this)
makeStatusBarBlack(window)
// start the app (coroutine because splashscreen)
lifecycleScope.launch {
launch()
}
}
// @section SIGN_IN
override fun onSignIn(view: View, wantDelete: Boolean, dialog: Dialog?) {
val acc = Account(
view.findViewById<EditText>(R.id.usernameField).text.toString(),
view.findViewById<EditText>(R.id.passwordField).text.toString(),
0
)
var checked = false
lifecycleScope.launch {
val btn = view.findViewById<Button>(R.id.signInButton)
while (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování")
if (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování.")
}
if (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování..")
}
if (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování...")
}
if (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování..")
}
if (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování.")
}
if (!checked) {
delay(300)
btn.text = String.format("%s", "Přihlašování")
}
}
btn.text = String.format("%s", "Přihlásit se")
}
lifecycleScope.launch {
val valid = Account.isValid(acc)
checked = true
if (valid) {
Account.save(acc)
if (!wantDelete) {
dialog?.dismiss()
}
launch()
} else {
if (wantDelete) {
Util.deleteFile(Vars.accountDataFilePath)
}
Toast.makeText(
applicationContext,
"Neplatné přihlašovací údaje nebo nejste připojeni k internetu, zkuste to znovu.",
Toast.LENGTH_LONG
).show()
}
}
}
// @section LAUNCH
private fun launch() {
setContentView(R.layout.activity_container)
/* check if there is a saved user account
* if yes, use that
* otherwise show the login activity
*/
val accSaved: Boolean = Account.haveSaved()
if (!accSaved) {
// show the sign in screen
showSignInScreen()
} else {
// show the splash/loading screen
showSplashScreen()
lifecycleScope.launch {
// launch!
launchPhase1()
}
}
}
override fun launchPhase1() {
val toast: Toast = Toast.makeText(
applicationContext,
"Nelze načíst data, zkuste se připojit k internetu.",
Toast.LENGTH_LONG
)
val toast2: Toast = Toast.makeText(
applicationContext,
"Připojeno k internetu.",
Toast.LENGTH_SHORT
)
lifecycleScope.launch {
// check if the file is available offline,
// if yes, load the data and don't connect to the internet
Vars.internetAvail = Util.internetAvail(applicationContext)
Vars.timeTableAvailOffline =
Util.fileExists(Vars.timeTableFilePath) && !Util.fileIsEmpty(Vars.timeTableFilePath)
Vars.marksAvailOffline =
Util.fileExists(Vars.marksFilePath) && !Util.fileIsEmpty(Vars.marksFilePath)
if (toastShown && Vars.internetAvail && !toast2Shown) {
toast2Shown = true
toast2.show()
setContent {
if (!loaded) {
launchPhase0(LocalContext.current, rememberCoroutineScope())
}
if (!Vars.timeTableAvailOffline || Vars.internetAvail) {
// set up the API (required for later uses of it)
Network.setupAPI()
when (signedInAndReadyToLaunch.value) {
true -> launchPhase0(LocalContext.current, rememberCoroutineScope())
else -> {}
}
if (Vars.internetAvail) {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// get the JSON data
val timeTableJSON = Network.getTimeTableJSON()
// try to save them into a file for later use
Vars.timeTableAvailOffline =
Util.saveStringToFile(Vars.timeTableFilePath, timeTableJSON)
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
// fetch and parse marks
Vars.marksJSON = Network.getMarksJSON()
Vars.marksAvailOffline =
Util.saveStringToFile(Vars.marksFilePath, Vars.marksJSON)
Vars.marks = Parsing.parseMarksJSON(Vars.marksJSON)
// continue to launch phase 2
launchPhase2()
} else {
if (Vars.timeTableAvailOffline) {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// load the offline data
val timeTableJSON = Util.fileToString(Vars.timeTableFilePath)
// parse the data
if (timeTableJSON != "") {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
// continue launching
launchPhase2()
} else {
// make a toast or something
toast.show()
// try to reconnect to the internet
launchPhase1()
}
} else {
// make a toast or something
if (!toastShown) {
toast.show()
toastShowing = true
toastShown = true
}
// try to reconnect to the internet
launchPhase1()
}
}
} else {
// load offline
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// load the offline data
val timeTableJSON = Util.fileToString(Vars.timeTableFilePath)
Vars.marksJSON = Util.fileToString(Vars.marksFilePath)
if (Vars.marksJSON == "") {
Vars.marksJSON =
"""{"message": "Zatím žádné známky, zkuste aktualizovat data."}"""
}
// parse the data
if (timeTableJSON != "") {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
Vars.marks = Parsing.parseMarksJSON(Vars.marksJSON)
// continue launching
launchPhase2()
} else {
// make a toast or something
if (!toastShown) {
toast.show()
toastShowing = true
toastShown = true
}
// try to reconnect to the internet
launchPhase1()
SolonTheme {
when (screenToShow.value) {
Screen.Main -> MainScreen()
Screen.Splash -> SplashScreen()
Screen.SignIn -> SignInScreen()
}
}
}
}
private fun launchPhase2() {
Vars.days = week
for ((i, day) in week.withIndex()) {
Vars.dayMap[i] = day
Vars.dayList.add(day)
}
@Composable
fun MainScreen(modifier: Modifier = Modifier) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
Box(
modifier = barModifier,
contentAlignment = Alignment.Center
) {
ToolbarScreen()
}
// set the main content view
ft(R.id.app_root, MainFragment(), R.anim.slide_up, R.anim.slide_down)
}
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
when (isShowing.value) {
IsShowing.Timetable -> TimetableScreen()
IsShowing.Marks -> MarksScreen()
IsShowing.Menu -> MenuScreen()
IsShowing.About -> AboutScreen()
}
}
// @section SPLASH_SCREENS
private fun showSignInScreen() {
ft(R.id.app_root, SignInMainFragment(), R.anim.slide_up, R.anim.slide_down)
}
private fun showSplashScreen() {
ft(R.id.app_root, SplashFragment(), R.anim.slide_up, R.anim.slide_down)
}
// Fragment Transition
private fun ft(container: Int, frag: Fragment, animIn: Int, animOut: Int) {
if (Util.isAppInForeground(applicationContext, packageName)) {
val ft = supportFragmentManager.beginTransaction()
ft.setCustomAnimations(
animIn,
animOut
)
ft.replace(container, frag)
ft.commit()
Box(
modifier = barModifier,
contentAlignment = Alignment.Center
) {
NavbarScreen()
}
}
}
}
@@ -1,202 +0,0 @@
package com.odweta.solon
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
class MainFragment : Fragment() {
private var done: Boolean = false
private var role: String = " - Student"
private lateinit var navbar: BottomNavigationView
private lateinit var toolbar: TextView
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.activity_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
MainActivity.setListener(requireContext())
setup(view)
}
private fun setup(view: View) {
toolbar = view.findViewById(R.id.toolbar)
//toolbar.setAutoSizeTextTypeUniformWithConfiguration(60, 65, 1, 0)
toolbar.textSize = resources.getDimension(R.dimen.text_size_toolbar) / resources.displayMetrics.scaledDensity
toolbar.ellipsize = null
toolbar.setHorizontallyScrolling(false)
role = if (Parsing.userIsParent()) {
" Rodič"
} else {
" Student"
}
if (
Util.fileToString(Vars.userDataFilePath) != "" &&
JSONObject(Util.fileToString(Vars.userDataFilePath)).has("fullName")
) {
toolbar.text =
String.format(
"%s",
"${JSONObject(
Util.fileToString(
Vars.userDataFilePath
)
).getString("fullName")}$role"
)
} else {
toolbar.text = String.format("%s", "Přihlašte se, prosím.")
}
navbar = view.findViewById(R.id.bottomNav)
var frag = requireFragmentManager().findFragmentById(R.id.fragmentContainer)
if (!Vars.resetNav) {
if (frag == null || frag is CalWeekFragment) {
frag = CalWeekFragment()
navbar.selectedItemId = R.id.nav_timetable
}
if (frag is MarksFragment) {
frag = MarksFragment()
navbar.selectedItemId = R.id.nav_marks
}
if (frag is MenuFragment) {
frag = MenuFragment()
navbar.selectedItemId = R.id.nav_menu
}
} else {
frag = CalWeekFragment()
navbar.selectedItemId = R.id.nav_timetable
Vars.resetNav = false
}
requireFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, frag)
.commit()
navbar.setOnItemSelectedListener {
val ft = requireFragmentManager().beginTransaction()
when (it.itemId) {
R.id.nav_timetable -> {
ft.setCustomAnimations(
R.anim.slide_up,
R.anim.slide_down,
)
ft.replace(R.id.fragmentContainer, CalWeekFragment())
}
R.id.nav_marks -> {
ft.setCustomAnimations(
R.anim.slide_up,
R.anim.slide_down,
)
ft.replace(R.id.fragmentContainer, MarksFragment())
}
R.id.nav_menu -> {
ft.setCustomAnimations(
R.anim.slide_up,
R.anim.slide_down,
)
ft.replace(R.id.fragmentContainer, MenuFragment())
}
else -> return@setOnItemSelectedListener false
}
ft.commit()
true
}
setOnInternetAvailListener()
done = true
}
private fun setOnInternetAvailListener() {
lifecycleScope.launch {
while (true) {
if (Util.internetAvail(requireContext()) && !Vars.internetAvail) {
Vars.internetAvail = Util.internetAvail(requireContext())
withContext(Dispatchers.Main) {
fetchNewData()
}
} else if (!Vars.internetAvail) {
Vars.internetAvail = Util.internetAvail(requireContext())
toolbar.text = String.format("%s", "Bez připojení k internetu.")
} else {
Vars.internetAvail = Util.internetAvail(requireContext())
}
delay(1000) // Check for internet availability every second
}
}
}
private fun fetchNewData() {
lifecycleScope.launch {
done = false
// variables for the loading animation
val loadingArray: Array<String> = arrayOf(
"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
// show a loading animation
while (!done && Util.internetAvail(requireContext())) {
for (text in loadingArray) {
toolbar.text = text
delay(delayTime)
}
for (text in loadingArray.reversedArray().slice(1..<loadingArray.size)) {
toolbar.text = text
delay(delayTime)
}
delay(delayTime)
}
// determine the role of the student
role = if (Parsing.userIsParent()) {
" Rodič"
} else {
" Student"
}
// display the full name of the user in the toolbar
toolbar.text =
String.format(
"%s",
"${JSONObject(
Util.fileToString(
Vars.userDataFilePath
)
).getString("fullName")}$role"
)
}
lifecycleScope.launch {
MainActivity.callLaunchPhase1()
}
}
}
@@ -1,307 +0,0 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.app.Dialog
import android.graphics.Typeface
import android.os.Bundle
import android.view.GestureDetector
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.Space
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.DialogFragment
// details for the marks of one subject
class MarkDetailsFragment : DialogFragment() {
private lateinit var gestureDetector: GestureDetector
private var initialTouchY: Float = 0f
private val swipeThreshold = 100
private lateinit var rootLayout: LinearLayout
private lateinit var scrollView: ScrollView
private var isScrollingToDismissEnabled = false
private var isScrollingToTop = true
private lateinit var dialog: Dialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set the style so the dialog is fullscreen
setStyle(STYLE_NORMAL, R.style.Theme_Solon)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// inflate the layout
val view = inflater.inflate(R.layout.activity_mark_details, container, false)
// get the root layout
rootLayout = view.findViewById(R.id.one_mark_root)
// set up gestures
gestures(view)
// construct the view
construct()
// return the view
return view
}
@SuppressLint("ClickableViewAccessibility")
private fun gestures(view: View) {
// Set up GestureDetector
gestureDetector = GestureDetector(requireContext(), SwipeGestureListener())
scrollView = view.findViewById(R.id.mark_detail_scroll)
scrollView.scrollY = 0
scrollView.setOnTouchListener { _, event ->
scrollView.performClick()
gestureDetector.onTouchEvent(event)
// Return false to allow normal scrolling
false
}
scrollView.viewTreeObserver.addOnScrollChangedListener {
isScrollingToTop = !scrollView.canScrollVertically(-1)
}
// Attach the gesture detector to the root layout
view.setOnTouchListener { _, event ->
view.performClick()
handleTouchEvent(event)
}
}
inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
// Reset the flag when the user touches the screen
isScrollingToDismissEnabled = false
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffY = e1?.let { e2.y - it.y } ?: 0f
if (diffY > swipeThreshold) {
// Swiped down
if (isScrollingToTop) {
// Enable dismissing only if scrolled to top
isScrollingToDismissEnabled = true
dismiss()
}
}
return false
}
}
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
private fun construct() {
/* show all the marks in separate views with yet another onclick listener for even more
* details
*/
// TV == TextView
// LL == LinearLayout
// DR == Drawable
// FL == FrameLayout
// SV == ScrollView
val marks = MarksFragment.currentMarks
val idx = MarksFragment.idx
if (idx !in 0 until marks.size) {
return
}
val font = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
val gradeTV = TextView(requireContext())
gradeTV.text = marks[idx].grade
gradeTV.textSize = 60f
gradeTV.setTypeface(font, Typeface.BOLD)
gradeTV.background = Util.getAvgDrawable(requireContext(), marks[idx!!].grade)
gradeTV.gravity = Gravity.CENTER
val pr = LinearLayout.LayoutParams(
Util.getDisplayWidth(requireContext())/3,
Util.getDisplayWidth(requireContext())/3
)
pr.setMargins(0, 100, 0, 100)
gradeTV.layoutParams = pr
gradeTV.setPadding(20, 20, 20, 20)
gradeTV.setTextColor(ContextCompat.getColor(requireContext(), R.color.white))
val subjectDesc = makeTV("Předmět:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/2)
val subjectTV = makeTV(Vars.subjectNameMap[marks[idx!!].subject] ?: marks[idx!!].subject, Typeface.NORMAL, 22f)
val descDesc = makeTV("Popis:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3)
val descTV = makeTV(marks[idx!!].theme, Typeface.NORMAL, 22f)
val dateDesc = makeTV("Datum:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3)
val dateTV = makeTV(getDate(marks[idx!!].date), Typeface.NORMAL, 22f)
val weightDesc = makeTV("Váha:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3)
val weightTV = makeTV((marks[idx!!].weight).replace(".", ","), Typeface.NORMAL, 22f)
val subjectRow = makeRow(subjectDesc, subjectTV)
val descRow = makeRow(descDesc, descTV)
val dateRow = makeRow(dateDesc, dateTV)
val weightRow = makeRow(weightDesc, weightTV)
rootLayout.addView(gradeTV)
rootLayout.addView(subjectRow)
rootLayout.addView(descRow)
rootLayout.addView(dateRow)
rootLayout.addView(weightRow)
}
private fun getDate(dateIn: String): String {
val dateList = dateIn
.split("T")[0]
.split("-")
return "${dateList[2]}. ${dateList[1]}. ${dateList[0]}"
}
private fun makeTV(text: String, typeface: Int, width: Int): TextView {
val font = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
val tv = TextView(requireContext())
tv.text = text
tv.textSize = 25f
tv.textAlignment = View.TEXT_ALIGNMENT_CENTER
tv.setPadding(40, 20, 20, 20)
tv.setTypeface(font, typeface)
tv.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
val p = LinearLayout.LayoutParams(
width,
LinearLayout.LayoutParams.MATCH_PARENT
)
tv.layoutParams = p
tv.gravity = Gravity.CENTER_VERTICAL
tv.ellipsize = null
tv.setHorizontallyScrolling(false)
return tv
}
private fun makeTV(text: String, typeface: Int, size: Float): TextView {
val font = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
val tv = TextView(requireContext())
tv.text = text
tv.textSize = size
tv.textAlignment = View.TEXT_ALIGNMENT_CENTER
tv.setPadding(40, 20, 20, 20)
val p = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
p.gravity = Gravity.CENTER
tv.layoutParams = p
tv.setTypeface(font, typeface)
tv.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
tv.gravity = Gravity.CENTER_VERTICAL
tv.ellipsize = null
tv.setHorizontallyScrolling(false)
return tv
}
private fun makeRow(left: TextView, right: TextView): LinearLayout {
val ll = LinearLayout(requireContext())
ll.orientation = LinearLayout.VERTICAL
ll.gravity = Gravity.CENTER_HORIZONTAL
val p = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
p.setMargins(20, 0, 20, 0)
ll.layoutParams = p
ll.background = ResourcesCompat.getDrawable(resources, R.drawable.pastel_yellow_no_stroke, null)!!
ll.addView(left)
ll.addView(right)
val otherLL = LinearLayout(requireContext())
otherLL.orientation = LinearLayout.VERTICAL
otherLL.addView(createSpace())
val root = LinearLayout(requireContext())
root.orientation = LinearLayout.VERTICAL
root.layoutParams = LinearLayout.LayoutParams(
Util.getDisplayWidth(requireContext())-40,
LinearLayout.LayoutParams.MATCH_PARENT
)
root.addView(ll)
root.addView(otherLL)
return root
}
private fun createSpace(): Space {
val space = Space(requireContext())
space.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)
)
return space
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog.setCancelable(true)
return dialog
}
@Suppress("SameReturnValue")
private fun handleTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialTouchY = event.rawY
}
MotionEvent.ACTION_MOVE -> {
val deltaY = event.rawY - initialTouchY
dialog.window?.attributes?.y = deltaY.toInt()
dialog.window?.attributes?.windowAnimations = 0
dialog.show()
// Check if the dialog has been dragged beyond a threshold and dismiss if necessary
if (deltaY > 200) {
dismiss()
}
}
MotionEvent.ACTION_UP -> {
initialTouchY = 0f
}
}
return true
}
}
@@ -0,0 +1,497 @@
package com.odweta.solon
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
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.fillMaxHeight
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.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
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.PastelGreen
import com.odweta.solon.ui.theme.PastelGreenTransparent
import com.odweta.solon.ui.theme.PastelOrange
import com.odweta.solon.ui.theme.PastelOrangeTransparent
import com.odweta.solon.ui.theme.PastelRed
import com.odweta.solon.ui.theme.PastelRedTransparent
import com.odweta.solon.ui.theme.PastelYellow
import com.odweta.solon.ui.theme.PastelYellowTransparent
enum class MarksShow {
All,
Subject,
Detail
}
var marksShow: MutableState<MarksShow> = mutableStateOf(MarksShow.All)
private var subjectToShow: String = ""
private var markToShow: Mark = Mark()
@Composable
fun MarksScreen() {
when (marksShow.value) {
MarksShow.All -> MarksAll()
MarksShow.Subject -> MarksSubject()
MarksShow.Detail -> MarksDetail()
}
}
private fun getAvgColor(avg: String, isBorder: Boolean = false): Color {
val aux = avg.replace(",", ".").toFloat().toInt()
return if (aux in 1..<2) {
if (isBorder) {
PastelGreen
} else {
PastelGreenTransparent
}
} else if (aux in 2..<3) {
if (isBorder) {
PastelYellow
} else {
PastelYellowTransparent
}
} else if (aux in 3..<4) {
if (isBorder) {
PastelOrange
} else {
PastelOrangeTransparent
}
} else {
if (isBorder) {
PastelRed
} else {
PastelRedTransparent
}
}
}
@Composable
fun MarksAll() {
val scrollState = rememberScrollState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 5.dp)
.verticalScroll(scrollState)
) {
for ((subject, markList) in marks) {
var marksString = ""
for (m in 0 until markList.size-1) {
if (markList[m].grade in "0123456789") {
marksString += markList[m].grade + ", "
}
}
marksString += markList[markList.size-1].grade
if (marks.keys.indexOf(subject) > 0) {
Box(modifier = Modifier.height(10.dp))
}
Row(
modifier = Modifier
.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(
modifier = Modifier
.width(210.dp)
.fillMaxHeight()
.background(
color = getAvgColor(Util.avg(markList)), // decide on the color (not transparent)
shape = RoundedCornerShape(roundedCornerDimen)
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = subject.uppercase(),
fontSize = 35.sp,
color = Color.White,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier
.width(100.dp)
)
Box(
modifier = Modifier
.width(10.dp)
.fillMaxHeight()
.background(Color.Black)
)
Text(
text = Util.avg(markList).replace(".", ","),
fontSize = 30.sp,
color = Color.White,
textAlign = TextAlign.Center,
modifier = Modifier
.width(100.dp)
)
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
Text(
text = marksString,
fontSize = 22.sp,
color = Color.White
)
}
}
}
}
}
@Composable
fun MarksSubject() {
val scrollState = rememberScrollState()
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Top,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 5.dp)
) {
Button(
onClick = {
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(
imageVector = Icons.Default.ArrowBackIosNew, // Replace with your desired icon
contentDescription = "Back icon",
modifier = Modifier.size(35.dp) // Adjust size if needed
)
}
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
.fillMaxWidth()
.border(
width = 5.dp,
color = PastelYellow,
shape = RoundedCornerShape(roundedCornerDimen)
)
.background(PastelYellowTransparent)
.padding(vertical = 20.dp)
) // subject
}
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
text = Util.avg(marks[subjectToShow]!!).replace(".", ","),
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier
.width(100.dp)
.height(100.dp)
.border(
width = 5.dp,
color = getAvgColor(
Util.avg(marks[subjectToShow]!!),
true
),
shape = RoundedCornerShape(roundedCornerDimen)
)
.background(
getAvgColor(
Util.avg(marks[subjectToShow]!!)
)
)
.padding(vertical = 32.5.dp)
) // average
}
Spacer(modifier = Modifier.height(20.dp))
Column(modifier = Modifier.verticalScroll(scrollState)) {
for ((i, mark) in marks[subjectToShow]!!.withIndex()) {
if (i > 0) {
Spacer(modifier = Modifier.height(10.dp))
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 5.dp)
.background(
color = getAvgColor(mark.grade),
shape = RoundedCornerShape(roundedCornerDimen)
)
.padding(vertical = 20.dp)
.clickable {
markToShow = mark
marksShow.value = MarksShow.Detail
}
) {
Box(
modifier = Modifier
.width(100.dp)
.align(Alignment.CenterVertically),
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 = 23.sp
) // theme
}
}
}
}
}
}
@Composable
fun MarksDetail() {
val scrollState = rememberScrollState()
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Top,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 5.dp)
.verticalScroll(scrollState)
) {
Button(
onClick = {
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(
imageVector = Icons.Default.ArrowBackIosNew, // Replace with your desired icon
contentDescription = "Back icon",
modifier = Modifier.size(35.dp) // Adjust size if needed
)
}
Spacer(modifier = Modifier.height(20.dp))
Row {
Box(modifier = Modifier.height(100.dp)) {
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(PastelYellowTransparent)
.padding(vertical = 20.dp)
) // description
}
} // description
Row {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
text = markToShow.grade,
color = Color.White,
textAlign = TextAlign.Center,
fontSize = 35.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
}
} // grade
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 5.dp)
.background(
color = PastelYellowTransparent,
shape = RoundedCornerShape(roundedCornerDimen)
)
.padding(vertical = 20.dp, horizontal = 10.dp)
) {
Box(
modifier = Modifier
.width(150.dp)
.align(Alignment.CenterVertically),
contentAlignment = Alignment.CenterStart
) {
Text(
text = "Váha:",
color = Color.White,
fontSize = 30.sp
)
}
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.weight(1f),
contentAlignment = Alignment.CenterEnd
) {
Text(
text = markToShow.weight.replace(".", ","),
color = Color.White,
fontSize = 23.sp
)
}
} // weight
Spacer(modifier = Modifier.height(10.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 5.dp)
.background(
color = PastelYellowTransparent,
shape = RoundedCornerShape(roundedCornerDimen)
)
.padding(vertical = 20.dp, horizontal = 10.dp)
) {
Box(
modifier = Modifier
.width(150.dp)
.align(Alignment.CenterVertically),
contentAlignment = Alignment.CenterStart
) {
Text(
text = "Datum:",
color = Color.White,
fontSize = 30.sp
)
}
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.weight(1f),
contentAlignment = Alignment.CenterEnd
) {
val rawDate: String = markToShow.date.split("T")[0]
val year = rawDate.split("-")[0]
val month = rawDate.split("-")[1]
val day = rawDate.split("-")[2]
val date = "$day. $month. $year"
Text(
text = date,
color = Color.White,
fontSize = 23.sp
)
}
} // date
}
}
@@ -1,313 +0,0 @@
package com.odweta.solon
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.Space
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.contains
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
@Suppress("DEPRECATION")
class MarksFragment : Fragment() {
private lateinit var inflater: LayoutInflater
private var container: ViewGroup? = null
private var savedInstanceState: Bundle? = null
private val marksOneSubjectDialog: DialogFragment = MarksOneSubjectFragment()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
this.inflater = inflater
this.container = container
this.savedInstanceState = savedInstanceState
if (Vars.marksAvailOffline && !Util.fileIsEmpty(Vars.marksFilePath)) {
Vars.marksJSON = Util.fileToString(Vars.marksFilePath)
Vars.marks = Parsing.parseMarksJSON(Vars.marksJSON)
} else {
lifecycleScope.launch {
Vars.marksJSON = Network.getMarksJSON()
Vars.marksAvailOffline = Util.saveStringToFile(Vars.marksFilePath, Vars.marksJSON)
Vars.marks = Parsing.parseMarksJSON(Vars.marksJSON)
}
}
if (
Vars.marks.keys.contains("-") &&
Vars.marks.keys.size == 1
) {
val view = inflater.inflate(R.layout.activity_no_marks, container, false)
val root = view.findViewById<LinearLayout>(R.id.no_mark_container)
for (subject in Vars.marks.keys) {
val viewToAdd = Vars.marks[subject]?.let { createMarkView(it) } as View
if (!root.contains(viewToAdd)) {
root.addView(viewToAdd)
}
}
return view
} else {
val view = inflater.inflate(R.layout.activity_marks, container, false)
val root = view.findViewById<LinearLayout>(R.id.root)
for ((i, subject) in Vars.marks.keys.withIndex()) {
root.addView(Vars.marks[subject]?.let { createMarkView(it) })
if (i < Vars.marks.keys.size-1) {
root.addView(createSpace())
} else {
root.addView(createSpace2())
}
}
return view
}
}
private fun createSpace(): Space {
val space = Space(requireContext())
space.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)
)
return space
}
private fun createSpace2(): Space {
val space = Space(requireContext())
space.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/2
)
return space
}
private fun makeDR(marks: MutableList<Mark>): GradientDrawable {
val avgDrawable = GradientDrawable()
avgDrawable.shape = GradientDrawable.RECTANGLE
avgDrawable.cornerRadius = resources.getDimension(R.dimen.corner_radius)
avgDrawable.setColor(
if (!marks.contains(Mark().none())) {
Util.getAvgBgColor(requireContext(), Util.avg(marks))
} else {
ContextCompat.getColor(
requireContext(),
R.color.pastel_orange_transparent
)
}
)
return avgDrawable
}
private fun createMarkView(marks: MutableList<Mark>): LinearLayout {
val markRoot = LinearLayout(requireContext())
markRoot.orientation = LinearLayout.HORIZONTAL
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
params.setMargins(20, 0, 20, 0)
params.height = 160
markRoot.layoutParams = params
markRoot.gravity = Gravity.CENTER_VERTICAL
val customFont = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
val rootDR = makeDR(marks)
val flDR = makeDR(marks)
val separatorDR = makeDR(marks)
markRoot.background = rootDR
if (marks[0].subject == "Zatím žádné známky.") {
markRoot.gravity = Gravity.CENTER
val tv = TextView(requireContext())
tv.text = String.format("%s", "Zatím žádné známky.")
tv.textSize = 30f
tv.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
tv.gravity = Gravity.CENTER
tv.setTypeface(customFont, Typeface.BOLD)
markRoot.addView(tv)
return markRoot
}
val subjectTextView = TextView(requireContext())
subjectTextView.text = marks[0].subject.uppercase()
//subjectTextView.setPadding(20, 20, 20, 20)
//subjectTextView.setAutoSizeTextTypeUniformWithConfiguration(65, 85, 1, 0)
subjectTextView.textSize = resources.getDimension(R.dimen.text_size_subject_number) / resources.displayMetrics.scaledDensity
subjectTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
subjectTextView.gravity = Gravity.CENTER
subjectTextView.setTypeface(customFont, Typeface.BOLD)
val fl2 = FrameLayout(requireContext())
fl2.layoutParams = LinearLayout.LayoutParams(
160,
FrameLayout.LayoutParams.MATCH_PARENT,
)
fl2.addView(subjectTextView)
val avgTextView = TextView(requireContext())
avgTextView.typeface = customFont
val text = if (!marks.contains(Mark().none())) {
Util.avg(marks).replace(".", ",")
} else {
"-"
}
avgTextView.text = text
//avgTextView.setPadding(20, 20, 20, 20)
//avgTextView.setAutoSizeTextTypeUniformWithConfiguration(65, 85, 1, 0)
avgTextView.textSize = resources.getDimension(R.dimen.text_size_subject_number) / resources.displayMetrics.scaledDensity
avgTextView.gravity = Gravity.CENTER // Center text vertically
avgTextView.setTypeface(customFont, Typeface.NORMAL)
avgTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
val frameLayout = FrameLayout(requireContext())
frameLayout.layoutParams = LinearLayout.LayoutParams(
160,
FrameLayout.LayoutParams.MATCH_PARENT,
)
frameLayout.addView(avgTextView)
val flLL = LinearLayout(requireContext())
flLL.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
flLL.orientation = LinearLayout.HORIZONTAL
flLL.background = flDR
val separator = LinearLayout(requireContext())
separator.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
separator.gravity = Gravity.CENTER_VERTICAL
separator.background = separatorDR
val space = TextView(requireContext())
space.setPadding(0, 40, 0, 40)
space.gravity = Gravity.CENTER_VERTICAL
space.text = " "
space.textSize = 22f
space.background = ResourcesCompat.getDrawable(resources, R.color.black, null)
space.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
separator.addView(space)
flLL.addView(fl2)
flLL.addView(separator)
flLL.addView(frameLayout)
markRoot.addView(flLL)
val marksTextView = TextView(requireContext())
var markString = ""
for ((i, mark) in marks.withIndex()) {
markString += mark.grade
if (i < marks.size - 1) {
markString += ", "
}
}
marksTextView.text = markString
marksTextView.setPadding(20, 20, 20, 20)
//marksTextView.setAutoSizeTextTypeUniformWithConfiguration(45, 65, 1, 0)
marksTextView.textSize = resources.getDimension(R.dimen.text_size_mark) / resources.displayMetrics.scaledDensity
marksTextView.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
marksTextView.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
marksTextView.gravity = Gravity.CENTER
marksTextView.ellipsize = null
marksTextView.setHorizontallyScrolling(false)
markRoot.addView(marksTextView)
// add an onclick event listener for mark details
markRoot.setOnClickListener {
/* mark details will be a DialogFragment
* and will slide up from the bottom
*/
currentMarks = marks
if (marksOneSubjectDialog.dialog?.isShowing == false || marksOneSubjectDialog.dialog == null) {
marksOneSubjectDialog.show(requireFragmentManager(), "fragment_mark_details")
}
}
return markRoot
}
companion object {
var currentMarks: MutableList<Mark> = mutableListOf()
var idx: Int = 0
}
}
class Mark {
lateinit var grade: String // markText
lateinit var weight: String // weight
lateinit var subject: String // subjectId -> parse that
lateinit var date: String // markDate
lateinit var theme: String // theme
override fun toString(): String {
return "mark: ${this.grade} * ${this.weight}, subject: ${this.subject}, description: ${this.theme}, date: ${this.date}"
}
fun none(): Mark {
val mark = Mark()
mark.subject = "Zatím žádné známky."
mark.grade = "0"
mark.weight = "0"
mark.date = "-"
mark.theme = "-"
return mark
}
}
@@ -1,310 +0,0 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.app.Dialog
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.view.GestureDetector
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.DialogFragment
// details for the marks of one subject
@Suppress("DEPRECATION")
class MarksOneSubjectFragment : DialogFragment() {
private var initialTouchY: Float = 0f
private lateinit var rootLayout: LinearLayout
private lateinit var scrollView: ScrollView
private val swipeThreshold = 100
private var isScrollingToDismissEnabled = false
private var isScrollingToTop = true
private lateinit var gestureDetector: GestureDetector
private lateinit var dialog: Dialog
private val markDetailsDialog: DialogFragment = MarkDetailsFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set the style so the dialog is fullscreen
setStyle(STYLE_NORMAL, R.style.Theme_Solon)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// inflate the layout
val view = inflater.inflate(R.layout.activity_marks_one_subject, container, false)
// get the root layout
rootLayout = view.findViewById(R.id.mark_details_root)
// set up gestures
gestures(view)
// construct the view
construct()
// return the view
return view
}
@SuppressLint("ClickableViewAccessibility")
private fun gestures(view: View) {
gestureDetector = GestureDetector(requireContext(), SwipeGestureListener())
scrollView = view.findViewById(R.id.mark_details_scroll)
scrollView.scrollY = 0
scrollView.setOnTouchListener { _, event ->
scrollView.performClick()
gestureDetector.onTouchEvent(event)
// Return false to allow normal scrolling
false
}
scrollView.viewTreeObserver.addOnScrollChangedListener {
isScrollingToTop = !scrollView.canScrollVertically(-1)
}
// Attach the gesture detector to the root layout
view.setOnTouchListener { _, event ->
view.performClick()
handleTouchEvent(event, null)
}
}
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
private fun construct() {
/* show all the marks in separate views with yet another onclick listener for even more
* details
*/
// TV == TextView
// LL == LinearLayout
// DR == Drawable
// FL == FrameLayout
// SV == ScrollView
val marks = MarksFragment.currentMarks
val customFont = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
val bgColor = if (!marks.contains(Mark().none())) {
Util.getAvgBgColor(requireContext(), Util.avg(marks))
} else {
ContextCompat.getColor(
requireContext(),
R.color.pastel_orange
)
}
val bgStroke = if (!marks.contains(Mark().none())) {
Util.getAvgStrokeColor(requireContext(), Util.avg(marks))
} else {
ContextCompat.getColor(
requireContext(),
R.color.pastel_orange
)
}
val bg = GradientDrawable()
bg.setColor(bgColor)
bg.setStroke(10, bgStroke)
bg.cornerRadius = resources.getDimension(R.dimen.corner_radius)
// the view for all the details of one subject (not the marks)
// e.g. average in that subject, hours per week maybe idk
val subjectDetailsLL = LinearLayout(requireContext())
subjectDetailsLL.orientation = LinearLayout.HORIZONTAL
subjectDetailsLL.gravity = Gravity.CENTER_VERTICAL
subjectDetailsLL.setPadding(40, 40, 40, 40)
subjectDetailsLL.background = bg
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
params.setMargins(0, 40, 0, 40)
subjectDetailsLL.layoutParams = params
/* items:
* - whole name of the subject
* - avg
* - maybe more idk
*/
val wholeNameTV = TextView(requireContext())
// index by the abbrev and get the name (if null just fallback to the abbrev)
wholeNameTV.text = Vars.subjectNameMap[marks[0].subject] ?: marks[0].subject
wholeNameTV.gravity = Gravity.CENTER_VERTICAL
wholeNameTV.setTypeface(customFont, Typeface.BOLD)
wholeNameTV.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
wholeNameTV.ellipsize = null
wholeNameTV.setHorizontallyScrolling(false)
subjectDetailsLL.addView(wholeNameTV)
val avgTV = TextView(requireContext())
avgTV.text = "${Util.avg(marks).replace(".", ",")} " // plus a space for the italic stuff
avgTV.textSize = 28f
avgTV.setTypeface(customFont, Typeface.BOLD_ITALIC)
avgTV.textAlignment = View.TEXT_ALIGNMENT_TEXT_END
avgTV.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
if (wholeNameTV.text.length < 15) {
wholeNameTV.textSize = 28f
} else {
wholeNameTV.textSize = 24f
}
avgTV.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
subjectDetailsLL.addView(avgTV)
// a vertical LL for individual marks
val marksLL = LinearLayout(requireContext())
marksLL.orientation = LinearLayout.VERTICAL
for ((i, mark) in marks.withIndex()) {
val markLL = LinearLayout(requireContext())
markLL.orientation = LinearLayout.HORIZONTAL
markLL.gravity = Gravity.CENTER_VERTICAL
val markTV = TextView(requireContext())
markTV.text = mark.grade
markTV.setPadding(30, 30, 30, 30)
markTV.textSize = 30f
markTV.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
val descTV = TextView(requireContext())
descTV.text = mark.theme
descTV.setPadding(20, 20, 20, 20)
descTV.textSize = 20f
descTV.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.white
)
)
descTV.setOnTouchListener { _, event ->
handleTouchEvent(event, i)
}
descTV.setTypeface(customFont, Typeface.NORMAL)
descTV.ellipsize = null
descTV.setHorizontallyScrolling(false)
markLL.setOnTouchListener { _, event ->
handleTouchEvent(event, i)
}
markLL.addView(markTV)
markLL.addView(descTV)
marksLL.addView(markLL)
}
// add everything to the root layout
rootLayout.addView(subjectDetailsLL)
rootLayout.addView(marksLL)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog.setCancelable(true)
return dialog
}
@Suppress("SameReturnValue", "DEPRECATION")
private fun handleTouchEvent(event: MotionEvent, i: Int? = null): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialTouchY = event.rawY
}
MotionEvent.ACTION_MOVE -> {
val deltaY = event.rawY - initialTouchY
dialog.window?.attributes?.y = deltaY.toInt()
dialog.window?.attributes?.windowAnimations = 0
dialog.show()
// Check if the dialog has been dragged beyond a threshold and dismiss if necessary
if (deltaY > 200) {
dismiss()
}
}
MotionEvent.ACTION_UP -> {
if (dialog.isShowing) {
i?.let {
MarksFragment.idx = it
if (
it in 0 until MarksFragment.currentMarks.size &&
markDetailsDialog.dialog?.isShowing == false ||
markDetailsDialog.dialog == null
) {
markDetailsDialog.show(
requireFragmentManager(),
"fragment_mark_details"
)
}
}
}
initialTouchY = 0f
}
}
return true
}
inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
// Reset the flag when the user touches the screen
isScrollingToDismissEnabled = false
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffY = e1?.let { e2.y - it.y } ?: 0f
if (diffY > swipeThreshold) {
// Swiped down
if (isScrollingToTop) {
// Enable dismissing only if scrolled to top
isScrollingToDismissEnabled = true
dismiss()
}
}
return false
}
}
}
@@ -0,0 +1,164 @@
package com.odweta.solon
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.odweta.solon.ui.theme.PastelYellow
import kotlin.system.exitProcess
@Composable
fun MenuScreen(modifier: Modifier = Modifier) {
var showDialog by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Button to trigger the dialog
Button(
onClick = { showDialog = true },
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(230.dp)
.height(80.dp)
) {
Text(
text = "Odhlásit se",
fontSize = 23.sp
)
}
Spacer(modifier = Modifier.height(100.dp))
// Dialog implementation
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false }, // Close the dialog on dismiss
title = {
Text(text = "Odhlásit se", color = Color.White)
},
text = {
Text("Opravdu se chcete odhlásit? Tato akce zavře aplikaci.", color = Color.White)
},
containerColor = Color.Black,
modifier = Modifier
.border(width = 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(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(230.dp)
.height(80.dp)
) {
Text(
text = "Změnit přihlášení",
fontSize = 23.sp
)
}
Spacer(modifier = Modifier.height(100.dp))
Button(onClick = {
isShowing.value = IsShowing.About
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(230.dp)
.height(80.dp)
) {
Text(
text = "O aplikaci",
fontSize = 23.sp
)
}
}
}
@@ -1,82 +0,0 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import kotlin.system.exitProcess
class MenuFragment : Fragment() {
private lateinit var signOutDialog: Dialog
private lateinit var signInDialog: DialogFragment
private lateinit var aboutDialog: DialogFragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_menu, container, false)
addCallbacks(view)
return view
}
@SuppressLint("InflateParams")
private fun addCallbacks(view: View) {
//val settings = view.findViewById<Button>(R.id.menu_button_settings)
val signOut = view.findViewById<Button>(R.id.menu_button_sign_out)
val signIn = view.findViewById<Button>(R.id.menu_button_sign_in)
val about = view.findViewById<Button>(R.id.menu_button_about)
//settings.setOnClickListener {
// SettingsFragment().show(requireFragmentManager(), "fragment_settings")
//}
// init dialogs
val builder: AlertDialog.Builder = AlertDialog.Builder(context, R.style.Theme_Solon_AlertDialog)
builder
.setView(LayoutInflater.from(context).inflate(R.layout.alert_dialog_sign_out, null))
.setPositiveButton("Ano") { dialog, _ ->
Util.deleteFile(Vars.userDataFilePath)
Util.deleteFile(Vars.timeTableFilePath)
Util.deleteFile(Vars.marksFilePath)
Util.deleteFile(Vars.accountDataFilePath)
dialog.dismiss()
exitProcess(0)
}
.setNegativeButton("Ne") { dialog, _ ->
dialog.dismiss()
}
signOutDialog = builder.create()
signInDialog = SignInFragment()
aboutDialog = AboutFragment()
signOut.setOnClickListener {
if (!signOutDialog.isShowing) {
signOutDialog.show()
}
}
signIn.setOnClickListener {
if (signInDialog.dialog?.isShowing == false || signInDialog.dialog == null) {
signInDialog.show(requireFragmentManager(), "fragment_sign_in")
}
}
about.setOnClickListener {
if (aboutDialog.dialog?.isShowing == false || aboutDialog.dialog == null) {
aboutDialog.show(requireFragmentManager(), "fragment_about")
}
}
}
}
@@ -0,0 +1,98 @@
package com.odweta.solon
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.odweta.solon.ui.theme.PastelYellow
private var timetableColor: MutableState<Color> = mutableStateOf(PastelYellow)
private var marksColor: MutableState<Color> = mutableStateOf(Color.White)
private var menuColor: MutableState<Color> = mutableStateOf(Color.White)
@Composable
fun NavbarScreen(modifier: Modifier = Modifier) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Button(
onClick = {
isShowing.value = IsShowing.Timetable
timetableColor.value = PastelYellow
marksColor.value = Color.White
menuColor.value = Color.White
},
colors = ButtonDefaults.buttonColors(
contentColor = timetableColor.value,
containerColor = Color.Black
)
) {
Icon(
imageVector = Icons.Default.Schedule,
contentDescription = "Schedule Icon",
modifier = Modifier.size(25.dp)
)
}
Spacer(modifier = Modifier.width(30.dp))
Button(
onClick = {
isShowing.value = IsShowing.Marks
timetableColor.value = Color.White
marksColor.value = PastelYellow
menuColor.value = Color.White
marksShow.value = MarksShow.All
},
colors = ButtonDefaults.buttonColors(
contentColor = marksColor.value,
containerColor = Color.Black
)
) {
Icon(
painter = painterResource(R.drawable.marks),
contentDescription = "Marks Icon",
modifier = Modifier.size(25.dp)
)
}
Spacer(modifier = Modifier.width(30.dp))
Button(
onClick = {
isShowing.value = IsShowing.Menu
timetableColor.value = Color.White
marksColor.value = Color.White
menuColor.value = PastelYellow
},
colors = ButtonDefaults.buttonColors(
contentColor = menuColor.value,
containerColor = Color.Black
)
) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = "Menu Icon",
modifier = Modifier.size(25.dp)
)
}
}
}
@@ -1,5 +1,6 @@
package com.odweta.solon
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.FormBody
@@ -13,13 +14,13 @@ class Network {
suspend fun getToken(): String {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(Vars.tokenUrl)
.url(tokenUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.post(
FormBody.Builder()
.add("grant_type", "password")
.add("username", Vars.username)
.add("password", Vars.password)
.add("username", username)
.add("password", password)
.add("client_id", "test_client")
.add("scope", "openid offline_access profile sol_api")
.build()
@@ -27,10 +28,11 @@ class Network {
.build()
try {
val resp = Vars.client.newCall(request).execute()
val resp = client.newCall(request).execute()
val respString = resp.body?.string()
val respJson = respString?.let { JSONObject(it) }
respJson.toString()
respString.toString()
//val respJson = respString?.let { JSONObject(it) }
//respJson.toString()
} catch (e: JSONException) {
"""{"error": "json"}"""
} catch (e: Exception) {
@@ -42,32 +44,37 @@ class Network {
suspend fun setupAPI() {
return withContext(Dispatchers.IO) {
val token = getToken()
Vars.token = "NETWORK_UNREACHABLE"
if (!JSONObject(token).has("error")) {
Vars.token = JSONObject(token).get("access_token").toString()
token = "NETWORK_UNREACHABLE"
token = getToken()
if (""""error": """ !in token) {
token = JSONObject(token).get("access_token").toString()
}
if (Vars.token == "NETWORK_UNREACHABLE") {
Vars.userData = ""
Vars.studentId = ""
Vars.internetAvail = false
if (token == "NETWORK_UNREACHABLE" || """"error": """ in token) {
userData = ""
studentId = ""
internetAvail = false
return@withContext
}
Vars.internetAvail = true
Vars.userData = getUserDataJSON()
Util.saveStringToFile(Vars.userDataFilePath, Vars.userData)
Vars.studentId = getStudentId()
//Vars.syid = getSYID()
Vars.syid = ""
internetAvail = true
userData = getUserDataJSON()
Util.saveStringToFile(userDataFilePath, userData)
if (!Parsing.userIsParent()) {
studentClass = JSONObject(
Util.fileToString(userDataFilePath)
).getJSONObject("class").get("name").toString()
}
studentId = getStudentId()
//syid = getSYID()
syid = ""
Vars.marksUrl = "${Vars.apiBaseUrl}/v1/students/${Vars.studentId}/marks/list"//?SemesterId=${Vars.syid}"
marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list"//?SemesterId=${syid}"
Vars.isAPISetUp = true
isAPISetUp = true
}
}
/*private fun getSYID(): String {
val f = Util.fileToString(Vars.userDataFilePath)
val f = Util.fileToString(userDataFilePath)
lateinit var syidRoot: String
lateinit var toAdd: String
@@ -99,9 +106,9 @@ class Network {
private fun getStudentId(): String {
return if (!Parsing.userIsParent()) {
JSONObject(Vars.userData).getString("personID")
JSONObject(userData).getString("personID")
} else {
JSONObject(Vars.userData).getJSONArray("children")
JSONObject(userData).getJSONArray("children")
.getJSONObject(0)
.getString("id")
}
@@ -110,14 +117,14 @@ class Network {
private suspend fun getUserDataJSON(): String {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(Vars.userDataUrl)
.url(userDataUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ${Vars.token}") // using Bearer auth token
.header("Authorization", "Bearer $token") // using Bearer auth token
.get()
.build()
try {
val resp = Vars.client.newCall(request).execute()
val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString()
respString
} catch (e: IOException) {
@@ -129,20 +136,20 @@ class Network {
suspend fun getTimeTableJSON(): String {
return withContext(Dispatchers.IO) {
val userIsParent = Parsing.userIsParent()
var url = Vars.timeTableUrl
var url = timeTableUrl
if (userIsParent) {
url += "?studentId=${Vars.studentId}"
url += "?studentId=$studentId"
}
val request = Request.Builder()
.url(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ${Vars.token}")
.header("Authorization", "Bearer $token")
.get()
.build()
try {
val resp = Vars.client.newCall(request).execute()
val resp = client.newCall(request).execute()
resp.body?.string().toString()
} catch (e: IOException) {
"""{"error": "network"}"""
@@ -153,14 +160,14 @@ class Network {
suspend fun getMarksJSON(): String {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(Vars.marksUrl)
.url(marksUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ${Vars.token}")
.header("Authorization", "Bearer $token")
.get()
.build()
try {
val resp = Vars.client.newCall(request).execute()
val resp = client.newCall(request).execute()
resp.body?.string().toString()
} catch (e: IOException) {
"""{"error": "network"}"""
@@ -44,8 +44,8 @@ class Parsing {
}
fun userIsParent(): Boolean {
return if (Vars.userData != "")
JSONObject(Vars.userData).getString("userType") == "parent"
return if (userData != "")
JSONObject(userData).getString("userType") == "parent"
else false
}
@@ -208,6 +208,15 @@ class Parsing {
}
}
}
val sCopy = subjects.toList()
for (s in 0 until sCopy.size) {
if ("-" in subjects[s].number) {
subjects[s].number = "1"
subjects[s].start = "08:00"
subjects[s].end = "08:45"
}
}
}
/*
@@ -320,7 +329,7 @@ class Parsing {
subjectMap[subj.getString("id")] = subj.getString("abbrev")
// map names to abbrevs
Vars.subjectNameMap[subj.getString("abbrev")] = subj.getString("name")
subjectNameMap[subj.getString("abbrev")] = subj.getString("name")
}
for (i in 0..<marksJSONArray.length()) {
@@ -354,4 +363,4 @@ class Parsing {
return marks
}
}
}
}
@@ -1,94 +0,0 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Bundle
import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import androidx.fragment.app.DialogFragment
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.ScrollView
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class SettingsFragment : DialogFragment() {
private lateinit var scrollView: ScrollView
private var isScrollingToDismissEnabled = false
private var isScrollingToTop = true
private val gestureDetector: GestureDetector by lazy {
GestureDetector(requireContext(), SwipeGestureListener())
}
private val swipeThreshold = 100
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.Theme_Solon)
}
inner class SwipeGestureListener : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
// Reset the flag when the user touches the screen
isScrollingToDismissEnabled = false
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffY = e1?.let { e2.y - it.y } ?: 0f
if (diffY > swipeThreshold) {
// Swiped down
if (isScrollingToTop) {
// Enable dismissing only if scrolled to top
isScrollingToDismissEnabled = true
dismiss()
}
}
return false
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_settings, container, false)
scrollView = view.findViewById(R.id.scrollViewSettings)
scrollView.scrollY = 0
scrollView.setOnTouchListener { _, event ->
scrollView.performClick()
gestureDetector.onTouchEvent(event)
// Return false to allow normal scrolling
false
}
scrollView.viewTreeObserver.addOnScrollChangedListener {
isScrollingToTop = !scrollView.canScrollVertically(-1)
}
val button: Button = view.findViewById(R.id.setting_language_button_choice) // replace with your button ID
button.setOnClickListener {
Dialogs.LanguageChange(requireContext()).show()
}
return view
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation
return dialog
}
}
@@ -0,0 +1,217 @@
package com.odweta.solon
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
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.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.odweta.solon.ui.theme.PastelYellow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
private var buttonText: MutableState<String> = mutableStateOf("Přihlásit se")
private fun changeButtonText(s: String) {
buttonText.value = s
}
fun signInButtonCallback(ctx: Context, coscope: CoroutineScope, buttonText: String, usernameText: String, passwordText: String) {
username = usernameText
password = passwordText
val acc = Account(username, password, 0)
var checked = false
coscope.launch {
while (!checked) {
delay(300)
changeButtonText("Přihlašování")
if (!checked) {
delay(300)
changeButtonText("Přihlašování.")
}
if (!checked) {
delay(300)
changeButtonText("Přihlašování..")
}
if (!checked) {
delay(300)
changeButtonText("Přihlašování...")
}
if (!checked) {
delay(300)
changeButtonText("Přihlašování..")
}
if (!checked) {
delay(300)
changeButtonText("Přihlašování.")
}
if (!checked) {
delay(300)
changeButtonText("Přihlašování")
}
}
changeButtonText("Přihlásit se")
}
coscope.launch {
signedInAndReadyToLaunch.value = false
val valid = Account.isValid(acc)
checked = true
if (valid) {
Account.save(acc)
signedInAndReadyToLaunch.value = true
screenToShow.value = Screen.Splash
} else {
Toast.makeText(
ctx,
"Neplatné přihlašovací údaje nebo nejste připojeni k internetu, zkuste to znovu.",
Toast.LENGTH_LONG
).show()
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SignInScreen() {
var usernameText by remember { mutableStateOf("") }
var passwordText by remember { mutableStateOf("") }
val ctx = LocalContext.current
val coscope = rememberCoroutineScope()
var isPasswordVisible by remember { mutableStateOf(false) } // State for visibility toggle
val visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation()
val scrollState = rememberScrollState()
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(R.drawable.solon_just_yellow),
contentDescription = "Solon logo",
modifier = Modifier.size(230.dp)
)
TextField(
value = usernameText,
onValueChange = { input -> usernameText = input },
modifier = Modifier
.border(width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen))
.width(230.dp),
colors = TextFieldDefaults.colors(
focusedTextColor = Color.White, // White text
unfocusedTextColor = Color.White, // White text when not focused
focusedContainerColor = Color.Black, // Black background when focused
unfocusedContainerColor = Color.Black, // Black background when not focused
cursorColor = PastelYellow, // Yellow cursor
focusedIndicatorColor = Color.Black, // Yellow border when focused
unfocusedIndicatorColor = Color.Black // Yellow border when not focused
),
)
Spacer(modifier = Modifier.height(20.dp))
TextField(
value = passwordText,
onValueChange = { input -> passwordText = input },
visualTransformation = visualTransformation,
modifier = Modifier
.width(230.dp)
.border(
width = 5.dp,
color = PastelYellow,
shape = RoundedCornerShape(
roundedCornerDimen
)
),
colors = TextFieldDefaults.colors(
focusedTextColor = Color.White, // White text
unfocusedTextColor = Color.White, // White text when not focused
focusedContainerColor = Color.Black, // Black background when focused
unfocusedContainerColor = Color.Black, // Black background when not focused
cursorColor = PastelYellow, // Yellow cursor
focusedIndicatorColor = Color.Black, // Yellow border when focused
unfocusedIndicatorColor = Color.Black // Yellow border when not focused
),
trailingIcon = {
// Toggle button for showing/hiding password
IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
Icon(
imageVector = if (isPasswordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff,
contentDescription = if (isPasswordVisible) "Hide password" else "Show password",
tint = Color.Gray // You can customize the icon color
)
}
}
)
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = {
signInButtonCallback(ctx, coscope, buttonText.value, usernameText, passwordText)
},
colors = ButtonDefaults.buttonColors(
contentColor = Color.White,
containerColor = Color.Black
),
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
color = PastelYellow
)
.width(230.dp)
) {
Text(
text = buttonText.value,
fontSize = 23.sp
)
}
}
}
}
@@ -1,140 +0,0 @@
package com.odweta.solon
import android.app.Dialog
import android.graphics.Typeface
import android.os.Bundle
import android.text.InputType
import android.view.GestureDetector
import android.view.Gravity
import androidx.fragment.app.DialogFragment
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.ToggleButton
import androidx.core.content.res.ResourcesCompat
import com.google.android.material.textfield.TextInputEditText
import kotlin.math.abs
class SignInFragment : DialogFragment() {
private lateinit var gestureDetector: GestureDetector
private var initialTouchY: Float = 0f
private val swipeThreshold = 100
private val swipeVelocityThreshold = 100
private lateinit var dialog: Dialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set up GestureDetector
gestureDetector = GestureDetector(requireContext(), SwipeGestureListener())
setStyle(STYLE_NORMAL, R.style.Theme_Solon)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_sign_in, container, false)
MainActivity.setListener(requireContext())
view.setOnTouchListener { _, event ->
view.performClick()
handleTouchEvent(event)
}
return view
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog.setCancelable(true)
return dialog
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.signInButton).setOnClickListener {
Vars.resetNav = true
MainActivity.callOnSignIn(view, false, dialog)
}
// Find views
val passwordEditText = view.findViewById<EditText>(R.id.passwordField)
val showPasswordToggleButton = view.findViewById<ImageButton>(R.id.showPasswordToggleButton)
showPasswordToggleButton.bringToFront()
showPasswordToggleButton.background = ResourcesCompat.getDrawable(resources, R.drawable.hide, null)
// Set up toggle button to reveal/hide password
showPasswordToggleButton.setOnClickListener { _ ->
if (Vars.isChecked) {
// Show password
passwordEditText.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
showPasswordToggleButton.background = ResourcesCompat.getDrawable(resources, R.drawable.show, null)
Vars.isChecked = false
} else {
// Hide password
passwordEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
passwordEditText.typeface = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
showPasswordToggleButton.background = ResourcesCompat.getDrawable(resources, R.drawable.hide, null)
Vars.isChecked = true
}
// Move cursor to the end of the text
passwordEditText.setSelection(passwordEditText.text?.length ?: 0)
}
}
inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
return true
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffY = e1?.let { e2.y.minus(it.y) } ?: 0f
if (diffY > swipeThreshold && abs(velocityY) > swipeVelocityThreshold) {
// Swiped down
dismiss()
return true
}
return false
}
}
@Suppress("SameReturnValue")
private fun handleTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialTouchY = event.rawY
}
MotionEvent.ACTION_MOVE -> {
val deltaY = event.rawY - initialTouchY
dialog.window?.attributes?.y = deltaY.toInt()
dialog.window?.attributes?.windowAnimations = 0
dialog.show()
// Check if the dialog has been dragged beyond a threshold and dismiss if necessary
if (deltaY > 200) {
dismiss()
}
}
MotionEvent.ACTION_UP -> {
initialTouchY = 0f
}
}
return true
}
}
@@ -1,53 +0,0 @@
package com.odweta.solon
import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.ImageButton
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
class SignInMainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_sign_in, container, false)
MainActivity.setListener(requireContext())
view.findViewById<Button>(R.id.signInButton).setOnClickListener {
MainActivity.callOnSignIn(view, true, null)
}
// Find views
val passwordEditText = view.findViewById<EditText>(R.id.passwordField)
val showPasswordToggleButton = view.findViewById<ImageButton>(R.id.showPasswordToggleButton)
showPasswordToggleButton.bringToFront()
showPasswordToggleButton.background = ResourcesCompat.getDrawable(resources, R.drawable.hide, null)
// Set up toggle button to reveal/hide password
showPasswordToggleButton.setOnClickListener { _ ->
if (Vars.isChecked) {
// Show password
passwordEditText.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
showPasswordToggleButton.background = ResourcesCompat.getDrawable(resources, R.drawable.show, null)
Vars.isChecked = false
} else {
// Hide password
passwordEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
passwordEditText.typeface = context?.let { ResourcesCompat.getFont(it, R.font.roboto) }
showPasswordToggleButton.background = ResourcesCompat.getDrawable(resources, R.drawable.hide, null)
Vars.isChecked = true
}
// Move cursor to the end of the text
passwordEditText.setSelection(passwordEditText.text?.length ?: 0)
}
return view
}
}
@@ -0,0 +1,57 @@
package com.odweta.solon
import android.content.Context
import android.widget.ImageView
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
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.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.bumptech.glide.Glide
@Composable
fun SplashScreen() {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.solon_just_yellow),
contentDescription = "Solon logo",
modifier = Modifier.size(200.dp)
)
AndroidView(
modifier = Modifier.size(200.dp),
factory = { context: Context ->
ImageView(context).apply {
// Set a placeholder if needed
// setImageResource(R.drawable.placeholder)
}
},
update = { imageView ->
Glide.with(imageView.context)
.load(R.drawable.loading)
.into(imageView)
}
)
}
}
}
@@ -1,32 +0,0 @@
package com.odweta.solon
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
class SplashFragment : Fragment() {
private lateinit var img: ImageView
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.activity_splash_screen, container, false)
img = view.findViewById(R.id.loading_gif)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// start the loading animation
Glide.with(requireContext())
.load(R.drawable.loading3)
.into(img)
}
}
@@ -0,0 +1,281 @@
package com.odweta.solon
import android.annotation.SuppressLint
import android.text.format.Time
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
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.res.painterResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.odweta.solon.ui.theme.PastelGreen
import com.odweta.solon.ui.theme.PastelGreenTransparent
import com.odweta.solon.ui.theme.PastelRed
import com.odweta.solon.ui.theme.PastelRedTransparent
import com.odweta.solon.ui.theme.PastelYellow
import com.odweta.solon.ui.theme.PastelYellowTransparent
import kotlinx.coroutines.delay
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TimetableScreen() {
val pagerState = rememberPagerState( pageCount = { days.size } )
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) { pageIndex ->
DayScreen(days[pageIndex])
}
}
private fun getSubjectColor(s: Subject, transparent: Boolean): Color {
return if (s.free) {
if (transparent) {
PastelGreenTransparent
} else {
PastelGreen
}
} else if (s.substitute == "1") {
if (transparent) {
PastelRedTransparent
} else {
PastelRed
}
} else {
if (transparent) {
PastelYellowTransparent
} else {
PastelYellow
}
}
}
fun isTimeBetween(givenTime: LocalTime, startTime: LocalTime, endTime: LocalTime): Boolean {
// Check if the given time is between startTime and endTime
return givenTime.isAfter(startTime) && givenTime.isBefore(endTime)
}
@SuppressLint("ModifierFactoryExtensionFunction")
@Composable
fun subjectModifier(d: Day, s: Subject, lectureNumber: Int): Modifier {
val dayFormatter = DateTimeFormatter.ofPattern("dd. MM. yyyy")
val hourFormatter = DateTimeFormatter.ofPattern("HH:mm:ss")
val dateParsed = LocalDate.parse(d.date + " " + LocalDate.now().year, dayFormatter)
val startTimeParsed = LocalTime.parse(s.start + ":00", hourFormatter)
val endTimeParsed = LocalTime.parse(s.end + ":00", hourFormatter)
val todayDate = LocalDate.now()
val now = LocalTime.now()
if (dateParsed == todayDate && isTimeBetween(now, startTimeParsed, endTimeParsed)) {
currentLectureNumber = lectureNumber
}
return if (lectureNumber == currentLectureNumber && dateParsed == todayDate) {
Modifier
.fillMaxWidth()
.height(100.dp)
.background(
color = getSubjectColor(s, true), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
.border(
width = 5.dp,
color = getSubjectColor(s, false),
shape = RoundedCornerShape(roundedCornerDimen)
)
} else {
Modifier
.fillMaxWidth()
.height(100.dp)
.background(
color = getSubjectColor(s, true), // decide on the color (transparent)
shape = RoundedCornerShape(roundedCornerDimen)
)
}
}
@Composable
private fun DayScreen(day: Day) {
val scrollState = rememberScrollState()
Column(modifier = Modifier.fillMaxSize()) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.padding(bottom = 20.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = "${day.name} ${day.date}",
color = Color.White,
fontSize = 30.sp,
fontWeight = FontWeight.Bold,
fontStyle = FontStyle.Italic
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 5.dp)
.verticalScroll(scrollState)
) {
for (subject in day.subjects) {
if (day.subjects.indexOf(subject) > 0) {
Box(modifier = Modifier.height(10.dp)) {
if (subject.name == day.subjects[day.subjects.indexOf(subject) - 1].name) {
Image(
painter = painterResource(R.drawable.squiggly_line_yellow),
contentDescription = "Spojovač stejných předmětů",
modifier = Modifier.fillMaxSize()
)
}
}
}
Row(
modifier = subjectModifier(day, subject, subject.number.toInt())
) {
if (subject.substitute != "2") {
Box(
modifier = Modifier
.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 = subject.name.uppercase(),
color = Color.White,
fontSize = 25.sp,
fontWeight = FontWeight.Bold
)
}
Text(
text = subject.place,
color = Color.White,
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
)
}
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxHeight()
.padding(vertical = 5.dp),
contentAlignment = Alignment.Center
) {
if (!Parsing.userIsParent()) {
if (
subject.teacher == "Jindřich Svoboda" &&
studentClass == "5. G"
) {
Image(
painter = painterResource(R.drawable.spock),
contentDescription = "Spock",
modifier = Modifier.fillMaxSize()
)
}
}
}
if (subject.substitute != "2") {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(end = 20.dp),
verticalArrangement = Arrangement.Center,
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
)
}
}
}
}
}
}
}
}
@@ -0,0 +1,15 @@
package com.odweta.solon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
@Composable
fun ToolbarScreen() {
Text(
text = toolbarText.value,
fontSize = 25.sp,
color = Color.White
)
}
@@ -18,8 +18,13 @@ import android.util.DisplayMetrics
import android.view.WindowManager
import android.view.animation.AnimationUtils
import android.widget.Button
import androidx.compose.ui.graphics.Color
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.IOException
import java.nio.charset.Charset
@@ -45,7 +50,7 @@ class Util {
fun saveStringToFile(filePath: String, s: String): Boolean {
// get a file object with the specified path
val f = File(Vars.fileDir, filePath)
val f = File(fileDir, filePath)
// if it does not exist, create it
if (!f.exists()) {
@@ -67,7 +72,7 @@ class Util {
}
fun fileToString(filePath: String): String {
val f = File(Vars.fileDir, filePath)
val f = File(fileDir, filePath)
return if (f.exists()) {
if (f.canRead()) {
@@ -85,12 +90,12 @@ class Util {
}
fun fileExists(filePath: String): Boolean {
val f = File(Vars.fileDir, filePath)
val f = File(fileDir, filePath)
return f.exists()
}
fun fileIsEmpty(filePath: String): Boolean {
val f = File(Vars.fileDir, filePath)
val f = File(fileDir, filePath)
val content = f.readText()
return content == ""
}
@@ -110,7 +115,7 @@ class Util {
}
fun deleteFile(path: String): Boolean {
val f = File(Vars.fileDir, path)
val f = File(fileDir, path)
if (f.exists()) {
try {
@@ -173,23 +178,20 @@ class Util {
return sum
}
fun getAvgBgColor(ctx: Context, avg: String): Int {
var color: Int = R.color.pastel_orange_transparent
/*fun getAvgBgColor(ctx: Context, avg: String): Color {
var color: Color = PastelOrangeTransparent
if (avg.toFloat() in 1f..<2f) {
color = R.color.pastel_green_transparent
color = PastelGreenTransparent
} else if (avg.toFloat() in 2f..<3f) {
color = R.color.pastel_yellow_transparent
color = PastelYellowTransparent
} else if (avg.toFloat() in 3f..<4f) {
color = R.color.pastel_orange_transparent
color = PastelOrangeTransparent
} else if (avg.toFloat() in 4f..5f) {
color = R.color.pastel_red_transparent
color = PastelRedTransparent
}
return ContextCompat.getColor(
ctx,
color
)
return color
}
fun getAvgStrokeColor(ctx: Context, avg: String): Int {
var color: Int = R.color.pastel_orange
@@ -217,6 +219,6 @@ class Util {
d.cornerRadius = 8f
return d
}
}*/
}
}
@@ -1,55 +0,0 @@
package com.odweta.solon
import okhttp3.OkHttpClient
import java.io.File
class Vars {
companion object {
const val accountDataFilePath: String = "account_data.txt"
const val timeTableFilePath: String = "timetable.json"
const val userDataFilePath: String = "userdata.json"
const val marksFilePath: String = "marks.json"
var fileDir: File = File("")
var width: Int = 0
var height: Int = 0
var internetAvail: Boolean = false
var dayCounter: Int = 0
var timeTableAvailOffline: Boolean = false
var marksAvailOffline: Boolean = false
val subjectNameMap = mutableMapOf<String, String>()
val dayMap = mutableMapOf<Int, Day>()
val dayList = mutableListOf<Day>()
var days = listOf<Day>()
var marksJSON: String = ""
var marks: MutableMap<String, MutableList<Mark>> = mutableMapOf()
var username: String = ""
var password: String = ""
val client = OkHttpClient()
var resetNav: Boolean = false
var isAPISetUp: Boolean = false
var selectedDay: Int = 0
var dayEndString: String = ""
var isChecked: Boolean = true
const val apiBaseUrl = "https://aplikace.skolaonline.cz/solapi/api"
const val tokenUrl = "$apiBaseUrl/connect/token"
var token: String = ""
const val userDataUrl = "$apiBaseUrl/v1/user"
var userData: String = ""
var studentId: String = ""
const val timeTableUrl = "$apiBaseUrl/v1/timeTable"
var syid = "" // school year id
var marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list?SemesterId=$syid"
}
}
@@ -0,0 +1,40 @@
package com.odweta.solon.ui.theme
import androidx.compose.ui.graphics.Color
/*val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)*/
val Primary = Color(0xFFFFCC33)
val Secondary = Color(0xFF000000)
val Black = Color(0xFF000000)
val White = Color(0xFFFFFFFF)
val Transparent = Color(0x00000000)
val PastelRed = Color(0xFFFF6666)
val PastelGreen = Color(0xFF00CC99)
val PastelOrange = Color(0xFFFF9933)
val PastelYellow = Color(0xFFFFCC33)
val PastelRedTransparent = Color(0x50FF6666)
val PastelGreenTransparent = Color(0x5000CC99)
val PastelOrangeTransparent = Color(0x50FF9933)
val PastelYellowTransparent = Color(0x50FFCC33)
val TimetableSubstitute = Color(0xFFFF6666)
val TimetableSubstituteTransparentBg = Color(0x50FF6666)
val TimetableEvent = Color(0xFF6699CC)
val TimetableEventTransparentBg = Color(0x506699CC)
val TimetableFree = Color(0xFF00CC99)
val TimetableFreeTransparentBg = Color(0x5000CC99)
val PrimaryTransparentBg = Color(0x50FFCC33)
@@ -0,0 +1,191 @@
package com.odweta.solon
import android.view.Window
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import com.odweta.solon.ui.theme.PastelYellow
import com.odweta.solon.ui.theme.PastelYellowTransparent
import okhttp3.OkHttpClient
import java.io.File
// Global values (dimens, modifiers, ...)
val roundedCornerDimen = 8.dp
val borderWidthDimen = 5.dp
val barModifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(20.dp)
//.background(PastelYellowTransparent)
.border(
width = borderWidthDimen,
color = PastelYellow,
shape = RoundedCornerShape(roundedCornerDimen)
)
fun makeStatusBarBlack(window: Window) {
window.statusBarColor = Color.Black.toArgb()
}
const val accountDataFilePath: String = "account_data.txt"
const val timeTableFilePath: String = "timetable.json"
const val userDataFilePath: String = "userdata.json"
const val marksFilePath: String = "marks.json"
lateinit var fileDir: File
var width: Int = 0
var height: Int = 0
var internetAvail: Boolean = false
var loaded: Boolean = false
var studentClass = "1. A"
var currentLectureNumber = 0
enum class IsShowing {
Timetable,
Marks,
Menu,
About
}
enum class Screen {
Main,
Splash,
SignIn
}
var screenToShow: MutableState<Screen> = mutableStateOf(Screen.Main)
var signedInAndReadyToLaunch: MutableState<Boolean> = mutableStateOf(false)
var isShowing: MutableState<IsShowing> = mutableStateOf(IsShowing.Timetable)
var toolbarText: MutableState<String> = mutableStateOf("")
var done: Boolean = false
var timeTableAvailOffline: Boolean = false
var marksAvailOffline: Boolean = false
val subjectNameMap = mutableMapOf<String, String>()
val dayMap = mutableMapOf<Int, Day>()
val dayList = mutableListOf<Day>()
var days = listOf<Day>()
var marksJSON: String = ""
var marks: MutableMap<String, MutableList<Mark>> = mutableMapOf()
var username: String = ""
var password: String = ""
val client = OkHttpClient()
var resetNav: Boolean = false
var isAPISetUp: Boolean = false
var selectedDay: Int = 0
var dayEndString: String = ""
var isChecked: Boolean = true
const val apiBaseUrl = "https://aplikace.skolaonline.cz/solapi/api"
const val tokenUrl = "$apiBaseUrl/connect/token"
var token: String = ""
const val userDataUrl = "$apiBaseUrl/v1/user"
var userData: String = ""
var studentId: String = ""
const val timeTableUrl = "$apiBaseUrl/v1/timeTable"
var syid = "" // school year id
var marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list?SemesterId=$syid"
class Subject {
lateinit var name: String
lateinit var place: String
lateinit var teacher: String
lateinit var substitute: String
lateinit var number: String
lateinit var start: String
lateinit var end: String
var free: Boolean = false
companion object {
fun freeSubject(number: String, start: String, end: String): Subject {
val s = Subject()
s.name = "Volno"
s.place = "Volno"
s.teacher = "Volno"
s.substitute = "0"
s.number = number
s.start = start
s.end = end
s.free = true
return s
}
fun empty(): Subject {
val s = Subject()
s.name = "err"
s.place = "error"
s.teacher = "error"
s.substitute = "0"
s.number = "0"
s.start = "00:00"
s.end = "00:00"
s.free = false
return s
}
}
override fun toString(): String {
return "subject ${this.name} at ${this.place} from ${this.start} to ${this.end}; free? ${this.free}"
}
}
class Day {
var name: String = ""
var date: String = ""
var subjects: List<Subject> = mutableListOf()
}
class Mark {
lateinit var grade: String // markText
lateinit var weight: String // weight
lateinit var subject: String // subjectId -> parse that
lateinit var date: String // markDate
lateinit var theme: String // theme
override fun toString(): String {
return "mark: ${this.grade} * ${this.weight}, subject: ${this.subject}, description: ${this.theme}, date: ${this.date}"
}
fun none(): Mark {
val mark = Mark()
mark.subject = "Zatím žádné známky."
mark.grade = "0"
mark.weight = "0"
mark.date = "-"
mark.theme = "-"
return mark
}
}
@@ -0,0 +1,58 @@
package com.odweta.solon.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
primary = Black,
secondary = White,
tertiary = Secondary
)
private val LightColorScheme = lightColorScheme(
primary = Black,
secondary = White,
tertiary = Secondary
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun SolonTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
@@ -0,0 +1,34 @@
package com.odweta.solon.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="100%"
android:duration="300" />
</set>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%"
android:toYDelta="0%"
android:duration="600"
android:interpolator="@android:anim/accelerate_interpolator" />
</set>
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/corner_radius" />
<solid android:color="@color/black" />
<stroke android:width="5dp" android:color="@color/pastel_yellow" />
</shape>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/corner_radius" />
<solid android:color="@color/pastel_yellow_transparent" />
</shape>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_clicked_background" android:state_pressed="true" />
<item android:drawable="@drawable/button_background" />
</selector>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/corner_radius" />
<solid android:color="#90FFCC33" />
</shape>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="2dp" /> <!-- Adjust width as needed -->
<solid android:color="@color/white" /> <!-- Set desired color -->
</shape>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
</vector>
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<!-- Background Color -->
<path android:fillColor="#000000"
android:pathData="M0,0h108v108h-108z"/>
<!-- Vertical Grid Lines -->
<path android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<path android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<path android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<!-- Repeat the lines for remaining grid -->
<!-- Horizontal Grid Lines -->
<path android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<path android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<path android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<!-- Repeat the lines for remaining grid -->
<!-- Additional Grid Lines within the Central Area -->
<path android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<path android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#000000"
android:strokeWidth="0.8"/>
<!-- Repeat similar paths for the remaining inner lines -->
</vector>

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/pastel_yellow_transparent" />
<corners android:radius="@dimen/corner_radius" />
</shape>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/corner_radius" />
<solid android:color="@color/transparent" />
<stroke android:width="10px" android:color="@color/pastel_yellow" />
</shape>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
<font
app:fontStyle="normal"
app:fontWeight="400"
app:font="@font/roboto_src"/>
</font-family>
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

@@ -1,108 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:background="@color/black"
app:layout_constraintBottom_toTopOf="@+id/imageView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:background="@drawable/toolbar_background"
android:fontFamily="@font/roboto"
android:gravity="center"
android:padding="10dp"
android:text="@string/about_app"
android:textColor="@color/white"
android:textSize="@dimen/text_size_toolbar" />
</com.google.android.material.appbar.AppBarLayout>
<ImageView
android:id="@+id/imageView2"
android:layout_width="300dp"
android:layout_height="300dp"
android:contentDescription="@string/solon_logo"
app:layout_constraintBottom_toTopOf="@+id/podstata"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
app:srcCompat="@drawable/solon_500_yellow" />
<TextView
android:id="@+id/podstata"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="8dp"
android:fontFamily="monospace"
android:text="@string/slogan"
android:textAllCaps="false"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:textStyle="italic"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
<TextView
android:id="@+id/textView"
android:layout_width="299dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="250dp"
android:text="@string/desc_about"
android:textAlignment="center"
android:textColor="#FFFFFF"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.416"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2"
app:layout_constraintVertical_bias="1.0"
tools:ignore="TextSizeCheck" />
<TextView
android:id="@+id/link"
style="@style/LinkText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Vývoj: @Odweta"
android:textColor="#FFFFFF"
android:textFontWeight="400"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintVertical_bias="0.3" />
<TextView
android:id="@+id/sourceCode"
style="@style/LinkText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/source_code"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:fontFamily="@font/roboto">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/dayPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/app_root" />
@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbarFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black"
app:layout_constraintBottom_toTopOf="@+id/fragmentContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_margin="20dp"
android:background="@drawable/toolbar_background"
android:fontFamily="@font/roboto"
android:gravity="center"
android:padding="15dp"
android:textSize="25sp"
android:textColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/black"
app:layout_constraintBottom_toTopOf="@+id/bottomNav"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbarFrame"
app:layout_constraintVertical_bias="0.0" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/toolbar_background"
android:layout_margin="20dp"
android:fontFamily="@font/roboto"
app:itemTextColor="@color/white"
android:padding="5dp"
app:layout_constraintTop_toBottomOf="@+id/fragmentContainer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/menu_navbar" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/mark_detail_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/one_mark_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/mark_details_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/mark_details_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--<Button
android:id="@+id/menu_button_settings"
android:layout_width="190dp"
android:layout_height="80dp"
android:layout_marginTop="200dp"
android:layout_marginBottom="50dp"
android:background="@drawable/button_background_selector"
android:text="@string/settings"
android:textSize="22sp"
app:layout_constraintBottom_toTopOf="@+id/menu_button_sign_out"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />-->
<Button
android:id="@+id/menu_button_sign_out"
android:layout_width="190dp"
android:layout_height="80dp"
android:background="@drawable/button_background_selector"
android:text="@string/sign_out"
android:textSize="22sp"
app:cornerRadius="@dimen/corner_radius"
app:layout_constraintBottom_toTopOf="@+id/menu_button_sign_in"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/menu_button_sign_in"
android:layout_width="190dp"
android:layout_height="80dp"
android:background="@drawable/button_background_selector"
android:ellipsize="none"
android:scrollHorizontally="false"
android:text="@string/sign_in"
android:textSize="22sp"
app:cornerRadius="@dimen/corner_radius"
app:layout_constraintBottom_toTopOf="@+id/menu_button_about"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/menu_button_sign_out" />
<Button
android:id="@+id/menu_button_about"
android:layout_width="190dp"
android:layout_height="80dp"
android:background="@drawable/button_background_selector"
android:text="@string/about_app"
android:textSize="22sp"
app:cornerRadius="@dimen/corner_radius"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/menu_button_sign_in" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/no_mark_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundGravity="center"
android:gravity="center"
android:orientation="vertical" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:background="@color/black"
app:layout_constraintBottom_toTopOf="@+id/scrollViewSettings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:background="@drawable/toolbar_background"
android:fontFamily="@font/roboto"
android:gravity="center"
android:padding="10dp"
android:text="@string/settings"
android:textColor="@color/white"
android:textSize="25sp" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:id="@+id/scrollViewSettings"
android:layout_width="409dp"
android:layout_height="673dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="20dp"
android:paddingTop="10dp"
android:paddingRight="20dp"
android:paddingBottom="10dp"
tools:ignore="UselessParent">
<TextView
android:layout_width="180dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/lang_change"
android:textColor="#FFFFFF"
android:textColorHighlight="#FFFFFF"
android:textColorHint="#FFFFFF"
android:textColorLink="#FFFFFF"
android:textSize="20sp" />
<Button
android:id="@+id/setting_language_button_choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/button_background_selector"
android:text="@string/lang_choose" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,104 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/logo"
android:layout_width="286dp"
android:layout_height="96dp"
android:layout_marginTop="120dp"
android:contentDescription="@string/solon_logo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.496"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/solon_just_yellow" />
<LinearLayout
android:id="@+id/frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/signInButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/logo">
<EditText
android:id="@+id/usernameField"
android:layout_width="240dp"
android:layout_height="50dp"
android:layout_marginBottom="10dp"
android:background="@drawable/button_background"
android:ems="10"
android:hint="@string/username"
android:importantForAutofill="no"
android:inputType="text"
android:paddingHorizontal="20dp"
android:textColor="@color/white"
android:textColorHighlight="@color/white"
android:textColorHint="#C8FFFFFF"
android:textColorLink="@color/white"
android:textCursorDrawable="@drawable/cursor"
android:theme="@style/sign_in_field"
app:circularflow_radiusInDP="20dp" />
<LinearLayout
android:layout_width="240dp"
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/button_background">
<EditText
android:id="@+id/passwordField"
android:layout_width="190dp"
android:layout_height="match_parent"
android:ems="10"
android:background="@color/transparent"
android:hint="@string/password"
android:importantForAutofill="no"
android:inputType="textPassword"
android:paddingHorizontal="20dp"
android:textColor="@color/white"
android:textColorHighlight="@color/white"
android:textColorHint="#C8FFFFFF"
android:textColorLink="@color/white"
android:textCursorDrawable="@drawable/cursor"
android:theme="@style/sign_in_field" />
<ImageButton
android:id="@+id/showPasswordToggleButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="20dp"/>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/signInButton"
android:layout_width="240dp"
android:layout_height="80dp"
android:background="@drawable/button_background_selector"
android:fontFamily="@font/roboto"
android:text="@string/sign_in_"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/frame"
app:layout_constraintVertical_bias="0.056" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView3"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_marginTop="115dp"
android:layout_marginBottom="116dp"
android:contentDescription="@string/kolaonline_enhanced"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.16"
app:srcCompat="@drawable/solon_500_yellow" />
<ImageView
android:id="@+id/loading_gif"
android:layout_width="200dp"
android:layout_height="200dp"
android:contentDescription="@string/loading_gif"
android:src="@drawable/loading3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView3"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sign_out_warning"
android:textColor="@color/white"
android:textSize="20sp"
android:windowBackground="@drawable/button_background"
android:layout_margin="20dp"
android:gravity="start" />
</LinearLayout>
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/layoutDayMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:fontFamily="@font/roboto"
android:orientation="vertical" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="20dp">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lang_cz"
android:textColor="@color/white"
android:layout_marginHorizontal="20dp"
android:theme="@style/Theme.Solon.RadioButton" />
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lang_en"
android:textColor="@color/white"
android:layout_marginHorizontal="20dp"
android:theme="@style/Theme.Solon.RadioButton" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_timetable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@android:drawable/ic_dialog_dialer"
android:title="@string/timetable" />
<item
android:id="@+id/nav_marks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@mipmap/grade2_foreground"
android:title="@string/marks" />
<item
android:id="@+id/nav_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/menu_240"
android:title="@string/menu" />
</menu>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/launcher_background"/>
<foreground android:drawable="@mipmap/launcher_foreground"/>
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/launcher_background"/>
<foreground android:drawable="@mipmap/launcher_foreground"/>
</adaptive-icon>
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="MonochromeLauncherIcon">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="MonochromeLauncherIcon">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More