made solon 2.0
@@ -1,3 +1,15 @@
|
||||
*.swp
|
||||
TODO.md
|
||||
./.idea/
|
||||
*.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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/help" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/mobile/help" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@@ -1 +1 @@
|
||||
Solon
|
||||
solon
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>
|
||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
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()
|
||||
}
|
||||
setContent {
|
||||
if (!loaded) {
|
||||
launchPhase0(LocalContext.current, rememberCoroutineScope())
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// @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")
|
||||
when (signedInAndReadyToLaunch.value) {
|
||||
true -> launchPhase0(LocalContext.current, rememberCoroutineScope())
|
||||
else -> {}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
if (!Vars.timeTableAvailOffline || Vars.internetAvail) {
|
||||
// set up the API (required for later uses of it)
|
||||
Network.setupAPI()
|
||||
|
||||
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)
|
||||
Box(
|
||||
modifier = barModifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
NavbarScreen()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
*/
|
||||
)
|
||||
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
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>
|
||||
|
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 |
|
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>
|
||||
|
Before Width: | Height: | Size: 4.7 KiB |
|
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>
|
||||
@@ -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>
|
||||
|
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>
|
||||
|
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>
|
||||