Unified repository layer into a single class; added splash screen (WIP); using delegate classes by Zhuinden for view binding; changed UI to more so resemble "Material Design 2"

This commit is contained in:
Deniz Düzgören
2020-03-09 07:43:43 +01:00
parent 9ed5d7bb5f
commit 31b1bc5d4e
78 changed files with 764 additions and 855 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -1,2 +1,2 @@
#Wed Feb 12 10:01:56 CET 2020 #Mon Mar 02 18:08:17 CET 2020
gradle.version=6.1-rc-1 gradle.version=6.1.1
Binary file not shown.
Binary file not shown.
+11 -11
View File
File diff suppressed because one or more lines are too long
@@ -1,58 +1,46 @@
package com.denizd.qwark.activity package com.denizd.qwark.activity
import android.graphics.Color import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewAnimationUtils
import android.view.WindowManager import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.database.QwarkRepository
import com.denizd.qwark.databinding.ActivityMainBinding import com.denizd.qwark.databinding.ActivityMainBinding
import com.denizd.qwark.fragment.* import com.denizd.qwark.fragment.*
import com.denizd.qwark.util.Dependencies
import com.denizd.qwark.util.QwarkPreferences
import com.denizd.qwark.viewmodel.MainViewModel import com.denizd.qwark.viewmodel.MainViewModel
import kotlin.math.hypot
import kotlin.random.Random
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity() {
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::inflate)
private var _binding: ActivityMainBinding? = null
private val binding: ActivityMainBinding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setTheme(R.style.AppTheme) setTheme(R.style.AppTheme)
_binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
// ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42) // ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42)
Dependencies.repo = QwarkRepository(application)
viewModel = ViewModelProvider(this)[MainViewModel::class.java] viewModel = ViewModelProvider(this)[MainViewModel::class.java]
viewModel.applyTheme(window, this) viewModel.applyTheme(window, this)
viewModel.checkForFirstTimeYearNotice(this) viewModel.checkForFirstTimeYearNotice(this)
binding.appBarLayout.also { appBarLayout ->
appBarLayout.setOnApplyWindowInsetsListener { _, insets ->
(appBarLayout.layoutParams as ViewGroup.MarginLayoutParams)
.topMargin = insets.systemWindowInsetTop
insets
}
}
// TODO remove this translucent app bar layout stuff, it's hard to maintain
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
binding.bottomNav.setOnNavigationItemSelectedListener { item -> binding.bottomNav.setOnNavigationItemSelectedListener { item ->
binding.appBarLayout.setExpanded(true) binding.appBarLayout.setExpanded(true)
loadFragment(viewModel.getFragmentType(item.itemId)) loadFragment(viewModel.getFragmentType(item.itemId))
@@ -78,6 +66,27 @@ class MainActivity : FragmentActivity() {
// } // }
// TODO reselected listener for scrolling up // TODO reselected listener for scrolling up
resources.getStringArray(R.array.greetings).also { array ->
binding.greetingTextView.text = String.format(array[Random.nextInt(array.size)], "TODO")
}
Handler().postDelayed({
binding.splashScreen.apply {
val cx = width / 2
val cy = height / 2
val initialRadius = hypot(cx.toDouble(), cy.toDouble()).toFloat()
ViewAnimationUtils.createCircularReveal(this, cx, cy, initialRadius, 0f).also { anim ->
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
visibility = View.GONE
}
})
anim.start()
}
}
}, 1200)
} }
override fun onResume() { override fun onResume() {
@@ -88,11 +97,6 @@ class MainActivity : FragmentActivity() {
} }
} }
override fun onDestroy() {
super.onDestroy()
_binding = null
}
private fun loadFragment(type: String): Boolean { private fun loadFragment(type: String): Boolean {
val fragment = supportFragmentManager.findFragmentByTag(type) ?: viewModel.getFragment(type) val fragment = supportFragmentManager.findFragmentByTag(type) ?: viewModel.getFragment(type)
@@ -53,7 +53,7 @@ class CourseAdapter(private var courses: List<CourseExam>,
val context = holder.averageTextView.context val context = holder.averageTextView.context
holder.backgroundView.setBackgroundColor( holder.backgroundView.setBackgroundColor(
context.getColor(if (currentItem.advanced) R.color.colorAccent else R.color.colorCardBackground) context.getColor(if (currentItem.advanced) R.color.colorAccent else R.color.colorBackground)
) )
val textColour = context.getColor(if (currentItem.advanced) R.color.colorTextInverted else R.color.colorText) val textColour = context.getColor(if (currentItem.advanced) R.color.colorTextInverted else R.color.colorText)
@@ -1,39 +1,39 @@
package com.denizd.qwark.adapter package com.denizd.qwark.adapter
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.denizd.qwark.R import com.denizd.qwark.databinding.AverageCardBinding
import com.denizd.qwark.util.QwarkUtil import com.denizd.qwark.util.QwarkUtil
import com.denizd.qwark.model.HistoricalAvg import com.denizd.qwark.model.HistoricalAvg
import com.denizd.qwark.util.getUserDefinedAverage
import java.util.* import java.util.*
internal class HistoricalAverageAdapter(private var averages: List<HistoricalAvg>?) : RecyclerView.Adapter<HistoricalAverageAdapter.AverageViewHolder>() { class HistoricalAverageAdapter(private val averages: List<HistoricalAvg>, private val gradeType: Int) : RecyclerView.Adapter<HistoricalAverageAdapter.AverageViewHolder>() {
internal class AverageViewHolder(view: View) : RecyclerView.ViewHolder(view) { class AverageViewHolder(val binding: AverageCardBinding) : RecyclerView.ViewHolder(binding.root)
val grade: TextView = view.findViewById(R.id.grade)
val time: TextView = view.findViewById(R.id.time)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AverageViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AverageViewHolder = AverageViewHolder(
val v = LayoutInflater.from(parent.context).inflate(R.layout.average_card, parent, false) AverageCardBinding.inflate(
return AverageViewHolder(v) LayoutInflater.from(parent.context),
} parent,
false
)
)
override fun onBindViewHolder(holder: AverageViewHolder, position: Int) { override fun onBindViewHolder(holder: AverageViewHolder, position: Int) {
val currentItem = averages!![position] val currentItem = averages[position]
val leadingZero = if (currentItem.average.toDouble() < 10.0) "0%s" else "%s" with (holder.binding) {
holder.grade.text = String.format(leadingZero, currentItem.average) // TODO this conflicts with Points (Precise) grade.text = currentItem.average.getUserDefinedAverage(gradeType)
holder.time.text = QwarkUtil.getSimpleDateTimeFormat().format(Date(currentItem.time)) time.text = QwarkUtil.getSimpleDateTimeFormat().format(Date(currentItem.time))
}
} }
override fun getItemCount(): Int = averages?.size ?: 0 override fun getItemCount(): Int = averages.size
fun setAverages(averages: List<HistoricalAvg>) { // fun setAverages(averages: List<HistoricalAvg>) {
this.averages = averages // this.averages = averages
notifyDataSetChanged() // notifyDataSetChanged()
} // }
} }
@@ -1,26 +0,0 @@
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)
fun getCourse(courseId: Int): CourseExam = dao.getCourse(courseId)[0]
}
@@ -1,47 +0,0 @@
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 getFinalGrade(finalGradeId: Int) = dao.getFinalGrade(finalGradeId)[0]
fun update(finalGrade: FinalGrade) { dao.update(finalGrade) }
}
@@ -1,31 +0,0 @@
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)
fun getGrade(gradeId: Int) = dao.getGrade(gradeId)[0]
}
@@ -1,20 +0,0 @@
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) }
fun getNote(noteId: Int) = dao.getNote(noteId)[0]
}
@@ -1,74 +0,0 @@
package com.denizd.qwark.database
import android.app.Application
import android.util.Log
import androidx.lifecycle.LiveData
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.*
import com.denizd.qwark.util.QwarkPreferences
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> = dao.getCoursesByYearIdAsList(prefs.getSchoolYear()).getSorted(
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 updateParticipation(timesHandRaised: Int, timesSpoken: Int, participationId: Int) {
dao.updateParticipation(timesHandRaised, timesSpoken, participationId)
}
fun getSchoolYearName() = prefs.getSchoolYearName()
}
@@ -30,7 +30,7 @@ interface QwarkDao {
fun getCoursesByYearIdAsList(yearId: Int): List<CourseExam> fun getCoursesByYearIdAsList(yearId: Int): List<CourseExam>
@Query("SELECT *, -1 AS exam_time FROM course WHERE course_id = :courseId") @Query("SELECT *, -1 AS exam_time FROM course WHERE course_id = :courseId")
fun getCourse(courseId: Int): List<CourseExam> fun getCourse(courseId: Int): CourseExam
@Insert @Insert
fun insert(course: Course) fun insert(course: Course)
@@ -68,7 +68,7 @@ interface QwarkDao {
fun deleteGrade(gradeId: Int) fun deleteGrade(gradeId: Int)
@Query("SELECT * FROM grade WHERE grade_id = :gradeId") @Query("SELECT * FROM grade WHERE grade_id = :gradeId")
fun getGrade(gradeId: Int): List<Grade> fun getGrade(gradeId: Int): Grade
// notes // notes
@@ -97,7 +97,7 @@ interface QwarkDao {
fun getAllCategories(): List<String> fun getAllCategories(): List<String>
@Query("SELECT * FROM note WHERE note_id = :noteId") @Query("SELECT * FROM note WHERE note_id = :noteId")
fun getNote(noteId: Int): List<Note> fun getNote(noteId: Int): Note
// historical averages // historical averages
@@ -165,7 +165,7 @@ interface QwarkDao {
fun insertAll(finalGrades: Array<FinalGrade>) fun insertAll(finalGrades: Array<FinalGrade>)
@Query("SELECT * FROM final_grade WHERE final_grade_id = :finalGradeId") @Query("SELECT * FROM final_grade WHERE final_grade_id = :finalGradeId")
fun getFinalGrade(finalGradeId: Int): List<FinalGrade> fun getFinalGrade(finalGradeId: Int): FinalGrade
// school days // school days
@@ -14,7 +14,7 @@ import com.denizd.qwark.model.Note
import com.denizd.qwark.model.SchoolYear 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) @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 class QwarkDatabase : RoomDatabase() {
abstract fun dao(): QwarkDao abstract fun dao(): QwarkDao
@@ -0,0 +1,204 @@
package com.denizd.qwark.database
import android.app.Application
import androidx.lifecycle.LiveData
import com.denizd.qwark.model.*
import com.denizd.qwark.util.*
import java.util.*
class QwarkRepository(app: Application) {
private val dao: QwarkDao = QwarkDatabase.getInstance(app.applicationContext).dao()
val prefs: QwarkPreferences = QwarkPreferences(app.applicationContext)
// misc
private val days: Array<String> = arrayOf("mon", "tue", "wed", "thu", "fri")
// monday: 1, increment by 1 for each day
val dayId: Int = when (prefs.getParticipationDay()) {
0 -> with (Calendar.getInstance()) {
if (get(Calendar.DAY_OF_WEEK) - 1 in 1..5) get(Calendar.DAY_OF_WEEK) - 1 else 1
}
else -> prefs.getParticipationDay()
}
fun initParticipationTables() {
for (day in days) {
dao.insert(SchoolDay(day, 0, prefs.getSchoolYear()))
}
}
fun link(courseId: Int) { dao.link(courseId, getDayType(), prefs.getSchoolYear()) }
fun removeLink(relationId: Int) { dao.removeLink(relationId) }
fun copyCourses(yearId: Int) {
val currentSchoolYearId = prefs.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 (prefs.getGradeType() == QwarkUtil.GRADE_PRECISE) "0.00" else "0",
oralWeighting = course.oralWeighting,
gradeCount = course.gradeCount,
time = System.currentTimeMillis(),
courseId = 0,
yearId = currentSchoolYearId
)
)
}
}
fun initNewFinalScore(name: String) {
val finalScoreId = getMaxFinalScoreId(true)
val finalGrade = FinalGrade("", "-1", "basic", "x 1", -1, 0, finalScoreId)
dao.insert(ScoreProfile(name, finalScoreId))
val values = ArrayList<FinalGrade>()
repeat(24) { values.add(finalGrade.copy(type = "basic", note = "x 1")) }
repeat(2) {
repeat(4) { values.add(finalGrade.copy(type = "adv", note = "x 2")) }
values.add(finalGrade.copy(note = "x 1"))
}
for (i in 1..5) {
values.add(finalGrade.copy(type = "exam", note = "P$i x ${if (i == 5) "2" else "5"}"))
}
dao.insertAll(values.toTypedArray())
}
// insert
fun insert(course: Course) { dao.insert(course) }
fun insert(grade: Grade) { dao.insert(grade) }
fun insert(note: Note) { dao.insert(note) }
fun insert(historicalAvg: HistoricalAvg) { dao.insert(historicalAvg) }
fun insert(schoolYear: SchoolYear) { dao.insert(schoolYear) }
fun insert(scoreProfile: ScoreProfile) { dao.insert(scoreProfile) }
fun insert(schoolDay: SchoolDay) { dao.insert(schoolDay) }
fun insert(participation: Participation) { dao.insert(participation) }
// insert multiple
fun insertAll(finalGrades: Array<FinalGrade>) { dao.insertAll(finalGrades) }
// set
fun setCurrentScoreProfile(scoreProfile: ScoreProfile) {
prefs.setInt(PreferenceKey.SCORE_PROFILE_ID, scoreProfile.scoreProfileId)
.setString(PreferenceKey.SCORE_PROFILE_NAME, scoreProfile.name)
}
fun setCurrentSchoolYear(schoolYear: SchoolYear) {
prefs.setInt(PreferenceKey.SCHOOL_YEAR_ID, schoolYear.yearId)
.setString(PreferenceKey.SCHOOL_YEAR_NAME, schoolYear.year)
}
// update
fun update(course: Course) { dao.update(course) }
fun update(finalGrade: FinalGrade) { dao.update(finalGrade) }
fun updateLinkedGrades() {
for (finalGrade in dao.getFinalGradesAsList(prefs.getFinalScoreId())) {
if (finalGrade.courseId != -1) {
updateFinalGradeAverage(finalGrade.courseId, finalGrade.finalGradeId)
}
}
}
fun updateFinalGradeAverage(courseId: Int, finalGradeId: Int) {
dao.getCourse(courseId).also { course ->
dao.updateFinalGradeAverage(
course.name,
course.average.roundToInt(),
finalGradeId
)
}
}
fun updateAverage(average: String, gradeCount: Int, courseId: Int) { dao.updateAverage(average, gradeCount, courseId) }
fun update(grade: Grade) { dao.update(grade) }
fun update(note: Note) { dao.update(note) }
fun updateDismissed(newDismissedStatus: Boolean, noteId: Int) { dao.updateDismissed(newDismissedStatus, noteId) }
fun updateParticipation(participation: String, courseId: Int) {
dao.updateParticipation(participation, courseId)
}
fun updateParticipation(timesHandRaised: Int, timesSpoken: Int, participationId: Int) {
dao.updateParticipation(timesHandRaised, timesSpoken, participationId)
}
fun updateSchoolYear(name: String) {
dao.update(SchoolYear(name, prefs.getCurrentSchoolYearId()))
setCurrentSchoolYear(SchoolYear(name, prefs.getCurrentSchoolYearId()))
}
fun updateScoreProfile(name: String) {
dao.update(ScoreProfile(name, prefs.getScoreProfileId()))
setCurrentScoreProfile(ScoreProfile(name, prefs.getScoreProfileId()))
}
// delete
fun delete(course: Course) { dao.delete(course) }
fun deleteGrade(gradeId: Int) { dao.deleteGrade(gradeId) }
fun deleteNote(noteId: Int) { dao.deleteNote(noteId) }
fun delete(participation: Participation) { dao.delete(participation) }
fun deleteSchoolYear() {
dao.deleteSchoolYear(prefs.getCurrentSchoolYearId())
setCurrentSchoolYear(SchoolYear("", 0))
}
fun deleteScoreProfile() {
dao.deleteScoreProfile(prefs.getScoreProfileId())
setCurrentScoreProfile(ScoreProfile("", 0))
}
// live data
val allNotes: LiveData<List<Note>> = dao.allNotes
val allCoursesForToday: LiveData<List<Course>> = dao.getCoursesByDayType(getDayType(), prefs.getSchoolYear())
val allYears: LiveData<List<SchoolYear>> = dao.allYears
val allScoreProfiles: LiveData<List<ScoreProfile>> = dao.allScoreProfiles
// get
fun getCourse(courseId: Int): CourseExam = dao.getCourse(courseId)
fun getSchoolYearName(courseId: Int) = dao.getSchoolYearName(courseId)
fun getFinalGrade(finalGradeId: Int) = dao.getFinalGrade(finalGradeId)
fun getGrade(gradeId: Int) = dao.getGrade(gradeId)
fun getNote(noteId: Int) = dao.getNote(noteId)
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]
private fun getMaxFinalScoreId(increment: Boolean): Int {
if (increment) {
prefs.setInt(PreferenceKey.MAX_SCORE_PROFILE_ID, getMaxFinalScoreId(false) + 1)
.setInt(PreferenceKey.SCORE_PROFILE_ID, getMaxFinalScoreId(false) + 1)
}
return prefs.getInt(PreferenceKey.MAX_SCORE_PROFILE_ID)
}
// get multiple
fun getCoursesByYearId(yearId: Int): LiveData<List<CourseExam>> = dao.getCoursesByYearId(yearId)
fun getAllCourses() = dao.getAllCourseExams()
fun getAveragesForCourses(courseId: Int): List<HistoricalAvg> = dao.getAveragesForCourses(courseId)
fun getFinalGrades(finalScoreId: Int): LiveData<List<FinalGrade>> = dao.getFinalGrades(finalScoreId)
fun getAllYears(): List<SchoolYear> = dao.getAllYears()
fun getGradesByForeignKey(courseId: Int): LiveData<List<Grade>> = dao.getGradesByForeignKey(courseId)
fun getNotesForCategory(category: String): LiveData<List<Note>> = dao.notesForCategory(category)
fun getAllCategories(): List<String> = dao.getAllCategories()
fun getDays(): List<SchoolDay> = dao.getDays(prefs.getSchoolYear())
fun getCourses(): List<CourseExam> =
dao.getCoursesByYearIdAsList(prefs.getSchoolYear()).getSorted(prefs.getCourseSortType())
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
)
)
}
}
@@ -1,125 +0,0 @@
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
)
)
}
}
}
@@ -11,6 +11,7 @@ import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.adapter.CourseAdapter import com.denizd.qwark.adapter.CourseAdapter
import com.denizd.qwark.sheet.ConfirmDeletionSheet import com.denizd.qwark.sheet.ConfirmDeletionSheet
@@ -26,11 +27,9 @@ import com.denizd.qwark.viewmodel.CourseViewModel
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import java.lang.NumberFormatException import java.lang.NumberFormatException
class CourseFragment : QwarkFragment(), CourseAdapter.CourseClickListener { class CourseFragment : QwarkFragment(R.layout.padded_recycler_view), CourseAdapter.CourseClickListener {
private var _binding: PaddedRecyclerViewBinding? = null
private val binding: PaddedRecyclerViewBinding get() = _binding!!
private val binding: PaddedRecyclerViewBinding by viewBinding(PaddedRecyclerViewBinding::bind)
private val viewModel: CourseViewModel by viewModels() private val viewModel: CourseViewModel by viewModels()
private lateinit var recyclerViewAdapter: CourseAdapter private lateinit var recyclerViewAdapter: CourseAdapter
@@ -42,10 +41,7 @@ class CourseFragment : QwarkFragment(), CourseAdapter.CourseClickListener {
viewModel.allCourses.observe(this, Observer { courses -> viewModel.allCourses.observe(this, Observer { courses ->
recyclerViewAdapter.setCourses(courses.getSorted(viewModel.getCourseSortType())) recyclerViewAdapter.setCourses(courses.getSorted(viewModel.getCourseSortType()))
if (scrollPosition == null) { if (scrollPosition == null) {
binding.recyclerView.apply { binding.recyclerView.scheduleLayoutAnimation()
scrollToPosition(0)
scheduleLayoutAnimation()
}
} else { } else {
binding.recyclerView.layoutManager?.onRestoreInstanceState(scrollPosition) binding.recyclerView.layoutManager?.onRestoreInstanceState(scrollPosition)
} }
@@ -70,11 +66,6 @@ class CourseFragment : QwarkFragment(), CourseAdapter.CourseClickListener {
) )
} }
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -102,18 +93,6 @@ class CourseFragment : QwarkFragment(), CourseAdapter.CourseClickListener {
} }
} }
override fun onResume() {
super.onResume()
binding.recyclerView.apply {
applyPadding(horizontalPadding = 4)
}
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
override fun onCourseClick(course: CourseExam) { override fun onCourseClick(course: CourseExam) {
appBar.setExpanded(true) appBar.setExpanded(true)
val f = GradeFragment() val f = GradeFragment()
@@ -1,6 +1,5 @@
package com.denizd.qwark.fragment package com.denizd.qwark.fragment
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -11,22 +10,19 @@ import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.adapter.FinalGradeAdapter import com.denizd.qwark.adapter.FinalGradeAdapter
import com.denizd.qwark.sheet.ConfirmDeletionSheet
import com.denizd.qwark.sheet.FinalsCreateSheet
import com.denizd.qwark.sheet.FinalsOptionsSheet
import com.denizd.qwark.databinding.FinalsFragmentBinding import com.denizd.qwark.databinding.FinalsFragmentBinding
import com.denizd.qwark.sheet.FinalsLinkSheet
import com.denizd.qwark.model.FinalGrade import com.denizd.qwark.model.FinalGrade
import com.denizd.qwark.sheet.*
import com.denizd.qwark.viewmodel.FinalsViewModel import com.denizd.qwark.viewmodel.FinalsViewModel
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
class FinalsFragment : QwarkFragment(), FinalGradeAdapter.FinalGradeClickListener { class FinalsFragment : QwarkFragment(R.layout.finals_fragment), FinalGradeAdapter.FinalGradeClickListener {
private lateinit var viewModel: FinalsViewModel private lateinit var viewModel: FinalsViewModel
private var _binding: FinalsFragmentBinding? = null private val binding: FinalsFragmentBinding by viewBinding(FinalsFragmentBinding::bind)
private val binding: FinalsFragmentBinding get() = _binding!!
private var pointsText = "" private var pointsText = ""
private var gradeText = "" private var gradeText = ""
@@ -80,11 +76,6 @@ class FinalsFragment : QwarkFragment(), FinalGradeAdapter.FinalGradeClickListene
binding.gradeTextview.text = gradeText 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -94,7 +85,16 @@ class FinalsFragment : QwarkFragment(), FinalGradeAdapter.FinalGradeClickListene
} else { } else {
getString(R.string.score_profile_template, viewModel.getScoreProfileName()) getString(R.string.score_profile_template, viewModel.getScoreProfileName())
} }
fab.hide() fab.apply {
// temporary until the scroll situation can be figured out
// show()
hide()
// text = getString(R.string.conversion_table)
// setOnClickListener {
// openBottomSheet(FinalGradeTableSheet())
// }
}
// binding.scrollView.addFabScrollListener()
val layoutManagerBasic = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) val layoutManagerBasic = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
val layoutManagerAdvanced = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) val layoutManagerAdvanced = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
@@ -110,20 +110,9 @@ class FinalsFragment : QwarkFragment(), FinalGradeAdapter.FinalGradeClickListene
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.paddingView.apply {
applyPadding()
}
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
binding.textLayout?.applyPadding()
}
setTextForGradeTextViews() setTextForGradeTextViews()
} }
override fun onDestroy() {
super.onDestroy()
_binding = null
}
override fun onFinalGradeClick(finalGrade: FinalGrade) { override fun onFinalGradeClick(finalGrade: FinalGrade) {
openBottomSheet(FinalsOptionsSheet().also { sheet -> openBottomSheet(FinalsOptionsSheet().also { sheet ->
sheet.arguments = Bundle().apply { putInt("finalGradeId", finalGrade.finalGradeId) } sheet.arguments = Bundle().apply { putInt("finalGradeId", finalGrade.finalGradeId) }
@@ -146,6 +135,7 @@ class FinalsFragment : QwarkFragment(), FinalGradeAdapter.FinalGradeClickListene
fun getAllCourses() = viewModel.getAllCourses() fun getAllCourses() = viewModel.getAllCourses()
fun viewLinkedCourse(courseId: Int) { fun viewLinkedCourse(courseId: Int) {
appBar.setExpanded(true)
val f = GradeFragment().also { fragment -> val f = GradeFragment().also { fragment ->
fragment.arguments = Bundle().apply { fragment.arguments = Bundle().apply {
putInt("courseId", courseId) putInt("courseId", courseId)
@@ -4,12 +4,12 @@ import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.adapter.GradeAdapter import com.denizd.qwark.adapter.GradeAdapter
import com.denizd.qwark.sheet.ConfirmDeletionSheet import com.denizd.qwark.sheet.ConfirmDeletionSheet
@@ -23,7 +23,7 @@ import com.denizd.qwark.util.getUserDefinedAverage
import com.denizd.qwark.viewmodel.GradeViewModel import com.denizd.qwark.viewmodel.GradeViewModel
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
class GradeFragment : QwarkFragment(), GradeAdapter.GradeClickListener { class GradeFragment : QwarkFragment(R.layout.grade_fragment), GradeAdapter.GradeClickListener {
private lateinit var currentCourse: CourseExam private lateinit var currentCourse: CourseExam
private var average = "" private var average = ""
@@ -35,8 +35,7 @@ class GradeFragment : QwarkFragment(), GradeAdapter.GradeClickListener {
private val viewModel: GradeViewModel by viewModels() private val viewModel: GradeViewModel by viewModels()
private lateinit var inflater: LayoutInflater private lateinit var inflater: LayoutInflater
private var _binding: GradeFragmentBinding? = null private val binding: GradeFragmentBinding by viewBinding(GradeFragmentBinding::bind)
private val binding: GradeFragmentBinding get() = _binding!!
private var scrollPosition: Parcelable? = null private var scrollPosition: Parcelable? = null
@@ -75,11 +74,6 @@ class GradeFragment : QwarkFragment(), GradeAdapter.GradeClickListener {
adapter = GradeAdapter(ArrayList(), this, getGradeType()) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -109,16 +103,6 @@ class GradeFragment : QwarkFragment(), GradeAdapter.GradeClickListener {
} }
} }
override fun onResume() {
super.onResume()
binding.paddingView.applyPadding()
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
private fun insertNewHistoricalAverage() { private fun insertNewHistoricalAverage() {
viewModel.insert( viewModel.insert(
HistoricalAvg( HistoricalAvg(
@@ -145,7 +129,7 @@ class GradeFragment : QwarkFragment(), GradeAdapter.GradeClickListener {
override fun onGradeLongClick(gradeId: Int) { override fun onGradeLongClick(gradeId: Int) {
val deletionSheet = ConfirmDeletionSheet(getString(R.string.confirm_grade_deletion)) { val deletionSheet = ConfirmDeletionSheet(getString(R.string.confirm_grade_deletion)) {
viewModel.delete(gradeId) viewModel.deleteGrade(gradeId)
} }
openBottomSheet(deletionSheet) openBottomSheet(deletionSheet)
} }
@@ -9,6 +9,7 @@ import android.view.ViewGroup
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.adapter.NoteAdapter import com.denizd.qwark.adapter.NoteAdapter
import com.denizd.qwark.sheet.ConfirmDeletionSheet import com.denizd.qwark.sheet.ConfirmDeletionSheet
@@ -21,7 +22,7 @@ import com.denizd.qwark.model.Note
import com.denizd.qwark.viewmodel.NoteViewModel import com.denizd.qwark.viewmodel.NoteViewModel
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
class NoteFragment : QwarkFragment(), NoteAdapter.OnNoteClickListener, OnBackPressed { class NoteFragment : QwarkFragment(R.layout.padded_recycler_view), NoteAdapter.OnNoteClickListener, OnBackPressed {
private lateinit var viewModel: NoteViewModel private lateinit var viewModel: NoteViewModel
private val adapter = NoteAdapter(ArrayList(), this) private val adapter = NoteAdapter(ArrayList(), this)
@@ -30,8 +31,7 @@ class NoteFragment : QwarkFragment(), NoteAdapter.OnNoteClickListener, OnBackPre
private lateinit var appBarLayout: AppBarLayout private lateinit var appBarLayout: AppBarLayout
private var _binding: PaddedRecyclerViewBinding? = null private val binding: PaddedRecyclerViewBinding by viewBinding(PaddedRecyclerViewBinding::bind)
private val binding: PaddedRecyclerViewBinding get() = _binding!!
private var isInCategoryView = false private var isInCategoryView = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -53,11 +53,6 @@ class NoteFragment : QwarkFragment(), NoteAdapter.OnNoteClickListener, OnBackPre
StaggeredGridLayoutManager(getGridColumnCount(newConfig), StaggeredGridLayoutManager.VERTICAL) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -79,16 +74,6 @@ class NoteFragment : QwarkFragment(), NoteAdapter.OnNoteClickListener, OnBackPre
binding.recyclerView.addFabScrollListener() binding.recyclerView.addFabScrollListener()
} }
override fun onResume() {
super.onResume()
binding.recyclerView.applyPadding(horizontalPadding = 4)
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
return if (isInCategoryView) { return if (isInCategoryView) {
sortByCategory("") sortByCategory("")
@@ -9,6 +9,7 @@ import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.adapter.ParticipationAdapter import com.denizd.qwark.adapter.ParticipationAdapter
import com.denizd.qwark.databinding.GradeFragmentBinding import com.denizd.qwark.databinding.GradeFragmentBinding
@@ -19,10 +20,9 @@ import com.denizd.qwark.viewmodel.ParticipationCourseViewModel
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
class ParticipationCourseFragment : QwarkFragment(), ParticipationAdapter.ParticipationClickListener { class ParticipationCourseFragment : QwarkFragment(R.layout.grade_fragment), ParticipationAdapter.ParticipationClickListener {
private var _binding: GradeFragmentBinding? = null private val binding: GradeFragmentBinding by viewBinding(GradeFragmentBinding::bind)
private val binding: GradeFragmentBinding get() = _binding!!
private var course: String = "" private var course: String = ""
private var courseId: Int = 0 private var courseId: Int = 0
private var advanced: Boolean = false private var advanced: Boolean = false
@@ -55,11 +55,6 @@ class ParticipationCourseFragment : QwarkFragment(), ParticipationAdapter.Partic
}) })
} }
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -99,16 +94,6 @@ class ParticipationCourseFragment : QwarkFragment(), ParticipationAdapter.Partic
binding.average.text = averageText binding.average.text = averageText
} }
override fun onResume() {
super.onResume()
binding.paddingView.applyPadding()
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
override fun onParticipationClick(participation: Participation) { override fun onParticipationClick(participation: Participation) {
if (participation.time > System.currentTimeMillis() - 1000 * 60 * 90) { if (participation.time > System.currentTimeMillis() - 1000 * 60 * 90) {
openBottomSheet(ClassSessionSheet().also { sheet -> openBottomSheet(ClassSessionSheet().also { sheet ->
@@ -10,6 +10,7 @@ import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.adapter.CourseAdapter import com.denizd.qwark.adapter.CourseAdapter
import com.denizd.qwark.databinding.PaddedRecyclerViewBinding import com.denizd.qwark.databinding.PaddedRecyclerViewBinding
@@ -19,10 +20,9 @@ import com.denizd.qwark.sheet.ConfirmDeletionSheet
import com.denizd.qwark.sheet.ParticipationLinkSheet import com.denizd.qwark.sheet.ParticipationLinkSheet
import com.denizd.qwark.viewmodel.ParticipationViewModel import com.denizd.qwark.viewmodel.ParticipationViewModel
internal class ParticipationFragment : QwarkFragment(), CourseAdapter.CourseClickListener { internal class ParticipationFragment : QwarkFragment(R.layout.padded_recycler_view), CourseAdapter.CourseClickListener {
private var _binding: PaddedRecyclerViewBinding? = null private val binding: PaddedRecyclerViewBinding by viewBinding(PaddedRecyclerViewBinding::bind)
private val binding: PaddedRecyclerViewBinding get() = _binding!!
private lateinit var viewModel: ParticipationViewModel private lateinit var viewModel: ParticipationViewModel
private val recyclerViewAdapter = CourseAdapter(ArrayList(), this, null, true) private val recyclerViewAdapter = CourseAdapter(ArrayList(), this, null, true)
@@ -59,11 +59,6 @@ internal class ParticipationFragment : QwarkFragment(), CourseAdapter.CourseClic
}) })
} }
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -85,16 +80,6 @@ internal class ParticipationFragment : QwarkFragment(), CourseAdapter.CourseClic
} }
} }
override fun onResume() {
super.onResume()
binding.recyclerView.applyPadding(horizontalPadding = 4)
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
override fun onCourseClick(course: CourseExam) { override fun onCourseClick(course: CourseExam) {
appBar.setExpanded(true) appBar.setExpanded(true)
(context as FragmentActivity).supportFragmentManager.beginTransaction() (context as FragmentActivity).supportFragmentManager.beginTransaction()
@@ -6,7 +6,9 @@ import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -15,7 +17,7 @@ import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
open class QwarkFragment : Fragment() { open class QwarkFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) {
private lateinit var _context: Context private lateinit var _context: Context
@@ -32,7 +34,6 @@ open class QwarkFragment : Fragment() {
appBarTitleView.text = value appBarTitleView.text = value
field = value field = value
} }
private lateinit var statusBarView: View
lateinit var appBar: AppBarLayout lateinit var appBar: AppBarLayout
lateinit var snackBarContainer: CoordinatorLayout lateinit var snackBarContainer: CoordinatorLayout
lateinit var fab: ExtendedFloatingActionButton lateinit var fab: ExtendedFloatingActionButton
@@ -49,12 +50,17 @@ open class QwarkFragment : Fragment() {
/** /**
* Initialise view resources * Initialise view resources
*/ */
statusBarView = view.rootView.findViewById(R.id.status_bar_view)
appBarTitleView = view.rootView.findViewById(R.id.app_bar_title) appBarTitleView = view.rootView.findViewById(R.id.app_bar_title)
superTitleView = view.rootView.findViewById(R.id.super_title) superTitleView = view.rootView.findViewById(R.id.super_title)
appBar = view.rootView.findViewById(R.id.app_bar_layout) appBar = view.rootView.findViewById(R.id.app_bar_layout)
snackBarContainer = view.rootView.findViewById(R.id.root_view) snackBarContainer = view.rootView.findViewById(R.id.root_view)
fab = view.rootView.findViewById(R.id.fab) fab = view.rootView.findViewById(R.id.fab)
if (this is GradeFragment || this is ParticipationCourseFragment) {
snackBarContainer.resetBottomPadding()
} else {
snackBarContainer.applyBottomPadding()
}
} }
/** /**
@@ -88,19 +94,21 @@ open class QwarkFragment : Fragment() {
}) })
} }
/** protected fun NestedScrollView.addFabScrollListener() {
* This function must not be invoked in onViewCreated, as the view will not have been attached setOnScrollChangeListener { _, _, dy, _, doy ->
* to its lifecycle owner and therefore cannot calculate the top padding properly. Invoke in when {
* onResume instead. dy > doy -> fab.hide()
*/ dy < doy -> fab.show()
protected fun <T : View> T.applyPadding(horizontalPadding: Int = 0) { }
val horizontal = horizontalPadding.dpToPx() }
setPadding( }
horizontal,
(statusBarView.height / 1) + appBar.height, private fun <T : View> T.resetBottomPadding() {
horizontal, setPadding(paddingLeft, paddingTop, paddingRight, 0)
0 }
)
private fun <T : View> T.applyBottomPadding() {
setPadding(paddingLeft, paddingTop, paddingRight, 56.dpToPx())
} }
/** /**
@@ -1,12 +1,11 @@
package com.denizd.qwark.fragment package com.denizd.qwark.fragment
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.sheet.ConfirmDeletionSheet import com.denizd.qwark.sheet.ConfirmDeletionSheet
import com.denizd.qwark.sheet.CopyCoursesSheet import com.denizd.qwark.sheet.CopyCoursesSheet
@@ -17,17 +16,11 @@ import com.denizd.qwark.model.SchoolYear
import com.denizd.qwark.viewmodel.SettingsViewModel import com.denizd.qwark.viewmodel.SettingsViewModel
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
class SettingsFragment : QwarkFragment() { class SettingsFragment : QwarkFragment(R.layout.settings_fragment) {
private var _binding: SettingsFragmentBinding? = null private val binding: SettingsFragmentBinding by viewBinding(SettingsFragmentBinding::bind)
private val binding: SettingsFragmentBinding get() = _binding!!
private val viewModel: SettingsViewModel by viewModels() private val viewModel: SettingsViewModel by viewModels()
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -94,7 +87,7 @@ class SettingsFragment : QwarkFragment() {
) )
) )
try { try {
setText(viewModel.getCurrentSchoolYear(), false) setText(viewModel.getSchoolYearName(), false)
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
// do nothing if no school years exist or none has been selected // do nothing if no school years exist or none has been selected
} }
@@ -212,16 +205,6 @@ class SettingsFragment : QwarkFragment() {
} }
} }
override fun onResume() {
super.onResume()
binding.settingsScrollView.applyPadding()
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
fun copyCourses(yearId: Int) { fun copyCourses(yearId: Int) {
viewModel.copyCourses(yearId) viewModel.copyCourses(yearId)
Snackbar Snackbar
@@ -9,18 +9,16 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast import android.widget.Toast
import com.denizd.lawrence.util.viewBinding
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.databinding.ClassSessionSheetBinding import com.denizd.qwark.databinding.ClassSessionSheetBinding
import com.denizd.qwark.fragment.ParticipationCourseFragment import com.denizd.qwark.fragment.ParticipationCourseFragment
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlin.random.Random import kotlin.random.Random
class ClassSessionSheet : BottomSheetDialogFragment() { class ClassSessionSheet : QwarkSheet(R.layout.class_session_sheet) {
private var _binding: ClassSessionSheetBinding? = null private val binding: ClassSessionSheetBinding by viewBinding(ClassSessionSheetBinding::bind)
private val binding: ClassSessionSheetBinding get() = _binding!!
private lateinit var mContext: Context
private lateinit var participationFragment: ParticipationCourseFragment private lateinit var participationFragment: ParticipationCourseFragment
private var timesHandRaised: Int = 0 private var timesHandRaised: Int = 0
@@ -36,16 +34,6 @@ class ClassSessionSheet : BottomSheetDialogFragment() {
private var participationId = -1 private var participationId = -1
private val time: Long = System.currentTimeMillis() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -61,7 +49,7 @@ class ClassSessionSheet : BottomSheetDialogFragment() {
binding.handRaisedButton.apply { binding.handRaisedButton.apply {
setCompoundDrawables(context.getDrawable(R.drawable.hand)?.also { drawable -> setCompoundDrawables(context.getDrawable(R.drawable.hand)?.also { drawable ->
drawable.bounds = Rect(0, 0, 256, 256) drawable.bounds = Rect(0, 0, 256, 256)
drawable.setTint(mContext.getColor(R.color.colorAccent)) drawable.setTint(context.getColor(R.color.colorAccent))
}, null, null, null) }, null, null, null)
text = "$timesHandRaised" text = "$timesHandRaised"
setOnClickListener { setOnClickListener {
@@ -78,7 +66,7 @@ class ClassSessionSheet : BottomSheetDialogFragment() {
binding.spokenButton.apply { binding.spokenButton.apply {
setCompoundDrawables(context.getDrawable(R.drawable.speech)?.also { drawable -> setCompoundDrawables(context.getDrawable(R.drawable.speech)?.also { drawable ->
drawable.bounds = Rect(0, 0, 256, 256) drawable.bounds = Rect(0, 0, 256, 256)
drawable.setTint(mContext.getColor(R.color.colorAccent)) drawable.setTint(context.getColor(R.color.colorAccent))
}, null, null, null) }, null, null, null)
text = "$timesSpoken" text = "$timesSpoken"
setOnClickListener { setOnClickListener {
@@ -102,7 +90,7 @@ class ClassSessionSheet : BottomSheetDialogFragment() {
} }
} }
binding.finishButton.apply { binding.saveButton.apply {
setOnClickListener { setOnClickListener {
makeToast(R.string.long_press_to_finish) makeToast(R.string.long_press_to_finish)
} }
@@ -124,14 +112,10 @@ class ClassSessionSheet : BottomSheetDialogFragment() {
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} }
override fun onDestroy() {
super.onDestroy()
_binding = null
}
private fun makeToast(stringResource: Int) { private fun makeToast(stringResource: Int) {
Toast Toast
.makeText(mContext, getString(stringResource), Toast.LENGTH_LONG) .makeText(context, getString(stringResource), Toast.LENGTH_LONG)
.show() .show()
} }
@@ -0,0 +1,38 @@
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.TextSheetBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class FinalGradeTableSheet : BottomSheetDialogFragment() {
private var _binding: TextSheetBinding? = null
private val binding: TextSheetBinding 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 = TextSheetBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.title.text = mContext.getString(R.string.points_to_grade_conversion_title)
binding.content.text = mContext.getString(R.string.points_to_grade_conversion)
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
@@ -119,7 +119,7 @@ class GradeCreateSheet : BottomSheetDialogFragment() {
note = binding.noteEditText.text.toString(), note = binding.noteEditText.text.toString(),
time = if (::grade.isInitialized) grade.time else System.currentTimeMillis(), time = if (::grade.isInitialized) grade.time else System.currentTimeMillis(),
gradeId = if (::grade.isInitialized) grade.gradeId else 0, gradeId = if (::grade.isInitialized) grade.gradeId else 0,
courseId = courseId, courseId = if (::grade.isInitialized) grade.courseId else courseId,
examTime = if (binding.examSwitch.isChecked) examDate else -1L examTime = if (binding.examSwitch.isChecked) examDate else -1L
) )
if (::grade.isInitialized) { if (::grade.isInitialized) {
@@ -37,7 +37,7 @@ class HistoricalAverageSheet : BottomSheetDialogFragment() {
binding.title.text = getString(R.string.averages_for_course, arguments?.getString("courseName")) binding.title.text = getString(R.string.averages_for_course, arguments?.getString("courseName"))
binding.recyclerView.apply { binding.recyclerView.apply {
layoutManager = GridLayoutManager(context, 1) layoutManager = GridLayoutManager(context, 1)
adapter = HistoricalAverageAdapter(data) adapter = HistoricalAverageAdapter(data, targetFragment.getGradeType())
} }
} }
@@ -0,0 +1,24 @@
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.annotation.LayoutRes
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
open class QwarkSheet(@LayoutRes private val layoutId: Int) : BottomSheetDialogFragment() {
private lateinit var _context: Context
override fun getContext(): Context = _context
override fun onAttach(context: Context) {
super.onAttach(context)
_context = context
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
LayoutInflater.from(context).inflate(layoutId, container, false)
}
@@ -10,7 +10,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
open class TextInputSheet : BottomSheetDialogFragment() { open class TextInputSheet : BottomSheetDialogFragment() {
protected var _binding: TextInputSheetBinding? = null private var _binding: TextInputSheetBinding? = null
protected val binding: TextInputSheetBinding get() = _binding!! protected val binding: TextInputSheetBinding get() = _binding!!
private lateinit var mContext: Context private lateinit var mContext: Context
@@ -0,0 +1,12 @@
package com.denizd.lawrence.util
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import androidx.viewbinding.ViewBinding
// written by Zhuinden (props to them!)
inline fun <T : ViewBinding> FragmentActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) =
lazy(LazyThreadSafetyMode.NONE) {
bindingInflater.invoke(layoutInflater)
}
@@ -0,0 +1,7 @@
package com.denizd.qwark.util
import com.denizd.qwark.database.QwarkRepository
object Dependencies {
lateinit var repo: QwarkRepository
}
@@ -0,0 +1,50 @@
package com.denizd.lawrence.util
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.observe
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
// written by Zhuinden (props to them!)
class FragmentViewBindingDelegate<T : ViewBinding>(
val fragment: Fragment,
val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T> {
private var _binding: T? = null
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
_binding = null
}
})
}
}
})
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
val binding = _binding
if (binding != null) {
return binding
}
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
}
return viewBindingFactory(thisRef.requireView()).also { _binding = it }
}
}
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)
@@ -17,5 +17,6 @@ enum class PreferenceKey(val value: String) {
PARTICIPATION_DAY("participation_day"), PARTICIPATION_DAY("participation_day"),
// Bools // Bools
SHOW_GRADE_AVERAGE("show_grade_average") SHOW_GRADE_AVERAGE("show_grade_average"),
FIRST_LAUNCH("first_launch")
} }
@@ -4,51 +4,48 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
class QwarkPreferences private constructor(context: Context) { class QwarkPreferences(context: Context) {
private val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) private val prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private val edit: SharedPreferences.Editor = prefs.edit() private val edit: SharedPreferences.Editor = prefs.edit()
companion object {
private var qwarkPrefs: QwarkPreferences? = null
fun getPrefs(context: Context): QwarkPreferences {
if (qwarkPrefs == null) {
synchronized (QwarkPreferences::class) {
qwarkPrefs = QwarkPreferences(context)
}
}
return qwarkPrefs!!
}
}
fun getString(key: PreferenceKey, default: String = ""): String = prefs.getString(key.value, default) ?: default fun getString(key: PreferenceKey, default: String = ""): String = prefs.getString(key.value, default) ?: default
fun getInt(key: PreferenceKey, default: Int = 0): Int = prefs.getInt(key.value, default) fun getInt(key: PreferenceKey, default: Int = 0): Int = prefs.getInt(key.value, default)
fun getBool(key: PreferenceKey, default: Boolean = false): Boolean = prefs.getBoolean(key.value, default) fun getBool(key: PreferenceKey, default: Boolean = false): Boolean = prefs.getBoolean(key.value, default)
fun putString(key: PreferenceKey, value: String): QwarkPreferences { fun setString(key: PreferenceKey, value: String): QwarkPreferences {
edit.putString(key.value, value).apply() edit.putString(key.value, value).apply()
return this return this
} }
fun putInt(key: PreferenceKey, value: Int): QwarkPreferences { fun setInt(key: PreferenceKey, value: Int): QwarkPreferences {
edit.putInt(key.value, value).apply() edit.putInt(key.value, value).apply()
return this return this
} }
fun putBool(key: PreferenceKey, value: Boolean): QwarkPreferences { fun setBool(key: PreferenceKey, value: Boolean): QwarkPreferences {
edit.putBoolean(key.value, value).apply() edit.putBoolean(key.value, value).apply()
return this return this
} }
// TODO remove all of these
fun getShowGradeAverage(): Boolean = getBool(PreferenceKey.SHOW_GRADE_AVERAGE) fun getShowGradeAverage(): Boolean = getBool(PreferenceKey.SHOW_GRADE_AVERAGE)
fun getGradeType(): Int = getInt(PreferenceKey.GRADE_TYPE) fun getGradeType(): Int = getInt(PreferenceKey.GRADE_TYPE)
fun getCourseSortType(): Int = getInt(PreferenceKey.COURSE_SORT_TYPE, 1) fun getCourseSortType(): Int = getInt(PreferenceKey.COURSE_SORT_TYPE, 1)
fun setCourseSortType(value: Int) { putInt(PreferenceKey.COURSE_SORT_TYPE, value) }
fun getSchoolYear(): Int = getInt(PreferenceKey.SCHOOL_YEAR_ID) fun getSchoolYear(): Int = getInt(PreferenceKey.SCHOOL_YEAR_ID)
fun getSchoolYearName(): String = getString(PreferenceKey.SCHOOL_YEAR_NAME) fun getSchoolYearName(): String = getString(PreferenceKey.SCHOOL_YEAR_NAME)
fun getScoreProfileName(): String = getString(PreferenceKey.SCORE_PROFILE_NAME) fun getScoreProfileName(): String = getString(PreferenceKey.SCORE_PROFILE_NAME)
fun getScoreProfileId(): Int = getInt(PreferenceKey.SCORE_PROFILE_ID) fun getScoreProfileId(): Int = getInt(PreferenceKey.SCORE_PROFILE_ID)
fun getParticipationDisplay(): Int = getInt(PreferenceKey.PARTICIPATION_DISPLAY) fun getParticipationDisplay(): Int = getInt(PreferenceKey.PARTICIPATION_DISPLAY)
fun getParticipationDay(): Int = getInt(PreferenceKey.PARTICIPATION_DAY) fun getParticipationDay(): Int = getInt(PreferenceKey.PARTICIPATION_DAY)
fun getAppTheme(): Int = getInt(PreferenceKey.APP_THEME, 2)
fun getFinalScoreId(): Int = getInt(PreferenceKey.SCORE_PROFILE_ID)
fun getFirstLaunch(): Boolean = getBool(PreferenceKey.FIRST_LAUNCH)
fun getCurrentSchoolYearId(): Int = getInt(PreferenceKey.SCHOOL_YEAR_ID)
fun setParticipationDisplay(value: Int) { putInt(PreferenceKey.PARTICIPATION_DISPLAY, value) } fun setCourseSortType(value: Int) { setInt(PreferenceKey.COURSE_SORT_TYPE, value) }
fun setParticipationDay(value: Int) { putInt(PreferenceKey.PARTICIPATION_DAY, value) } fun setParticipationDisplay(value: Int) { setInt(PreferenceKey.PARTICIPATION_DISPLAY, value) }
fun setParticipationDay(value: Int) { setInt(PreferenceKey.PARTICIPATION_DAY, value) }
fun setAppTheme(value: Int) { setInt(PreferenceKey.APP_THEME, value) }
fun setFirstLaunch(value: Boolean) { setBool(PreferenceKey.FIRST_LAUNCH, value) }
fun setShowGradeAverage(value: Boolean) { setBool(PreferenceKey.SHOW_GRADE_AVERAGE, value) }
fun setGradeType(value: Int) { setInt(PreferenceKey.GRADE_TYPE, value) }
} }
@@ -2,32 +2,29 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizd.qwark.database.CourseRepository
import com.denizd.qwark.model.Course import com.denizd.qwark.model.Course
import com.denizd.qwark.model.CourseExam import com.denizd.qwark.model.CourseExam
import com.denizd.qwark.model.HistoricalAvg import com.denizd.qwark.model.HistoricalAvg
import com.denizd.qwark.util.QwarkPreferences
class CourseViewModel(application: Application) : QwarkViewModel(application) { class CourseViewModel(application: Application) : QwarkViewModel(application) {
private val repo = CourseRepository(application) val allCourses: LiveData<List<CourseExam>> = repo.getCoursesByYearId(repo.prefs.getSchoolYear())
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(application)
val allCourses: LiveData<List<CourseExam>> = repo.getCoursesByYearId(getSchoolYear())
fun getShowGradeAverage(): Boolean = prefs.getShowGradeAverage() fun getShowGradeAverage(): Boolean = repo.prefs.getShowGradeAverage()
fun getGradeType(): Int = prefs.getGradeType() fun getGradeType(): Int = repo.prefs.getGradeType()
fun getCourseSortType(): Int = prefs.getCourseSortType() fun getCourseSortType(): Int = repo.prefs.getCourseSortType()
fun getSchoolYear(): Int = prefs.getSchoolYear() fun getSchoolYear(): Int = repo.prefs.getSchoolYear()
fun getSchoolYearName(): String = prefs.getSchoolYearName() fun getSchoolYearName(): String = repo.prefs.getSchoolYearName()
fun insert(avg: HistoricalAvg) = doAsync { repo.insert(avg) }
fun getAveragesForCourses(courseId: Int): List<HistoricalAvg> = returnBlocking { fun getAveragesForCourses(courseId: Int): List<HistoricalAvg> = returnBlocking {
repo.getAveragesForCourses(courseId) repo.getAveragesForCourses(courseId)
} }
fun insert(course: Course) = doAsync { repo.insert(course) } fun insert(course: Course) = doAsync { repo.insert(course) }
fun insert(historicalAvg: HistoricalAvg) = doAsync { repo.insert(historicalAvg) }
fun update(course: Course) = doAsync { repo.update(course) } fun update(course: Course) = doAsync { repo.update(course) }
fun delete(course: Course) = doAsync { repo.delete(course) } fun delete(course: Course) = doAsync { repo.delete(course) }
fun getCourse(courseId: Int): CourseExam = returnBlocking { repo.getCourse(courseId) } fun getCourse(courseId: Int): CourseExam = returnBlocking { repo.getCourse(courseId) }
} }
@@ -3,7 +3,6 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.denizd.qwark.database.FinalsRepository
import com.denizd.qwark.model.CourseExam import com.denizd.qwark.model.CourseExam
import com.denizd.qwark.model.FinalGrade import com.denizd.qwark.model.FinalGrade
import com.denizd.qwark.model.SchoolYear import com.denizd.qwark.model.SchoolYear
@@ -13,8 +12,7 @@ import java.lang.NumberFormatException
class FinalsViewModel(app: Application) : QwarkViewModel(app) { class FinalsViewModel(app: Application) : QwarkViewModel(app) {
private val repo = FinalsRepository(app) val allFinalGrades: LiveData<List<FinalGrade>> = repo.getFinalGrades(repo.prefs.getFinalScoreId())
val allFinalGrades: LiveData<List<FinalGrade>> = repo.getFinalGrades(repo.getFinalScoreId())
fun update(finalGrade: FinalGrade) = viewModelScope.launch { fun update(finalGrade: FinalGrade) = viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -24,8 +22,8 @@ class FinalsViewModel(app: Application) : QwarkViewModel(app) {
fun getSchoolYearName(courseId: Int) = returnBlocking { repo.getSchoolYearName(courseId) } fun getSchoolYearName(courseId: Int) = returnBlocking { repo.getSchoolYearName(courseId) }
fun getScoreProfileName(): String = repo.getScoreProfileName() fun getScoreProfileName(): String = repo.prefs.getScoreProfileName()
fun getGradeType(): Int = repo.getGradeType() fun getGradeType(): Int = repo.prefs.getGradeType()
fun getFinalScore(basicGrades: List<FinalGrade>, advancedGrades: List<FinalGrade>, examGrades: List<FinalGrade>): Int = try { fun getFinalScore(basicGrades: List<FinalGrade>, advancedGrades: List<FinalGrade>, examGrades: List<FinalGrade>): Int = try {
var finalScore = 0 var finalScore = 0
@@ -109,5 +107,5 @@ class FinalsViewModel(app: Application) : QwarkViewModel(app) {
fun getCourse(courseId: Int) = returnBlocking { repo.getCourse(courseId) } fun getCourse(courseId: Int) = returnBlocking { repo.getCourse(courseId) }
fun getFinalGrade(finalGradeId: Int) = returnBlocking { repo.getFinalGrade(finalGradeId) } fun getFinalGrade(finalGradeId: Int) = returnBlocking { repo.getFinalGrade(finalGradeId) }
fun getCourseSortType() = repo.getCourseSortType() fun getCourseSortType() = repo.prefs.getCourseSortType()
} }
@@ -2,14 +2,12 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizd.qwark.database.GradeRepository
import com.denizd.qwark.model.Grade import com.denizd.qwark.model.Grade
import com.denizd.qwark.model.HistoricalAvg import com.denizd.qwark.model.HistoricalAvg
class GradeViewModel(application: Application) : QwarkViewModel(application) { class GradeViewModel(application: Application) : QwarkViewModel(application) {
private val repo = GradeRepository(application) fun getGradeType(): Int = repo.prefs.getGradeType()
fun getGradeType(): Int = repo.getGradeType()
fun getCourse(courseId: Int) = returnBlocking { repo.getCourse(courseId) } fun getCourse(courseId: Int) = returnBlocking { repo.getCourse(courseId) }
@@ -22,7 +20,7 @@ class GradeViewModel(application: Application) : QwarkViewModel(application) {
fun updateAverage(average: String, gradeCount: Int, courseId: Int) = doAsync { fun updateAverage(average: String, gradeCount: Int, courseId: Int) = doAsync {
repo.updateAverage(average, gradeCount, courseId) repo.updateAverage(average, gradeCount, courseId)
} }
fun delete(gradeId: Int) = doAsync { repo.delete(gradeId) } fun deleteGrade(gradeId: Int) = doAsync { repo.deleteGrade(gradeId) }
fun insert(avg: HistoricalAvg) = doAsync { repo.insert(avg) } fun insert(avg: HistoricalAvg) = doAsync { repo.insert(avg) }
fun getGrade(gradeId: Int) = returnBlocking { repo.getGrade(gradeId) } fun getGrade(gradeId: Int) = returnBlocking { repo.getGrade(gradeId) }
@@ -2,23 +2,24 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.util.Log
import android.view.Window import android.view.Window
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.denizd.qwark.R import com.denizd.qwark.R
import com.denizd.qwark.database.QwarkRepository
import com.denizd.qwark.fragment.* import com.denizd.qwark.fragment.*
import com.denizd.qwark.fragment.CourseFragment import com.denizd.qwark.fragment.CourseFragment
import com.denizd.qwark.fragment.FinalsFragment import com.denizd.qwark.fragment.FinalsFragment
import com.denizd.qwark.fragment.NoteFragment import com.denizd.qwark.fragment.NoteFragment
import com.denizd.qwark.fragment.ParticipationFragment import com.denizd.qwark.fragment.ParticipationFragment
import com.denizd.qwark.fragment.SettingsFragment import com.denizd.qwark.fragment.SettingsFragment
import com.denizd.qwark.util.Dependencies
import com.denizd.qwark.util.QwarkUtil import com.denizd.qwark.util.QwarkUtil
class MainViewModel(val app: Application) : AndroidViewModel(app) { class MainViewModel(val app: Application) : QwarkViewModel(app) {
private val prefs = PreferenceManager.getDefaultSharedPreferences(app.applicationContext)
fun getFragmentType(type: Int): String = when (type) { // TODO replace with Fragment#tag fun getFragmentType(type: Int): String = when (type) { // TODO replace with Fragment#tag
R.id.courses -> "CourseFragment" R.id.courses -> "CourseFragment"
@@ -37,24 +38,25 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) {
} }
fun applyTheme(window: Window, context: Context) { fun applyTheme(window: Window, context: Context) {
when (prefs.getInt("dark_mode", 2)) { AppCompatDelegate.setDefaultNightMode(when (repo.prefs.getAppTheme()) {
0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) 0 -> AppCompatDelegate.MODE_NIGHT_NO
1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) 1 -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
} })
val barColour = ContextCompat.getColor(context, R.color.colorBackground) ContextCompat.getColor(context, R.color.colorBackground).also { barColour ->
window.navigationBarColor = barColour window.navigationBarColor = barColour
window.statusBarColor = barColour window.statusBarColor = barColour
} }
}
fun checkForFirstTimeYearNotice(context: Context) { fun checkForFirstTimeYearNotice(context: Context) {
if (prefs.getBoolean("first_launch", true)) { if (repo.prefs.getFirstLaunch()) {
QwarkUtil.createInfoDialog( QwarkUtil.createInfoDialog(
context, context,
app.getString(R.string.alert_create_school_year_title), app.getString(R.string.alert_create_school_year_title),
app.getString(R.string.alert_create_school_year_desc) app.getString(R.string.alert_create_school_year_desc)
) )
prefs.edit().putBoolean("first_launch", false).apply() repo.prefs.setFirstLaunch(false)
} }
} }
} }
@@ -2,18 +2,12 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.* import androidx.lifecycle.*
import com.denizd.qwark.database.NoteRepository
import com.denizd.qwark.model.Note 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) : QwarkViewModel(application) { class NoteViewModel(application: Application) : QwarkViewModel(application) {
private val repo = NoteRepository(application)
private val reloadTrigger = MutableLiveData<String>() private val reloadTrigger = MutableLiveData<String>()
val allNotes: LiveData<List<Note>>? = Transformations.switchMap(reloadTrigger) { category -> val allNotes: LiveData<List<Note>> = Transformations.switchMap(reloadTrigger) { category ->
if (category == "") { if (category == "") {
repo.allNotes repo.allNotes
} else { } else {
@@ -21,60 +15,19 @@ class NoteViewModel(application: Application) : QwarkViewModel(application) {
} }
} }
init { init { refresh("") }
refresh("")
}
fun refresh(category: String) { fun refresh(category: String) { reloadTrigger.value = category }
reloadTrigger.value = category
}
fun insert(note: Note) { fun insert(note: Note) = doAsync { repo.insert(note) }
viewModelScope.launch {
withContext(Dispatchers.IO) {
repo.insert(note)
}
}
}
fun updateDismissedStatus(newDismissedStatus: Boolean, noteId: Int) { fun updateDismissedStatus(newDismissedStatus: Boolean, noteId: Int) = doAsync { repo.updateDismissed(newDismissedStatus, noteId) }
viewModelScope.launch {
withContext(Dispatchers.IO) {
repo.updateDismissed(newDismissedStatus, noteId)
}
}
}
fun delete(noteId: Int) { fun delete(noteId: Int) = doAsync { repo.deleteNote(noteId) }
viewModelScope.launch {
withContext(Dispatchers.IO) {
repo.delete(noteId)
}
}
}
// // TODO get this as LiveData variable to get changes fun update(note: Note) = doAsync { repo.update(note) }
// fun getNotesForCategory(category: String): LiveData<List<Note>>? {
// return runBlocking {
// withContext(Dispatchers.IO) {
// repo.notesForCategory(category)
// }
// }
// }
fun update(note: Note) { fun getAllCategories(): List<String> = returnBlocking { repo.getAllCategories() }
viewModelScope.launch {
withContext(Dispatchers.IO) {
repo.update(note)
}
}
}
fun getAllCategories(): List<String> = runBlocking {
withContext(Dispatchers.IO) {
repo.getAllCategories()
}
}
fun getNote(noteId: Int) = returnBlocking { repo.getNote(noteId) } fun getNote(noteId: Int) = returnBlocking { repo.getNote(noteId) }
} }
@@ -2,13 +2,10 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizd.qwark.database.ParticipationRepository
import com.denizd.qwark.model.Participation import com.denizd.qwark.model.Participation
class ParticipationCourseViewModel(app: Application) : QwarkViewModel(app) { class ParticipationCourseViewModel(app: Application) : QwarkViewModel(app) {
private val repo = ParticipationRepository(app)
fun getAllParticipationsForCourse(courseId: Int): LiveData<List<Participation>> = returnBlocking { fun getAllParticipationsForCourse(courseId: Int): LiveData<List<Participation>> = returnBlocking {
repo.getAllParticipationsForCourse(courseId) repo.getAllParticipationsForCourse(courseId)
} }
@@ -22,6 +19,6 @@ class ParticipationCourseViewModel(app: Application) : QwarkViewModel(app) {
fun createParticipation(timesHandRaised: Int, timesSpoken: Int, time: Long, courseId: Int) = doAsync { fun createParticipation(timesHandRaised: Int, timesSpoken: Int, time: Long, courseId: Int) = doAsync {
repo.insert(timesHandRaised, timesSpoken, time, courseId) repo.insert(timesHandRaised, timesSpoken, time, courseId)
} }
fun getSchoolYearName() = repo.getSchoolYearName() fun getSchoolYearName() = repo.prefs.getSchoolYearName()
fun delete(participation: Participation) = doAsync { repo.delete(participation) } fun delete(participation: Participation) = doAsync { repo.delete(participation) }
} }
@@ -2,15 +2,12 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizd.qwark.database.ParticipationRepository
import com.denizd.qwark.model.Course import com.denizd.qwark.model.Course
import com.denizd.qwark.model.CourseExam import com.denizd.qwark.model.CourseExam
import com.denizd.qwark.model.Participation import com.denizd.qwark.model.Participation
import com.denizd.qwark.model.SchoolDay
class ParticipationViewModel(app: Application) : QwarkViewModel(app) { class ParticipationViewModel(app: Application) : QwarkViewModel(app) {
private val repo = ParticipationRepository(app)
val allCoursesForToday: LiveData<List<Course>> = repo.allCoursesForToday val allCoursesForToday: LiveData<List<Course>> = repo.allCoursesForToday
val dayId: Int = repo.dayId val dayId: Int = repo.dayId
@@ -18,5 +15,5 @@ class ParticipationViewModel(app: Application) : QwarkViewModel(app) {
fun link(courseId: Int) = doAsync { repo.link(courseId) } fun link(courseId: Int) = doAsync { repo.link(courseId) }
fun removeLink(relationId: Int) = doAsync { repo.removeLink(relationId) } fun removeLink(relationId: Int) = doAsync { repo.removeLink(relationId) }
fun delete(participation: Participation) = doAsync { repo.delete(participation) } fun delete(participation: Participation) = doAsync { repo.delete(participation) }
fun getSchoolYearName() = repo.getSchoolYearName() fun getSchoolYearName() = repo.prefs.getSchoolYearName()
} }
@@ -3,24 +3,23 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.denizd.qwark.database.QwarkRepository
import com.denizd.qwark.util.Dependencies
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
open class QwarkViewModel(app: Application) : AndroidViewModel(app) { open class QwarkViewModel(app: Application) : AndroidViewModel(app) {
protected val repo: QwarkRepository = Dependencies.repo
fun doAsync(function: () -> Unit) { fun doAsync(function: () -> Unit) {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
withContext(Dispatchers.IO) {
function() function()
} }
} }
}
fun <T> returnBlocking(function: () -> T): T = runBlocking { fun <T> returnBlocking(function: () -> T): T = runBlocking(Dispatchers.IO) {
withContext(Dispatchers.IO) {
function() function()
} }
}
} }
@@ -3,38 +3,34 @@ package com.denizd.qwark.viewmodel
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizd.qwark.database.QwarkClient 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.ScoreProfile
import com.denizd.qwark.model.SchoolYear import com.denizd.qwark.model.SchoolYear
class SettingsViewModel(private val app: Application) : QwarkViewModel(app) { class SettingsViewModel(private val app: Application) : QwarkViewModel(app) {
private val repo = SettingsRepository(app)
val allYears: LiveData<List<SchoolYear>> = repo.allYears val allYears: LiveData<List<SchoolYear>> = repo.allYears
val allScoreProfiles: LiveData<List<ScoreProfile>> = repo.allScoreProfiles val allScoreProfiles: LiveData<List<ScoreProfile>> = repo.allScoreProfiles
private val prefs: QwarkPreferences = QwarkPreferences.getPrefs(app.applicationContext)
// Preference getters // Preference getters
fun getAppTheme() = repo.getAppTheme() fun getAppTheme() = repo.prefs.getAppTheme()
fun getShowGradeAverage() = repo.getShowGradeAverage() fun getShowGradeAverage() = repo.prefs.getShowGradeAverage()
fun getCourseSortType(): Int = prefs.getCourseSortType() fun getCourseSortType(): Int = repo.prefs.getCourseSortType()
fun getGradeType() = repo.getGradeType() fun getGradeType() = repo.prefs.getGradeType()
fun getScoreProfileName() = repo.getScoreProfileName() fun getScoreProfileName() = repo.prefs.getScoreProfileName()
fun getCurrentSchoolYear(): String = repo.getCurrentSchoolYear() fun getSchoolYearName(): String = repo.prefs.getSchoolYearName()
fun getCurrentSchoolYearId(): Int = repo.getCurrentSchoolYearId() fun getCurrentSchoolYearId(): Int = repo.prefs.getCurrentSchoolYearId()
fun getParticipationDisplay(): Int = repo.getParticipationDisplay() fun getParticipationDisplay(): Int = repo.prefs.getParticipationDisplay()
fun getParticipationDay(): Int = repo.getParticipationDay() fun getParticipationDay(): Int = repo.prefs.getParticipationDay()
// Preference setters // Preference setters
fun setAppTheme(value: Int) { repo.setAppTheme(value) } fun setAppTheme(value: Int) { repo.prefs.setAppTheme(value) }
fun setShowGradeAverage(value: Boolean) { repo.setShowGradeAverage(value) } fun setShowGradeAverage(value: Boolean) { repo.prefs.setShowGradeAverage(value) }
fun setCourseSortType(value: Int) { prefs.setCourseSortType(value) } fun setCourseSortType(value: Int) { repo.prefs.setCourseSortType(value) }
fun setGradeType(value: Int) { repo.setGradeType(value) } fun setGradeType(value: Int) { repo.prefs.setGradeType(value) }
fun setCurrentScoreProfile(scoreProfile: ScoreProfile) { repo.setCurrentScoreProfile(scoreProfile) } fun setCurrentScoreProfile(scoreProfile: ScoreProfile) { repo.setCurrentScoreProfile(scoreProfile) }
fun updateSchoolYear(schoolYear: SchoolYear) { repo.setCurrentSchoolYear(schoolYear) } fun updateSchoolYear(schoolYear: SchoolYear) { repo.setCurrentSchoolYear(schoolYear) }
fun setParticipationDisplay(value: Int) { repo.setParticipationDisplay(value) } fun setParticipationDisplay(value: Int) { repo.prefs.setParticipationDisplay(value) }
fun setParticipationDay(value: Int) { repo.setParticipationDay(value) } fun setParticipationDay(value: Int) { repo.prefs.setParticipationDay(value) }
// Database functions // Database functions
fun insert(schoolYear: SchoolYear) = doAsync { repo.insert(schoolYear) } fun insert(schoolYear: SchoolYear) = doAsync { repo.insert(schoolYear) }
@@ -32,6 +32,7 @@
</LinearLayout> </LinearLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="vertical" android:scrollbars="vertical"
@@ -16,7 +16,7 @@
android:id="@+id/average" android:id="@+id/average"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="32dp" android:layout_marginTop="8dp"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:textColor="@color/colorAccentedControl" android:textColor="@color/colorAccentedControl"
@@ -27,7 +27,7 @@
android:id="@+id/weighting" android:id="@+id/weighting"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="8dp"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:textColor="@color/colorText" android:textColor="@color/colorText"
@@ -59,6 +59,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_marginBottom="16dp"
android:paddingTop="12dp" android:paddingTop="12dp"
android:paddingBottom="12dp" android:paddingBottom="12dp"
app:cornerRadius="12dp" app:cornerRadius="12dp"
+46 -30
View File
@@ -7,46 +7,20 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:animateLayoutChanges="true" android:animateLayoutChanges="true"
android:fitsSystemWindows="false"
android:background="@color/colorBackground" android:background="@color/colorBackground"
tools:context=".activity.MainActivity"> tools:context=".activity.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/root_view" android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="56dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<View
android:id="@+id/status_bar_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/colorAppBarBackground"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/app_bar_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout" android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="84dp" android:layout_height="84dp"
app:elevation="0dp" app:elevation="0dp"
android:background="@color/colorAppBarBackground" android:background="@color/colorBackground"
android:layout_gravity="top" android:layout_gravity="top"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@@ -57,7 +31,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:contentInsetStart="0dp" app:contentInsetStart="0dp"
android:elevation="0dp"> android:elevation="0dp"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -102,7 +77,11 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
</androidx.constraintlayout.widget.ConstraintLayout> <androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
@@ -139,11 +118,48 @@
android:layout_height="1dp" android:layout_height="1dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:background="@color/colorHintLight" android:background="@color/strokeColor"
android:clickable="false" android:clickable="false"
android:focusable="false" android:focusable="false"
app:layout_anchor="@+id/bottom_nav" app:layout_anchor="@+id/bottom_nav"
android:layout_gravity="top" android:layout_gravity="top"
android:fitsSystemWindows="false"/> android:fitsSystemWindows="false"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="false">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/splash_screen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground">
<ImageView
android:id="@+id/image_view"
android:layout_width="120dp"
android:layout_height="184dp"
android:scaleType="centerInside"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/bolt"/>
<TextView
android:id="@+id/greeting_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:fontFamily="@font/manrope_semibold"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/image_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
+2 -2
View File
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:layout_marginVertical="3dp" android:layout_marginVertical="3dp"
android:focusable="true" android:focusable="true"
android:clickable="true" android:clickable="true">
android:backgroundTint="@color/colorCardBackground">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -55,14 +55,14 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:id="@+id/finish_button" android:id="@+id/save_button"
app:strokeWidth="2dp" app:strokeWidth="2dp"
app:strokeColor="@color/colorAccent" app:strokeColor="@color/colorAccent"
android:textColor="@color/colorAccent" android:textColor="@color/colorAccent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:text="@string/finish" android:text="@string/save"
app:rippleColor="@color/colorAccent"/> app:rippleColor="@color/colorAccent"/>
</LinearLayout> </LinearLayout>
+3 -1
View File
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -7,6 +7,7 @@
android:clipToPadding="false"> android:clipToPadding="false">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="vertical"> android:scrollbars="vertical">
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
+5 -3
View File
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -65,9 +67,9 @@
style="@android:style/Widget.ProgressBar.Horizontal" style="@android:style/Widget.ProgressBar.Horizontal"
android:id="@+id/progress_bar" android:id="@+id/progress_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="2dp" android:layout_height="3dp"
android:progressTint="@color/colorAccent" android:progressTint="@color/colorAccent"
android:progressBackgroundTint="@color/colorCardBackground"/> android:progressBackgroundTint="@color/colorBackground"/>
</LinearLayout> </LinearLayout>
+3 -1
View File
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
+3 -1
View File
@@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/colorBackground"> android:background="@color/colorBackground">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
@@ -9,6 +10,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layoutAnimation="@anim/layout_animation_fall_down" android:layoutAnimation="@anim/layout_animation_fall_down"
android:scrollbars="vertical"/> android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</LinearLayout> </LinearLayout>
+8 -16
View File
@@ -20,15 +20,13 @@
android:layoutAnimation="@anim/layout_animation_fall_down"> android:layoutAnimation="@anim/layout_animation_fall_down">
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/QwarkCard"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/cardCornerRadius"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_marginBottom="3dp">
app:cardElevation="2dp"
app:cardBackgroundColor="@color/colorCardBackground">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -79,15 +77,13 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/QwarkCard"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/cardCornerRadius"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_marginBottom="3dp">
app:cardElevation="2dp"
app:cardBackgroundColor="@color/colorCardBackground">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -290,15 +286,13 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/QwarkCard"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/cardCornerRadius"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_marginBottom="3dp">
app:cardElevation="2dp"
app:cardBackgroundColor="@color/colorCardBackground">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -376,15 +370,13 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/QwarkCard"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/cardCornerRadius"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:layout_marginBottom="3dp" android:layout_marginBottom="3dp">
app:cardElevation="2dp"
app:cardBackgroundColor="@color/colorCardBackground">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
+1 -5
View File
@@ -29,11 +29,7 @@
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:hint="@string/school_year_cap" android:hint="@string/school_year_cap"
android:textColorHint="@color/colorHint" android:textColorHint="@color/colorHint"
android:importantForAutofill="no" android:importantForAutofill="no">
app:boxCornerRadiusTopEnd="12dp"
app:boxCornerRadiusBottomEnd="12dp"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusBottomStart="12dp">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_text" android:id="@+id/edit_text"
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@color/colorBackground">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorText"
android:textSize="22sp"
android:fontFamily="@font/manrope_semibold"
android:layout_marginTop="16dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"/>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorText"
android:fontFeatureSettings="tnum"
android:layout_marginBottom="16dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"/>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
+4 -3
View File
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/QwarkCard"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:layout_marginVertical="3dp" android:layout_marginVertical="3dp">
android:backgroundTint="@color/colorCardBackground">
<LinearLayout <LinearLayout
android:id="@+id/backgroundLayout" android:id="@+id/backgroundLayout"
+3
View File
@@ -11,6 +11,7 @@
<string name="edit">Bearbeiten</string> <string name="edit">Bearbeiten</string>
<string name="finish">Beenden</string> <string name="finish">Beenden</string>
<string name="create">Erstellen</string> <string name="create">Erstellen</string>
<string name="save">Speichern</string>
<string name="general">Allgemein</string> <string name="general">Allgemein</string>
<string name="alert_create_school_year_title">Erstelle ein Schuljahr</string> <string name="alert_create_school_year_title">Erstelle ein Schuljahr</string>
<string name="alert_create_school_year_desc">Bevor du Kurse und Abschlussnoten hinzufügst, gehe in die Einstellungen und erstelle ein Schuljahr und ein Abschlussprofil!</string> <string name="alert_create_school_year_desc">Bevor du Kurse und Abschlussnoten hinzufügst, gehe in die Einstellungen und erstelle ein Schuljahr und ein Abschlussprofil!</string>
@@ -138,6 +139,8 @@
<string name="clear_final_grade_error">Note kann nicht gelöscht werden; ist bereits leer</string> <string name="clear_final_grade_error">Note kann nicht gelöscht werden; ist bereits leer</string>
<string name="pick_a_year">Wähle ein Schuljahr</string> <string name="pick_a_year">Wähle ein Schuljahr</string>
<string name="pick_a_course">Wähle einen Kurs</string> <string name="pick_a_course">Wähle einen Kurs</string>
<string name="conversion_table">Umrechnungstabelle</string>
<string name="points_to_grade_conversion_title">Punkte zu Note-Umrechnungstabelle</string>
<!-- SettingsFragment.kt --> <!-- SettingsFragment.kt -->
<string name="settings">Einstellungen</string> <string name="settings">Einstellungen</string>
+1 -1
View File
@@ -2,7 +2,6 @@
<resources> <resources>
<color name="colorAccent">#6abf69</color> <color name="colorAccent">#6abf69</color>
<color name="colorBackground">#121212</color> <color name="colorBackground">#121212</color>
<color name="colorAppBarBackground">#E6121212</color>
<color name="colorCardBackground">#222222</color> <color name="colorCardBackground">#222222</color>
<color name="colorTextHighlight">@color/colorAccent</color> <color name="colorTextHighlight">@color/colorAccent</color>
<color name="colorText">#e0e0e0</color> <color name="colorText">#e0e0e0</color>
@@ -14,4 +13,5 @@
<color name="colorTextWhite">#e0e0e0</color> <color name="colorTextWhite">#e0e0e0</color>
<color name="colorAccentedControl">@color/colorText</color> <color name="colorAccentedControl">@color/colorText</color>
<color name="colorSplashLogo">@color/colorText</color> <color name="colorSplashLogo">@color/colorText</color>
<color name="strokeColor">#424242</color>
</resources> </resources>
+3 -3
View File
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<color name="colorAccent">#388e3c</color> <color name="colorAccent">#388e3c</color>
<color name="colorBackground">#e0e0e0</color> <color name="colorBackground">#ffffff</color>
<color name="colorAppBarBackground">#E6e0e0e0</color> <color name="colorCardBackground">#e0e0e0</color>
<color name="colorCardBackground">#f5f5f5</color>
<color name="colorTextHighlight">@color/colorAccent</color> <color name="colorTextHighlight">@color/colorAccent</color>
<color name="colorText">#212121</color> <color name="colorText">#212121</color>
<color name="colorHint">#757575</color> <color name="colorHint">#757575</color>
@@ -14,6 +13,7 @@
<color name="colorTextWhite">#e0e0e0</color> <color name="colorTextWhite">#e0e0e0</color>
<color name="colorAccentedControl">@color/colorAccent</color> <color name="colorAccentedControl">@color/colorAccent</color>
<color name="colorSplashLogo">@color/colorAccent</color> <color name="colorSplashLogo">@color/colorAccent</color>
<color name="strokeColor">#bdbdbd</color>
<color name="mtrl_textinput_default_box_stroke_color" tools:override="true">#757575</color> <color name="mtrl_textinput_default_box_stroke_color" tools:override="true">#757575</color>
</resources> </resources>
+10 -1
View File
@@ -11,6 +11,7 @@
<string name="edit">Edit</string> <string name="edit">Edit</string>
<string name="finish">Finish</string> <string name="finish">Finish</string>
<string name="create">Create</string> <string name="create">Create</string>
<string name="save">Save</string>
<string name="general">General</string> <string name="general">General</string>
<string name="alert_create_school_year_title">Create a school year</string> <string name="alert_create_school_year_title">Create a school year</string>
<string name="alert_create_school_year_desc">Before adding courses and final grades, please do make sure to go into the settings and create a school year and a score profile!</string> <string name="alert_create_school_year_desc">Before adding courses and final grades, please do make sure to go into the settings and create a school year and a score profile!</string>
@@ -18,6 +19,12 @@
<string name="powered_by_qwark" translatable="false">Powered by Qwark</string> <string name="powered_by_qwark" translatable="false">Powered by Qwark</string>
<string name="made_by_deniz" translatable="false">Made by Deniz</string> <string name="made_by_deniz" translatable="false">Made by Deniz</string>
<string-array name="greetings">
<item>Hello %s!</item>
<item>Hi %s!</item>
<item>Hope you\'re doing well, %s.</item>
</string-array>
<!-- CourseFragment.kt --> <!-- CourseFragment.kt -->
<string name="course">Course</string> <string name="course">Course</string>
<string name="courses">Courses</string> <string name="courses">Courses</string>
@@ -196,7 +203,9 @@
<string name="clear_final_grade_error">Grade cannot be cleared; is already empty</string> <string name="clear_final_grade_error">Grade cannot be cleared; is already empty</string>
<string name="pick_a_year">Pick a school year</string> <string name="pick_a_year">Pick a school year</string>
<string name="pick_a_course">Pick a course</string> <string name="pick_a_course">Pick a course</string>
<string name="points_to_grade_conversion" translatable="false">900 823 > 1.0\n822 805 > 1.1\n804 787 > 1.2\n786 769 > 1.3\n768 751 > 1.4\n750 733 > 1.5\n732 715 > 1.6\n714 697 > 1.7\n696 679 > 1.8\n678 661 > 1.9\n660 643 > 2.0\n\n642 625 > 2.1\n624 607 > 2.2\n606 589 > 2.3\n588 571 > 2.4\n570 553 > 2.5\n552 535 > 2.6\n534 517 > 2.7\n516 499 > 2.8\n498 481 > 2.9\n480 463 > 3.0\n\n462 445 > 3.1\n444 427 > 3.2\n426 409 > 3.3\n408 391 > 3.4\n390 373 > 3.5\n372 355 > 3.6\n354 337 > 3.7\n336 319 > 3.8\n318 301 > 3.9\n300 > 4.0</string> <string name="conversion_table">Conversion table</string>
<string name="points_to_grade_conversion_title">Points to grade conversion table</string>
<string name="points_to_grade_conversion" translatable="false">900 823 → 1.0\n822 805 → 1.1\n804 787 → 1.2\n786 769 → 1.3\n768 751 → 1.4\n750 733 → 1.5\n732 715 → 1.6\n714 697 → 1.7\n696 679 → 1.8\n678 661 → 1.9\n660 643 → 2.0\n\n642 625 → 2.1\n624 607 → 2.2\n606 589 → 2.3\n588 571 → 2.4\n570 553 → 2.5\n552 535 → 2.6\n534 517 → 2.7\n516 499 → 2.8\n498 481 → 2.9\n480 463 → 3.0\n\n462 445 → 3.1\n444 427 → 3.2\n426 409 → 3.3\n408 391 → 3.4\n390 373 → 3.5\n372 355 → 3.6\n354 337 → 3.7\n336 319 → 3.8\n318 301 → 3.9\n300 → 4.0</string>
<string name="approximate_placeholder" translatable="false">≈ %s</string> <string name="approximate_placeholder" translatable="false">≈ %s</string>
<!-- SettingsFragment.kt --> <!-- SettingsFragment.kt -->
+10 -3
View File
@@ -3,7 +3,7 @@
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">@color/colorAccent</item> <item name="colorPrimary">@color/colorAccent</item>
<item name="colorPrimaryDark">@android:color/transparent</item> <item name="colorPrimaryDark">@color/colorBackground</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="android:fontFamily">@font/manrope_medium</item> <item name="android:fontFamily">@font/manrope_medium</item>
<item name="android:colorEdgeEffect">@color/colorAccent</item> <item name="android:colorEdgeEffect">@color/colorAccent</item>
@@ -13,7 +13,6 @@
<item name="shapeAppearanceSmallComponent">@style/ShapeSmallComponent</item> <item name="shapeAppearanceSmallComponent">@style/ShapeSmallComponent</item>
<item name="shapeAppearanceMediumComponent">@style/ShapeMediumComponent</item> <item name="shapeAppearanceMediumComponent">@style/ShapeMediumComponent</item>
<item name="shapeAppearanceLargeComponent">@style/ShapeLargeComponent</item> <item name="shapeAppearanceLargeComponent">@style/ShapeLargeComponent</item>
<item name="cardBackgroundColor">@color/colorCardBackground</item>
<item name="cornerRadius">@dimen/cardCornerRadius</item> <item name="cornerRadius">@dimen/cardCornerRadius</item>
<item name="cardElevation">3dp</item> <item name="cardElevation">3dp</item>
<item name="android:textColor">@color/colorText</item> <item name="android:textColor">@color/colorText</item>
@@ -23,11 +22,19 @@
<item name="boxCornerRadiusBottomEnd">@dimen/cardCornerRadius</item> <item name="boxCornerRadiusBottomEnd">@dimen/cardCornerRadius</item>
<item name="boxStrokeColor">@color/colorAccent</item> <item name="boxStrokeColor">@color/colorAccent</item>
<item name="boxStrokeWidth">1dp</item> <item name="boxStrokeWidth">1dp</item>
<!-- <item name="android:statusBarColor">@color/colorAppBarBackground</item>--> <!-- <item name="android:statusBarColor">@color/colorBackground</item>-->
<!-- <item name="android:windowTranslucentStatus">true</item>--> <!-- <item name="android:windowTranslucentStatus">true</item>-->
<!-- <item name="android:windowDrawsSystemBarBackgrounds">true</item>--> <!-- <item name="android:windowDrawsSystemBarBackgrounds">true</item>-->
</style> </style>
<style name="QwarkCard" parent="Widget.MaterialComponents.CardView">
<item name="cardBackgroundColor">@color/colorBackground</item>
<item name="strokeColor">@color/strokeColor</item>
<item name="strokeWidth">1dp</item>
<item name="cardCornerRadius">12dp</item>
<item name="cardElevation">0dp</item>
</style>
<!-- Launch / splash screen --> <!-- Launch / splash screen -->
<style name="AppTheme.Launcher"> <style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/launch_screen</item> <item name="android:windowBackground">@drawable/launch_screen</item>
+1 -1
View File
@@ -8,7 +8,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.0-alpha09' classpath 'com.android.tools.build:gradle:4.0.0-beta01'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
+2 -2
View File
@@ -1,6 +1,6 @@
#Mon Feb 03 16:46:00 CET 2020 #Mon Mar 02 18:07:39 CET 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip