Initial commit
This commit is contained in:
+66
@@ -0,0 +1,66 @@
|
|||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
#Wed Feb 12 10:01:56 CET 2020
|
||||||
|
gradle.version=6.1-rc-1
|
||||||
Binary file not shown.
Binary file not shown.
Generated
+119
@@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<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>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+25
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="BintrayJCenter" />
|
||||||
|
<option name="name" value="BintrayJCenter" />
|
||||||
|
<option name="url" value="https://jcenter.bintray.com/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="Google" />
|
||||||
|
<option name="name" value="Google" />
|
||||||
|
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+9
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Qwark.iml" filepath="$PROJECT_DIR$/.idea/modules/Qwark.iml" group="Qwark" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/app.iml" group="Qwark/app" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+19
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.linked.project.id="Qwark" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="java-gradle" name="Java-Gradle">
|
||||||
|
<configuration>
|
||||||
|
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/../../build" />
|
||||||
|
<option name="BUILDABLE" value="false" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$/../..">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/../../.gradle" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+210
File diff suppressed because one or more lines are too long
Generated
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.linked.project.id="Qwark" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="java-gradle" name="Java-Gradle">
|
||||||
|
<configuration>
|
||||||
|
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
|
||||||
|
<option name="BUILDABLE" value="false" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
+210
File diff suppressed because one or more lines are too long
@@ -0,0 +1,62 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 29
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.denizd.qwark"
|
||||||
|
minSdkVersion 23
|
||||||
|
targetSdkVersion 29
|
||||||
|
versionCode 2
|
||||||
|
versionName "0.2.0"
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled true
|
||||||
|
shrinkResources true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation 'androidx.core:core-ktx:1.2.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
|
||||||
|
implementation "androidx.fragment:fragment-ktx:1.2.1"
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
|
implementation 'com.google.android.material:material:1.2.0-alpha04'
|
||||||
|
|
||||||
|
implementation 'androidx.preference:preference-ktx:1.1.0'
|
||||||
|
|
||||||
|
def lifecycle_version = "2.2.0"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||||
|
|
||||||
|
def room_version = "2.2.3" // "2.1.0-alpha04"
|
||||||
|
implementation "androidx.room:room-runtime:2.2.3"
|
||||||
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
|
||||||
|
|
||||||
|
implementation 'com.shawnlin:number-picker:2.4.8'
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "com.denizd.qwark",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"properties": [],
|
||||||
|
"versionCode": 1,
|
||||||
|
"versionName": "1",
|
||||||
|
"enabled": true,
|
||||||
|
"outputFile": "app-release.apk"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.denizd.qwark">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme.Launcher">
|
||||||
|
<activity android:name=".activity.MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,137 @@
|
|||||||
|
package com.denizd.qwark.activity
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.graphics.Color
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.fragment.*
|
||||||
|
import com.denizd.qwark.fragment.CourseFragment
|
||||||
|
import com.denizd.qwark.fragment.ParticipationFragment
|
||||||
|
import com.denizd.qwark.viewmodel.MainViewModel
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|
||||||
|
internal class MainActivity : AppCompatActivity(R.layout.activity_main) {
|
||||||
|
|
||||||
|
private lateinit var toolbarTitle: TextView
|
||||||
|
private lateinit var bottomNavigationView: BottomNavigationView
|
||||||
|
private lateinit var viewModel: MainViewModel
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setTheme(R.style.AppTheme)
|
||||||
|
|
||||||
|
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
|
||||||
|
viewModel.applyTheme(window, this)
|
||||||
|
viewModel.checkForFirstTimeYearNotice(this)
|
||||||
|
|
||||||
|
val actionBar = findViewById<AppBarLayout>(R.id.app_bar_layout)
|
||||||
|
toolbarTitle = findViewById(R.id.toolbarTxt)
|
||||||
|
bottomNavigationView = findViewById(R.id.bottom_nav)
|
||||||
|
|
||||||
|
findViewById<AppBarLayout>(R.id.app_bar_layout).also { appBarLayout ->
|
||||||
|
appBarLayout.setOnApplyWindowInsetsListener { _, insets ->
|
||||||
|
(appBarLayout.layoutParams as ViewGroup.MarginLayoutParams)
|
||||||
|
.topMargin = insets.systemWindowInsetTop
|
||||||
|
insets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.decorView.systemUiVisibility = if (resources.getBoolean(R.bool.isLight)) {
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||||
|
} else {
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
}
|
||||||
|
|
||||||
|
window.attributes = window.attributes.apply {
|
||||||
|
flags and WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS.inv()
|
||||||
|
}
|
||||||
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
|
||||||
|
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
|
||||||
|
actionBar.setExpanded(true)
|
||||||
|
loadFragment(viewModel.getFragmentType(item.itemId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO figure out why this isn't working
|
||||||
|
// if (savedInstanceState == null) {
|
||||||
|
// supportFragmentManager.beginTransaction().apply {
|
||||||
|
// val fragments = arrayOf(
|
||||||
|
// CourseFragment(),
|
||||||
|
//// GradeFragment(),
|
||||||
|
// NoteFragment(),
|
||||||
|
// ParticipationFragment(),
|
||||||
|
//// ParticipationCourseFragment(),
|
||||||
|
// FinalsFragment(),
|
||||||
|
// SettingsFragment()
|
||||||
|
// )
|
||||||
|
// for (fragment in fragments) {
|
||||||
|
// add(R.id.fragment_container, fragment, fragment.name)
|
||||||
|
// if (fragment !is CourseFragment) hide(fragment)
|
||||||
|
// }
|
||||||
|
// }.commit()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO reselected listener for scrolling up
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (supportFragmentManager.fragments.size == 0) {
|
||||||
|
bottomNavigationView.selectedItemId = R.id.courses
|
||||||
|
loadFragment(viewModel.getFragmentType(R.id.courses))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadFragment(type: String): Boolean {
|
||||||
|
val fragment = supportFragmentManager.findFragmentByTag(type) ?: viewModel.getFragment(type)
|
||||||
|
|
||||||
|
if (fragment.lifecycle.currentState != Lifecycle.State.INITIALIZED) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
supportFragmentManager.beginTransaction().replace(R.id.fragment_container, fragment, type)
|
||||||
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit()
|
||||||
|
|
||||||
|
// with (supportFragmentManager) {
|
||||||
|
// beginTransaction()
|
||||||
|
// .hide(fragments[fragments.size - 1]) // should this be 0?
|
||||||
|
// .show(findFragmentByTag(type) ?: TODO())
|
||||||
|
// .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
|
// .commit()
|
||||||
|
// }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
// Log.d("TAG2", "${supportFragmentManager.}") // supportFragmentManager.fragments[0].tag} ${supportFragmentManager.fragments[0].javaClass.simpleName
|
||||||
|
when (supportFragmentManager.fragments[0].tag) {
|
||||||
|
"GradeFragment", "ParticipationCourseFragment" -> {
|
||||||
|
bottomNavigationView.visibility = View.INVISIBLE
|
||||||
|
findViewById<View>(R.id.view).visibility = View.INVISIBLE
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
"NoteFragment" -> {
|
||||||
|
if ((supportFragmentManager.fragments[0] as NoteFragment).onBackPressed()) {
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
bottomNavigationView.visibility = View.VISIBLE
|
||||||
|
findViewById<View>(R.id.view).visibility = View.VISIBLE
|
||||||
|
super.onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.util.getUserDefinedAverage
|
||||||
|
import com.denizd.qwark.util.roundToInt
|
||||||
|
|
||||||
|
class CourseAdapter(private var courses: List<CourseExam>,
|
||||||
|
private val onClickListener: CourseClickListener,
|
||||||
|
private val gradeType: Int?,
|
||||||
|
private val isParticipationFragment: Boolean = false) : RecyclerView.Adapter<CourseAdapter.CourseViewHolder>() {
|
||||||
|
|
||||||
|
class CourseViewHolder(view: View, private val clickListener: CourseClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
|
val backgroundView: ConstraintLayout = view.findViewById(R.id.backgroundLayout)
|
||||||
|
val titleTextView: TextView = view.findViewById(R.id.title)
|
||||||
|
val averageTextView: TextView = view.findViewById(R.id.average)
|
||||||
|
val imageView: ImageView = view.findViewById(R.id.image_view)
|
||||||
|
lateinit var currentCourse: CourseExam
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
view.setOnLongClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onCourseClick(currentCourse)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClick(v: View?): Boolean {
|
||||||
|
clickListener.onCourseLongClick(currentCourse)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CourseViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.course_card, parent, false)
|
||||||
|
return CourseViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: CourseViewHolder, position: Int) {
|
||||||
|
val currentItem = courses[position]
|
||||||
|
val context = holder.averageTextView.context
|
||||||
|
|
||||||
|
holder.backgroundView.setBackgroundColor(
|
||||||
|
context.getColor(if (currentItem.advanced) R.color.colorAccent else R.color.colorCardBackground)
|
||||||
|
)
|
||||||
|
val textColour = context.getColor(if (currentItem.advanced) R.color.colorTextInverted else R.color.colorText)
|
||||||
|
|
||||||
|
val titleText = if (currentItem.advanced) context.getString(R.string.advanced_abbreviated_placeholder) else "%s"
|
||||||
|
holder.titleTextView.text = String.format(titleText, currentItem.name)
|
||||||
|
val averageText = when (currentItem.gradeCount) {
|
||||||
|
0 -> context.getString(R.string.average_points_no_grade_placeholder)
|
||||||
|
else -> when (gradeType) {
|
||||||
|
QwarkUtil.GRADE_PRECISE, QwarkUtil.GRADE_ROUNDED ->
|
||||||
|
context.resources.getQuantityString(R.plurals.average_points_placeholder,
|
||||||
|
if (currentItem.average == "1.00") 1 else 2,
|
||||||
|
currentItem.average.getUserDefinedAverage(gradeType)
|
||||||
|
)
|
||||||
|
else -> context.getString(R.string.average_grade_placeholder, currentItem.average.getUserDefinedAverage(gradeType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var timeLeft = ((currentItem.examTime.toDouble() - QwarkUtil.timeAtMidnight.toDouble()) / (1000.0 * 60.0 * 60.0 * 24.0)).roundToInt()
|
||||||
|
val examText = when (timeLeft) {
|
||||||
|
0 -> context.getString(R.string.exam_today_placeholder)
|
||||||
|
else -> {
|
||||||
|
when {
|
||||||
|
timeLeft > 7 -> {
|
||||||
|
timeLeft /= 7
|
||||||
|
context.resources.getQuantityString(R.plurals.exam_weeks_placeholder, timeLeft, timeLeft.toString())
|
||||||
|
}
|
||||||
|
timeLeft in 1..7 -> context.resources.getQuantityString(R.plurals.exam_days_placeholder, timeLeft, timeLeft.toString())
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
holder.averageTextView.text = if (isParticipationFragment) {
|
||||||
|
val args = currentItem.participation.split('|')
|
||||||
|
context.getString(R.string.participation_placeholder, args[0], args[1])
|
||||||
|
} else buildString {
|
||||||
|
append(averageText)
|
||||||
|
append(examText)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentItem.icon.isNotEmpty()) {
|
||||||
|
holder.imageView.setImageDrawable(
|
||||||
|
context.getDrawable(QwarkUtil.getDrawableIntForString(currentItem.icon))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
holder.titleTextView.setTextColor(textColour)
|
||||||
|
holder.averageTextView.setTextColor(textColour)
|
||||||
|
holder.imageView.setColorFilter(textColour)
|
||||||
|
holder.currentCourse = currentItem // QwarkUtil.getCourseExamAsCourse(currentItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = courses.size
|
||||||
|
|
||||||
|
fun setCourses(courses: List<CourseExam>) {
|
||||||
|
this.courses = courses
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CourseClickListener {
|
||||||
|
fun onCourseClick(course: CourseExam)
|
||||||
|
fun onCourseLongClick(course: CourseExam)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
|
||||||
|
internal class FinalGradeAdapter(private var finalGrades: List<FinalGrade>, private val onClickListener: FinalGradeClickListener) : RecyclerView.Adapter<FinalGradeAdapter.FinalGradeViewHolder>() {
|
||||||
|
|
||||||
|
internal class FinalGradeViewHolder(view: View, private val clickListener: FinalGradeClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
|
internal val name: TextView = view.findViewById(R.id.name)
|
||||||
|
internal val grade: TextView = view.findViewById(R.id.grade)
|
||||||
|
internal val multiplier: TextView = view.findViewById(R.id.multiplier)
|
||||||
|
internal lateinit var currentFinalGrade: FinalGrade
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
// view.setOnLongClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onFinalGradeClick(currentFinalGrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClick(v: View?): Boolean {
|
||||||
|
// clickListener.onFinalGradeLongClick(currentFinalGrade)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FinalGradeViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.final_grade_item, parent, false)
|
||||||
|
return FinalGradeViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: FinalGradeViewHolder, position: Int) {
|
||||||
|
val currentItem = finalGrades[position]
|
||||||
|
|
||||||
|
if (currentItem.grade == "-1") {
|
||||||
|
holder.name.text = "---"
|
||||||
|
holder.grade.text = "––"
|
||||||
|
} else {
|
||||||
|
holder.name.text = currentItem.name
|
||||||
|
holder.grade.text = currentItem.grade // QwarkUtil.getGradeWithLeadingZero(currentItem.grade.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.multiplier.text = currentItem.note
|
||||||
|
val linkDrawable = if (currentItem.courseId != -1) {
|
||||||
|
R.drawable.link
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
holder.multiplier.setCompoundDrawablesWithIntrinsicBounds(linkDrawable, 0, 0, 0)
|
||||||
|
holder.currentFinalGrade = currentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = finalGrades.size
|
||||||
|
|
||||||
|
fun setFinalGrades(finalGrades: List<FinalGrade>) {
|
||||||
|
this.finalGrades = finalGrades
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface FinalGradeClickListener {
|
||||||
|
fun onFinalGradeClick(finalGrade: FinalGrade)
|
||||||
|
// fun onFinalGradeLongClick(finalGradeId: Int)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.denizd.qwark.util.getUserDefinedAverage
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class GradeAdapter(private var grades: List<Grade>, private val onClickListener: GradeClickListener, private val gradeType: Int) : RecyclerView.Adapter<GradeAdapter.GradeViewHolder>() {
|
||||||
|
|
||||||
|
class GradeViewHolder(view: View, private val clickListener: GradeClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
|
val grade: TextView = view.findViewById(R.id.grade)
|
||||||
|
val notes: TextView = view.findViewById(R.id.notes)
|
||||||
|
val info: TextView = view.findViewById(R.id.info)
|
||||||
|
val progressBar: ProgressBar = view.findViewById(R.id.progress_bar)
|
||||||
|
lateinit var currentGrade: Grade
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
view.setOnLongClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onGradeClick(currentGrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClick(v: View?): Boolean {
|
||||||
|
clickListener.onGradeLongClick(currentGrade.gradeId)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GradeViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.grade_card, parent, false)
|
||||||
|
return GradeViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: GradeViewHolder, position: Int) {
|
||||||
|
val currentItem = grades[position]
|
||||||
|
|
||||||
|
holder.grade.text = if (currentItem.grade == -1) {
|
||||||
|
"??"
|
||||||
|
} else {
|
||||||
|
QwarkUtil.getGradeAsString(currentItem.grade, true).getUserDefinedAverage(gradeType, true)
|
||||||
|
}
|
||||||
|
holder.notes.text = if (currentItem.note.isEmpty()) "---" else currentItem.note
|
||||||
|
holder.info.text = buildString {
|
||||||
|
append(
|
||||||
|
holder.info.context.getString(
|
||||||
|
if (currentItem.verbal) R.string.verbal_placeholder else R.string.written_placeholder
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (currentItem.examTime != -1L) append(
|
||||||
|
holder.info.context.getString(
|
||||||
|
R.string.exam_on_placeholder,
|
||||||
|
QwarkUtil.getSimpleDateFormat().format(Date(currentItem.examTime)
|
||||||
|
).toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.progressBar.apply {
|
||||||
|
max = 100
|
||||||
|
progress = currentItem.weighting
|
||||||
|
}
|
||||||
|
holder.currentGrade = currentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = grades.size
|
||||||
|
|
||||||
|
fun setGrades(grades: List<Grade>) {
|
||||||
|
this.grades = grades
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GradeClickListener {
|
||||||
|
fun onGradeClick(grade: Grade)
|
||||||
|
fun onGradeLongClick(gradeId: Int)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal class HistoricalAverageAdapter(private var averages: List<HistoricalAvg>?) : RecyclerView.Adapter<HistoricalAverageAdapter.AverageViewHolder>() {
|
||||||
|
|
||||||
|
internal class AverageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
val grade: TextView = view.findViewById(R.id.grade)
|
||||||
|
val time: TextView = view.findViewById(R.id.time)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AverageViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.average_card, parent, false)
|
||||||
|
return AverageViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: AverageViewHolder, position: Int) {
|
||||||
|
val currentItem = averages!![position]
|
||||||
|
|
||||||
|
val leadingZero = if (currentItem.average.toDouble() < 10.0) "0%s" else "%s"
|
||||||
|
holder.grade.text = String.format(leadingZero, currentItem.average) // TODO this conflicts with Points (Precise)
|
||||||
|
holder.time.text = QwarkUtil.getSimpleDateTimeFormat().format(Date(currentItem.time))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = averages?.size ?: 0
|
||||||
|
|
||||||
|
fun setAverages(averages: List<HistoricalAvg>) {
|
||||||
|
this.averages = averages
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.model.CourseIcon
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
|
||||||
|
internal class IconAdapter(private var icons: List<CourseIcon>, private val onClickListener: IconClickListener) : RecyclerView.Adapter<IconAdapter.IconViewHolder>() {
|
||||||
|
|
||||||
|
private var selectedItem = 0
|
||||||
|
|
||||||
|
internal class IconViewHolder(view: View, private val clickListener: IconClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||||
|
|
||||||
|
val imageView: ImageView = view.findViewById(R.id.image_view)
|
||||||
|
val card: MaterialCardView = view.findViewById(R.id.card)
|
||||||
|
var image: Int = 0
|
||||||
|
var key: String = ""
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onIconClick(key, image, adapterPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IconViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.icon_item, parent, false)
|
||||||
|
return IconViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: IconViewHolder, position: Int) {
|
||||||
|
val currentItem = icons[position]
|
||||||
|
|
||||||
|
val context = holder.card.context
|
||||||
|
|
||||||
|
holder.imageView.setImageDrawable(context.getDrawable(currentItem.value))
|
||||||
|
holder.image = currentItem.value
|
||||||
|
holder.key = currentItem.key
|
||||||
|
|
||||||
|
holder.card.setCardBackgroundColor(context.getColor(if (position == selectedItem) {
|
||||||
|
R.color.colorAccent
|
||||||
|
} else {
|
||||||
|
R.color.colorCardBackground
|
||||||
|
}))
|
||||||
|
holder.imageView.setColorFilter(context.getColor(if (position == selectedItem) {
|
||||||
|
R.color.colorBackground
|
||||||
|
} else {
|
||||||
|
R.color.colorText
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = icons.size
|
||||||
|
|
||||||
|
fun setIcons(icons: List<CourseIcon>) {
|
||||||
|
this.icons = icons
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelectedItem(position: Int) {
|
||||||
|
selectedItem = position
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IconClickListener {
|
||||||
|
fun onIconClick(key: String, value: Int, position: Int)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.text.SpannableString
|
||||||
|
import android.text.style.StrikethroughSpan
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
|
|
||||||
|
internal class NoteAdapter(private var notes: List<Note>, private val onClickListener: OnNoteClickListener) : RecyclerView.Adapter<NoteAdapter.NoteViewHolder>() {
|
||||||
|
|
||||||
|
internal class NoteViewHolder(view: View, private val clickListener: OnNoteClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
|
val content: TextView = view.findViewById(R.id.content)
|
||||||
|
val created: TextView = view.findViewById(R.id.created)
|
||||||
|
val category: Chip = view.findViewById(R.id.category_chip)
|
||||||
|
internal lateinit var currentNote: Note
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
view.setOnLongClickListener(this)
|
||||||
|
|
||||||
|
category.setOnClickListener {
|
||||||
|
clickListener.onCategoryChipClick(currentNote.category)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onNoteClick(currentNote)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClick(v: View?): Boolean {
|
||||||
|
clickListener.onNoteLongClick(currentNote)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.note_card, parent, false)
|
||||||
|
return NoteViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
|
||||||
|
val currentItem = notes[position]
|
||||||
|
|
||||||
|
val content = SpannableString(currentItem.content)
|
||||||
|
|
||||||
|
val contentColour = ContextCompat.getColor(holder.content.context, if (currentItem.dismissed) {
|
||||||
|
content.setSpan(StrikethroughSpan(), 0, content.length, 0)
|
||||||
|
R.color.colorHint
|
||||||
|
} else {
|
||||||
|
R.color.colorText
|
||||||
|
})
|
||||||
|
|
||||||
|
holder.content.setText(content, TextView.BufferType.SPANNABLE)
|
||||||
|
holder.content.setTextColor(contentColour)
|
||||||
|
holder.created.text = QwarkUtil.getCreatedString(holder.created.context, currentItem.time)
|
||||||
|
holder.category.text = if (currentItem.category.isEmpty()) "---" else currentItem.category
|
||||||
|
holder.category.visibility = if (currentItem.category.isEmpty()) View.GONE else View.VISIBLE
|
||||||
|
holder.currentNote = currentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = notes.size
|
||||||
|
|
||||||
|
fun setNotes(notes: List<Note>) {
|
||||||
|
this.notes = notes
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface OnNoteClickListener {
|
||||||
|
fun onNoteClick(note: Note)
|
||||||
|
fun onNoteLongClick(note: Note)
|
||||||
|
fun onCategoryChipClick(category: String)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.model.Participation
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ParticipationAdapter(private var participations: List<Participation>, private val onClickListener: ParticipationClickListener) : RecyclerView.Adapter<ParticipationAdapter.ParticipationViewHolder>() {
|
||||||
|
|
||||||
|
class ParticipationViewHolder(view: View, private val onClickListener: ParticipationClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
|
||||||
|
val imageView: ImageView = view.findViewById(R.id.image_view)
|
||||||
|
val time: TextView = view.findViewById(R.id.time)
|
||||||
|
val participationText: TextView = view.findViewById(R.id.participation)
|
||||||
|
lateinit var participation: Participation
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
view.setOnLongClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
onClickListener.onParticipationClick(participation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClick(v: View?): Boolean {
|
||||||
|
onClickListener.onParticipationLongClick(participation)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParticipationViewHolder =
|
||||||
|
ParticipationViewHolder(LayoutInflater.from(parent.context).inflate(
|
||||||
|
R.layout.participation_card,
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
), onClickListener)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ParticipationViewHolder, position: Int) {
|
||||||
|
val currentItem = participations[position]
|
||||||
|
|
||||||
|
holder.imageView.setImageDrawable(holder.imageView.context.getDrawable(
|
||||||
|
with (currentItem) {
|
||||||
|
when {
|
||||||
|
timesHandRaised > timesSpoken -> R.drawable.hand
|
||||||
|
timesHandRaised < timesSpoken -> R.drawable.speech
|
||||||
|
else -> R.drawable.cross
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
holder.time.text = QwarkUtil.getSimpleDateTimeFormat().format(Date(currentItem.time))
|
||||||
|
holder.participationText.text = holder.participationText.context.getString(
|
||||||
|
R.string.participation_placeholder,
|
||||||
|
currentItem.timesHandRaised.toString(),
|
||||||
|
currentItem.timesSpoken.toString()
|
||||||
|
)
|
||||||
|
holder.participation = currentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = participations.size
|
||||||
|
|
||||||
|
fun setParticipations(participations: List<Participation>) {
|
||||||
|
this.participations = participations
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParticipationClickListener {
|
||||||
|
fun onParticipationClick(participation: Participation)
|
||||||
|
fun onParticipationLongClick(participation: Participation)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.model.SchoolDay
|
||||||
|
|
||||||
|
class SchoolDayAdapter(private var days: List<SchoolDay>, private val onClickListener: DayClickListener) : RecyclerView.Adapter<SchoolDayAdapter.DayViewHolder>() {
|
||||||
|
|
||||||
|
class DayViewHolder(view: View, private val clickListener: DayClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||||
|
|
||||||
|
val title: TextView = view.findViewById(R.id.title)
|
||||||
|
lateinit var currentDay: SchoolDay
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onDayClick(currentDay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DayViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.year_card, parent, false)
|
||||||
|
return DayViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: DayViewHolder, position: Int) {
|
||||||
|
val currentItem = days[position]
|
||||||
|
|
||||||
|
holder.title.text = holder.title.context.resources.getStringArray(R.array.days)[dayMap[currentItem.day] ?: 0]
|
||||||
|
holder.currentDay = currentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = days.size
|
||||||
|
|
||||||
|
fun setDays(days: List<SchoolDay>) {
|
||||||
|
this.days = days
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dayMap: Map<String, Int> = mapOf(
|
||||||
|
"mon" to 0,
|
||||||
|
"tue" to 1,
|
||||||
|
"wed" to 2,
|
||||||
|
"thu" to 3,
|
||||||
|
"fri" to 4
|
||||||
|
)
|
||||||
|
|
||||||
|
interface DayClickListener {
|
||||||
|
fun onDayClick(day: SchoolDay)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.denizd.qwark.adapter
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
|
||||||
|
internal class YearAdapter(private var schoolYears: List<SchoolYear>, private val onClickListener: YearClickListener) : RecyclerView.Adapter<YearAdapter.YearViewHolder>() {
|
||||||
|
|
||||||
|
internal class YearViewHolder(view: View, private val clickListener: YearClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||||
|
|
||||||
|
internal val title: TextView = view.findViewById(R.id.title)
|
||||||
|
internal lateinit var currentSchoolYear: SchoolYear
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
clickListener.onYearClick(currentSchoolYear)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YearViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.year_card, parent, false)
|
||||||
|
return YearViewHolder(v, onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: YearViewHolder, position: Int) {
|
||||||
|
val currentItem = schoolYears[position]
|
||||||
|
|
||||||
|
holder.title.text = currentItem.year
|
||||||
|
holder.currentSchoolYear = currentItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = schoolYears.size
|
||||||
|
|
||||||
|
fun setYears(schoolYears: List<SchoolYear>) {
|
||||||
|
this.schoolYears = schoolYears
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface YearClickListener {
|
||||||
|
fun onYearClick(schoolYear: SchoolYear)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
|
||||||
|
internal class CourseRepository(app: Application) {
|
||||||
|
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(app).dao()
|
||||||
|
// val allCourses: LiveData<List<Course>> = dao.allCourses
|
||||||
|
// val allHistoricalAverages: LiveData<List<HistoricalAvg>> = dao.allHistoricalAverages
|
||||||
|
|
||||||
|
fun insert(course: Course) { dao.insert(course) }
|
||||||
|
fun update(course: Course) { dao.update(course) }
|
||||||
|
fun delete(course: Course) { dao.delete(course) }
|
||||||
|
|
||||||
|
fun insert(avg: HistoricalAvg) { dao.insert(avg) }
|
||||||
|
|
||||||
|
fun getCoursesByYearId(yearId: Int): LiveData<List<CourseExam>> = dao.getCoursesByYearId(yearId)
|
||||||
|
fun getAveragesForCourses(courseId: Int): List<HistoricalAvg> = dao.getAveragesForCourses(courseId)
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.util.QwarkPreferences
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.RoundingMode
|
||||||
|
|
||||||
|
class FinalsRepository(app: Application) {
|
||||||
|
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(app).dao()
|
||||||
|
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(app)
|
||||||
|
|
||||||
|
fun getScoreProfileName(): String = prefs.getScoreProfileName()
|
||||||
|
fun getFinalGrades(finalScoreId: Int): LiveData<List<FinalGrade>> = dao.getFinalGrades(finalScoreId)
|
||||||
|
fun getFinalScoreId() = prefs.getScoreProfileId()
|
||||||
|
fun getCourseSortType() = prefs.getCourseSortType()
|
||||||
|
fun getGradeType() = prefs.getGradeType()
|
||||||
|
|
||||||
|
fun updateFinalGradeAverage(courseId: Int, finalGradeId: Int) {
|
||||||
|
val course = dao.getCourse(courseId)[0]
|
||||||
|
dao.updateFinalGradeAverage(
|
||||||
|
course.name,
|
||||||
|
BigDecimal(course.average).setScale(0, RoundingMode.HALF_UP).toInt(),
|
||||||
|
finalGradeId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateLinkedGrades() {
|
||||||
|
val finalGrades = dao.getFinalGradesAsList(getFinalScoreId())
|
||||||
|
for (finalGrade in finalGrades) {
|
||||||
|
if (finalGrade.courseId != -1) {
|
||||||
|
updateFinalGradeAverage(finalGrade.courseId, finalGrade.finalGradeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSchoolYearName(courseId: Int) = dao.getSchoolYearName(courseId)
|
||||||
|
|
||||||
|
fun getAllYears() = dao.getAllYears()
|
||||||
|
fun getAllCourses() = dao.getAllCourseExams()
|
||||||
|
|
||||||
|
fun getCourse(courseId: Int) = dao.getCourse(courseId)[0]
|
||||||
|
|
||||||
|
fun update(finalGrade: FinalGrade) { dao.update(finalGrade) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
import com.denizd.qwark.util.*
|
||||||
|
import com.denizd.qwark.util.QwarkPreferences
|
||||||
|
|
||||||
|
class GradeRepository(app: Application) {
|
||||||
|
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(app).dao()
|
||||||
|
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(app)
|
||||||
|
|
||||||
|
fun getGradeType(): Int = prefs.getInt(PreferenceKey.GRADE_TYPE)
|
||||||
|
|
||||||
|
// TODO this might crash
|
||||||
|
fun getCourse(courseId: Int) = dao.getCourse(courseId)[0]
|
||||||
|
|
||||||
|
fun insert(grade: Grade) { dao.insert(grade) }
|
||||||
|
fun update(grade: Grade) { dao.update(grade) }
|
||||||
|
fun delete(gradeId: Int) { dao.deleteGrade(gradeId) }
|
||||||
|
|
||||||
|
fun insert(avg: HistoricalAvg) { dao.insert(avg) }
|
||||||
|
fun updateAverage(average: String, gradeCount: Int, courseId: Int) {
|
||||||
|
dao.updateAverage(average, gradeCount, courseId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGradesByForeignKey(courseId: Int): LiveData<List<Grade>> = dao.getGradesByForeignKey(courseId)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
|
||||||
|
class NoteRepository(app: Application) {
|
||||||
|
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(app).dao()
|
||||||
|
val allNotes: LiveData<List<Note>> = dao.allNotes
|
||||||
|
|
||||||
|
fun getNotesForCategory(category: String): LiveData<List<Note>> = dao.notesForCategory(category)
|
||||||
|
fun getAllCategories(): List<String> = dao.getAllCategories()
|
||||||
|
fun insert(note: Note) { dao.insert(note) }
|
||||||
|
fun update(note: Note) { dao.update(note) }
|
||||||
|
fun updateDismissed(newDismissedStatus: Boolean, noteId: Int) { dao.updateDismissed(newDismissedStatus, noteId) }
|
||||||
|
fun delete(noteId: Int) { dao.deleteNote(noteId) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.util.QwarkPreferences
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.Participation
|
||||||
|
import com.denizd.qwark.model.SchoolDay
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.util.round
|
||||||
|
import com.denizd.qwark.util.roundToInt
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ParticipationRepository(app: Application) {
|
||||||
|
|
||||||
|
private val days: Array<String> = arrayOf("mon", "tue", "wed", "thu", "fri")
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(app).dao()
|
||||||
|
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(app.applicationContext)
|
||||||
|
// monday: 1, increment by 1 for each day
|
||||||
|
val dayId: Int = when (prefs.getParticipationDay()) {
|
||||||
|
0 -> if (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1 in 1..5)
|
||||||
|
Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1 else 1
|
||||||
|
else -> prefs.getParticipationDay()
|
||||||
|
}
|
||||||
|
val allCoursesForToday: LiveData<List<Course>> = dao.getCoursesByDayType(getDayType(), prefs.getSchoolYear())
|
||||||
|
|
||||||
|
fun initParticipationTables() {
|
||||||
|
for (day in days) {
|
||||||
|
dao.insert(SchoolDay(day, 0, prefs.getSchoolYear()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getParticipationCount(participations: List<Participation>) = if (prefs.getParticipationDisplay() == 0) {
|
||||||
|
arrayOf(
|
||||||
|
(participations.map { it.timesHandRaised }.sum().toDouble() / participations.size.toDouble()).round(),
|
||||||
|
(participations.map { it.timesSpoken }.sum().toDouble() / participations.size.toDouble()).round()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
arrayOf(
|
||||||
|
(participations.map { it.timesHandRaised }.sum()),
|
||||||
|
(participations.map { it.timesSpoken }.sum())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDayType(): String = if (dayId in 1..5) days[dayId - 1] else days[1]
|
||||||
|
fun getDays(): List<SchoolDay> = dao.getDays(prefs.getSchoolYear())
|
||||||
|
fun getCourses(): List<CourseExam> = QwarkUtil.getCoursesSorted(
|
||||||
|
dao.getCoursesByYearIdAsList(prefs.getSchoolYear()),
|
||||||
|
prefs.getCourseSortType()
|
||||||
|
)
|
||||||
|
fun link(courseId: Int) { dao.link(courseId, getDayType(), prefs.getSchoolYear()) }
|
||||||
|
fun removeLink(relationId: Int) { dao.removeLink(relationId) }
|
||||||
|
fun getAllParticipationsForCourse(courseId: Int): LiveData<List<Participation>> =
|
||||||
|
dao.getAllParticipationsForCourse(courseId)
|
||||||
|
fun insert(timesHandRaised: Int, timesSpoken: Int, time: Long, courseId: Int) {
|
||||||
|
dao.insert(
|
||||||
|
Participation(
|
||||||
|
timesHandRaised,
|
||||||
|
timesSpoken,
|
||||||
|
time,
|
||||||
|
0,
|
||||||
|
courseId,
|
||||||
|
dayId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fun delete(participation: Participation) { dao.delete(participation) }
|
||||||
|
fun updateParticipation(participation: String, courseId: Int) { dao.updateParticipation(participation, courseId) }
|
||||||
|
fun getSchoolYearName() = prefs.getSchoolYearName()
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import java.io.OutputStreamWriter
|
||||||
|
import java.net.Socket
|
||||||
|
|
||||||
|
class QwarkClient(applicationContext: Context) {
|
||||||
|
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(applicationContext).dao()
|
||||||
|
|
||||||
|
fun send(finish: () -> Unit) {
|
||||||
|
try {
|
||||||
|
Socket("172.20.10.7", 22222).use { socket ->
|
||||||
|
OutputStreamWriter(socket.getOutputStream(), "utf-8").use { writer ->
|
||||||
|
writer.write(dao.getNotesAsList().prepare())
|
||||||
|
writer.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<Note>.prepare(): String {
|
||||||
|
var result = ""
|
||||||
|
forEach { note ->
|
||||||
|
result += "$${note.content}^${note.dismissed}^${note.time}^${note.category}^${note.noteId}|"
|
||||||
|
}
|
||||||
|
result += '\\'
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.room.*
|
||||||
|
import com.denizd.qwark.model.*
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface QwarkDao {
|
||||||
|
|
||||||
|
// courses
|
||||||
|
@get:Query("SELECT *, -1 AS exam_time FROM course ORDER BY time")
|
||||||
|
val allCourses: LiveData<List<CourseExam>>
|
||||||
|
|
||||||
|
@Query("SELECT course.*, (SELECT MIN(exam_time) FROM grade WHERE grade.course_id = course.course_id AND exam_time >= :time) AS exam_time FROM course GROUP BY course.course_id ORDER BY time")
|
||||||
|
fun getAllCourseExams(time: Long = QwarkUtil.timeAtMidnight): List<CourseExam>
|
||||||
|
|
||||||
|
@Query("SELECT *, -1 AS exam_time FROM course WHERE course_id != :u ORDER BY time")
|
||||||
|
fun getAllCourses(u: Int = -1): List<CourseExam> // the u variable is an unnecessary workaround to return a list instead of LiveData
|
||||||
|
|
||||||
|
@Query("SELECT course.*, (SELECT MIN(exam_time) FROM grade WHERE grade.course_id = course.course_id AND exam_time >= :time) AS exam_time FROM course WHERE year_id = :yearId GROUP BY course.course_id ORDER BY time")
|
||||||
|
fun getCoursesByYearId(yearId: Int, time: Long = QwarkUtil.timeAtMidnight): LiveData<List<CourseExam>>
|
||||||
|
|
||||||
|
@Query("SELECT *, -1 AS exam_time FROM course WHERE year_id = :yearId ORDER BY time")
|
||||||
|
fun getCoursesByYearIdAsList(yearId: Int): List<CourseExam>
|
||||||
|
|
||||||
|
@Query("SELECT *, -1 AS exam_time FROM course WHERE course_id = :courseId")
|
||||||
|
fun getCourse(courseId: Int): List<CourseExam>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(course: Course)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(course: Course)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(course: Course)
|
||||||
|
|
||||||
|
@Query("UPDATE course SET average = :average, grade_count = :gradeCount WHERE course_id = :courseId")
|
||||||
|
fun updateAverage(average: String, gradeCount: Int, courseId: Int)
|
||||||
|
|
||||||
|
@Query("UPDATE course SET participation = :participation WHERE course_id = :courseId")
|
||||||
|
fun updateParticipation(participation: String, courseId: Int)
|
||||||
|
|
||||||
|
|
||||||
|
// grades
|
||||||
|
@get:Query("SELECT * FROM grade ORDER BY time")
|
||||||
|
val allGrades: LiveData<List<Grade>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM grade WHERE course_id = :courseId ORDER BY time")
|
||||||
|
fun getGradesByForeignKey(courseId: Int): LiveData<List<Grade>>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(grade: Grade)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(grade: Grade)
|
||||||
|
|
||||||
|
@Query("DELETE FROM grade WHERE grade_id = :gradeId")
|
||||||
|
fun deleteGrade(gradeId: Int)
|
||||||
|
|
||||||
|
|
||||||
|
// notes
|
||||||
|
@get:Query("SELECT * FROM note ORDER BY time DESC")
|
||||||
|
val allNotes: LiveData<List<Note>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM note ORDER BY time DESC")
|
||||||
|
fun getNotesAsList(): List<Note>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(note: Note)
|
||||||
|
|
||||||
|
@Query("UPDATE note SET dismissed = :newDismissedStatus WHERE note_id = :noteId")
|
||||||
|
fun updateDismissed(newDismissedStatus: Boolean, noteId: Int)
|
||||||
|
|
||||||
|
@Query("DELETE FROM note WHERE note_id = :noteId")
|
||||||
|
fun deleteNote(noteId: Int)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM note WHERE category = :category ORDER BY time DESC")
|
||||||
|
fun notesForCategory(category: String): LiveData<List<Note>>
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(note: Note)
|
||||||
|
|
||||||
|
@Query("SELECT DISTINCT category FROM note")
|
||||||
|
fun getAllCategories(): List<String>
|
||||||
|
|
||||||
|
|
||||||
|
// historical averages
|
||||||
|
@get:Query("SELECT * FROM historical_avg ORDER BY course_id DESC")
|
||||||
|
val allHistoricalAverages: LiveData<List<HistoricalAvg>>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(historicalAvg: HistoricalAvg)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM historical_avg WHERE course_id = :courseId ORDER BY time DESC")
|
||||||
|
fun getAveragesForCourses(courseId: Int): List<HistoricalAvg>
|
||||||
|
|
||||||
|
|
||||||
|
// years
|
||||||
|
@get:Query("SELECT * FROM school_year ORDER BY year_id")
|
||||||
|
val allYears: LiveData<List<SchoolYear>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM school_year WHERE year_id != :u ORDER BY year_id")
|
||||||
|
fun getAllYears(u: Int = -1): List<SchoolYear>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(schoolYear: SchoolYear)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(schoolYear: SchoolYear)
|
||||||
|
|
||||||
|
@Query("DELETE FROM school_year WHERE year_id = :yearId")
|
||||||
|
fun deleteSchoolYear(yearId: Int)
|
||||||
|
|
||||||
|
|
||||||
|
// final scores
|
||||||
|
@get:Query("SELECT * FROM score_profile ORDER BY score_profile_id")
|
||||||
|
val allScoreProfiles: LiveData<List<ScoreProfile>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM score_profile WHERE score_profile_id = :scoreProfileId")
|
||||||
|
fun getScoreProfileNames(scoreProfileId: Int): List<ScoreProfile>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(scoreProfile: ScoreProfile)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(scoreProfile: ScoreProfile)
|
||||||
|
|
||||||
|
@Query("DELETE FROM score_profile WHERE score_profile_id = :scoreProfileId")
|
||||||
|
fun deleteScoreProfile(scoreProfileId: Int)
|
||||||
|
|
||||||
|
@Query("SELECT school_year.year FROM school_year, course WHERE course.year_id = school_year.year_id AND course.course_id = :courseId")
|
||||||
|
fun getSchoolYearName(courseId: Int): String
|
||||||
|
|
||||||
|
|
||||||
|
// final grades
|
||||||
|
@Query("SELECT * FROM final_grade WHERE score_profile_id = :scoreProfileId ORDER BY final_grade_id")
|
||||||
|
fun getFinalGrades(scoreProfileId: Int): LiveData<List<FinalGrade>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM final_grade WHERE score_profile_id = :scoreProfileId ORDER BY final_grade_id")
|
||||||
|
fun getFinalGradesAsList(scoreProfileId: Int): List<FinalGrade>
|
||||||
|
|
||||||
|
@Query("UPDATE final_grade SET name = :name, grade = :grade WHERE final_grade_id = :finalGradeId")
|
||||||
|
fun updateFinalGradeAverage(name: String, grade: Int, finalGradeId: Int)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(finalGrade: FinalGrade)
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertAll(finalGrades: Array<FinalGrade>)
|
||||||
|
|
||||||
|
|
||||||
|
// school days
|
||||||
|
@Insert
|
||||||
|
fun insert(schoolDay: SchoolDay)
|
||||||
|
|
||||||
|
|
||||||
|
// participation stuff
|
||||||
|
@Query("SELECT course.*, cs.relation_id AS oral_weighting FROM course, course_schoolday_relation AS cs, school_day WHERE course.course_id = cs.course_id AND school_day.day_id = cs.day_id AND school_day.day = :dayType AND school_day.year_id = :yearId")
|
||||||
|
fun getCoursesByDayType(dayType: String, yearId: Int): LiveData<List<Course>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM school_day WHERE year_id = :yearId")
|
||||||
|
fun getDays(yearId: Int): List<SchoolDay>
|
||||||
|
|
||||||
|
@Query("INSERT INTO course_schoolday_relation VALUES (:courseId, (SELECT day_id FROM school_day WHERE day = :dayType AND year_id = :yearId), NULL)")
|
||||||
|
fun link(courseId: Int, dayType: String, yearId: Int)
|
||||||
|
|
||||||
|
@Query("DELETE FROM course_schoolday_relation WHERE relation_id = :relationId")
|
||||||
|
fun removeLink(relationId: Int)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM participation WHERE course_id = :courseId")
|
||||||
|
fun getAllParticipationsForCourse(courseId: Int): LiveData<List<Participation>>
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(participation: Participation)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(participation: Participation)
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import com.denizd.qwark.model.*
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
|
||||||
|
@Database(entities = [Course::class, Grade::class, Note::class, HistoricalAvg::class, SchoolYear::class, FinalGrade::class, ScoreProfile::class, Participation::class, SchoolDay::class, CourseDayRelation::class], version = 9, exportSchema = false)
|
||||||
|
internal abstract class QwarkDatabase : RoomDatabase() {
|
||||||
|
|
||||||
|
abstract fun dao(): QwarkDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
// my first successful migration! lol
|
||||||
|
private val migrationAddYearIdToDay = object : Migration(3, 4) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("DROP TABLE school_day")
|
||||||
|
database.execSQL("CREATE TABLE school_day ( day TEXT NOT NULL, day_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, year_id INT NOT NULL, FOREIGN KEY (year_id) REFERENCES school_year (year_id) ON DELETE CASCADE )")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val migrationAddExamTimeToGrade = object : Migration(4, 5) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE grades ADD COLUMN exam_time INTEGER NOT NULL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val migrationAddGradeCount = object : Migration(5, 6) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE course ADD COLUMN grade_count INTEGER NOT NULL DEFAULT 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val migrationAddPrimaryKeyToRelation = object : Migration(6, 7) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.apply {
|
||||||
|
execSQL("DROP TABLE course_schoolday_relation")
|
||||||
|
execSQL("""
|
||||||
|
CREATE TABLE course_schoolday_relation (
|
||||||
|
course_id INTEGER NOT NULL,
|
||||||
|
day_id INTEGER NOT NULL,
|
||||||
|
relation_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
FOREIGN KEY (course_id) REFERENCES course (course_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (day_id) REFERENCES school_day (day_id) ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val migrationParticipationDateToTime = object : Migration(7, 8) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.apply {
|
||||||
|
execSQL("DROP TABLE participation")
|
||||||
|
execSQL("""
|
||||||
|
CREATE TABLE participation (
|
||||||
|
times_hand_raised INTEGER NOT NULL,
|
||||||
|
times_spoken INTEGER NOT NULL,
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
participation_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
course_id INTEGER NOT NULL,
|
||||||
|
day_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (course_id) REFERENCES course (course_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (day_id) REFERENCES school_day (day_id) ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val migrationAddParticipation = object : Migration(8, 9) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE course ADD COLUMN participation TEXT NOT NULL DEFAULT '--|--'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var instance: QwarkDatabase? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context): QwarkDatabase {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized(QwarkDatabase::class) {
|
||||||
|
instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
QwarkDatabase::class.java,
|
||||||
|
"qwark_database")
|
||||||
|
.addMigrations(
|
||||||
|
migrationAddGradeCount,
|
||||||
|
migrationAddPrimaryKeyToRelation,
|
||||||
|
migrationParticipationDateToTime,
|
||||||
|
migrationAddParticipation
|
||||||
|
).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package com.denizd.qwark.database
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.util.PreferenceKey
|
||||||
|
import com.denizd.qwark.util.QwarkPreferences
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import com.denizd.qwark.model.ScoreProfile
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
|
||||||
|
class SettingsRepository(app: Application) {
|
||||||
|
|
||||||
|
private val dao: QwarkDao = QwarkDatabase.getInstance(app).dao()
|
||||||
|
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(app.applicationContext)
|
||||||
|
val allYears: LiveData<List<SchoolYear>> = dao.allYears
|
||||||
|
val allScoreProfiles: LiveData<List<ScoreProfile>> = dao.allScoreProfiles
|
||||||
|
|
||||||
|
// Preference getters
|
||||||
|
fun getAppTheme(): Int = prefs.getInt(PreferenceKey.APP_THEME, 2)
|
||||||
|
fun getShowGradeAverage(): Boolean = prefs.getBool(PreferenceKey.SHOW_GRADE_AVERAGE)
|
||||||
|
fun getGradeType(): Int = prefs.getInt(PreferenceKey.GRADE_TYPE)
|
||||||
|
fun getCurrentSchoolYear(): String = prefs.getString(PreferenceKey.SCHOOL_YEAR_NAME)
|
||||||
|
fun getCurrentSchoolYearId(): Int = prefs.getInt(PreferenceKey.SCHOOL_YEAR_ID)
|
||||||
|
private fun getScoreProfileId(): Int = prefs.getInt(PreferenceKey.SCORE_PROFILE_ID, 0)
|
||||||
|
fun getScoreProfileName(): String = prefs.getString(PreferenceKey.SCORE_PROFILE_NAME)
|
||||||
|
fun getParticipationDisplay(): Int = prefs.getParticipationDisplay()
|
||||||
|
fun getParticipationDay(): Int = prefs.getParticipationDay()
|
||||||
|
|
||||||
|
private fun getMaxFinalScoreId(increment: Boolean): Int {
|
||||||
|
if (increment) {
|
||||||
|
prefs.putInt(PreferenceKey.MAX_SCORE_PROFILE_ID, getMaxFinalScoreId(false) + 1)
|
||||||
|
.putInt(PreferenceKey.SCORE_PROFILE_ID, getMaxFinalScoreId(false) + 1)
|
||||||
|
}
|
||||||
|
return prefs.getInt(PreferenceKey.MAX_SCORE_PROFILE_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preference setters
|
||||||
|
fun setAppTheme(value: Int) { prefs.putInt(PreferenceKey.APP_THEME, value) }
|
||||||
|
fun setShowGradeAverage(value: Boolean) { prefs.putBool(PreferenceKey.SHOW_GRADE_AVERAGE, value) }
|
||||||
|
fun setGradeType(value: Int) { prefs.putInt(PreferenceKey.GRADE_TYPE, value) }
|
||||||
|
fun setParticipationDisplay(value: Int) { prefs.setParticipationDisplay(value) }
|
||||||
|
fun setParticipationDay(value: Int) { prefs.setParticipationDay(value) }
|
||||||
|
|
||||||
|
fun setCurrentScoreProfile(scoreProfile: ScoreProfile) {
|
||||||
|
prefs.putInt(PreferenceKey.SCORE_PROFILE_ID, scoreProfile.scoreProfileId)
|
||||||
|
.putString(PreferenceKey.SCORE_PROFILE_NAME, scoreProfile.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCurrentSchoolYear(schoolYear: SchoolYear) {
|
||||||
|
prefs.putInt(PreferenceKey.SCHOOL_YEAR_ID, schoolYear.yearId)
|
||||||
|
.putString(PreferenceKey.SCHOOL_YEAR_NAME, schoolYear.year)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database functions
|
||||||
|
fun insert(schoolYear: SchoolYear) { dao.insert(schoolYear) }
|
||||||
|
fun insert(scoreProfile: ScoreProfile) { dao.insert(scoreProfile) }
|
||||||
|
|
||||||
|
fun initNewFinalScore(name: String) {
|
||||||
|
val finalScoreId = getMaxFinalScoreId(true)
|
||||||
|
dao.insert(ScoreProfile(name, finalScoreId))
|
||||||
|
|
||||||
|
val values = ArrayList<FinalGrade>()
|
||||||
|
for (i in 0 until 24) {
|
||||||
|
values.add(FinalGrade("", "-1", "basic", "x 1", -1, 0, finalScoreId))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until 3) {
|
||||||
|
values.add(FinalGrade("", "-1", "adv", "x 2", -1, 0, finalScoreId))
|
||||||
|
}
|
||||||
|
values.add(FinalGrade("", "-1", "adv", "x 1", -1, 0, finalScoreId))
|
||||||
|
for (i in 0 until 3) {
|
||||||
|
values.add(FinalGrade("", "-1", "adv", "x 2", -1, 0, finalScoreId))
|
||||||
|
}
|
||||||
|
values.add(FinalGrade("", "-1", "adv", "x 1", -1, 0, finalScoreId))
|
||||||
|
|
||||||
|
for (i in 1..5) {
|
||||||
|
values.add(FinalGrade("", "-1", "exam", "P$i x ${if (i == 5) "2" else "5"}", -1, 0, finalScoreId))
|
||||||
|
}
|
||||||
|
|
||||||
|
dao.insertAll(values.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSchoolYear(name: String) {
|
||||||
|
dao.update(SchoolYear(name, getCurrentSchoolYearId()))
|
||||||
|
setCurrentSchoolYear(SchoolYear(name, getCurrentSchoolYearId()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateScoreProfile(name: String) {
|
||||||
|
dao.update(ScoreProfile(name, getScoreProfileId()))
|
||||||
|
setCurrentScoreProfile(ScoreProfile(name, getScoreProfileId()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteSchoolYear() {
|
||||||
|
dao.deleteSchoolYear(getCurrentSchoolYearId())
|
||||||
|
setCurrentSchoolYear(SchoolYear("", 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteScoreProfile() {
|
||||||
|
dao.deleteScoreProfile(getScoreProfileId())
|
||||||
|
setCurrentScoreProfile(ScoreProfile("", 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyCourses(yearId: Int) {
|
||||||
|
val currentSchoolYearId = getCurrentSchoolYearId()
|
||||||
|
val courses = dao.getCoursesByYearIdAsList(yearId)
|
||||||
|
for (course in courses) {
|
||||||
|
dao.insert(
|
||||||
|
Course(
|
||||||
|
name = course.name,
|
||||||
|
advanced = course.advanced,
|
||||||
|
icon = course.icon,
|
||||||
|
colour = course.colour,
|
||||||
|
average = if (getGradeType() == QwarkUtil.GRADE_PRECISE) "0.00" else "0",
|
||||||
|
oralWeighting = course.oralWeighting,
|
||||||
|
gradeCount = course.gradeCount,
|
||||||
|
time = System.currentTimeMillis(),
|
||||||
|
courseId = 0,
|
||||||
|
yearId = currentSchoolYearId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.CourseAdapter
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.CourseCreateBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.CourseOptionsBottomSheet
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.*
|
||||||
|
import com.denizd.qwark.sheet.HistoricalAverageBottomSheet
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.util.calculateTotalAverage
|
||||||
|
import com.denizd.qwark.viewmodel.CourseViewModel
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import java.lang.NumberFormatException
|
||||||
|
|
||||||
|
internal class CourseFragment : QwarkFragment(), CourseAdapter.CourseClickListener {
|
||||||
|
|
||||||
|
private var _binding: PaddedRecyclerViewBinding? = null
|
||||||
|
private val binding: PaddedRecyclerViewBinding get() = _binding!!
|
||||||
|
|
||||||
|
private lateinit var viewModel: CourseViewModel
|
||||||
|
private lateinit var recyclerViewAdapter: CourseAdapter
|
||||||
|
|
||||||
|
private var scrollPosition: Parcelable? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
viewModel = ViewModelProvider(this)[CourseViewModel::class.java]
|
||||||
|
recyclerViewAdapter = CourseAdapter(ArrayList(), this, viewModel.getGradeType())
|
||||||
|
viewModel.allCourses.observe(this, Observer { courses ->
|
||||||
|
val c = QwarkUtil.getCoursesSorted(courses, viewModel.getCourseSortType())
|
||||||
|
recyclerViewAdapter.setCourses(c)
|
||||||
|
if (scrollPosition == null) {
|
||||||
|
binding.recyclerView.scrollToPosition(0)
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
} else {
|
||||||
|
binding.recyclerView.layoutManager?.onRestoreInstanceState(scrollPosition)
|
||||||
|
}
|
||||||
|
superTitle = try {
|
||||||
|
courses.calculateTotalAverage(
|
||||||
|
context,
|
||||||
|
viewModel.getGradeType(),
|
||||||
|
viewModel.getSchoolYearName(),
|
||||||
|
viewModel.getShowGradeAverage()
|
||||||
|
)
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
getString(R.string.grade_format_error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
binding.recyclerView.layoutManager = StaggeredGridLayoutManager(
|
||||||
|
getGridColumnCount(newConfig),
|
||||||
|
StaggeredGridLayoutManager.VERTICAL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = PaddedRecyclerViewBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
appBarTitle = getString(R.string.your_courses)
|
||||||
|
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter = recyclerViewAdapter
|
||||||
|
layoutManager = StaggeredGridLayoutManager(
|
||||||
|
getGridColumnCount(resources.configuration),
|
||||||
|
StaggeredGridLayoutManager.VERTICAL
|
||||||
|
)
|
||||||
|
addFabScrollListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
fab.apply {
|
||||||
|
show()
|
||||||
|
text = getString(R.string.add_course)
|
||||||
|
setOnClickListener {
|
||||||
|
if (viewModel.getSchoolYearName().isBlank()) {
|
||||||
|
presentErrorSnackBar(getString(R.string.no_school_year_selected))
|
||||||
|
} else {
|
||||||
|
createCourseDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
applyPadding(verticalPadding = 4)
|
||||||
|
/**
|
||||||
|
* On first app launch, setting the padding will not scroll the recycler view items
|
||||||
|
* down. This is a workaround to scroll the recycler view down programmatically.
|
||||||
|
*/
|
||||||
|
// TODO this actually doesn't do anything
|
||||||
|
// if (!QwarkUtil.hasAppLaunched) {
|
||||||
|
// scrollToPosition(0)
|
||||||
|
// QwarkUtil.hasAppLaunched = true
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseClick(course: CourseExam) {
|
||||||
|
appBar.setExpanded(true)
|
||||||
|
val f = GradeFragment()
|
||||||
|
f.arguments = Bundle().apply {
|
||||||
|
putString("schoolYear", viewModel.getSchoolYearName())
|
||||||
|
putInt("courseId", course.courseId)
|
||||||
|
}
|
||||||
|
(context as FragmentActivity).supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, f)
|
||||||
|
.addToBackStack("GradeFragment")
|
||||||
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseLongClick(course: CourseExam) {
|
||||||
|
createOptionsDialog(course)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createOptionsDialog(course: CourseExam) {
|
||||||
|
openBottomSheet(CourseOptionsBottomSheet().also { sheet ->
|
||||||
|
QwarkUtil.putCourseInBundle(sheet, course)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun presentErrorSnackBar(message: String) {
|
||||||
|
Snackbar
|
||||||
|
.make(snackBarContainer, message, Snackbar.LENGTH_LONG)
|
||||||
|
.setBackgroundTint(context.getColor(R.color.colorWarning))
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// functions for use by bottom sheets
|
||||||
|
fun insert(course: Course) { viewModel.insert(course) }
|
||||||
|
fun update(course: Course) { viewModel.update(course) }
|
||||||
|
fun getSchoolYear() = viewModel.getSchoolYear()
|
||||||
|
fun getGradeType() = viewModel.getGradeType()
|
||||||
|
fun getAveragesForCourse(courseId: Int) = viewModel.getAveragesForCourses(courseId)
|
||||||
|
|
||||||
|
fun createHistoryDialog(courseId: Int, course: String) {
|
||||||
|
openBottomSheet(HistoricalAverageBottomSheet().also { sheet ->
|
||||||
|
sheet.arguments = Bundle(2).apply {
|
||||||
|
putString("courseName", course)
|
||||||
|
putInt("courseId", courseId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCourseDialog(course: CourseExam? = null) {
|
||||||
|
openBottomSheet(CourseCreateBottomSheet().also { sheet ->
|
||||||
|
if (course != null) QwarkUtil.putCourseInBundle(sheet, course)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteCourse(course: Course) {
|
||||||
|
openBottomSheet(
|
||||||
|
ConfirmDeletionBottomSheet(
|
||||||
|
getString(R.string.delete_course_and_data_confirmation, course.name)
|
||||||
|
) {
|
||||||
|
viewModel.delete(course)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.FinalGradeAdapter
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.FinalsCreateBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.FinalsOptionsBottomSheet
|
||||||
|
import com.denizd.qwark.databinding.FinalsFragmentBinding
|
||||||
|
import com.denizd.qwark.sheet.FinalsLinkBottomSheet
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import com.denizd.qwark.viewmodel.FinalsViewModel
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
|
internal class FinalsFragment : QwarkFragment(), FinalGradeAdapter.FinalGradeClickListener {
|
||||||
|
|
||||||
|
private lateinit var viewModel: FinalsViewModel
|
||||||
|
private var _binding: FinalsFragmentBinding? = null
|
||||||
|
private val binding: FinalsFragmentBinding get() = _binding!!
|
||||||
|
|
||||||
|
private var pointsText = ""
|
||||||
|
private var gradeText = ""
|
||||||
|
|
||||||
|
private val basicCoursesAdapter = FinalGradeAdapter(ArrayList(), this)
|
||||||
|
private val advancedCoursesAdapter = FinalGradeAdapter(ArrayList(), this)
|
||||||
|
private val examGradesAdapter = FinalGradeAdapter(ArrayList(), this)
|
||||||
|
|
||||||
|
private var basicScrollPosition: Parcelable? = null
|
||||||
|
private var advancedScrollPosition: Parcelable? = null
|
||||||
|
private var examScrollPosition: Parcelable? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
viewModel = ViewModelProvider(this)[FinalsViewModel::class.java]
|
||||||
|
viewModel.updateLinkedGrades {
|
||||||
|
viewModel.allFinalGrades?.observe(this, Observer { finalGrades ->
|
||||||
|
// createOverview(finalGrades, view.rootView) // TODO export to bitmap
|
||||||
|
if (basicScrollPosition == null) {
|
||||||
|
binding.basicCoursesRecyclerview.scheduleLayoutAnimation()
|
||||||
|
binding.advancedCoursesRecyclerview.scheduleLayoutAnimation()
|
||||||
|
binding.finalExamsRecyclerview.scheduleLayoutAnimation()
|
||||||
|
}
|
||||||
|
saveScrollPositions()
|
||||||
|
|
||||||
|
val basicGrades = finalGrades.filter { f -> f.type == "basic" }
|
||||||
|
val advancedGrades = finalGrades.filter { f -> f.type == "adv" }
|
||||||
|
val examGrades = finalGrades.filter { f -> f.type == "exam" }
|
||||||
|
|
||||||
|
basicCoursesAdapter.setFinalGrades(basicGrades)
|
||||||
|
advancedCoursesAdapter.setFinalGrades(advancedGrades)
|
||||||
|
examGradesAdapter.setFinalGrades(examGrades)
|
||||||
|
|
||||||
|
val finalScore = viewModel.getFinalScore(basicGrades, advancedGrades, examGrades)
|
||||||
|
if (finalScore == -1) {
|
||||||
|
binding.pointsTextview.text = "---"
|
||||||
|
binding.gradeTextview.text = "---"
|
||||||
|
} else {
|
||||||
|
pointsText = "$finalScore"
|
||||||
|
gradeText = getString(R.string.approximate_placeholder, viewModel.getFinalGrade(finalScore).toString())
|
||||||
|
setTextForGradeTextViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreScrollPositions()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTextForGradeTextViews() {
|
||||||
|
binding.pointsTextview.text = pointsText
|
||||||
|
binding.gradeTextview.text = gradeText
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = FinalsFragmentBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
appBarTitle = getString(R.string.your_final_score)
|
||||||
|
superTitle = if (viewModel.getScoreProfileName() == "") {
|
||||||
|
getString(R.string.no_score_profile_selected)
|
||||||
|
} else {
|
||||||
|
getString(R.string.score_profile_template, viewModel.getScoreProfileName())
|
||||||
|
}
|
||||||
|
fab.hide()
|
||||||
|
|
||||||
|
val layoutManagerBasic = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
val layoutManagerAdvanced = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
val layoutManagerExams = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
binding.basicCoursesRecyclerview.layoutManager = layoutManagerBasic
|
||||||
|
binding.advancedCoursesRecyclerview.layoutManager = layoutManagerAdvanced
|
||||||
|
binding.finalExamsRecyclerview.layoutManager = layoutManagerExams
|
||||||
|
|
||||||
|
binding.basicCoursesRecyclerview.adapter = basicCoursesAdapter
|
||||||
|
binding.advancedCoursesRecyclerview.adapter = advancedCoursesAdapter
|
||||||
|
binding.finalExamsRecyclerview.adapter = examGradesAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.paddingView.apply {
|
||||||
|
applyPadding()
|
||||||
|
}
|
||||||
|
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
|
binding.textLayout?.applyPadding()
|
||||||
|
}
|
||||||
|
setTextForGradeTextViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinalGradeClick(finalGrade: FinalGrade) {
|
||||||
|
openBottomSheet(FinalsOptionsBottomSheet().also { sheet ->
|
||||||
|
QwarkUtil.putFinalGradeInBundle(sheet, finalGrade)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun createEditGradeSheet(finalGrade: FinalGrade) {
|
||||||
|
openBottomSheet(FinalsCreateBottomSheet().also { sheet ->
|
||||||
|
QwarkUtil.putFinalGradeInBundle(sheet, finalGrade)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun createLinkGradeSheet(finalGrade: FinalGrade) {
|
||||||
|
openBottomSheet(FinalsLinkBottomSheet().also { sheet ->
|
||||||
|
QwarkUtil.putFinalGradeInBundle(sheet, finalGrade)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getAllYears() = viewModel.getAllYears()
|
||||||
|
internal fun getAllCourses() = viewModel.getAllCourses()
|
||||||
|
|
||||||
|
internal fun viewLinkedCourse(courseId: Int) {
|
||||||
|
val f = GradeFragment()
|
||||||
|
QwarkUtil.putCourseInBundle(f, viewModel.getCourse(courseId))
|
||||||
|
val d = viewModel.getSchoolYearName(courseId)
|
||||||
|
Log.d("TAG", d)
|
||||||
|
f.arguments?.putString("schoolYear", d)
|
||||||
|
activity?.supportFragmentManager?.beginTransaction()
|
||||||
|
?.replace(R.id.fragment_container, f)
|
||||||
|
?.addToBackStack("GradeFragment")
|
||||||
|
?.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
|
?.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun clearFinalGrade(finalGrade: FinalGrade) {
|
||||||
|
val deletionSheet = ConfirmDeletionBottomSheet(getString(R.string.clear_final_grade_desc, finalGrade.name)) {
|
||||||
|
val newFinalGrade = FinalGrade(
|
||||||
|
name = "",
|
||||||
|
grade = "-1",
|
||||||
|
type = finalGrade.type,
|
||||||
|
note = finalGrade.note,
|
||||||
|
courseId = -1,
|
||||||
|
finalGradeId = finalGrade.finalGradeId,
|
||||||
|
scoreProfileId = finalGrade.scoreProfileId
|
||||||
|
)
|
||||||
|
viewModel.update(newFinalGrade)
|
||||||
|
}
|
||||||
|
openBottomSheet(deletionSheet)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun clearFinalGradeError() {
|
||||||
|
Snackbar
|
||||||
|
.make(snackBarContainer, getString(R.string.clear_final_grade_error), Snackbar.LENGTH_LONG)
|
||||||
|
.setBackgroundTint(context.getColor(R.color.colorWarning))
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun update(finalGrade: FinalGrade) { viewModel.update(finalGrade) }
|
||||||
|
|
||||||
|
// TODO perhaps replace with a bottom sheet
|
||||||
|
// private fun createOverview(finalGrades: List<FinalGrade>, view: View) {
|
||||||
|
// val dialogBuilder = MaterialAlertDialogBuilder(context)
|
||||||
|
// val v = View.inflate(context, R.layout.finals_overview, null)
|
||||||
|
// val l = v.findViewById<LinearLayout>(R.id.linear_layout)
|
||||||
|
//
|
||||||
|
// for (finalGrade in finalGrades) {
|
||||||
|
// val fv = View.inflate(context, R.layout.finals_overview_item, null)
|
||||||
|
// fv.findViewById<TextView>(R.id.course).text = finalGrade.name
|
||||||
|
// fv.findViewById<TextView>(R.id.grade).text = finalGrade.grade
|
||||||
|
// l.addView(fv)
|
||||||
|
// }
|
||||||
|
// l.addView(View.inflate(context, R.layout.powered_by_qwark, null))
|
||||||
|
// val dialog = dialogBuilder.setView(v).create()
|
||||||
|
// dialog.show()
|
||||||
|
// save(drawToBitmap(l, v.width, v.height))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun drawToBitmap(layout: View, width: Int, height: Int): Bitmap {
|
||||||
|
// val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
// val canvas = Canvas(bmp)
|
||||||
|
// layout.measure(
|
||||||
|
// View.MeasureSpec.makeMeasureSpec(canvas.width, View.MeasureSpec.EXACTLY),
|
||||||
|
// View.MeasureSpec.makeMeasureSpec(canvas.height, View.MeasureSpec.EXACTLY))
|
||||||
|
// layout.layout(0,0,layout.measuredWidth,layout.measuredHeight)
|
||||||
|
// layout.draw(canvas)
|
||||||
|
// return bmp
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private fun save(bitmap: Bitmap) {
|
||||||
|
// val name = "image"
|
||||||
|
// val relativeLocation = Environment.DIRECTORY_PICTURES + File.pathSeparator + "Qwark"
|
||||||
|
//
|
||||||
|
// val contentValues = ContentValues().apply {
|
||||||
|
// put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)
|
||||||
|
// put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
|
||||||
|
//
|
||||||
|
// // without this part causes "Failed to create new MediaStore record" exception to be invoked (uri is null below)
|
||||||
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
// put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||||
|
// var stream: OutputStream? = null
|
||||||
|
// var uri: Uri? = null
|
||||||
|
// val contentResolver = context.contentResolver
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// uri = contentResolver.insert(contentUri, contentValues)
|
||||||
|
// if (uri == null) {
|
||||||
|
// throw IOException("Failed to create new MediaStore record.")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// stream = contentResolver.openOutputStream(uri)
|
||||||
|
//
|
||||||
|
// if (stream == null) {
|
||||||
|
// throw IOException("Failed to get output stream.")
|
||||||
|
// }
|
||||||
|
// if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)) {
|
||||||
|
// throw IOException("Failed to save bitmap.")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//// Snackbar.make(mCoordinator, R.string.image_saved_success, Snackbar.LENGTH_INDEFINITE).setAction("Open") {
|
||||||
|
//// val intent = Intent()
|
||||||
|
//// intent.type = "image/*"
|
||||||
|
//// intent.action = Intent.ACTION_VIEW
|
||||||
|
//// intent.data = contentUri
|
||||||
|
//// startActivity(Intent.createChooser(intent, "Select Gallery App"))
|
||||||
|
//// }.show()
|
||||||
|
//
|
||||||
|
// } catch (e: IOException) {
|
||||||
|
// if (uri != null) {
|
||||||
|
// contentResolver.delete(uri, null, null)
|
||||||
|
// }
|
||||||
|
// throw IOException(e)
|
||||||
|
//
|
||||||
|
// } finally {
|
||||||
|
// stream?.close()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
private fun saveScrollPositions(reset: Boolean = false) {
|
||||||
|
if (reset) {
|
||||||
|
basicScrollPosition = null
|
||||||
|
advancedScrollPosition = null
|
||||||
|
examScrollPosition = null
|
||||||
|
} else {
|
||||||
|
basicScrollPosition = binding.basicCoursesRecyclerview.layoutManager?.onSaveInstanceState()
|
||||||
|
advancedScrollPosition = binding.advancedCoursesRecyclerview.layoutManager?.onSaveInstanceState()
|
||||||
|
examScrollPosition = binding.finalExamsRecyclerview.layoutManager?.onSaveInstanceState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restoreScrollPositions() {
|
||||||
|
binding.basicCoursesRecyclerview.layoutManager?.onRestoreInstanceState(basicScrollPosition)
|
||||||
|
binding.advancedCoursesRecyclerview.layoutManager?.onRestoreInstanceState(advancedScrollPosition)
|
||||||
|
binding.finalExamsRecyclerview.layoutManager?.onRestoreInstanceState(examScrollPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getCourseSortType() = viewModel.getCourseSortType()
|
||||||
|
fun getGradeType() = viewModel.getGradeType()
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.GradeAdapter
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.GradeCreateBottomSheet
|
||||||
|
import com.denizd.qwark.databinding.GradeFragmentBinding
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.util.calculateAverage
|
||||||
|
import com.denizd.qwark.util.getUserDefinedAverage
|
||||||
|
import com.denizd.qwark.viewmodel.GradeViewModel
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|
||||||
|
class GradeFragment : QwarkFragment(), GradeAdapter.GradeClickListener {
|
||||||
|
|
||||||
|
private lateinit var currentCourse: CourseExam
|
||||||
|
private var average = ""
|
||||||
|
private var schoolYear = ""
|
||||||
|
private var openedFirstTime = false
|
||||||
|
private lateinit var weightingString: String
|
||||||
|
private lateinit var adapter: GradeAdapter
|
||||||
|
|
||||||
|
private lateinit var viewModel: GradeViewModel
|
||||||
|
private lateinit var inflater: LayoutInflater
|
||||||
|
|
||||||
|
private var _binding: GradeFragmentBinding? = null
|
||||||
|
private val binding: GradeFragmentBinding get() = _binding!!
|
||||||
|
|
||||||
|
private var scrollPosition: Parcelable? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
viewModel = ViewModelProvider(this)[GradeViewModel::class.java]
|
||||||
|
|
||||||
|
// currentCourse = QwarkUtil.getCourseFromBundle(arguments)
|
||||||
|
schoolYear = arguments?.getString("schoolYear") ?: ""
|
||||||
|
currentCourse = viewModel.getCourse(arguments?.getInt("courseId") ?: -1)
|
||||||
|
average = currentCourse.average
|
||||||
|
weightingString = "${currentCourse.oralWeighting} / ${100 - currentCourse.oralWeighting}"
|
||||||
|
|
||||||
|
viewModel.getGradesByForeignKey(currentCourse.courseId).observe(this, Observer { grades ->
|
||||||
|
|
||||||
|
adapter.setGrades(grades)
|
||||||
|
|
||||||
|
if (scrollPosition == null) {
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
// } else {
|
||||||
|
// binding.recyclerView.layoutManager?.onRestoreInstanceState(scrollPosition)
|
||||||
|
}
|
||||||
|
// average = viewModel.calculateAverage(grades.filter { it.grade != -1 }, currentCourse.oralWeighting)
|
||||||
|
grades.filter { it.grade != -1 }.also { filteredGrades ->
|
||||||
|
average = filteredGrades.calculateAverage(
|
||||||
|
currentCourse.oralWeighting
|
||||||
|
)
|
||||||
|
binding.average.text = if (filteredGrades.isEmpty()) "X" else average.getUserDefinedAverage(getGradeType())
|
||||||
|
viewModel.updateAverage(average, filteredGrades.size, currentCourse.courseId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openedFirstTime) {
|
||||||
|
insertNewHistoricalAverage()
|
||||||
|
} else {
|
||||||
|
openedFirstTime = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
adapter = GradeAdapter(ArrayList(), this, getGradeType())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = GradeFragmentBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
superTitle = schoolYear
|
||||||
|
appBarTitle = String.format(
|
||||||
|
(if (currentCourse.advanced) getString(R.string.advanced_abbreviated_placeholder) else "%s"),
|
||||||
|
currentCourse.name
|
||||||
|
)
|
||||||
|
fab.hide()
|
||||||
|
with(view.rootView) {
|
||||||
|
findViewById<BottomNavigationView>(R.id.bottom_nav).visibility = View.GONE
|
||||||
|
findViewById<View>(R.id.view).visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.addGradeButton.setOnClickListener {
|
||||||
|
presentGradeDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.average.text = currentCourse.average
|
||||||
|
binding.weighting.text = weightingString
|
||||||
|
|
||||||
|
binding.recyclerView.adapter = adapter
|
||||||
|
binding.recyclerView.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
|
binding.openParticipationFragmentButton.setOnClickListener {
|
||||||
|
openParticipationCourseFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.paddingView.applyPadding()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun insertNewHistoricalAverage() {
|
||||||
|
viewModel.insert(
|
||||||
|
HistoricalAvg(
|
||||||
|
courseId = currentCourse.courseId,
|
||||||
|
average = average,
|
||||||
|
time = System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun presentGradeDialog(grade: Grade? = null) {
|
||||||
|
openBottomSheet(GradeCreateBottomSheet().also { sheet ->
|
||||||
|
if (grade == null) {
|
||||||
|
sheet.arguments = Bundle(1).apply { putInt("courseId", currentCourse.courseId) }
|
||||||
|
} else {
|
||||||
|
QwarkUtil.putGradeInBundle(sheet, grade)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGradeClick(grade: Grade) {
|
||||||
|
presentGradeDialog(grade)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGradeLongClick(gradeId: Int) {
|
||||||
|
val deletionSheet = ConfirmDeletionBottomSheet(getString(R.string.confirm_grade_deletion)) {
|
||||||
|
viewModel.delete(gradeId)
|
||||||
|
}
|
||||||
|
openBottomSheet(deletionSheet)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openParticipationCourseFragment() {
|
||||||
|
appBar.setExpanded(true)
|
||||||
|
val f = ParticipationCourseFragment()
|
||||||
|
f.arguments = Bundle().apply {
|
||||||
|
putString("course", currentCourse.name)
|
||||||
|
putInt("courseId", currentCourse.courseId)
|
||||||
|
putBoolean("advanced", currentCourse.advanced)
|
||||||
|
}
|
||||||
|
// for targetFragment to work, ParticipationFragment needs to be active
|
||||||
|
// TODO fix the dependency on targetFragment (maybe introduce a ParticipationCourseViewModel)
|
||||||
|
// f.setTargetFragment((context as FragmentActivity).supportFragmentManager.findFragmentByTag("ParticipationFragment"), 42)
|
||||||
|
(context as FragmentActivity).supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, f)
|
||||||
|
.addToBackStack(f.name)
|
||||||
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(grade: Grade) { viewModel.insert(grade) }
|
||||||
|
fun update(grade: Grade) { viewModel.update(grade) }
|
||||||
|
fun getGradeType(): Int = viewModel.getGradeType()
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.NoteAdapter
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.NoteCreateBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.NoteDisplayBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.NoteOptionsBottomSheet
|
||||||
|
import com.denizd.qwark.util.OnBackPressed
|
||||||
|
import com.denizd.qwark.databinding.*
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.viewmodel.NoteViewModel
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
|
||||||
|
class NoteFragment : QwarkFragment(), NoteAdapter.OnNoteClickListener, OnBackPressed {
|
||||||
|
|
||||||
|
private lateinit var viewModel: NoteViewModel
|
||||||
|
private val adapter = NoteAdapter(ArrayList(), this)
|
||||||
|
|
||||||
|
private var scrollPosition: Parcelable? = null
|
||||||
|
|
||||||
|
private lateinit var appBarLayout: AppBarLayout
|
||||||
|
|
||||||
|
private var _binding: PaddedRecyclerViewBinding? = null
|
||||||
|
private val binding: PaddedRecyclerViewBinding get() = _binding!!
|
||||||
|
private var isInCategoryView = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
viewModel = ViewModelProvider(this)[NoteViewModel::class.java]
|
||||||
|
viewModel.allNotes?.observe(this@NoteFragment, Observer<List<Note>> { notes ->
|
||||||
|
adapter.setNotes(notes)
|
||||||
|
if (scrollPosition == null) {
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
} else {
|
||||||
|
restoreScrollPosition()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
binding.recyclerView.layoutManager =
|
||||||
|
StaggeredGridLayoutManager(getGridColumnCount(newConfig), StaggeredGridLayoutManager.VERTICAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = PaddedRecyclerViewBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
appBarLayout = view.rootView.findViewById(R.id.app_bar_layout)
|
||||||
|
superTitle = ""
|
||||||
|
appBarTitle = getString(R.string.your_notes)
|
||||||
|
|
||||||
|
binding.recyclerView.adapter = adapter
|
||||||
|
binding.recyclerView.layoutManager =
|
||||||
|
StaggeredGridLayoutManager(getGridColumnCount(resources.configuration), StaggeredGridLayoutManager.VERTICAL)
|
||||||
|
|
||||||
|
fab.apply {
|
||||||
|
show()
|
||||||
|
text = getString(R.string.add_note)
|
||||||
|
setOnClickListener {
|
||||||
|
createNoteDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.recyclerView.addFabScrollListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.recyclerView.applyPadding(verticalPadding = 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
return if (isInCategoryView) {
|
||||||
|
sortByCategory("")
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sortByCategory(category: String) {
|
||||||
|
appBarLayout.setExpanded(true)
|
||||||
|
if (category.isEmpty()) {
|
||||||
|
isInCategoryView = false
|
||||||
|
superTitle = ""
|
||||||
|
} else {
|
||||||
|
isInCategoryView = true
|
||||||
|
superTitle = getString(R.string.quotation_marks, category)
|
||||||
|
}
|
||||||
|
viewModel.refresh(category)
|
||||||
|
scrollPosition = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNoteClick(note: Note) {
|
||||||
|
openBottomSheet(NoteDisplayBottomSheet().also { sheet ->
|
||||||
|
QwarkUtil.putNoteInBundle(sheet, note)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateDismissed(note: Note) {
|
||||||
|
saveScrollPosition()
|
||||||
|
viewModel.updateDismissedStatus(!note.dismissed, note.noteId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteNote(noteId: Int) {
|
||||||
|
val deletionSheet = ConfirmDeletionBottomSheet(getString(R.string.confirm_note_deletion)) {
|
||||||
|
scrollPosition = binding.recyclerView.layoutManager?.onSaveInstanceState()
|
||||||
|
viewModel.delete(noteId)
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
}
|
||||||
|
openBottomSheet(deletionSheet)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNoteLongClick(note: Note) {
|
||||||
|
createOptionsSheet(note)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCategoryChipClick(category: String) {
|
||||||
|
sortByCategory(category)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createOptionsSheet(note: Note) {
|
||||||
|
openBottomSheet(NoteOptionsBottomSheet().also { sheet ->
|
||||||
|
QwarkUtil.putNoteInBundle(sheet, note)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createNoteDialog(note: Note? = null) {
|
||||||
|
val createSheet = NoteCreateBottomSheet().also { sheet ->
|
||||||
|
if (note != null) QwarkUtil.putNoteInBundle(sheet, note)
|
||||||
|
}
|
||||||
|
openBottomSheet(createSheet)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveScrollPosition() {
|
||||||
|
scrollPosition = binding.recyclerView.layoutManager?.onSaveInstanceState()
|
||||||
|
}
|
||||||
|
private fun restoreScrollPosition() {
|
||||||
|
binding.recyclerView.layoutManager?.onRestoreInstanceState(scrollPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGridColumnCount(config: Configuration): Int = super.getGridColumnCount(config) + 1
|
||||||
|
|
||||||
|
fun insert(note: Note) { viewModel.insert(note) }
|
||||||
|
fun update(note: Note) { viewModel.update(note) }
|
||||||
|
fun getAllCategories(): List<String> = viewModel.getAllCategories()
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.ParticipationAdapter
|
||||||
|
import com.denizd.qwark.databinding.GradeFragmentBinding
|
||||||
|
import com.denizd.qwark.model.Participation
|
||||||
|
import com.denizd.qwark.sheet.ClassSessionSheet
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.viewmodel.ParticipationCourseViewModel
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|
||||||
|
class ParticipationCourseFragment : QwarkFragment(), ParticipationAdapter.ParticipationClickListener {
|
||||||
|
|
||||||
|
private var _binding: GradeFragmentBinding? = null
|
||||||
|
private val binding: GradeFragmentBinding get() = _binding!!
|
||||||
|
private var course: String = ""
|
||||||
|
private var courseId: Int = 0
|
||||||
|
private var advanced: Boolean = false
|
||||||
|
private val recyclerViewAdapter = ParticipationAdapter(ArrayList(), this)
|
||||||
|
// private lateinit var participationFragment: ParticipationFragment
|
||||||
|
private var averageText = ""
|
||||||
|
|
||||||
|
private lateinit var viewModel: ParticipationCourseViewModel
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
course = arguments?.getString("course") ?: ""
|
||||||
|
courseId = arguments?.getInt("courseId") ?: 0
|
||||||
|
advanced = arguments?.getBoolean("advanced") ?: false
|
||||||
|
viewModel = ViewModelProvider(this)[ParticipationCourseViewModel::class.java]
|
||||||
|
// participationFragment = targetFragment as ParticipationFragment
|
||||||
|
// participationFragment.getAllParticipationsForCourse(courseId).observe(this, Observer { participations ->
|
||||||
|
viewModel.getAllParticipationsForCourse(courseId).observe(this, Observer { participations ->
|
||||||
|
recyclerViewAdapter.setParticipations(participations)
|
||||||
|
val participationArray = if (participations.isEmpty()) arrayOf("--", "--")
|
||||||
|
else viewModel.getParticipationCount(participations)
|
||||||
|
averageText = buildString {
|
||||||
|
append(participationArray[0])
|
||||||
|
append(" : ")
|
||||||
|
append(participationArray[1])
|
||||||
|
}
|
||||||
|
binding.average.text = averageText
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
viewModel.updateParticipation("${participationArray[0]}|${participationArray[1]}", courseId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = GradeFragmentBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
fab.hide()
|
||||||
|
with (view.rootView) {
|
||||||
|
findViewById<BottomNavigationView>(R.id.bottom_nav).visibility = View.GONE
|
||||||
|
findViewById<View>(R.id.view).visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
superTitle = viewModel.getSchoolYearName()
|
||||||
|
appBarTitle = String.format(
|
||||||
|
(if (advanced) getString(R.string.advanced_abbreviated_placeholder) else "%s"),
|
||||||
|
course
|
||||||
|
)
|
||||||
|
binding.average.textSize = 64f
|
||||||
|
binding.weighting.text = getString(R.string.participation_ratio_desc)
|
||||||
|
binding.addGradeButton.apply {
|
||||||
|
text = getString(R.string.start_class_session)
|
||||||
|
setOnClickListener {
|
||||||
|
openBottomSheet(ClassSessionSheet().also { sheet ->
|
||||||
|
sheet.isCancelable = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter = recyclerViewAdapter
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.openParticipationFragmentButton.apply {
|
||||||
|
text = getString(R.string.open_grade_fragment)
|
||||||
|
setOnClickListener {
|
||||||
|
openGradeFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.average.text = averageText
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.paddingView.applyPadding()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onParticipationClick(participation: Participation) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onParticipationLongClick(participation: Participation) {
|
||||||
|
openBottomSheet(ConfirmDeletionBottomSheet(
|
||||||
|
getString(R.string.delete_participation_confirmation)
|
||||||
|
) {
|
||||||
|
viewModel.delete(participation)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openGradeFragment() {
|
||||||
|
appBar.setExpanded(true)
|
||||||
|
val f = GradeFragment()
|
||||||
|
f.arguments = Bundle().apply {
|
||||||
|
putString("schoolYear", viewModel.getSchoolYearName())
|
||||||
|
putInt("courseId", courseId)
|
||||||
|
}
|
||||||
|
(context as FragmentActivity).supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, f)
|
||||||
|
.addToBackStack("GradeFragment")
|
||||||
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createParticipation(timesHandRaised: Int, timesSpoken: Int, time: Long) {
|
||||||
|
viewModel.createParticipation(timesHandRaised, timesSpoken, time, courseId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.CourseAdapter
|
||||||
|
import com.denizd.qwark.databinding.PaddedRecyclerViewBinding
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.Participation
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.ParticipationLinkBottomSheet
|
||||||
|
import com.denizd.qwark.viewmodel.ParticipationViewModel
|
||||||
|
|
||||||
|
internal class ParticipationFragment : QwarkFragment(), CourseAdapter.CourseClickListener {
|
||||||
|
|
||||||
|
private var _binding: PaddedRecyclerViewBinding? = null
|
||||||
|
private val binding: PaddedRecyclerViewBinding get() = _binding!!
|
||||||
|
private lateinit var viewModel: ParticipationViewModel
|
||||||
|
private val recyclerViewAdapter = CourseAdapter(ArrayList(), this, null, true)
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
binding.recyclerView.layoutManager =
|
||||||
|
StaggeredGridLayoutManager(getGridColumnCount(newConfig), StaggeredGridLayoutManager.VERTICAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
viewModel = ViewModelProvider(this)[ParticipationViewModel::class.java]
|
||||||
|
viewModel.allCoursesForToday.observe(this, Observer { courses ->
|
||||||
|
recyclerViewAdapter.setCourses(
|
||||||
|
// TODO
|
||||||
|
courses.map { c ->
|
||||||
|
CourseExam(
|
||||||
|
c.name,
|
||||||
|
c.advanced,
|
||||||
|
c.icon,
|
||||||
|
c.colour,
|
||||||
|
c.average,
|
||||||
|
c.oralWeighting,
|
||||||
|
c.gradeCount,
|
||||||
|
c.participation,
|
||||||
|
c.time,
|
||||||
|
c.courseId,
|
||||||
|
c.yearId,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = PaddedRecyclerViewBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
superTitle = getDay()
|
||||||
|
appBarTitle = getString(R.string.your_participation)
|
||||||
|
fab.apply {
|
||||||
|
show()
|
||||||
|
text = getString(R.string.link_course)
|
||||||
|
setOnClickListener {
|
||||||
|
openLinkSheet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter = recyclerViewAdapter
|
||||||
|
layoutManager = StaggeredGridLayoutManager(
|
||||||
|
getGridColumnCount(resources.configuration), StaggeredGridLayoutManager.VERTICAL
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.recyclerView.applyPadding(verticalPadding = 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseClick(course: CourseExam) {
|
||||||
|
appBar.setExpanded(true)
|
||||||
|
(context as FragmentActivity).supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, ParticipationCourseFragment().also { fragment ->
|
||||||
|
fragment.arguments = Bundle().apply {
|
||||||
|
putString("course", course.name)
|
||||||
|
putInt("courseId", course.courseId)
|
||||||
|
putBoolean("advanced", course.advanced)
|
||||||
|
}
|
||||||
|
fragment.setTargetFragment(this, 42)
|
||||||
|
})
|
||||||
|
.addToBackStack("ParticipationCourseFragment")
|
||||||
|
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseLongClick(course: CourseExam) {
|
||||||
|
// in this context, Course#oralWeighting is used as CourseDayRelation#relationId
|
||||||
|
openRemoveLinkSheet(course.name, course.oralWeighting)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openLinkSheet() {
|
||||||
|
openBottomSheet(ParticipationLinkBottomSheet())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openRemoveLinkSheet(courseName: String, relationId: Int) {
|
||||||
|
openBottomSheet(ConfirmDeletionBottomSheet(
|
||||||
|
getString(R.string.confirm_remove_participation_link, courseName, getDay())
|
||||||
|
) {
|
||||||
|
viewModel.removeLink(relationId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDay(): String = resources.getStringArray(R.array.days)[viewModel.dayId - 1]
|
||||||
|
|
||||||
|
// target fragment functions
|
||||||
|
fun getCourses(): List<CourseExam> = viewModel.getCourses()
|
||||||
|
fun link(courseId: Int) { viewModel.link(courseId) }
|
||||||
|
fun delete(participation: Participation) { viewModel.delete(participation) }
|
||||||
|
fun getSchoolYearName() = viewModel.getSchoolYearName()
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
|
||||||
|
open class QwarkFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var _context: Context
|
||||||
|
|
||||||
|
private lateinit var superTitleView: TextView
|
||||||
|
private lateinit var appBarTitleView: TextView
|
||||||
|
|
||||||
|
open var superTitle: String = ""
|
||||||
|
set(value) {
|
||||||
|
superTitleView.text = value
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
open var appBarTitle = ""
|
||||||
|
set(value) {
|
||||||
|
appBarTitleView.text = value
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
private lateinit var statusBarView: View
|
||||||
|
lateinit var appBar: AppBarLayout
|
||||||
|
lateinit var snackBarContainer: CoordinatorLayout
|
||||||
|
lateinit var fab: ExtendedFloatingActionButton
|
||||||
|
|
||||||
|
val name: String = javaClass.simpleName
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
_context = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
/**
|
||||||
|
* Initialise view resources
|
||||||
|
*/
|
||||||
|
statusBarView = view.rootView.findViewById(R.id.status_bar_view)
|
||||||
|
appBarTitleView = view.rootView.findViewById(R.id.toolbarTxt)
|
||||||
|
superTitleView = view.rootView.findViewById(R.id.text_above)
|
||||||
|
appBar = view.rootView.findViewById(R.id.app_bar_layout)
|
||||||
|
snackBarContainer = view.rootView.findViewById(R.id.root_view)
|
||||||
|
fab = view.rootView.findViewById(R.id.fab)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwritten to return non-null context after onAttach has been invoked. If called before
|
||||||
|
* onAttach has been executed, an IllegalStateException will be thrown
|
||||||
|
*
|
||||||
|
* @return the context
|
||||||
|
*/
|
||||||
|
override fun getContext(): Context = _context
|
||||||
|
|
||||||
|
protected open fun getGridColumnCount(config: Configuration): Int {
|
||||||
|
return when (config.orientation) {
|
||||||
|
Configuration.ORIENTATION_PORTRAIT -> 1
|
||||||
|
else -> 2
|
||||||
|
} + if (resources.getBoolean(R.bool.isTablet)) 1 else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun openBottomSheet(sheet: BottomSheetDialogFragment) {
|
||||||
|
sheet.setTargetFragment(this, 42)
|
||||||
|
sheet.show((context as FragmentActivity).supportFragmentManager, sheet.javaClass.simpleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun RecyclerView.addFabScrollListener() {
|
||||||
|
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
when {
|
||||||
|
dy > 0 -> fab.hide()
|
||||||
|
dy < 0 -> fab.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function must not be invoked in onViewCreated, as the view will not have been attached
|
||||||
|
* to its lifecycle owner and therefore cannot calculate the top padding properly. Invoke in
|
||||||
|
* onResume instead.
|
||||||
|
*/
|
||||||
|
protected fun <T : View> T.applyPadding(verticalPadding: Int = 0) {
|
||||||
|
setPadding(
|
||||||
|
verticalPadding.dpToPx(),
|
||||||
|
(statusBarView.height / 1) + appBar.height,
|
||||||
|
verticalPadding.dpToPx(),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts integer DP values to PX for use by programmatic means of changing dimensions
|
||||||
|
*
|
||||||
|
* @return the dimension converted to PX
|
||||||
|
*/
|
||||||
|
private fun Int.dpToPx(): Int = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
this.toFloat(),
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
}
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
package com.denizd.qwark.fragment
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.sheet.ConfirmDeletionBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.CopyCoursesBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.ScoreProfileCreateBottomSheet
|
||||||
|
import com.denizd.qwark.sheet.YearCreateBottomSheet
|
||||||
|
import com.denizd.qwark.databinding.SettingsFragmentBinding
|
||||||
|
import com.denizd.qwark.model.ScoreProfile
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
import com.denizd.qwark.viewmodel.SettingsViewModel
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
|
||||||
|
class SettingsFragment : QwarkFragment() {
|
||||||
|
|
||||||
|
private var _binding: SettingsFragmentBinding? = null
|
||||||
|
private val binding: SettingsFragmentBinding get() = _binding!!
|
||||||
|
private lateinit var viewModel: SettingsViewModel
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
viewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = SettingsFragmentBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
superTitle = ""
|
||||||
|
appBarTitle = getString(R.string.settings)
|
||||||
|
fab.hide()
|
||||||
|
|
||||||
|
binding.darkModeDropdown.apply {
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter.createFromResource(
|
||||||
|
context,
|
||||||
|
R.array.dark_mode_options,
|
||||||
|
R.layout.dropdown_item
|
||||||
|
)
|
||||||
|
)
|
||||||
|
binding.darkModeDropdown.setText(
|
||||||
|
binding.darkModeDropdown.adapter.getItem(viewModel.getAppTheme()).toString(),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
binding.darkModeDropdown.setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.setAppTheme(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.showGradeAverageSwitch.apply {
|
||||||
|
isChecked = viewModel.getShowGradeAverage()
|
||||||
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
viewModel.setShowGradeAverage(isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.courseSortTypeDropdown.apply {
|
||||||
|
setAdapter(ArrayAdapter.createFromResource(context, R.array.course_sort_type_options, R.layout.dropdown_item))
|
||||||
|
setText(binding.courseSortTypeDropdown.adapter.getItem(viewModel.getCourseSortType()).toString(), false)
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.setCourseSortType(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.gradeTypeDropdown.apply {
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter.createFromResource(
|
||||||
|
context,
|
||||||
|
R.array.grade_type_array,
|
||||||
|
R.layout.dropdown_item
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setText(
|
||||||
|
adapter.getItem(viewModel.getGradeType()).toString(),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.setGradeType(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.allYears.observe(viewLifecycleOwner, Observer { years ->
|
||||||
|
binding.schoolYearDropdown.apply {
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter<String>(
|
||||||
|
context,
|
||||||
|
R.layout.dropdown_item,
|
||||||
|
years.map { year -> year.year }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
setText(viewModel.getCurrentSchoolYear(), false)
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
// do nothing if no school years exist or none has been selected
|
||||||
|
}
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.updateSchoolYear(years[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.addYearButton.setOnClickListener {
|
||||||
|
createSchoolYearSheet()
|
||||||
|
}
|
||||||
|
binding.editYearButton.setOnClickListener {
|
||||||
|
createSchoolYearSheet(edit = true) // TODO prohibit opening when text field is empty
|
||||||
|
}
|
||||||
|
binding.deleteYearButton.setOnClickListener {
|
||||||
|
if (binding.schoolYearDropdown.text.isNullOrBlank()) {
|
||||||
|
presentErrorSnackBar(getString(R.string.no_school_year_selected))
|
||||||
|
} else {
|
||||||
|
val deleteYearSheet = ConfirmDeletionBottomSheet(getString(R.string.delete_school_year_desc, binding.schoolYearDropdown.text.toString())) {
|
||||||
|
viewModel.deleteSchoolYear()
|
||||||
|
binding.schoolYearDropdown.setText("")
|
||||||
|
}
|
||||||
|
openBottomSheet(deleteYearSheet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.copyCoursesButton.setOnClickListener {
|
||||||
|
openBottomSheet(CopyCoursesBottomSheet())
|
||||||
|
}
|
||||||
|
// binding.copyCoursesButton.setOnLongClickListener {
|
||||||
|
// // TODO this is currently an experiment
|
||||||
|
// fun makeSnackbar(text: String) {
|
||||||
|
// Snackbar.make(snackBarContainer, text, Snackbar.LENGTH_LONG).show()
|
||||||
|
// }
|
||||||
|
// makeSnackbar("Sending")
|
||||||
|
// viewModel.send {
|
||||||
|
// makeSnackbar("Sent all notes successfully!")
|
||||||
|
// }
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
|
||||||
|
binding.participationCountDropdown.apply {
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter.createFromResource(
|
||||||
|
context,
|
||||||
|
R.array.participation_display_array,
|
||||||
|
R.layout.dropdown_item
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setText(
|
||||||
|
adapter.getItem(viewModel.getParticipationDisplay()).toString(),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.setParticipationDisplay(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.participationDayDropdown.apply {
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter.createFromResource(
|
||||||
|
context,
|
||||||
|
R.array.participation_day_array,
|
||||||
|
R.layout.dropdown_item
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setText(
|
||||||
|
adapter.getItem(viewModel.getParticipationDay()).toString(),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.setParticipationDay(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.allScoreProfiles.observe(viewLifecycleOwner, Observer { scoreProfiles ->
|
||||||
|
binding.scoreProfileDropdown.apply {
|
||||||
|
setAdapter(
|
||||||
|
ArrayAdapter<String>(
|
||||||
|
context,
|
||||||
|
R.layout.dropdown_item,
|
||||||
|
scoreProfiles.map { s -> s.name }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
setText(viewModel.getScoreProfileName(), false)
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
// do nothing if no score profiles exist or none has been selected
|
||||||
|
}
|
||||||
|
setOnItemClickListener { _, _, position, _ ->
|
||||||
|
viewModel.setCurrentScoreProfile(scoreProfiles[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.addScoreProfileButton.setOnClickListener {
|
||||||
|
createScoreProfileSheet()
|
||||||
|
}
|
||||||
|
binding.editScoreProfileButton.setOnClickListener {
|
||||||
|
createScoreProfileSheet(edit = true)
|
||||||
|
}
|
||||||
|
binding.deleteScoreProfileButton.setOnClickListener {
|
||||||
|
if (binding.scoreProfileDropdown.text.isNullOrBlank()) {
|
||||||
|
presentErrorSnackBar(getString(R.string.no_score_profile_selected))
|
||||||
|
} else {
|
||||||
|
val deleteScoreProfileSheet = ConfirmDeletionBottomSheet(
|
||||||
|
getString(R.string.delete_score_profile_desc,
|
||||||
|
binding.scoreProfileDropdown.text.toString())
|
||||||
|
) {
|
||||||
|
viewModel.deleteScoreProfile()
|
||||||
|
binding.scoreProfileDropdown.setText("")
|
||||||
|
}
|
||||||
|
openBottomSheet(deleteScoreProfileSheet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
binding.settingsScrollView.applyPadding()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyCourses(yearId: Int) {
|
||||||
|
viewModel.copyCourses(yearId)
|
||||||
|
Snackbar
|
||||||
|
.make(snackBarContainer, getString(R.string.copy_courses_success), Snackbar.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSchoolYears() = viewModel.allYears.value // TODO does this work on LiveData?
|
||||||
|
fun getCurrentSchoolYearId() = viewModel.getCurrentSchoolYearId()
|
||||||
|
|
||||||
|
private fun createSchoolYearSheet(edit: Boolean = false) {
|
||||||
|
val currentYear = binding.schoolYearDropdown.text.toString()
|
||||||
|
if (edit && currentYear == "") {
|
||||||
|
presentErrorSnackBar(getString(R.string.no_school_year_selected))
|
||||||
|
} else {
|
||||||
|
val yearCreateSheet = YearCreateBottomSheet().also { sheet ->
|
||||||
|
sheet.arguments = Bundle(1).apply {
|
||||||
|
putString("currentYear", if (edit) currentYear else "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openBottomSheet(yearCreateSheet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun presentErrorSnackBar(message: String) {
|
||||||
|
Snackbar
|
||||||
|
.make(snackBarContainer, message, Snackbar.LENGTH_LONG)
|
||||||
|
.setBackgroundTint(context.getColor(R.color.colorWarning))
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createScoreProfileSheet(edit: Boolean = false) {
|
||||||
|
val currentScoreProfile = binding.scoreProfileDropdown.text.toString()
|
||||||
|
if (edit && currentScoreProfile == "") {
|
||||||
|
presentErrorSnackBar(getString(R.string.no_score_profile_selected))
|
||||||
|
} else {
|
||||||
|
openBottomSheet(ScoreProfileCreateBottomSheet().also { sheet ->
|
||||||
|
sheet.arguments = Bundle(1).apply {
|
||||||
|
putString("currentScoreProfile", if (edit) currentScoreProfile else "")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSchoolYear(name: String) {
|
||||||
|
viewModel.updateSchoolYear(name)
|
||||||
|
binding.schoolYearDropdown.setText(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(schoolYear: SchoolYear) { viewModel.insert(schoolYear) }
|
||||||
|
|
||||||
|
fun updateScoreProfile(name: String) {
|
||||||
|
viewModel.updateScoreProfile(name)
|
||||||
|
binding.scoreProfileDropdown.setText(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initNewFinalScore(name: String) {
|
||||||
|
viewModel.initNewFinalScore(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data class intended to be used only as an entity for room. For access, use CourseExam class
|
||||||
|
*/
|
||||||
|
@Entity(tableName = "course")
|
||||||
|
@ForeignKey(
|
||||||
|
entity = SchoolYear::class,
|
||||||
|
parentColumns = ["key"], // this should be "year_id", but I'm too afraid to change it, since it may break the app
|
||||||
|
childColumns = ["year_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
data class Course(
|
||||||
|
val name: String,
|
||||||
|
val advanced: Boolean,
|
||||||
|
val icon: String,
|
||||||
|
val colour: String,
|
||||||
|
val average: String,
|
||||||
|
@ColumnInfo(name = "oral_weighting") val oralWeighting: Int,
|
||||||
|
@ColumnInfo(name = "grade_count") val gradeCount: Int,
|
||||||
|
val participation: String = "--|--",
|
||||||
|
val time: Long,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "course_id") val courseId: Int,
|
||||||
|
@ColumnInfo(name = "year_id") val yearId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "course_schoolday_relation",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Course::class,
|
||||||
|
parentColumns = ["course_id"],
|
||||||
|
childColumns = ["course_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = SchoolDay::class,
|
||||||
|
parentColumns = ["day_id"],
|
||||||
|
childColumns = ["day_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class CourseDayRelation(
|
||||||
|
@ColumnInfo(name = "course_id") val courseId: Int,
|
||||||
|
@ColumnInfo(name = "day_id") val dayId: Int,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "relation_id") val relationId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
|
||||||
|
data class CourseExam(
|
||||||
|
val name: String,
|
||||||
|
val advanced: Boolean,
|
||||||
|
val icon: String,
|
||||||
|
val colour: String,
|
||||||
|
val average: String,
|
||||||
|
@ColumnInfo(name = "oral_weighting") val oralWeighting: Int,
|
||||||
|
@ColumnInfo(name = "grade_count") val gradeCount: Int,
|
||||||
|
val participation: String = "--|--",
|
||||||
|
val time: Long,
|
||||||
|
@ColumnInfo(name = "course_id") val courseId: Int,
|
||||||
|
@ColumnInfo(name = "year_id") val yearId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "exam_time") val examTime: Long = -1L
|
||||||
|
)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
data class CourseIcon(val key: String, val value: Int)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.ForeignKey.CASCADE
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "final_grade")
|
||||||
|
@ForeignKey(
|
||||||
|
entity = ScoreProfile::class,
|
||||||
|
parentColumns = ["score_profile_id"],
|
||||||
|
childColumns = ["score_profile_id"],
|
||||||
|
onDelete = CASCADE
|
||||||
|
)
|
||||||
|
data class FinalGrade(
|
||||||
|
val name: String,
|
||||||
|
val grade: String,
|
||||||
|
val type: String,
|
||||||
|
val note: String,
|
||||||
|
@ColumnInfo(name = "course_id") val courseId: Int,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "final_grade_id") val finalGradeId: Int,
|
||||||
|
@ColumnInfo(name = "score_profile_id") val scoreProfileId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "grade")
|
||||||
|
@ForeignKey(
|
||||||
|
entity = Course::class,
|
||||||
|
parentColumns = ["course_id"],
|
||||||
|
childColumns = ["course_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
data class Grade(
|
||||||
|
val grade: Int,
|
||||||
|
val note: String,
|
||||||
|
val verbal: Boolean,
|
||||||
|
val time: Long,
|
||||||
|
val weighting: Int,
|
||||||
|
@ColumnInfo(name = "exam_time") val examTime: Long = -1L,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "grade_id") val gradeId: Int,
|
||||||
|
@ColumnInfo(name = "course_id") val courseId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.ForeignKey.CASCADE
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "historical_avg")
|
||||||
|
@ForeignKey(
|
||||||
|
entity = Course::class,
|
||||||
|
parentColumns = ["course_id"],
|
||||||
|
childColumns = ["course_id"],
|
||||||
|
onDelete = CASCADE)
|
||||||
|
data class HistoricalAvg(
|
||||||
|
val average: String,
|
||||||
|
val time: Long,
|
||||||
|
@ColumnInfo(name = "course_id") val courseId: Int
|
||||||
|
) {
|
||||||
|
@PrimaryKey(autoGenerate = true) var id: Int = 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "note")
|
||||||
|
data class Note(
|
||||||
|
val content: String,
|
||||||
|
val dismissed: Boolean,
|
||||||
|
val time: Long,
|
||||||
|
val category: String,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "note_id") val noteId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "participation",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = Course::class,
|
||||||
|
parentColumns = ["course_id"],
|
||||||
|
childColumns = ["course_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
),
|
||||||
|
ForeignKey(
|
||||||
|
entity = SchoolDay::class,
|
||||||
|
parentColumns = ["day_id"],
|
||||||
|
childColumns = ["day_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class Participation(
|
||||||
|
@ColumnInfo(name = "times_hand_raised") val timesHandRaised: Int,
|
||||||
|
@ColumnInfo(name = "times_spoken") val timesSpoken: Int,
|
||||||
|
val time: Long,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "participation_id") val participationId: Int,
|
||||||
|
@ColumnInfo(name = "course_id") val courseId: Int,
|
||||||
|
@ColumnInfo(name = "day_id") val dayId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "school_day",
|
||||||
|
foreignKeys = [
|
||||||
|
ForeignKey(
|
||||||
|
entity = SchoolYear::class,
|
||||||
|
parentColumns = ["year_id"],
|
||||||
|
childColumns = ["year_id"],
|
||||||
|
onDelete = ForeignKey.CASCADE
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
data class SchoolDay(
|
||||||
|
val day: String,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "day_id") val dayId: Int,
|
||||||
|
@ColumnInfo(name = "year_id") val yearId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "school_year")
|
||||||
|
data class SchoolYear(
|
||||||
|
val year: String,
|
||||||
|
@PrimaryKey(autoGenerate = true) @ColumnInfo(name = "year_id") val yearId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.denizd.qwark.model
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "score_profile")
|
||||||
|
data class ScoreProfile(
|
||||||
|
val name: String,
|
||||||
|
@PrimaryKey(autoGenerate = false) @ColumnInfo(name = "score_profile_id") val scoreProfileId: Int
|
||||||
|
)
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.databinding.ClassSessionSheetBinding
|
||||||
|
import com.denizd.qwark.fragment.ParticipationCourseFragment
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class ClassSessionSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: ClassSessionSheetBinding? = null
|
||||||
|
private val binding: ClassSessionSheetBinding get() = _binding!!
|
||||||
|
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var participationFragment: ParticipationCourseFragment
|
||||||
|
|
||||||
|
private var timesHandRaised: Int = 0
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
binding.handRaisedButton.text = "$value"
|
||||||
|
}
|
||||||
|
private var timesSpoken: Int = 0
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
binding.spokenButton.text = "$value"
|
||||||
|
}
|
||||||
|
private val time: Long = System.currentTimeMillis()
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = ClassSessionSheetBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
participationFragment = targetFragment as ParticipationCourseFragment
|
||||||
|
|
||||||
|
binding.handRaisedButton.apply {
|
||||||
|
setCompoundDrawables(context.getDrawable(R.drawable.hand).also { drawable ->
|
||||||
|
drawable?.bounds = Rect(0, 0, 256, 256)
|
||||||
|
drawable?.setTint(mContext.getColor(R.color.colorAccent))
|
||||||
|
}, null, null, null)
|
||||||
|
text = "$timesHandRaised"
|
||||||
|
setOnClickListener {
|
||||||
|
timesHandRaised += 1
|
||||||
|
}
|
||||||
|
setOnLongClickListener {
|
||||||
|
if (timesHandRaised > 0)
|
||||||
|
timesHandRaised -= 1
|
||||||
|
else
|
||||||
|
if (Random.nextInt(25) == 20) makeToast(R.string.self_esteem)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.spokenButton.apply {
|
||||||
|
setCompoundDrawables(context.getDrawable(R.drawable.speech).also { drawable ->
|
||||||
|
drawable?.bounds = Rect(0, 0, 256, 256)
|
||||||
|
drawable?.setTint(mContext.getColor(R.color.colorAccent))
|
||||||
|
}, null, null, null)
|
||||||
|
text = "$timesSpoken"
|
||||||
|
setOnClickListener {
|
||||||
|
timesSpoken += 1
|
||||||
|
}
|
||||||
|
setOnLongClickListener {
|
||||||
|
if (timesSpoken > 0)
|
||||||
|
timesSpoken -= 1
|
||||||
|
else
|
||||||
|
if (Random.nextInt(25) == 20) makeToast(R.string.self_esteem)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.cancelButton.apply {
|
||||||
|
setOnClickListener {
|
||||||
|
makeToast(R.string.long_press_to_cancel)
|
||||||
|
}
|
||||||
|
setOnLongClickListener {
|
||||||
|
dismiss()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.finishButton.apply {
|
||||||
|
setOnClickListener {
|
||||||
|
makeToast(R.string.long_press_to_finish)
|
||||||
|
}
|
||||||
|
setOnLongClickListener {
|
||||||
|
finish()
|
||||||
|
dismiss()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeToast(stringResource: Int) {
|
||||||
|
Toast
|
||||||
|
.makeText(mContext, getString(stringResource), Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun finish() {
|
||||||
|
participationFragment.createParticipation(
|
||||||
|
timesHandRaised, timesSpoken, time
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.denizd.qwark.databinding.ConfirmationDialogBinding
|
||||||
|
import com.denizd.qwark.viewmodel.ConfirmDeletionViewModel
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
class ConfirmDeletionBottomSheet() : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
constructor(text: String, confirmedAction: () -> Unit) : this() {
|
||||||
|
this.text = text
|
||||||
|
this.confirmedAction = confirmedAction
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _binding: ConfirmationDialogBinding? = null
|
||||||
|
private val binding: ConfirmationDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
|
||||||
|
private lateinit var text: String
|
||||||
|
private lateinit var confirmedAction: () -> Unit
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = ConfirmationDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val viewModel = ViewModelProvider(this)[ConfirmDeletionViewModel::class.java]
|
||||||
|
|
||||||
|
if (::text.isInitialized) {
|
||||||
|
viewModel.textBody = text
|
||||||
|
viewModel.confirmedFunction = confirmedAction
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.content.text = viewModel.textBody
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.confirmButton.setOnClickListener {
|
||||||
|
viewModel.confirmedFunction()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.YearAdapter
|
||||||
|
import com.denizd.qwark.databinding.RecyclerDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.SettingsFragment
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class CopyCoursesBottomSheet : BottomSheetDialogFragment(), YearAdapter.YearClickListener {
|
||||||
|
|
||||||
|
private var _binding: RecyclerDialogBinding? = null
|
||||||
|
private val binding: RecyclerDialogBinding get() = _binding!!
|
||||||
|
private lateinit var settingsTargetFragment: SettingsFragment
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = RecyclerDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
settingsTargetFragment = targetFragment as SettingsFragment
|
||||||
|
fillWithYears()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillWithYears() {
|
||||||
|
binding.title.text = getString(R.string.pick_a_year)
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = YearAdapter((settingsTargetFragment.getSchoolYears() ?: ArrayList()).filter { y ->
|
||||||
|
y.yearId != settingsTargetFragment.getCurrentSchoolYearId()
|
||||||
|
}, this@CopyCoursesBottomSheet)
|
||||||
|
scheduleLayoutAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onYearClick(schoolYear: SchoolYear) {
|
||||||
|
settingsTargetFragment.copyCourses(schoolYear.yearId)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.CourseCreateDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.CourseFragment
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class CourseCreateBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: CourseCreateDialogBinding? = null
|
||||||
|
private val binding: CourseCreateDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private var icon = ""
|
||||||
|
private lateinit var course: CourseExam
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (arguments?.size() ?: 0 != 0) course = QwarkUtil.getCourseFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = CourseCreateDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as CourseFragment
|
||||||
|
|
||||||
|
binding.oralWeightingEditText.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) {}
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
binding.writtenWeightingEditText.setText(if (binding.oralWeightingEditText.text.isNullOrBlank()) {
|
||||||
|
"100%"
|
||||||
|
} else {
|
||||||
|
"${100 - binding.oralWeightingEditText.text.toString().toInt()}%"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (::course.isInitialized) {
|
||||||
|
binding.contentEditText.setText(course.name)
|
||||||
|
binding.advancedCheckBox.isChecked = course.advanced
|
||||||
|
binding.oralWeightingEditText.setText("${course.oralWeighting}")
|
||||||
|
icon = if (course.icon == "") "circle" else course.icon
|
||||||
|
binding.title.text = getString(R.string.edit_course)
|
||||||
|
binding.createButton.text = getString(R.string.confirm)
|
||||||
|
} else {
|
||||||
|
icon = "circle"
|
||||||
|
binding.oralWeightingEditText.setText(60.toString())
|
||||||
|
}
|
||||||
|
binding.imageView.setImageResource(QwarkUtil.getDrawableIntForString(icon))
|
||||||
|
|
||||||
|
binding.imageButton.setOnClickListener {
|
||||||
|
openIconBottomSheet()
|
||||||
|
}
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.createButton.setOnClickListener {
|
||||||
|
val verbal = binding.oralWeightingEditText.text
|
||||||
|
when {
|
||||||
|
binding.contentEditText.text.toString().isEmpty() -> binding.contentEditText.error = getString(R.string.field_empty)
|
||||||
|
!verbal.isNullOrBlank() && verbal.toString().toInt() > 100 -> binding.oralWeightingEditText.error = getString(R.string.weighting_above_hundred)
|
||||||
|
else -> {
|
||||||
|
if (::course.isInitialized) {
|
||||||
|
targetFragment.update(
|
||||||
|
Course(
|
||||||
|
name = binding.contentEditText.text.toString(),
|
||||||
|
advanced = binding.advancedCheckBox.isChecked,
|
||||||
|
icon = icon,
|
||||||
|
colour = "",
|
||||||
|
average = course.average,
|
||||||
|
oralWeighting = if (verbal.isNullOrBlank()) 0 else verbal.toString().toInt(),
|
||||||
|
gradeCount = course.gradeCount,
|
||||||
|
time = course.time,
|
||||||
|
courseId = course.courseId,
|
||||||
|
yearId = targetFragment.getSchoolYear()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
targetFragment.insert(
|
||||||
|
Course(
|
||||||
|
name = binding.contentEditText.text.toString(),
|
||||||
|
advanced = binding.advancedCheckBox.isChecked,
|
||||||
|
icon = icon,
|
||||||
|
colour = "",
|
||||||
|
average = if (targetFragment.getGradeType() == QwarkUtil.GRADE_PRECISE) "0.00" else "0",
|
||||||
|
oralWeighting = if (verbal.isNullOrBlank()) 0 else verbal.toString().toInt(),
|
||||||
|
gradeCount = 0,
|
||||||
|
time = System.currentTimeMillis(),
|
||||||
|
courseId = 0,
|
||||||
|
yearId = targetFragment.getSchoolYear()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openIconBottomSheet() = fragmentManager?.let { fm ->
|
||||||
|
val sheet = IconPickerBottomSheet().apply {
|
||||||
|
arguments = Bundle(1).apply {
|
||||||
|
putString("currentIcon", icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sheet.setTargetFragment(this, 42)
|
||||||
|
sheet.show(fm, sheet.tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun setIcon(icon: String) {
|
||||||
|
binding.imageView.setImageResource(QwarkUtil.getDrawableIntForString(icon))
|
||||||
|
this.icon = icon
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.databinding.CourseOptionsDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.CourseFragment
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class CourseOptionsBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: CourseOptionsDialogBinding? = null
|
||||||
|
private val binding: CourseOptionsDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var course: CourseExam
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
course = QwarkUtil.getCourseFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = CourseOptionsDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as CourseFragment
|
||||||
|
|
||||||
|
binding.title.text = getString(R.string.options_for_course, course.name)
|
||||||
|
|
||||||
|
binding.viewHistoryButton.setOnClickListener {
|
||||||
|
targetFragment.createHistoryDialog(course.courseId, course.name)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.editCourseButton.setOnClickListener {
|
||||||
|
targetFragment.createCourseDialog(course)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.deleteCourseButton.setOnClickListener {
|
||||||
|
targetFragment.deleteCourse(QwarkUtil.getCourseExamAsCourse(course))
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.FinalGradeEditDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.FinalsFragment
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class FinalsCreateBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: FinalGradeEditDialogBinding? = null
|
||||||
|
private val binding: FinalGradeEditDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var finalGrade: FinalGrade
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
finalGrade = QwarkUtil.getFinalGradeFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = FinalGradeEditDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as FinalsFragment
|
||||||
|
|
||||||
|
binding.courseEdittext.setText(finalGrade.name)
|
||||||
|
|
||||||
|
// val gradeArray = ArrayAdapter.createFromResource(mContext, R.array.grades_points, R.layout.dropdown_item)
|
||||||
|
// binding.gradePicker.setAdapter(gradeArray)
|
||||||
|
// binding.gradePicker.setText(
|
||||||
|
// if (finalGrade.grade == "-1") {
|
||||||
|
// binding.gradePicker.adapter.getItem(0).toString()
|
||||||
|
// } else {
|
||||||
|
// QwarkUtil.getGradeWithLeadingZero(finalGrade.grade.toInt())
|
||||||
|
// },
|
||||||
|
// false
|
||||||
|
// )
|
||||||
|
binding.gradePicker.value = if (finalGrade.grade == "-1") 0 else finalGrade.grade.toInt()
|
||||||
|
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.confirmButton.setOnClickListener {
|
||||||
|
when {
|
||||||
|
binding.courseEdittext.text.isNullOrBlank() -> binding.courseEdittext.error = getString(
|
||||||
|
R.string.field_empty)
|
||||||
|
else -> {
|
||||||
|
val newFinalGrade = FinalGrade(
|
||||||
|
name = binding.courseEdittext.text.toString(),
|
||||||
|
grade = binding.gradePicker.value.toString(), // binding.gradePicker.text.toString().toInt().toString(), // ugly workaround to remove the leading zero
|
||||||
|
type = finalGrade.type,
|
||||||
|
note = finalGrade.note,
|
||||||
|
courseId = -1,
|
||||||
|
finalGradeId = finalGrade.finalGradeId,
|
||||||
|
scoreProfileId = finalGrade.scoreProfileId
|
||||||
|
)
|
||||||
|
targetFragment.update(newFinalGrade)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.CourseAdapter
|
||||||
|
import com.denizd.qwark.adapter.YearAdapter
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.RecyclerDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.FinalsFragment
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.RoundingMode
|
||||||
|
|
||||||
|
internal class FinalsLinkBottomSheet : BottomSheetDialogFragment(), YearAdapter.YearClickListener, CourseAdapter.CourseClickListener {
|
||||||
|
|
||||||
|
private lateinit var finalGrade: FinalGrade
|
||||||
|
private lateinit var binding: RecyclerDialogBinding
|
||||||
|
private var advanced = false
|
||||||
|
private var isSelectingCourse = false
|
||||||
|
private lateinit var finalsTargetFragment: FinalsFragment
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
finalGrade = QwarkUtil.getFinalGradeFromBundle(arguments)
|
||||||
|
advanced = finalGrade.type == "adv"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
binding = RecyclerDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
finalsTargetFragment = targetFragment as FinalsFragment
|
||||||
|
fillWithYears()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onYearClick(schoolYear: SchoolYear) {
|
||||||
|
fillWithCourses(schoolYear)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseClick(course: CourseExam) {
|
||||||
|
val newFinalGrade = FinalGrade(
|
||||||
|
name = course.name,
|
||||||
|
grade = BigDecimal(course.average.toDouble()).setScale(0, RoundingMode.HALF_UP).toString(),
|
||||||
|
type = finalGrade.type,
|
||||||
|
note = finalGrade.note,
|
||||||
|
courseId = course.courseId,
|
||||||
|
finalGradeId = finalGrade.finalGradeId,
|
||||||
|
scoreProfileId = finalGrade.scoreProfileId
|
||||||
|
)
|
||||||
|
finalsTargetFragment.update(newFinalGrade)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseLongClick(course: CourseExam) {}
|
||||||
|
|
||||||
|
private fun fillWithYears() {
|
||||||
|
binding.title.text = getString(R.string.pick_a_year)
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = YearAdapter(finalsTargetFragment.getAllYears(), this@FinalsLinkBottomSheet)
|
||||||
|
scheduleLayoutAnimation()
|
||||||
|
}
|
||||||
|
isSelectingCourse = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillWithCourses(schoolYear: SchoolYear) {
|
||||||
|
binding.title.text = getString(R.string.pick_a_course)
|
||||||
|
binding.recyclerView.adapter =
|
||||||
|
CourseAdapter(
|
||||||
|
QwarkUtil.getCoursesSorted(
|
||||||
|
finalsTargetFragment.getAllCourses().filter { c -> c.yearId == schoolYear.yearId && c.advanced == advanced },
|
||||||
|
finalsTargetFragment.getCourseSortType()
|
||||||
|
),
|
||||||
|
this,
|
||||||
|
finalsTargetFragment.getGradeType()
|
||||||
|
)
|
||||||
|
binding.recyclerView.scheduleLayoutAnimation()
|
||||||
|
isSelectingCourse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.databinding.FinalGradeOptionsDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.FinalsFragment
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class FinalsOptionsBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: FinalGradeOptionsDialogBinding? = null
|
||||||
|
private val binding: FinalGradeOptionsDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var finalGrade: FinalGrade
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
finalGrade = QwarkUtil.getFinalGradeFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = FinalGradeOptionsDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as FinalsFragment
|
||||||
|
|
||||||
|
binding.title.text = getString(R.string.options_for_final_grade, if (finalGrade.name.isEmpty()) getString(R.string.final_grade) else finalGrade.name)
|
||||||
|
|
||||||
|
if (finalGrade.type == "exam") binding.linkGradeButton.visibility = View.GONE
|
||||||
|
if (finalGrade.courseId == -1) binding.viewLinkedCourseButton.visibility = View.GONE
|
||||||
|
binding.editGradeButton.setOnClickListener {
|
||||||
|
targetFragment.createEditGradeSheet(finalGrade)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.linkGradeButton.setOnClickListener {
|
||||||
|
targetFragment.createLinkGradeSheet(finalGrade)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.viewLinkedCourseButton.setOnClickListener {
|
||||||
|
targetFragment.viewLinkedCourse(finalGrade.courseId)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.clearGradeButton.setOnClickListener {
|
||||||
|
if (finalGrade.grade == "-1") {
|
||||||
|
targetFragment.clearFinalGradeError()
|
||||||
|
} else {
|
||||||
|
targetFragment.clearFinalGrade(finalGrade)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.animation.LayoutTransition
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.GradeCreateDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.GradeFragment
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class GradeCreateBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: GradeCreateDialogBinding? = null
|
||||||
|
private val binding: GradeCreateDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var grade: Grade
|
||||||
|
private var courseId: Int = 0
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (arguments?.size() ?: 0 > 1) grade = QwarkUtil.getGradeFromBundle(arguments)
|
||||||
|
courseId = arguments?.getInt("courseId") ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = GradeCreateDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
LayoutTransition().apply {
|
||||||
|
setAnimateParentHierarchy(false)
|
||||||
|
(binding.root as LinearLayout).layoutTransition = this
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as GradeFragment
|
||||||
|
|
||||||
|
// initGradeDropdown()
|
||||||
|
|
||||||
|
binding.verbalSwitch.setOnCheckedChangeListener { switch, isChecked ->
|
||||||
|
binding.verbalSwitchText.text = when (isChecked) {
|
||||||
|
true -> getString(R.string.written)
|
||||||
|
false -> getString(R.string.verbal)
|
||||||
|
}
|
||||||
|
if (!isChecked) {
|
||||||
|
binding.examSwitch.isChecked = false
|
||||||
|
switch.isChecked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::grade.isInitialized) {
|
||||||
|
with (binding) {
|
||||||
|
title.text = getString(R.string.edit_grade)
|
||||||
|
createButton.text = getString(R.string.confirm)
|
||||||
|
gradePicker.value = if (grade.grade == -1) 0 else grade.grade
|
||||||
|
weightingEditText.setText(grade.weighting.toString())
|
||||||
|
noteEditText.setText(grade.note)
|
||||||
|
verbalSwitch.isChecked = !grade.verbal
|
||||||
|
examSwitch.isChecked = grade.examTime != -1L
|
||||||
|
examEditText.setText(QwarkUtil.getSimpleDateFormat().format(Date(grade.examTime)))
|
||||||
|
if (examSwitch.isChecked) examTextLayout.visibility = View.VISIBLE // also hide grade picker when applicable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.gradePicker.displayedValues = QwarkUtil.getGradeArray(targetFragment.getGradeType())
|
||||||
|
|
||||||
|
binding.examSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
setGradePickerVisibility(isChecked)
|
||||||
|
binding.verbalSwitch.isChecked = true
|
||||||
|
binding.examTextLayout.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
|
// if (!isChecked) initGradeDropdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.examEditText.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) {}
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
setGradePickerVisibility(binding.examSwitch.isChecked)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.createButton.setOnClickListener {
|
||||||
|
when {
|
||||||
|
binding.weightingEditText.text.isNullOrBlank() -> binding.weightingEditText.error =
|
||||||
|
getString(R.string.field_empty)
|
||||||
|
binding.weightingEditText.text.toString().toInt() > 100 -> binding.weightingEditText.error =
|
||||||
|
getString(R.string.weighting_above_hundred)
|
||||||
|
else -> {
|
||||||
|
try {
|
||||||
|
val examDate = if (binding.examSwitch.isChecked) parseExamDate() else 1L
|
||||||
|
val newGrade = Grade(
|
||||||
|
grade = if (binding.examSwitch.isChecked && examDate >= QwarkUtil.timeAtMidnight) -1 else binding.gradePicker.value,
|
||||||
|
verbal = !binding.verbalSwitch.isChecked,
|
||||||
|
weighting = binding.weightingEditText.text.toString().toInt(),
|
||||||
|
note = binding.noteEditText.text.toString(),
|
||||||
|
time = if (::grade.isInitialized) grade.time else System.currentTimeMillis(),
|
||||||
|
gradeId = if (::grade.isInitialized) grade.gradeId else 0,
|
||||||
|
courseId = courseId,
|
||||||
|
examTime = if (binding.examSwitch.isChecked) examDate else -1L
|
||||||
|
)
|
||||||
|
if (::grade.isInitialized) {
|
||||||
|
targetFragment.update(newGrade)
|
||||||
|
} else {
|
||||||
|
targetFragment.insert(newGrade)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
binding.examEditText.error = getString(R.string.date_format_issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// private fun initGradeDropdown() {
|
||||||
|
// binding.gradePicker.apply {
|
||||||
|
// setAdapter(
|
||||||
|
// ArrayAdapter.createFromResource(
|
||||||
|
// mContext,
|
||||||
|
// R.array.grades_points,
|
||||||
|
// R.layout.dropdown_item
|
||||||
|
// ).also {
|
||||||
|
// it.setDropDownViewResource(R.layout.dropdown_item)
|
||||||
|
// }
|
||||||
|
// ) // TODO replace with NumberPicker or custom solution
|
||||||
|
// setText(binding.gradePicker.adapter.getItem(0).toString(), false)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Throws(ParseException::class)
|
||||||
|
private fun parseExamDate(): Long = QwarkUtil.getSimpleDateFormat().parse(binding.examEditText.text.toString())?.time ?: 0L
|
||||||
|
|
||||||
|
// @Throws(NumberFormatException::class)
|
||||||
|
// private fun getGrade(): Int = binding.gradePicker.text.toString().toInt()
|
||||||
|
|
||||||
|
private fun setGradePickerVisibility(isExamGrade: Boolean) {
|
||||||
|
if (binding.examEditText.text.toString().length == 10) {
|
||||||
|
binding.gradePicker.visibility = try {
|
||||||
|
if (isExamGrade && parseExamDate() >= QwarkUtil.timeAtMidnight) {
|
||||||
|
View.GONE
|
||||||
|
} else {
|
||||||
|
View.VISIBLE
|
||||||
|
}
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.examEditText.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.HistoricalAverageAdapter
|
||||||
|
import com.denizd.qwark.databinding.RecyclerDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.CourseFragment
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class HistoricalAverageBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: RecyclerDialogBinding? = null
|
||||||
|
private val binding: RecyclerDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = RecyclerDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as CourseFragment
|
||||||
|
val data = targetFragment.getAveragesForCourse(arguments?.getInt("courseId") ?: -1)
|
||||||
|
|
||||||
|
binding.title.text = getString(R.string.averages_for_course, arguments?.getString("courseName"))
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
layoutManager = GridLayoutManager(context, 1)
|
||||||
|
adapter = HistoricalAverageAdapter(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.denizd.qwark.adapter.IconAdapter
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.IconDialogBinding
|
||||||
|
import com.denizd.qwark.model.CourseIcon
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
class IconPickerBottomSheet : BottomSheetDialogFragment(),
|
||||||
|
IconAdapter.IconClickListener {
|
||||||
|
|
||||||
|
private var _binding: IconDialogBinding? = null
|
||||||
|
private val binding: IconDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private var icon: String = ""
|
||||||
|
private lateinit var iconAdapter: IconAdapter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
icon = arguments?.getString("currentIcon") ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = IconDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as CourseCreateBottomSheet
|
||||||
|
val icons = QwarkUtil.drawablesList
|
||||||
|
iconAdapter = IconAdapter(icons, this)
|
||||||
|
|
||||||
|
if (icon != "") {
|
||||||
|
iconAdapter.setSelectedItem(icons.indexOf(CourseIcon(icon, QwarkUtil.getDrawableIntForString(icon))))
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
layoutManager = GridLayoutManager(mContext, 4)
|
||||||
|
adapter = iconAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
binding.confirmButton.setOnClickListener {
|
||||||
|
targetFragment.setIcon(icon)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIconClick(key: String, value: Int, position: Int) {
|
||||||
|
icon = key
|
||||||
|
iconAdapter.setSelectedItem(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.databinding.NoteCreateDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.NoteFragment
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class NoteCreateBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: NoteCreateDialogBinding? = null
|
||||||
|
private val binding: NoteCreateDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var note: Note
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (arguments?.size() ?: 0 != 0) note = QwarkUtil.getNoteFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = NoteCreateDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val targetFragment = targetFragment as NoteFragment
|
||||||
|
|
||||||
|
binding.categoryDropDown.setAdapter(
|
||||||
|
ArrayAdapter<String>(mContext, R.layout.dropdown_item, targetFragment.getAllCategories())
|
||||||
|
)
|
||||||
|
|
||||||
|
if (::note.isInitialized) {
|
||||||
|
binding.title.text = getString(R.string.edit_note)
|
||||||
|
binding.contentEditText.setText(note.content)
|
||||||
|
binding.categoryDropDown.setText(note.category, false)
|
||||||
|
binding.createButton.text = getString(R.string.confirm)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.createButton.setOnClickListener {
|
||||||
|
if (binding.contentEditText.text.isNullOrBlank()) {
|
||||||
|
binding.contentEditText.error = getString(R.string.field_empty)
|
||||||
|
} else {
|
||||||
|
if (::note.isInitialized) {
|
||||||
|
targetFragment.saveScrollPosition()
|
||||||
|
targetFragment.update(
|
||||||
|
Note(
|
||||||
|
content = binding.contentEditText.text.toString(),
|
||||||
|
dismissed = note.dismissed,
|
||||||
|
time = note.time,
|
||||||
|
category = binding.categoryDropDown.text.toString().trim(),
|
||||||
|
noteId = note.noteId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
targetFragment.insert(
|
||||||
|
Note(
|
||||||
|
content = binding.contentEditText.text.toString(),
|
||||||
|
dismissed = false,
|
||||||
|
time = System.currentTimeMillis(),
|
||||||
|
category = binding.categoryDropDown.text.toString().trim(),
|
||||||
|
noteId = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.denizd.qwark.databinding.NoteBottomSheetBinding
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class NoteDisplayBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: NoteBottomSheetBinding? = null
|
||||||
|
private val binding: NoteBottomSheetBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var note: Note
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
note = QwarkUtil.getNoteFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = NoteBottomSheetBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
// return super.onCreateDialog(savedInstanceState).also { dialog ->
|
||||||
|
// dialog.setOnShowListener {
|
||||||
|
// BottomSheetBehavior.from(dialog.findViewById<FrameLayout>(
|
||||||
|
// com.google.android.material.R.id.design_bottom_sheet
|
||||||
|
// )).apply {
|
||||||
|
// state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.title.text = QwarkUtil.getCreatedString(mContext, note.time)
|
||||||
|
val noteText = if (note.dismissed) getString(R.string.note_dismissed) else "%s"
|
||||||
|
binding.note.text = String.format(noteText, note.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.databinding.NoteOptionsDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.NoteFragment
|
||||||
|
import com.denizd.qwark.model.Note
|
||||||
|
import com.denizd.qwark.util.QwarkUtil
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class NoteOptionsBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
private var _binding: NoteOptionsDialogBinding? = null
|
||||||
|
private val binding: NoteOptionsDialogBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private lateinit var note: Note
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
note = QwarkUtil.getNoteFromBundle(arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = NoteOptionsDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
if (note.dismissed) {
|
||||||
|
binding.dismissedStatusText.text = getString(R.string.mark_note_undone)
|
||||||
|
binding.dismissedImageView.setImageDrawable(mContext.getDrawable(R.drawable.label))
|
||||||
|
}
|
||||||
|
if (note.category == "") {
|
||||||
|
binding.viewCategoryButton.visibility = View.GONE
|
||||||
|
}
|
||||||
|
binding.viewCategoryText.text = getString(R.string.view_notes_for_category, note.category)
|
||||||
|
|
||||||
|
val noteFragment = targetFragment as NoteFragment
|
||||||
|
|
||||||
|
binding.updateDismissedButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
noteFragment.updateDismissed(note)
|
||||||
|
}
|
||||||
|
binding.viewCategoryButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
noteFragment.sortByCategory(note.category)
|
||||||
|
}
|
||||||
|
binding.editNoteButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
noteFragment.createNoteDialog(note)
|
||||||
|
}
|
||||||
|
binding.deleteNoteButton.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
noteFragment.deleteNote(note.noteId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.adapter.CourseAdapter
|
||||||
|
import com.denizd.qwark.databinding.RecyclerDialogBinding
|
||||||
|
import com.denizd.qwark.fragment.ParticipationFragment
|
||||||
|
import com.denizd.qwark.model.*
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal class ParticipationLinkBottomSheet : BottomSheetDialogFragment(), CourseAdapter.CourseClickListener {
|
||||||
|
|
||||||
|
private lateinit var finalGrade: FinalGrade
|
||||||
|
private lateinit var binding: RecyclerDialogBinding
|
||||||
|
private lateinit var participationFragment: ParticipationFragment
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
binding = RecyclerDialogBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
participationFragment = targetFragment as ParticipationFragment
|
||||||
|
fillWithCourses()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseClick(course: CourseExam) {
|
||||||
|
participationFragment.link(course.courseId)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCourseLongClick(course: CourseExam) {}
|
||||||
|
|
||||||
|
private fun fillWithCourses() {
|
||||||
|
binding.title.text = getString(R.string.pick_a_course)
|
||||||
|
binding.recyclerView.apply {
|
||||||
|
adapter =
|
||||||
|
CourseAdapter(
|
||||||
|
participationFragment.getCourses().map { c ->
|
||||||
|
CourseExam(
|
||||||
|
c.name,
|
||||||
|
c.advanced,
|
||||||
|
c.icon,
|
||||||
|
c.colour,
|
||||||
|
c.average,
|
||||||
|
c.oralWeighting,
|
||||||
|
c.gradeCount,
|
||||||
|
c.participation,
|
||||||
|
c.time,
|
||||||
|
c.courseId,
|
||||||
|
c.yearId,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
},
|
||||||
|
this@ParticipationLinkBottomSheet,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
scheduleLayoutAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.fragment.SettingsFragment
|
||||||
|
|
||||||
|
internal class ScoreProfileCreateBottomSheet : TextInputBottomSheet() {
|
||||||
|
|
||||||
|
private lateinit var settingsFragment: SettingsFragment
|
||||||
|
private var currentScoreProfile: String = ""
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
currentScoreProfile = arguments?.getString("currentScoreProfile") ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
settingsFragment = targetFragment as SettingsFragment
|
||||||
|
|
||||||
|
binding.textInputLayout.hint = getString(R.string.score_profile)
|
||||||
|
if (currentScoreProfile != "") {
|
||||||
|
binding.title.text = getString(R.string.edit_score_profile)
|
||||||
|
binding.createButton.text = getString(R.string.confirm)
|
||||||
|
binding.editText.setText(currentScoreProfile)
|
||||||
|
} else {
|
||||||
|
binding.title.text = getString(R.string.add_score_profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun positiveButtonClicked() {
|
||||||
|
val name = binding.editText.text
|
||||||
|
if (name.isNullOrBlank()) {
|
||||||
|
binding.editText.error = getString(R.string.field_empty)
|
||||||
|
} else {
|
||||||
|
if (currentScoreProfile == "") {
|
||||||
|
settingsFragment.initNewFinalScore(name.toString())
|
||||||
|
} else {
|
||||||
|
settingsFragment.updateScoreProfile(name.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.positiveButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.denizd.qwark.databinding.TextInputSheetBinding
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
||||||
|
internal open class TextInputBottomSheet : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
protected var _binding: TextInputSheetBinding? = null
|
||||||
|
protected val binding: TextInputSheetBinding get() = _binding!!
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
mContext = context
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
_binding = TextInputSheetBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.createButton.setOnClickListener { positiveButtonClicked() }
|
||||||
|
binding.cancelButton.setOnClickListener { negativeButtonClicked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun positiveButtonClicked() { dismiss() }
|
||||||
|
protected open fun negativeButtonClicked() { dismiss() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.denizd.qwark.sheet
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.fragment.SettingsFragment
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
|
||||||
|
internal class YearCreateBottomSheet : TextInputBottomSheet() {
|
||||||
|
|
||||||
|
private lateinit var settingsFragment: SettingsFragment
|
||||||
|
private var currentYear: String = ""
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
currentYear = arguments?.getString("currentYear") ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
settingsFragment = targetFragment as SettingsFragment
|
||||||
|
|
||||||
|
binding.textInputLayout.hint = getString(R.string.school_year_cap)
|
||||||
|
if (currentYear != "") {
|
||||||
|
binding.title.text = getString(R.string.edit_school_year)
|
||||||
|
binding.createButton.text = getString(R.string.confirm)
|
||||||
|
binding.editText.setText(currentYear)
|
||||||
|
} else {
|
||||||
|
binding.title.text = getString(R.string.add_school_year)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun positiveButtonClicked() {
|
||||||
|
val name = binding.editText.text
|
||||||
|
if (binding.editText.text.isNullOrBlank()) {
|
||||||
|
binding.editText.error = getString(R.string.field_empty)
|
||||||
|
} else {
|
||||||
|
if (currentYear == "") {
|
||||||
|
settingsFragment.insert(SchoolYear(name.toString(), 0))
|
||||||
|
} else {
|
||||||
|
settingsFragment.setSchoolYear(name.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.positiveButtonClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.denizd.qwark.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import java.lang.NumberFormatException
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.RoundingMode
|
||||||
|
|
||||||
|
fun Double.round(scale: Int = 2): Double = BigDecimal(this).setScale(scale, RoundingMode.HALF_UP).toDouble()
|
||||||
|
fun Double.roundToInt(): Int = round(0).toInt()
|
||||||
|
fun BigDecimal.roundToInt(): Int = setScale(0, RoundingMode.HALF_UP).toInt()
|
||||||
|
fun String.roundToInt(): Int = BigDecimal(this.toDouble()).setScale(0, RoundingMode.HALF_UP).toInt()
|
||||||
|
fun String.roundToDouble(): Double = BigDecimal(this.toDouble()).setScale(2, RoundingMode.HALF_UP).toDouble()
|
||||||
|
fun Double.roundToGrade(): String {
|
||||||
|
val result = ((17 - this) / 3).round().toString()
|
||||||
|
return if (result.toDouble() < 1.0) "1.0" else result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Grade>.calculateAverage(oralWeighting: Int): String = if (this.isEmpty()) {
|
||||||
|
BigDecimal(0.0).setScale(2, RoundingMode.HALF_UP)
|
||||||
|
} else {
|
||||||
|
val oralGrades = this.filter { it.verbal }
|
||||||
|
val writtenGrades = this.filter { !it.verbal }
|
||||||
|
|
||||||
|
var oralGrade = 0.0
|
||||||
|
var oralPercentage = 0.0
|
||||||
|
var writtenGrade = 0.0
|
||||||
|
var writtenPercentage = 0.0
|
||||||
|
for (g in oralGrades) {
|
||||||
|
oralGrade += (g.grade.toDouble() * (g.weighting.toDouble() / 100.0))
|
||||||
|
oralPercentage += g.weighting.toDouble()
|
||||||
|
}
|
||||||
|
for (g in writtenGrades) {
|
||||||
|
writtenGrade += (g.grade.toDouble() * (g.weighting.toDouble() / 100.0))
|
||||||
|
writtenPercentage += g.weighting.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oralPercentage != 100.0) {
|
||||||
|
oralGrade = (oralGrade / (oralPercentage / 100.0))
|
||||||
|
}
|
||||||
|
if (writtenPercentage != 100.0) {
|
||||||
|
writtenGrade = (writtenGrade / (writtenPercentage / 100.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// return value for totalGrade
|
||||||
|
BigDecimal(
|
||||||
|
try {
|
||||||
|
when {
|
||||||
|
writtenPercentage == 0.0 -> oralGrade
|
||||||
|
oralPercentage == 0.0 -> writtenGrade
|
||||||
|
else -> (oralGrade * (oralWeighting.toDouble() / 100.0)) + (writtenGrade * ((100.0 - oralWeighting.toDouble()) / 100.0))
|
||||||
|
}
|
||||||
|
} catch (n: NumberFormatException) {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
).setScale(2, RoundingMode.HALF_UP)
|
||||||
|
}.toString()
|
||||||
|
|
||||||
|
fun List<CourseExam>.calculateTotalAverage(context: Context, gradeType: Int, schoolYearName: String, showGradeAverage: Boolean): String {
|
||||||
|
var totalAvg = 0.0
|
||||||
|
var totalCourses = 0.0
|
||||||
|
var totalGradeCount = 0
|
||||||
|
for (course in this) {
|
||||||
|
if (course.gradeCount > 0) {
|
||||||
|
val courseAverage = if (gradeType == QwarkUtil.GRADE_PRECISE) course.average.toDouble() else course.average.roundToInt().toDouble()
|
||||||
|
totalAvg += if (course.advanced) courseAverage * 2.0 else courseAverage
|
||||||
|
totalCourses += if (course.advanced) 2.0 else 1.0
|
||||||
|
totalGradeCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val finalAverage = if (totalCourses > 0.0) {
|
||||||
|
totalAvg /= totalCourses
|
||||||
|
totalAvg.round()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
schoolYearName.isBlank() -> context.getString(R.string.no_school_year_selected)
|
||||||
|
totalGradeCount == 0 -> context.getString(R.string.no_grades_entered_placeholder, schoolYearName)
|
||||||
|
gradeType in arrayOf(QwarkUtil.GRADE_PRECISE, QwarkUtil.GRADE_ROUNDED) -> context.getString(
|
||||||
|
if (showGradeAverage) R.string.average_and_grade_for_year else R.string.average_for_year,
|
||||||
|
schoolYearName,
|
||||||
|
finalAverage,
|
||||||
|
finalAverage.roundToGrade()
|
||||||
|
)
|
||||||
|
else -> context.getString(
|
||||||
|
R.string.average_grade_for_year,
|
||||||
|
schoolYearName,
|
||||||
|
finalAverage.toString().getUserDefinedAverage(gradeType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.getUserDefinedAverage(gradeType: Int?, isGradeAdapter: Boolean = false): String = when (gradeType) {
|
||||||
|
QwarkUtil.GRADE_PRECISE, null -> this
|
||||||
|
QwarkUtil.GRADE_ROUNDED -> {
|
||||||
|
if (isGradeAdapter)
|
||||||
|
QwarkUtil.getGradeWithLeadingZero(this.roundToInt())
|
||||||
|
else
|
||||||
|
this.roundToInt().toString()
|
||||||
|
}
|
||||||
|
else -> QwarkUtil.getNewGradeType(this.roundToInt(), gradeType)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.denizd.qwark.util
|
||||||
|
|
||||||
|
internal interface OnBackPressed {
|
||||||
|
fun onBackPressed(): Boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.denizd.qwark.util
|
||||||
|
|
||||||
|
enum class PreferenceKey(val value: String) {
|
||||||
|
|
||||||
|
// Strings
|
||||||
|
SCHOOL_YEAR_NAME("school_year_name"),
|
||||||
|
SCORE_PROFILE_NAME("score_profile_name"),
|
||||||
|
|
||||||
|
// Ints
|
||||||
|
APP_THEME("dark_mode"),
|
||||||
|
GRADE_TYPE("grade_type"),
|
||||||
|
SCHOOL_YEAR_ID("school_year"),
|
||||||
|
SCORE_PROFILE_ID("final_score_id"),
|
||||||
|
MAX_SCORE_PROFILE_ID("max_final_score_id"),
|
||||||
|
COURSE_SORT_TYPE("course_sort_type"),
|
||||||
|
PARTICIPATION_DISPLAY("participation_display"),
|
||||||
|
PARTICIPATION_DAY("participation_day"),
|
||||||
|
|
||||||
|
// Bools
|
||||||
|
SHOW_GRADE_AVERAGE("show_grade_average")
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.denizd.qwark.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
|
||||||
|
internal class QwarkPreferences private constructor(context: Context) {
|
||||||
|
|
||||||
|
private val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
private val edit: SharedPreferences.Editor = prefs.edit()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var qwarkPrefs: QwarkPreferences? = null
|
||||||
|
internal fun getPrefs(context: Context): QwarkPreferences {
|
||||||
|
if (qwarkPrefs == null) {
|
||||||
|
synchronized (QwarkPreferences::class) {
|
||||||
|
qwarkPrefs = QwarkPreferences(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qwarkPrefs!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getString(key: PreferenceKey, default: String = ""): String = prefs.getString(key.value, default) ?: default
|
||||||
|
internal fun getInt(key: PreferenceKey, default: Int = 0): Int = prefs.getInt(key.value, default)
|
||||||
|
internal fun getBool(key: PreferenceKey, default: Boolean = false): Boolean = prefs.getBoolean(key.value, default)
|
||||||
|
|
||||||
|
internal fun putString(key: PreferenceKey, value: String): QwarkPreferences {
|
||||||
|
edit.putString(key.value, value).apply()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
internal fun putInt(key: PreferenceKey, value: Int): QwarkPreferences {
|
||||||
|
edit.putInt(key.value, value).apply()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
internal fun putBool(key: PreferenceKey, value: Boolean): QwarkPreferences {
|
||||||
|
edit.putBoolean(key.value, value).apply()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getShowGradeAverage(): Boolean = getBool(PreferenceKey.SHOW_GRADE_AVERAGE)
|
||||||
|
internal fun getGradeType(): Int = getInt(PreferenceKey.GRADE_TYPE)
|
||||||
|
internal fun getCourseSortType(): Int = getInt(PreferenceKey.COURSE_SORT_TYPE, 1)
|
||||||
|
internal fun setCourseSortType(value: Int) { putInt(PreferenceKey.COURSE_SORT_TYPE, value) }
|
||||||
|
internal fun getSchoolYear(): Int = getInt(PreferenceKey.SCHOOL_YEAR_ID)
|
||||||
|
internal fun getSchoolYearName(): String = getString(PreferenceKey.SCHOOL_YEAR_NAME)
|
||||||
|
internal fun getScoreProfileName(): String = getString(PreferenceKey.SCORE_PROFILE_NAME)
|
||||||
|
internal fun getScoreProfileId(): Int = getInt(PreferenceKey.SCORE_PROFILE_ID)
|
||||||
|
fun getParticipationDisplay(): Int = getInt(PreferenceKey.PARTICIPATION_DISPLAY)
|
||||||
|
fun getParticipationDay(): Int = getInt(PreferenceKey.PARTICIPATION_DAY)
|
||||||
|
|
||||||
|
fun setParticipationDisplay(value: Int) { putInt(PreferenceKey.PARTICIPATION_DISPLAY, value) }
|
||||||
|
fun setParticipationDay(value: Int) { putInt(PreferenceKey.PARTICIPATION_DAY, value) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
package com.denizd.qwark.util
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.denizd.qwark.R
|
||||||
|
import com.denizd.qwark.databinding.ConfirmationDialogBinding
|
||||||
|
import com.denizd.qwark.model.*
|
||||||
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object QwarkUtil {
|
||||||
|
|
||||||
|
val drawablesList: ArrayList<CourseIcon> by lazy {
|
||||||
|
getDrawablesAsList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val drawables: Map<String, Int> = mapOf(
|
||||||
|
"circle" to R.drawable.ic_circle,
|
||||||
|
"arts" to R.drawable.ic_arts,
|
||||||
|
"biology" to R.drawable.ic_biology,
|
||||||
|
"chemistry" to R.drawable.ic_chemistry,
|
||||||
|
"chinese" to R.drawable.ic_chinese,
|
||||||
|
"cogwheel" to R.drawable.ic_cogwheel,
|
||||||
|
"compsci" to R.drawable.ic_compsci,
|
||||||
|
"drama" to R.drawable.ic_drama,
|
||||||
|
"english" to R.drawable.ic_english,
|
||||||
|
"french" to R.drawable.ic_french,
|
||||||
|
"geography" to R.drawable.ic_geography,
|
||||||
|
"german" to R.drawable.ic_german,
|
||||||
|
"gll" to R.drawable.ic_gll,
|
||||||
|
"help" to R.drawable.ic_help,
|
||||||
|
"history" to R.drawable.ic_history,
|
||||||
|
"idea" to R.drawable.ic_idea,
|
||||||
|
"information" to R.drawable.ic_information,
|
||||||
|
"latin" to R.drawable.ic_latin,
|
||||||
|
"maths" to R.drawable.ic_maths,
|
||||||
|
"music" to R.drawable.ic_music,
|
||||||
|
"pe" to R.drawable.ic_pe,
|
||||||
|
"pencil" to R.drawable.ic_pencil,
|
||||||
|
"philosophy" to R.drawable.ic_philosophy,
|
||||||
|
"physics" to R.drawable.ic_physics,
|
||||||
|
"politics" to R.drawable.ic_politics,
|
||||||
|
"religion" to R.drawable.ic_religion,
|
||||||
|
"spanish" to R.drawable.ic_spanish,
|
||||||
|
"tomato" to R.drawable.ic_tomato,
|
||||||
|
"turkish" to R.drawable.ic_turkish,
|
||||||
|
"wat" to R.drawable.ic_wat
|
||||||
|
)
|
||||||
|
|
||||||
|
private val numberedGrades: Map<Int, String> = mapOf(
|
||||||
|
15 to "1+",
|
||||||
|
14 to "1",
|
||||||
|
13 to "1-",
|
||||||
|
12 to "2+",
|
||||||
|
11 to "2",
|
||||||
|
10 to "2-",
|
||||||
|
9 to "3+",
|
||||||
|
8 to "3",
|
||||||
|
7 to "3-",
|
||||||
|
6 to "4+",
|
||||||
|
5 to "4",
|
||||||
|
4 to "4-",
|
||||||
|
3 to "5+",
|
||||||
|
2 to "5",
|
||||||
|
1 to "5-",
|
||||||
|
0 to "6"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val letteredGrades: Map<Int, String> = mapOf(
|
||||||
|
15 to "A+",
|
||||||
|
14 to "A",
|
||||||
|
13 to "A-",
|
||||||
|
12 to "B+",
|
||||||
|
11 to "B",
|
||||||
|
10 to "B-",
|
||||||
|
9 to "C+",
|
||||||
|
8 to "C",
|
||||||
|
7 to "C-",
|
||||||
|
6 to "D+",
|
||||||
|
5 to "D",
|
||||||
|
4 to "D-",
|
||||||
|
3 to "E+",
|
||||||
|
2 to "E",
|
||||||
|
1 to "E-",
|
||||||
|
0 to "F"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val lettersToPoints: Map<String, Int> = mapOf(
|
||||||
|
"A+" to 15,
|
||||||
|
"A" to 14,
|
||||||
|
"A-" to 13,
|
||||||
|
"B+" to 12,
|
||||||
|
"B" to 11,
|
||||||
|
"B-" to 10,
|
||||||
|
"C+" to 9,
|
||||||
|
"C" to 8,
|
||||||
|
"C-" to 7,
|
||||||
|
"D+" to 6,
|
||||||
|
"D" to 5,
|
||||||
|
"D-" to 4,
|
||||||
|
"E+" to 3,
|
||||||
|
"E" to 2,
|
||||||
|
"E-" to 1,
|
||||||
|
"F" to 0
|
||||||
|
)
|
||||||
|
|
||||||
|
private val gradesToPoints: Map<String, Int> = mapOf(
|
||||||
|
"1+" to 15,
|
||||||
|
"1" to 14,
|
||||||
|
"1-" to 13,
|
||||||
|
"2+" to 12,
|
||||||
|
"2" to 11,
|
||||||
|
"2-" to 10,
|
||||||
|
"3+" to 9,
|
||||||
|
"3" to 8,
|
||||||
|
"3-" to 7,
|
||||||
|
"4+" to 6,
|
||||||
|
"4" to 5,
|
||||||
|
"4-" to 4,
|
||||||
|
"5+" to 3,
|
||||||
|
"5" to 2,
|
||||||
|
"5-" to 1,
|
||||||
|
"6" to 0
|
||||||
|
)
|
||||||
|
|
||||||
|
val pointGradeArray: Array<String> = arrayOf(
|
||||||
|
"0", "1", "2", "3", "4", "5", "6",
|
||||||
|
"7", "8", "9", "10", "11", "12", "13", "14", "15"
|
||||||
|
)
|
||||||
|
private val numberedGradeArray: Array<String> = arrayOf(
|
||||||
|
"6", "5-", "5", "5+", "4-", "4", "4+",
|
||||||
|
"3-", "3", "3+", "2-", "2", "2+", "1-", "1", "1+"
|
||||||
|
)
|
||||||
|
private val letteredGradeArray: Array<String> = arrayOf(
|
||||||
|
"F", "E-", "E", "E+", "D-", "D", "D+",
|
||||||
|
"C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+"
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getGradeArray(gradeType: Int): Array<String> = when (gradeType) {
|
||||||
|
GRADE_NUMBERED -> numberedGradeArray
|
||||||
|
GRADE_LETTERED -> letteredGradeArray
|
||||||
|
else -> pointGradeArray
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Double.getGradeType(map: Map<Int, String>): String = map[this.roundToInt()] ?: ""
|
||||||
|
|
||||||
|
fun getNewGradeType(grade: Int, type: Int): String = when (type) {
|
||||||
|
GRADE_NUMBERED -> numberedGrades[grade] ?: "error"
|
||||||
|
GRADE_LETTERED -> letteredGrades[grade] ?: "error"
|
||||||
|
else -> "error"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNumberForLetter(grade: String): Int = lettersToPoints[grade] ?: -1
|
||||||
|
|
||||||
|
val timeAtMidnight: Long = GregorianCalendar().apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
const val GRADE_PRECISE = 0
|
||||||
|
const val GRADE_ROUNDED = 1
|
||||||
|
const val GRADE_NUMBERED = 2
|
||||||
|
const val GRADE_LETTERED = 3
|
||||||
|
|
||||||
|
private const val SORT_COURSES_TIME_ASC = 0
|
||||||
|
private const val SORT_COURSES_TIME_DESC = 1
|
||||||
|
private const val SORT_COURSES_NAME_ASC = 2
|
||||||
|
private const val SORT_COURSES_NAME_DESC = 3
|
||||||
|
private const val SORT_COURSES_ADV = 4
|
||||||
|
private const val SORT_COURSES_BASIC = 5
|
||||||
|
private const val SORT_COURSES_AVG_ASC = 6
|
||||||
|
private const val SORT_COURSES_AVG_DESC = 7
|
||||||
|
private const val SORT_COURSES_NEXT_EXAM = 8
|
||||||
|
|
||||||
|
fun getCoursesSorted(courses: List<CourseExam>, sortType: Int): List<CourseExam> = when (sortType) { // TODO check what ascending and descending really mean lol
|
||||||
|
SORT_COURSES_TIME_ASC -> courses.asReversed()
|
||||||
|
SORT_COURSES_TIME_DESC -> courses
|
||||||
|
SORT_COURSES_NAME_ASC -> courses.sortedBy { c -> c.name }
|
||||||
|
SORT_COURSES_NAME_DESC -> courses.sortedByDescending { c -> c.name }
|
||||||
|
SORT_COURSES_ADV -> courses.sortedByDescending { c -> c.advanced }
|
||||||
|
SORT_COURSES_BASIC -> courses.sortedBy { c -> c.advanced }
|
||||||
|
SORT_COURSES_AVG_ASC -> courses.sortedBy { c -> c.average.toDouble() }
|
||||||
|
SORT_COURSES_AVG_DESC -> courses.sortedByDescending { c -> c.average.toDouble() }
|
||||||
|
else -> courses.sortedBy { c -> if (c.examTime > 1L) c.examTime else Long.MAX_VALUE } // NEXT_EXAM
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGradeAsString(grade: Int, leadingZero: Boolean): String = (if (leadingZero && grade < 10) "0" else "") + grade
|
||||||
|
fun getGradeWithLeadingZero(grade: Int) = "${if (grade < 10) "0" else ""}$grade"
|
||||||
|
|
||||||
|
fun getSimpleDateTimeFormat(): SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd, HH:mm", Locale.ROOT)
|
||||||
|
fun getSimpleDateFormat(): SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
|
||||||
|
|
||||||
|
fun getDrawableIntForString(drawable: String): Int {
|
||||||
|
return drawables[drawable] ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMapAsCourseIcon(entry: Map.Entry<String, Int>): CourseIcon {
|
||||||
|
return CourseIcon(entry.key, entry.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDrawablesAsList(): ArrayList<CourseIcon> = ArrayList<CourseIcon>().also { list ->
|
||||||
|
for (drawable in drawables) {
|
||||||
|
list.add(getMapAsCourseIcon(drawable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCreatedString(context: Context, time: Long): String {
|
||||||
|
return context.getString(R.string.created_at, getSimpleDateTimeFormat().format(Date(time)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createInfoDialog(context: Context, title: String, body: String) {
|
||||||
|
val dialogBuilder = AlertDialog.Builder(context)
|
||||||
|
val dialogView = View.inflate(context, R.layout.info_display_dialog, null)
|
||||||
|
dialogView.findViewById<TextView>(R.id.info_title_textview).text = title
|
||||||
|
dialogView.findViewById<TextView>(R.id.info_body_textview).text = body
|
||||||
|
val dialog = dialogBuilder.setView(dialogView).create()
|
||||||
|
dialogView.findViewById<MaterialButton>(R.id.confirm_button).setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun putCourseInBundle(fragment: Fragment, course: CourseExam) {
|
||||||
|
val bundle = Bundle(11).apply {
|
||||||
|
putString("name", course.name)
|
||||||
|
putBoolean("advanced", course.advanced)
|
||||||
|
putString("icon", course.icon)
|
||||||
|
putString("colour", course.colour)
|
||||||
|
putString("average", course.average)
|
||||||
|
putInt("oralWeighting", course.oralWeighting)
|
||||||
|
putInt("gradeCount", course.gradeCount)
|
||||||
|
putLong("time", course.time)
|
||||||
|
putInt("courseId", course.courseId)
|
||||||
|
putInt("yearId", course.yearId)
|
||||||
|
putLong("examTime", course.examTime)
|
||||||
|
}
|
||||||
|
fragment.arguments = bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCourseFromBundle(bundle: Bundle?) = CourseExam(
|
||||||
|
name = bundle?.getString("name") ?: "",
|
||||||
|
advanced = bundle?.getBoolean("advanced") ?: false,
|
||||||
|
icon = bundle?.getString("icon") ?: "",
|
||||||
|
colour = bundle?.getString("colour") ?: "",
|
||||||
|
average = bundle?.getString("average") ?: "",
|
||||||
|
oralWeighting = bundle?.getInt("oralWeighting") ?: 0,
|
||||||
|
gradeCount = bundle?.getInt("gradeCount") ?: 0,
|
||||||
|
time = bundle?.getLong("time") ?: 0L,
|
||||||
|
courseId = bundle?.getInt("courseId") ?: 0,
|
||||||
|
yearId = bundle?.getInt("yearId") ?: 0,
|
||||||
|
examTime = bundle?.getLong("examTime") ?: 0L
|
||||||
|
)
|
||||||
|
|
||||||
|
fun putFinalGradeInBundle(fragment: Fragment, finalGrade: FinalGrade) {
|
||||||
|
val bundle = Bundle(7).apply {
|
||||||
|
putString("name", finalGrade.name)
|
||||||
|
putInt("courseId", finalGrade.courseId)
|
||||||
|
putString("grade", finalGrade.grade)
|
||||||
|
putString("type", finalGrade.type)
|
||||||
|
putString("note", finalGrade.note)
|
||||||
|
putInt("finalGradeId", finalGrade.finalGradeId)
|
||||||
|
putInt("scoreProfileId", finalGrade.scoreProfileId)
|
||||||
|
}
|
||||||
|
fragment.arguments = bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFinalGradeFromBundle(bundle: Bundle?) = FinalGrade(
|
||||||
|
name = bundle?.getString("name") ?: "",
|
||||||
|
courseId = bundle?.getInt("courseId") ?: 0,
|
||||||
|
grade = bundle?.getString("grade") ?: "",
|
||||||
|
type = bundle?.getString("type") ?: "",
|
||||||
|
note = bundle?.getString("note") ?: "",
|
||||||
|
finalGradeId = bundle?.getInt("finalGradeId") ?: 0,
|
||||||
|
scoreProfileId = bundle?.getInt("scoreProfileId") ?: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
fun putGradeInBundle(fragment: Fragment, grade: Grade) {
|
||||||
|
val bundle = Bundle(8).apply {
|
||||||
|
putInt("grade", grade.grade)
|
||||||
|
putString("note", grade.note)
|
||||||
|
putBoolean("verbal", grade.verbal)
|
||||||
|
putLong("time", grade.time)
|
||||||
|
putInt("weighting", grade.weighting)
|
||||||
|
putLong("examTime", grade.examTime)
|
||||||
|
putInt("gradeId", grade.gradeId)
|
||||||
|
putInt("courseId", grade.courseId)
|
||||||
|
}
|
||||||
|
fragment.arguments = bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGradeFromBundle(bundle: Bundle?) = Grade(
|
||||||
|
grade = bundle?.getInt("grade") ?: 0,
|
||||||
|
note = bundle?.getString("note") ?: "",
|
||||||
|
verbal = bundle?.getBoolean("verbal") ?: false,
|
||||||
|
time = bundle?.getLong("time") ?: 0L,
|
||||||
|
weighting = bundle?.getInt("weighting") ?: 0,
|
||||||
|
examTime = bundle?.getLong("examTime") ?: 0L,
|
||||||
|
gradeId = bundle?.getInt("gradeId") ?: 0,
|
||||||
|
courseId = bundle?.getInt("courseId") ?: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
fun putNoteInBundle(fragment: Fragment, note: Note) {
|
||||||
|
val bundle = Bundle(5).apply {
|
||||||
|
putString("content", note.content)
|
||||||
|
putBoolean("dismissed", note.dismissed)
|
||||||
|
putLong("time", note.time)
|
||||||
|
putString("category", note.category)
|
||||||
|
putInt("noteId", note.noteId)
|
||||||
|
}
|
||||||
|
fragment.arguments = bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNoteFromBundle(bundle: Bundle?) = Note(
|
||||||
|
content = bundle?.getString("content") ?: "",
|
||||||
|
dismissed = bundle?.getBoolean("dismissed") ?: false,
|
||||||
|
time = bundle?.getLong("time") ?: 0L,
|
||||||
|
category = bundle?.getString("category") ?: "",
|
||||||
|
noteId = bundle?.getInt("noteId") ?: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getCourseExamAsCourse(c: CourseExam): Course = Course(
|
||||||
|
name = c.name,
|
||||||
|
advanced = c.advanced,
|
||||||
|
icon = c.icon,
|
||||||
|
colour = c.colour,
|
||||||
|
average = c.average,
|
||||||
|
oralWeighting = c.oralWeighting,
|
||||||
|
gradeCount = c.gradeCount,
|
||||||
|
time = c.time,
|
||||||
|
courseId = c.courseId,
|
||||||
|
yearId = c.yearId
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.denizd.qwark.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
// workaround to preserve Fragment with constructor on rotation change
|
||||||
|
class ConfirmDeletionViewModel : ViewModel() {
|
||||||
|
|
||||||
|
var confirmedFunction: () -> Unit = {}
|
||||||
|
var textBody = ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.denizd.qwark.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.database.CourseRepository
|
||||||
|
import com.denizd.qwark.model.Course
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
import com.denizd.qwark.util.QwarkPreferences
|
||||||
|
|
||||||
|
class CourseViewModel(application: Application) : QwarkViewModel(application) {
|
||||||
|
|
||||||
|
private val repo = CourseRepository(application)
|
||||||
|
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(application)
|
||||||
|
val allCourses: LiveData<List<CourseExam>> = repo.getCoursesByYearId(getSchoolYear())
|
||||||
|
|
||||||
|
fun getShowGradeAverage(): Boolean = prefs.getShowGradeAverage()
|
||||||
|
fun getGradeType(): Int = prefs.getGradeType()
|
||||||
|
fun getCourseSortType(): Int = prefs.getCourseSortType()
|
||||||
|
fun getSchoolYear(): Int = prefs.getSchoolYear()
|
||||||
|
fun getSchoolYearName(): String = prefs.getSchoolYearName()
|
||||||
|
|
||||||
|
fun insert(avg: HistoricalAvg) = doAsync { repo.insert(avg) }
|
||||||
|
|
||||||
|
fun getAveragesForCourses(courseId: Int): List<HistoricalAvg> = returnBlocking {
|
||||||
|
repo.getAveragesForCourses(courseId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(course: Course) = doAsync { repo.insert(course) }
|
||||||
|
fun update(course: Course) = doAsync { repo.update(course) }
|
||||||
|
fun delete(course: Course) = doAsync { repo.delete(course) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.denizd.qwark.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.denizd.qwark.database.FinalsRepository
|
||||||
|
import com.denizd.qwark.model.CourseExam
|
||||||
|
import com.denizd.qwark.model.FinalGrade
|
||||||
|
import com.denizd.qwark.model.SchoolYear
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.lang.NumberFormatException
|
||||||
|
|
||||||
|
class FinalsViewModel(app: Application) : QwarkViewModel(app) {
|
||||||
|
|
||||||
|
private val repo = FinalsRepository(app)
|
||||||
|
val allFinalGrades: LiveData<List<FinalGrade>>? = repo.getFinalGrades(repo.getFinalScoreId())
|
||||||
|
|
||||||
|
fun update(finalGrade: FinalGrade) = viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
repo.update(finalGrade)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSchoolYearName(courseId: Int) = returnBlocking { repo.getSchoolYearName(courseId) }
|
||||||
|
|
||||||
|
fun getScoreProfileName(): String = repo.getScoreProfileName()
|
||||||
|
fun getGradeType(): Int = repo.getGradeType()
|
||||||
|
|
||||||
|
fun getFinalScore(basicGrades: List<FinalGrade>, advancedGrades: List<FinalGrade>, examGrades: List<FinalGrade>): Int = try {
|
||||||
|
var finalScore = 0
|
||||||
|
for (grade in basicGrades) {
|
||||||
|
finalScore += checkGrade(grade)
|
||||||
|
}
|
||||||
|
for (i in 0..2) {
|
||||||
|
finalScore += (checkGrade(advancedGrades[i]) * 2)
|
||||||
|
}
|
||||||
|
for (i in 4..6) {
|
||||||
|
finalScore += (checkGrade(advancedGrades[i]) * 2)
|
||||||
|
}
|
||||||
|
finalScore += checkGrade(advancedGrades[3]) + checkGrade(advancedGrades[7])
|
||||||
|
for (grade in examGrades) {
|
||||||
|
finalScore += if (grade.note == "P5 x 2") {
|
||||||
|
(checkGrade(grade) * 2)
|
||||||
|
} else {
|
||||||
|
(checkGrade(grade) * 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalScore
|
||||||
|
} catch (e: Exception) {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkGrade(finalGrade: FinalGrade): Int = if (finalGrade.grade.toInt() > -1) finalGrade.grade.toInt() else throw NumberFormatException()
|
||||||
|
|
||||||
|
fun getFinalGrade(points: Int): Double = when (points) {
|
||||||
|
in 823..900 -> 1.0
|
||||||
|
in 805..822 -> 1.1
|
||||||
|
in 787..804 -> 1.2
|
||||||
|
in 769..786 -> 1.3
|
||||||
|
in 751..768 -> 1.4
|
||||||
|
in 733..750 -> 1.5
|
||||||
|
in 715..732 -> 1.6
|
||||||
|
in 697..714 -> 1.7
|
||||||
|
in 679..696 -> 1.8
|
||||||
|
in 661..678 -> 1.9
|
||||||
|
in 643..660 -> 2.0
|
||||||
|
in 625..642 -> 2.1
|
||||||
|
in 607..624 -> 2.2
|
||||||
|
in 589..606 -> 2.3
|
||||||
|
in 571..588 -> 2.4
|
||||||
|
in 553..570 -> 2.5
|
||||||
|
in 535..552 -> 2.6
|
||||||
|
in 517..534 -> 2.7
|
||||||
|
in 499..516 -> 2.8
|
||||||
|
in 481..498 -> 2.9
|
||||||
|
in 463..480 -> 3.0
|
||||||
|
in 445..462 -> 3.1
|
||||||
|
in 427..444 -> 3.2
|
||||||
|
in 409..426 -> 3.3
|
||||||
|
in 391..408 -> 3.4
|
||||||
|
in 373..390 -> 3.5
|
||||||
|
in 355..372 -> 3.6
|
||||||
|
in 337..354 -> 3.7
|
||||||
|
in 319..336 -> 3.8
|
||||||
|
in 301..318 -> 3.9
|
||||||
|
else -> 4.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database
|
||||||
|
fun getAllYears(): List<SchoolYear> = returnBlocking { repo.getAllYears() }
|
||||||
|
|
||||||
|
fun getAllCourses(): List<CourseExam> = returnBlocking { repo.getAllCourses() }
|
||||||
|
|
||||||
|
fun updateFinalGradeAverage(courseId: Int, finalGradeId: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
repo.updateFinalGradeAverage(courseId, finalGradeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateLinkedGrades(attachObserver: () -> Unit) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) { repo.updateLinkedGrades() }
|
||||||
|
withContext(Dispatchers.Main) { attachObserver() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCourse(courseId: Int) = runBlocking {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
repo.getCourse(courseId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCourseSortType() = repo.getCourseSortType()
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.denizd.qwark.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizd.qwark.database.GradeRepository
|
||||||
|
import com.denizd.qwark.model.Grade
|
||||||
|
import com.denizd.qwark.model.HistoricalAvg
|
||||||
|
|
||||||
|
class GradeViewModel(application: Application) : QwarkViewModel(application) {
|
||||||
|
|
||||||
|
private val repo = GradeRepository(application)
|
||||||
|
fun getGradeType(): Int = repo.getGradeType()
|
||||||
|
|
||||||
|
fun getCourse(courseId: Int) = returnBlocking { repo.getCourse(courseId) }
|
||||||
|
|
||||||
|
fun getGradesByForeignKey(courseId: Int): LiveData<List<Grade>> = returnBlocking {
|
||||||
|
repo.getGradesByForeignKey(courseId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(grade: Grade) = doAsync { repo.insert(grade) }
|
||||||
|
fun update(grade: Grade) = doAsync { repo.update(grade) }
|
||||||
|
fun updateAverage(average: String, gradeCount: Int, courseId: Int) = doAsync {
|
||||||
|
repo.updateAverage(average, gradeCount, courseId)
|
||||||
|
}
|
||||||
|
fun delete(gradeId: Int) = doAsync { repo.delete(gradeId) }
|
||||||
|
|
||||||
|
fun insert(avg: HistoricalAvg) = doAsync { repo.insert(avg) }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user