Initial commit
This commit is contained in:
@@ -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) }
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.denizd.qwark.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.view.Window
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.denizd.qwark.R
|
||||
import com.denizd.qwark.fragment.*
|
||||
import com.denizd.qwark.fragment.CourseFragment
|
||||
import com.denizd.qwark.fragment.FinalsFragment
|
||||
import com.denizd.qwark.fragment.NoteFragment
|
||||
import com.denizd.qwark.fragment.ParticipationFragment
|
||||
import com.denizd.qwark.fragment.SettingsFragment
|
||||
import com.denizd.qwark.util.QwarkUtil
|
||||
|
||||
class MainViewModel(val app: Application) : AndroidViewModel(app) {
|
||||
|
||||
private val prefs = PreferenceManager.getDefaultSharedPreferences(app.applicationContext)
|
||||
|
||||
fun getFragmentType(type: Int): String = when (type) { // TODO replace with Fragment#tag
|
||||
R.id.courses -> "CourseFragment"
|
||||
R.id.notes -> "NoteFragment"
|
||||
R.id.participation -> "ParticipationFragment"
|
||||
R.id.finals -> "FinalsFragment"
|
||||
else -> "SettingsFragment"
|
||||
}
|
||||
|
||||
fun getFragment(type: String): QwarkFragment = when (type) {
|
||||
"CourseFragment" -> CourseFragment()
|
||||
"NoteFragment" -> NoteFragment()
|
||||
"ParticipationFragment" -> ParticipationFragment()
|
||||
"FinalsFragment" -> FinalsFragment()
|
||||
else -> SettingsFragment()
|
||||
}
|
||||
|
||||
fun applyTheme(window: Window, context: Context) {
|
||||
when (prefs.getInt("dark_mode", 2)) {
|
||||
0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
val barColour = ContextCompat.getColor(context, R.color.colorBackground)
|
||||
window.navigationBarColor = barColour
|
||||
window.statusBarColor = barColour
|
||||
}
|
||||
|
||||
fun checkForFirstTimeYearNotice(context: Context) {
|
||||
if (prefs.getBoolean("first_launch", true)) {
|
||||
QwarkUtil.createInfoDialog(
|
||||
context,
|
||||
app.getString(R.string.alert_create_school_year_title),
|
||||
app.getString(R.string.alert_create_school_year_desc)
|
||||
)
|
||||
prefs.edit().putBoolean("first_launch", false).apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.denizd.qwark.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.*
|
||||
import com.denizd.qwark.database.NoteRepository
|
||||
import com.denizd.qwark.model.Note
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class NoteViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val repo = NoteRepository(application)
|
||||
private val reloadTrigger = MutableLiveData<String>()
|
||||
val allNotes: LiveData<List<Note>>? = Transformations.switchMap(reloadTrigger) { category ->
|
||||
if (category == "") {
|
||||
repo.allNotes
|
||||
} else {
|
||||
repo.getNotesForCategory(category)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
refresh("")
|
||||
}
|
||||
|
||||
fun refresh(category: String) {
|
||||
reloadTrigger.value = category
|
||||
}
|
||||
|
||||
fun insert(note: Note) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
repo.insert(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateDismissedStatus(newDismissedStatus: Boolean, noteId: Int) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
repo.updateDismissed(newDismissedStatus, noteId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(noteId: Int) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
repo.delete(noteId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // TODO get this as LiveData variable to get changes
|
||||
// fun getNotesForCategory(category: String): LiveData<List<Note>>? {
|
||||
// return runBlocking {
|
||||
// withContext(Dispatchers.IO) {
|
||||
// repo.notesForCategory(category)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fun update(note: Note) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
repo.update(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllCategories(): List<String> = runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
repo.getAllCategories()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.denizd.qwark.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.denizd.qwark.database.ParticipationRepository
|
||||
import com.denizd.qwark.model.Participation
|
||||
|
||||
class ParticipationCourseViewModel(app: Application) : QwarkViewModel(app) {
|
||||
|
||||
private val repo = ParticipationRepository(app)
|
||||
|
||||
fun getAllParticipationsForCourse(courseId: Int): LiveData<List<Participation>> = returnBlocking {
|
||||
repo.getAllParticipationsForCourse(courseId)
|
||||
}
|
||||
fun getParticipationCount(participations: List<Participation>) = repo.getParticipationCount(participations)
|
||||
fun updateParticipation(participation: String, courseId: Int) = doAsync { repo.updateParticipation(participation, courseId) }
|
||||
fun createParticipation(timesHandRaised: Int, timesSpoken: Int, time: Long, courseId: Int) = doAsync {
|
||||
repo.insert(timesHandRaised, timesSpoken, time, courseId)
|
||||
}
|
||||
fun getSchoolYearName() = repo.getSchoolYearName()
|
||||
fun delete(participation: Participation) = doAsync { repo.delete(participation) }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.denizd.qwark.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.denizd.qwark.database.ParticipationRepository
|
||||
import com.denizd.qwark.model.Course
|
||||
import com.denizd.qwark.model.CourseExam
|
||||
import com.denizd.qwark.model.Participation
|
||||
import com.denizd.qwark.model.SchoolDay
|
||||
|
||||
class ParticipationViewModel(app: Application) : QwarkViewModel(app) {
|
||||
|
||||
private val repo = ParticipationRepository(app)
|
||||
val allCoursesForToday: LiveData<List<Course>> = repo.allCoursesForToday
|
||||
val dayId: Int = repo.dayId
|
||||
|
||||
fun getCourses(): List<CourseExam> = returnBlocking { repo.getCourses() }
|
||||
fun link(courseId: Int) = doAsync { repo.link(courseId) }
|
||||
fun removeLink(relationId: Int) = doAsync { repo.removeLink(relationId) }
|
||||
fun delete(participation: Participation) = doAsync { repo.delete(participation) }
|
||||
fun getSchoolYearName() = repo.getSchoolYearName()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.denizd.qwark.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
open class QwarkViewModel(app: Application) : AndroidViewModel(app) {
|
||||
|
||||
fun doAsync(function: () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
function()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> returnBlocking(function: () -> T): T = runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
function()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.denizd.qwark.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.denizd.qwark.database.QwarkClient
|
||||
import com.denizd.qwark.util.QwarkPreferences
|
||||
import com.denizd.qwark.database.SettingsRepository
|
||||
import com.denizd.qwark.model.ScoreProfile
|
||||
import com.denizd.qwark.model.SchoolYear
|
||||
|
||||
class SettingsViewModel(private val app: Application) : QwarkViewModel(app) {
|
||||
|
||||
private val repo = SettingsRepository(app)
|
||||
val allYears: LiveData<List<SchoolYear>> = repo.allYears
|
||||
val allScoreProfiles: LiveData<List<ScoreProfile>> = repo.allScoreProfiles
|
||||
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(app.applicationContext)
|
||||
|
||||
// Preference getters
|
||||
fun getAppTheme() = repo.getAppTheme()
|
||||
fun getShowGradeAverage() = repo.getShowGradeAverage()
|
||||
fun getCourseSortType(): Int = prefs.getCourseSortType()
|
||||
fun getGradeType() = repo.getGradeType()
|
||||
fun getScoreProfileName() = repo.getScoreProfileName()
|
||||
fun getCurrentSchoolYear(): String = repo.getCurrentSchoolYear()
|
||||
fun getCurrentSchoolYearId(): Int = repo.getCurrentSchoolYearId()
|
||||
fun getParticipationDisplay(): Int = repo.getParticipationDisplay()
|
||||
fun getParticipationDay(): Int = repo.getParticipationDay()
|
||||
|
||||
// Preference setters
|
||||
fun setAppTheme(value: Int) { repo.setAppTheme(value) }
|
||||
fun setShowGradeAverage(value: Boolean) { repo.setShowGradeAverage(value) }
|
||||
fun setCourseSortType(value: Int) { prefs.setCourseSortType(value) }
|
||||
fun setGradeType(value: Int) { repo.setGradeType(value) }
|
||||
fun setCurrentScoreProfile(scoreProfile: ScoreProfile) { repo.setCurrentScoreProfile(scoreProfile) }
|
||||
fun updateSchoolYear(schoolYear: SchoolYear) { repo.setCurrentSchoolYear(schoolYear) }
|
||||
fun setParticipationDisplay(value: Int) { repo.setParticipationDisplay(value) }
|
||||
fun setParticipationDay(value: Int) { repo.setParticipationDay(value) }
|
||||
|
||||
// Database functions
|
||||
fun insert(schoolYear: SchoolYear) = doAsync { repo.insert(schoolYear) }
|
||||
fun insert(scoreProfile: ScoreProfile) = doAsync { repo.insert(scoreProfile) }
|
||||
fun initNewFinalScore(name: String) = doAsync { repo.initNewFinalScore(name) }
|
||||
fun updateSchoolYear(name: String) = doAsync { repo.updateSchoolYear(name) }
|
||||
fun updateScoreProfile(name: String) = doAsync { repo.updateScoreProfile(name) }
|
||||
fun deleteSchoolYear() = doAsync { repo.deleteSchoolYear() }
|
||||
fun deleteScoreProfile() = doAsync { repo.deleteScoreProfile() }
|
||||
fun copyCourses(yearId: Int) = doAsync { repo.copyCourses(yearId) }
|
||||
|
||||
fun send(update: () -> Unit) = doAsync { QwarkClient(app.applicationContext).send(update) }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:duration="@android:integer/config_shortAnimTime">
|
||||
|
||||
<translate
|
||||
android:fromYDelta="20%"
|
||||
android:toYDelta="0"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
/>
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0"
|
||||
android:toAlpha="1"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
/>
|
||||
|
||||
<scale
|
||||
android:fromXScale="100%"
|
||||
android:fromYScale="100%"
|
||||
android:toXScale="100%"
|
||||
android:toYScale="100%"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"
|
||||
/>
|
||||
|
||||
</set>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layoutAnimation
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:animation="@anim/item_animation_fall_down"
|
||||
android:delay="15%"
|
||||
android:animationOrder="normal"/>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true" android:color="@color/colorAccent" />
|
||||
<item android:state_checked="false" android:color="@color/colorHint" />
|
||||
</selector>
|
||||
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="250dp" android:viewportHeight="80.79835"
|
||||
android:viewportWidth="53.145966" android:width="165dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@color/colorSplashLogo"
|
||||
android:pathData="m13.1664,80.416c-0.3983,-0.2161 -0.8014,-0.7006 -0.8958,-1.0765 -0.1244,-0.4955 1.4236,-5.6423 5.6281,-18.7126C23.6983,42.5979 23.6983,42.5979 12.4059,42.4656 1.7759,42.3411 1.0807,42.3038 0.5567,41.8293 0.2087,41.5142 0,41.0033 0,40.4664c0,-1.3788 12.2864,-39.2443 12.9487,-39.9066 0.5548,-0.5548 0.6746,-0.5598 13.6215,-0.5598 12.2693,0 13.0941,0.0293 13.5949,0.4825 1.1973,1.0835 1.1729,1.1538 -4.7012,13.5387 -3.0613,6.4544 -5.5659,11.8038 -5.5659,11.8877 0,0.0839 4.9799,0.1525 11.0664,0.1525 11.0664,0 11.0664,0 11.6606,0.6325 0.411,0.4375 0.568,0.8836 0.5091,1.4469 -0.0614,0.5874 -5.2732,7.9334 -18.7031,26.3618 -10.2399,14.0511 -18.8687,25.7229 -19.175,25.9375 -0.6914,0.4843 -1.1627,0.4788 -2.0895,-0.024zM36.4322,30.0933C25.9694,30.0251 25.8162,30.0163 25.2154,29.4519 24.8587,29.1168 24.6063,28.5702 24.6063,28.133c0,-0.4239 2.39,-5.7794 5.529,-12.3893 5.529,-11.6426 5.529,-11.6426 -4.4337,-11.6426 -9.9627,0 -9.9627,0 -15.3537,16.7349 -2.965,9.2042 -5.4434,16.88 -5.5075,17.0574 -0.0902,0.2497 2.4609,0.3393 11.3035,0.3969 11.42,0.0743 11.42,0.0743 11.9256,0.699 0.2781,0.3435 0.5056,0.9045 0.5056,1.2466 0,0.3421 -1.8455,6.3614 -4.101,13.3763 -2.2556,7.0149 -4.101,12.8701 -4.101,13.0115 0,0.1414 6.0001,-8.0045 13.3335,-18.102C47.0398,30.1625 47.0398,30.1625 36.4322,30.0933Z" android:strokeWidth="0.26458335"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,51 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M452,40h-24V0h-40v40H124V0H84v40H60C26.916,40 0,66.916 0,100v352c0,33.084 26.916,60 60,60h392c33.084,0 60,-26.916 60,-60V100C512,66.916 485.084,40 452,40zM472,452c0,11.028 -8.972,20 -20,20H60c-11.028,0 -20,-8.972 -20,-20V188h432V452zM472,148H40v-48c0,-11.028 8.972,-20 20,-20h24v40h40V80h264v40h40V80h24c11.028,0 20,8.972 20,20V148z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M76,230h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M156,230h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M236,230h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M316,230h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M396,230h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M76,310h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M156,310h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M236,310h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M316,310h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M76,390h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M156,390h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M236,390h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M316,390h40v40h-40z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M396,310h40v40h-40z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="m305.1,501h-98.3c-11.3,0 -20.4,-9.1 -20.4,-20.4v-44.8c-2.9,-1.1 -5.7,-2.3 -8.5,-3.5l-31.5,31.7c-7.7,7.6 -21.2,7.7 -28.9,0l-69.5,-69.5c-3.8,-3.8 -6,-9 -6,-14.4 0,-5.4 2.2,-10.6 6,-14.4l31.6,-31.7c-1.2,-2.8 -2.4,-5.6 -3.5,-8.5h-44.7c-11.3,0 -20.4,-9.1 -20.4,-20.4v-98.3c0,-11.3 9.1,-20.4 20.4,-20.4h44.8c1.1,-2.9 2.3,-5.7 3.5,-8.5l-31.7,-31.5c-8,-8 -8,-20.9 0,-28.9l69.5,-69.5c8,-8 20.9,-8 28.9,0l31.6,31.6c2.8,-1.2 5.6,-2.4 8.5,-3.5v-44.7c0,-11.3 9.1,-20.4 20.4,-20.4h98.3c11.3,0 20.4,9.1 20.4,20.4v44.8c2.9,1.1 5.7,2.3 8.5,3.5l31.5,-31.7c8,-8 20.9,-8 28.9,0l69.5,69.5c8,8 8,20.9 0,28.9l-31.6,31.6c1.2,2.8 2.4,5.6 3.5,8.5h44.8c11.3,0 20.4,9.1 20.4,20.4v98.3c0,11.3 -9.1,20.4 -20.4,20.4h-44.8c-1.1,2.9 -2.3,5.7 -3.5,8.5l31.6,31.6c8,8 8,20.9 0,28.9l-69.5,69.4c-7.7,7.7 -21.2,7.7 -28.9,0l-31.6,-31.6c-2.8,1.2 -5.6,2.4 -8.5,3.5v44.8c0,11.2 -9.1,20.3 -20.4,20.3zM227.3,460.2h57.4v-38.9c0,-9 5.9,-17 14.6,-19.6 10.3,-3.1 20.1,-7.1 29.1,-12 8,-4.3 17.8,-2.9 24.2,3.5l27.5,27.5 40.6,-40.6 -27.5,-27.5c-6.4,-6.4 -7.8,-16.2 -3.5,-24.2 4.9,-9 8.9,-18.7 12,-29.1 2.6,-8.7 10.5,-14.6 19.6,-14.6h39v-57.4h-39c-9,0 -17,-5.9 -19.6,-14.6 -3.1,-10.3 -7.1,-20.1 -12,-29.1 -4.3,-8 -2.9,-17.8 3.5,-24.2l27.5,-27.5 -40.6,-40.6 -27.5,27.5c-6.4,6.4 -16.2,7.8 -24.2,3.5 -9,-4.9 -18.7,-8.9 -29.1,-12 -8.7,-2.6 -14.6,-10.5 -14.6,-19.6v-39h-57.4v39c0,9 -5.9,17 -14.6,19.6 -10.3,3.1 -20.1,7.1 -29.1,12 -7.9,4.3 -17.8,2.9 -24.2,-3.5l-27.5,-27.5 -40.6,40.6 27.5,27.5c6.4,6.4 7.8,16.2 3.5,24.2 -4.9,9 -8.9,18.7 -12,29.1 -2.6,8.7 -10.5,14.6 -19.6,14.6h-39v57.4h39c9,0 17,5.9 19.6,14.6 3.1,10.3 7.1,20.1 12,29.1 4.3,8 2.9,17.8 -3.5,24.2l-27.5,27.5 40.6,40.6 27.5,-27.5c6.4,-6.4 16.2,-7.8 24.2,-3.5 9,4.9 18.8,8.9 29.1,12 8.7,2.6 14.6,10.5 14.6,19.6v38.9z"/>
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="m256,365.1c-60.2,0 -109.1,-48.9 -109.1,-109.1 0,-60.2 48.9,-109.1 109.1,-109.1 60.2,0 109.1,48.9 109.1,109.1 0,60.2 -48.9,109.1 -109.1,109.1zM256,187.7c-37.6,0 -68.3,30.6 -68.3,68.3 0,37.6 30.6,68.3 68.3,68.3 37.6,0 68.3,-30.6 68.3,-68.3 0,-37.6 -30.7,-68.3 -68.3,-68.3z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8,9h8v10L8,19L8,9zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M436.3,194.218c-17.545,-17.546 -46.094,-17.546 -63.639,0l-40.143,40.142V75c0,-24.813 -20.187,-45 -45,-45c-5.857,0 -11.449,1.136 -16.586,3.181C265.728,14.085 248.242,0 227.519,0s-38.209,14.085 -43.414,33.181C178.968,31.136 173.376,30 167.519,30c-20.723,0 -38.209,14.085 -43.414,33.181C118.968,61.136 113.376,60 107.519,60c-24.813,0 -45,20.187 -45,45v407h270V361.64L436.3,257.858c8.5,-8.5 13.181,-19.8 13.181,-31.82S444.8,202.717 436.3,194.218zM415.087,236.645L302.519,349.213V482h-210V105c0,-8.271 6.729,-15 15,-15s15,6.729 15,15v97h30V75c0,-8.271 6.729,-15 15,-15s15,6.729 15,15v127h30V45c0,-8.271 6.729,-15 15,-15s15,6.729 15,15v157h30V75c0,-8.271 6.729,-15 15,-15s15,6.729 15,15v181h-25c-52.383,0 -95,42.617 -95,95h30c0,-35.841 29.159,-65 65,-65h45.786l70.569,-70.569c5.85,-5.848 15.365,-5.848 21.213,0c2.833,2.833 4.394,6.6 4.394,10.607S417.92,233.812 415.087,236.645z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/colorText"
|
||||
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,4 @@
|
||||
<vector android:height="24dp" android:viewportHeight="511.73535"
|
||||
android:viewportWidth="511" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m405.035,255.867c0,-75.734 -118.934,-230.402 -132.535,-247.734 -8.535,-10.668 -25.066,-10.668 -33.602,0 -13.598,17.332 -132.531,172 -132.531,247.734 0,40.266 16.266,78.664 45.066,106.664h-23.734v42.668h42.668v106.668h42.668v-106.668h85.332v106.668h42.668v-106.668h42.664v-42.668h-23.734c28.801,-28 45.07,-66.398 45.07,-106.664zM255.699,56.398c24.801,33.602 48,68.801 69.066,104.801 -16.531,12 -26.398,31.199 -26.398,51.734v21.332c0,11.734 -9.602,21.332 -21.332,21.332 -11.734,0 -21.336,-9.598 -21.336,-21.332v-21.332c0,-34.402 -27.465,-62.668 -61.867,-64 19.203,-31.734 41.602,-65.066 61.867,-92.535zM149.035,255.867c0,-15.199 8.266,-38.133 21.332,-64h21.332c11.734,0 21.336,9.598 21.336,21.332v21.332c0,35.469 28.531,64 64,64 35.465,0 64,-28.531 64,-64v-21.332c0,-4.531 1.598,-9.066 4.266,-12.801 10.398,22.402 17.066,41.867 17.066,55.469 0,58.934 -47.734,106.664 -106.668,106.664 -58.934,0 -106.664,-47.73 -106.664,-106.664zM149.035,255.867"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,4 @@
|
||||
<vector android:height="24dp" android:viewportHeight="384"
|
||||
android:viewportWidth="384" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m304,0c-8.832,0 -16,7.168 -16,16v16h-96c-8.832,0 -16,7.168 -16,16s7.168,16 16,16h96v32h-72c-8.832,0 -16,7.168 -16,16s7.168,16 16,16h55.754l-79.754,45.566 -79.754,-45.566h39.754c8.832,0 16,-7.168 16,-16s-7.168,-16 -16,-16h-56v-32h32c8.832,0 16,-7.168 16,-16s-7.168,-16 -16,-16h-32v-16c0,-8.832 -7.168,-16 -16,-16s-16,7.168 -16,16v84.145c0,4.945 0.68,9.793 1.785,14.504 0.063,0.383 0.137,0.758 0.23,1.137 4.215,16.648 14.977,31.215 30.23,39.934l63.508,36.281 -63.516,36.289c-15.246,8.719 -26.008,23.285 -30.223,39.934 -0.086,0.379 -0.168,0.754 -0.23,1.137 -1.105,4.711 -1.785,9.551 -1.785,14.496v84.145c0,8.832 7.168,16 16,16s16,-7.168 16,-16v-16h96c8.832,0 16,-7.168 16,-16s-7.168,-16 -16,-16h-96v-32h48c8.832,0 16,-7.168 16,-16s-7.168,-16 -16,-16h-31.754l79.754,-45.566 79.754,45.566h-63.754c-8.832,0 -16,7.168 -16,16s7.168,16 16,16h80v32h-32c-8.832,0 -16,7.168 -16,16s7.168,16 16,16h32v16c0,8.832 7.168,16 16,16s16,-7.168 16,-16v-84.145c0,-4.961 -0.688,-9.816 -1.793,-14.543 -0.063,-0.352 -0.129,-0.703 -0.215,-1.047 -4.207,-16.664 -14.977,-31.258 -30.238,-39.977l-63.508,-36.289 63.508,-36.289c15.262,-8.719 26.031,-23.313 30.238,-39.977 0.078,-0.344 0.152,-0.695 0.215,-1.047 1.105,-4.727 1.793,-9.582 1.793,-14.543v-84.145c0,-8.832 -7.168,-16 -16,-16zM304,0"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<vector android:height="24dp" android:viewportHeight="512.0389"
|
||||
android:viewportWidth="512" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m319.723,183.184v-140.52h21.328v-42.664h-170.648v42.664h21.328v140.52c-87.461,35.199 -129.59,134.66 -94.125,222.117 35.461,87.461 134.922,129.59 222.117,94.125 87.457,-35.461 129.586,-134.918 94.125,-222.113 -17.332,-42.934 -51.195,-76.793 -94.125,-94.129zM219.195,218.652c9.063,-2.668 15.199,-10.934 15.199,-20.535v-155.453h42.664v155.453c0,9.332 6.129,17.867 15.195,20.535 10.402,3.199 20.27,7.465 29.602,13.328 -3.199,3.465 -6.668,6.934 -9.867,10.398 -24.266,26.137 -47.195,50.664 -85.59,65.863 -33.07,13.336 -65.598,16.535 -96.531,9.867 9.066,-47.195 43.465,-85.594 89.328,-99.457zM255.727,469.297c-62.93,0 -116.527,-45.863 -126.395,-107.992 10.668,1.867 21.602,2.668 32.535,2.668 27.73,-0.266 54.93,-5.867 80.527,-16.266 47.195,-18.93 75.727,-49.332 101.059,-76.262 3.73,-4 7.199,-7.73 10.934,-11.465 45.063,54.129 37.594,134.922 -16.535,179.984 -23.195,18.934 -52.262,29.332 -82.125,29.332zM255.727,469.297"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="m362.387,341.305c0,11.785 -9.555,21.336 -21.336,21.336s-21.328,-9.551 -21.328,-21.336c0,-11.781 9.547,-21.328 21.328,-21.328s21.336,9.547 21.336,21.328zM362.387,341.305"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="m319.723,405.301c0,11.785 -9.551,21.336 -21.332,21.336 -11.785,0 -21.332,-9.551 -21.332,-21.336 0,-11.781 9.547,-21.332 21.332,-21.332 11.781,0 21.332,9.551 21.332,21.332zM319.723,405.301"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="m234.395,405.301c0,11.785 -9.551,21.336 -21.332,21.336s-21.332,-9.551 -21.332,-21.336c0,-11.781 9.551,-21.332 21.332,-21.332s21.332,9.551 21.332,21.332zM234.395,405.301"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,4 @@
|
||||
<vector android:height="24dp" android:viewportHeight="384"
|
||||
android:viewportWidth="384" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m368,352h-80v-48h17.465c12.68,0 24.961,-3.719 35.504,-10.754l11.902,-7.934c7.352,-4.898 9.336,-14.832 4.441,-22.184 -4.898,-7.352 -14.832,-9.359 -22.184,-4.441l-11.906,7.938c-5.277,3.52 -11.414,5.375 -17.758,5.375h-7.578l-24,-48h31.578c12.68,0 24.961,-3.719 35.504,-10.754l11.902,-7.934c7.352,-4.898 9.336,-14.832 4.441,-22.184 -4.898,-7.352 -14.832,-9.352 -22.184,-4.441l-11.906,7.938c-5.277,3.52 -11.414,5.375 -17.758,5.375h-7.578l-24,-48h15.578c12.68,0 24.961,-3.719 35.504,-10.754l11.902,-7.934c7.352,-4.898 9.336,-14.832 4.441,-22.184 -4.906,-7.352 -14.84,-9.352 -22.184,-4.441l-11.906,7.938c-5.277,3.52 -11.414,5.375 -17.758,5.375h-24.184l-59.395,-103.938c-2.848,-4.984 -8.145,-8.063 -13.887,-8.063s-11.039,3.078 -13.887,8.063l-59.395,103.938h-24.184c-6.336,0 -12.48,-1.855 -17.75,-5.375l-11.906,-7.938c-7.336,-4.902 -17.277,-2.91 -22.184,4.441 -4.902,7.352 -2.91,17.285 4.441,22.184l11.902,7.934c10.535,7.035 22.816,10.754 35.496,10.754h15.578l-24,48h-7.578c-6.336,0 -12.48,-1.855 -17.75,-5.375l-11.906,-7.938c-7.344,-4.895 -17.285,-2.91 -22.184,4.441 -4.895,7.352 -2.91,17.285 4.441,22.184l11.902,7.934c10.535,7.035 22.816,10.754 35.496,10.754h31.578l-24,48h-7.578c-6.336,0 -12.48,-1.855 -17.75,-5.375l-11.906,-7.938c-7.344,-4.902 -17.285,-2.91 -22.184,4.441 -4.895,7.352 -2.91,17.285 4.441,22.184l11.902,7.934c10.535,7.035 22.816,10.754 35.496,10.754h17.465v48h-80c-8.832,0 -16,7.168 -16,16s7.168,16 16,16h352c8.832,0 16,-7.168 16,-16s-7.168,-16 -16,-16zM192,48.246 L228.434,112h-72.867zM240,144c0,2.488 0.574,4.938 1.688,7.16l20.426,40.84h-140.227l20.426,-40.84c1.113,-2.223 1.688,-4.672 1.688,-7.16zM240,224c0,2.488 0.574,4.938 1.688,7.16l20.426,40.84h-140.227l20.426,-40.84c1.113,-2.223 1.688,-4.672 1.688,-7.16zM128,304h128v48h-128zM128,304"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="551.13"
|
||||
android:viewportHeight="551.13">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m275.565,551.13c-151.944,0 -275.565,-123.621 -275.565,-275.565s123.621,-275.565 275.565,-275.565 275.565,123.621 275.565,275.565 -123.621,275.565 -275.565,275.565zM275.565,34.446c-132.955,0 -241.119,108.181 -241.119,241.119s108.164,241.12 241.119,241.12 241.119,-108.181 241.119,-241.119 -108.164,-241.12 -241.119,-241.12z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:viewportHeight="512"
|
||||
android:viewportWidth="512" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m305.1,501h-98.3c-11.3,0 -20.4,-9.1 -20.4,-20.4v-44.8c-2.9,-1.1 -5.7,-2.3 -8.5,-3.5l-31.5,31.7c-7.7,7.6 -21.2,7.7 -28.9,0l-69.5,-69.5c-3.8,-3.8 -6,-9 -6,-14.4 0,-5.4 2.2,-10.6 6,-14.4l31.6,-31.7c-1.2,-2.8 -2.4,-5.6 -3.5,-8.5h-44.7c-11.3,0 -20.4,-9.1 -20.4,-20.4v-98.3c0,-11.3 9.1,-20.4 20.4,-20.4h44.8c1.1,-2.9 2.3,-5.7 3.5,-8.5l-31.7,-31.5c-8,-8 -8,-20.9 0,-28.9l69.5,-69.5c8,-8 20.9,-8 28.9,0l31.6,31.6c2.8,-1.2 5.6,-2.4 8.5,-3.5v-44.7c0,-11.3 9.1,-20.4 20.4,-20.4h98.3c11.3,0 20.4,9.1 20.4,20.4v44.8c2.9,1.1 5.7,2.3 8.5,3.5l31.5,-31.7c8,-8 20.9,-8 28.9,0l69.5,69.5c8,8 8,20.9 0,28.9l-31.6,31.6c1.2,2.8 2.4,5.6 3.5,8.5h44.8c11.3,0 20.4,9.1 20.4,20.4v98.3c0,11.3 -9.1,20.4 -20.4,20.4h-44.8c-1.1,2.9 -2.3,5.7 -3.5,8.5l31.6,31.6c8,8 8,20.9 0,28.9l-69.5,69.4c-7.7,7.7 -21.2,7.7 -28.9,0l-31.6,-31.6c-2.8,1.2 -5.6,2.4 -8.5,3.5v44.8c0,11.2 -9.1,20.3 -20.4,20.3zM227.3,460.2h57.4v-38.9c0,-9 5.9,-17 14.6,-19.6 10.3,-3.1 20.1,-7.1 29.1,-12 8,-4.3 17.8,-2.9 24.2,3.5l27.5,27.5 40.6,-40.6 -27.5,-27.5c-6.4,-6.4 -7.8,-16.2 -3.5,-24.2 4.9,-9 8.9,-18.7 12,-29.1 2.6,-8.7 10.5,-14.6 19.6,-14.6h39v-57.4h-39c-9,0 -17,-5.9 -19.6,-14.6 -3.1,-10.3 -7.1,-20.1 -12,-29.1 -4.3,-8 -2.9,-17.8 3.5,-24.2l27.5,-27.5 -40.6,-40.6 -27.5,27.5c-6.4,6.4 -16.2,7.8 -24.2,3.5 -9,-4.9 -18.7,-8.9 -29.1,-12 -8.7,-2.6 -14.6,-10.5 -14.6,-19.6v-39h-57.4v39c0,9 -5.9,17 -14.6,19.6 -10.3,3.1 -20.1,7.1 -29.1,12 -7.9,4.3 -17.8,2.9 -24.2,-3.5l-27.5,-27.5 -40.6,40.6 27.5,27.5c6.4,6.4 7.8,16.2 3.5,24.2 -4.9,9 -8.9,18.7 -12,29.1 -2.6,8.7 -10.5,14.6 -19.6,14.6h-39v57.4h39c9,0 17,5.9 19.6,14.6 3.1,10.3 7.1,20.1 12,29.1 4.3,8 2.9,17.8 -3.5,24.2l-27.5,27.5 40.6,40.6 27.5,-27.5c6.4,-6.4 16.2,-7.8 24.2,-3.5 9,4.9 18.8,8.9 29.1,12 8.7,2.6 14.6,10.5 14.6,19.6v38.9z"/>
|
||||
<path android:fillColor="#FF000000" android:pathData="m256,365.1c-60.2,0 -109.1,-48.9 -109.1,-109.1 0,-60.2 48.9,-109.1 109.1,-109.1 60.2,0 109.1,48.9 109.1,109.1 0,60.2 -48.9,109.1 -109.1,109.1zM256,187.7c-37.6,0 -68.3,30.6 -68.3,68.3 0,37.6 30.6,68.3 68.3,68.3 37.6,0 68.3,-30.6 68.3,-68.3 0,-37.6 -30.7,-68.3 -68.3,-68.3z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M405.333,256h-384C8.533,256 0,264.533 0,277.333v21.333C0,416 96,512 213.333,512s213.333,-96 213.333,-213.333v-21.333C426.667,264.533 418.133,256 405.333,256zM213.333,469.333c-93.867,0 -170.667,-76.8 -170.667,-170.667H384C384,392.533 307.2,469.333 213.333,469.333z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M490.667,277.333h-85.333c-12.8,0 -21.333,8.533 -21.333,21.333c0,12.8 8.533,21.333 21.333,21.333H467.2c-10.667,36.267 -42.667,64 -83.2,64c-12.8,0 -21.333,8.533 -21.333,21.333c0,12.8 8.533,21.333 21.333,21.333c70.4,0 128,-57.6 128,-128C512,285.867 503.467,277.333 490.667,277.333z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M362.667,469.333h-320c-12.8,0 -21.333,8.533 -21.333,21.333S29.867,512 42.667,512h320c12.8,0 21.333,-8.533 21.333,-21.333S375.467,469.333 362.667,469.333z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M121.6,121.6c38.4,-36.267 36.267,-78.933 0,-115.2c-8.533,-8.533 -21.333,-8.533 -29.867,0c-8.533,8.533 -8.533,21.333 0,29.867c19.2,19.2 21.333,36.267 0,55.467c0,0 0,0 -2.133,2.133c-36.267,34.133 -34.133,76.8 2.133,113.067c8.533,8.533 21.333,8.533 29.867,0c8.533,-8.533 8.533,-21.333 0,-29.867C102.4,157.867 100.267,140.8 121.6,121.6z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M228.267,121.6c40.533,-36.267 36.267,-81.067 0,-115.2c-8.533,-8.533 -21.333,-8.533 -29.867,0c-8.533,8.533 -8.533,21.333 0,29.867c19.2,19.2 21.333,36.267 0,55.467c0,0 0,0 -2.133,2.133C160,128 162.133,170.667 198.4,206.933c8.533,8.533 21.333,8.533 29.867,0s8.533,-21.333 0,-29.867C209.067,157.867 206.933,140.8 228.267,121.6z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M334.933,121.6c40.533,-36.267 36.267,-81.067 0,-115.2c-8.533,-8.533 -21.333,-8.533 -29.867,0c-8.533,8.533 -8.533,21.333 0,29.867c19.2,19.2 21.333,36.267 0,55.467c0,0 0,0 -2.133,2.133c-38.4,36.267 -34.133,78.933 0,115.2c8.533,8.533 21.333,8.533 29.867,0s8.533,-21.333 0,-29.867C315.733,157.867 313.6,140.8 334.933,121.6z"/>
|
||||
</vector>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user