[Grades] Implement new Grades module (UI & API changes).

This commit is contained in:
Kuba Szczodrzyński 2020-03-06 21:09:05 +01:00
parent 4e8fdd2225
commit f17a02be54
69 changed files with 3009 additions and 2266 deletions

View File

@ -190,6 +190,8 @@ dependencies {
implementation 'com.github.jetradarmobile:android-snowfall:1.2.0'
implementation "io.coil-kt:coil:0.9.2"
implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
}
repositories {
mavenCentral()

View File

@ -45,6 +45,7 @@ import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
import pl.szczodrzynski.edziennik.utils.*
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
import java.util.concurrent.TimeUnit
@ -65,6 +66,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
val userActionManager by lazy { UserActionManager(this) }
val gradesManager by lazy { GradesManager(this) }
val db
get() = App.db

View File

@ -1080,3 +1080,15 @@ fun Throwable.toErrorCode() = when (this) {
private fun ApiResponse.Error.toErrorCode() = when (this.code) {
else -> ERROR_API_EXCEPTION
}
inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
if (a != null && b != null) {
return code(a, b)
}
return null
}
@kotlin.jvm.JvmName("averageOrNullOfInt")
fun Iterable<Int>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
@kotlin.jvm.JvmName("averageOrNullOfFloat")
fun Iterable<Float>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }

View File

@ -6,17 +6,11 @@ package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
class ConfigGrades(private val config: Config) {
companion object {
const val ORDER_BY_DATE_DESC = 0
const val ORDER_BY_SUBJECT_ASC = 1
const val ORDER_BY_DATE_ASC = 2
const val ORDER_BY_SUBJECT_DESC = 3
}
private var mOrderBy: Int? = null
var orderBy: Int
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: ORDER_BY_DATE_DESC }
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: GradesManager.ORDER_BY_DATE_DESC }
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
}
}

View File

@ -5,9 +5,10 @@
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getFloat
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class ProfileConfigGrades(private val config: ProfileConfig) {
private var mColorMode: Int? = null
@ -24,4 +25,18 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
var countZeroToAvg: Boolean
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
private var mPlusValue: Float? = null
var plusValue: Float?
get() { mPlusValue = mPlusValue ?: config.values.getFloat("plusValue"); return mPlusValue }
set(value) { config.set("plusValue", value); mPlusValue = value }
private var mMinusValue: Float? = null
var minusValue: Float?
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
set(value) { config.set("minusValue", value); mMinusValue = value }
private var mDontCountGrades: List<String>? = null
var dontCountGrades: List<String>
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
set(value) { config.set("dontCountGrades", value); mDontCountGrades = value }
}

View File

@ -94,10 +94,14 @@ fun HashMap<String, String?>.getLongList(key: String, default: List<Long>?): Lis
return this[key]?.let { gson.fromJson<List<Long>>(it, object: TypeToken<List<Long>>(){}.type) } ?: default
}
fun HashMap<String, String?>.getFloat(key: String): Float? {
return this[key]?.toFloatOrNull()
}
fun List<ConfigEntry>.toHashMap(profileId: Int, map: HashMap<String, String?>) {
map.clear()
forEach {
if (it.profileId == profileId)
map[it.key] = it.value
}
}
}

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.config.ConfigGrades.Companion.ORDER_BY_DATE_DESC
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
class ConfigMigration(app: App, config: Config) {
init { config.apply {

View File

@ -6,8 +6,8 @@ package pl.szczodrzynski.edziennik.config.utils
import pl.szczodrzynski.edziennik.config.ProfileConfig
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class ProfileConfigMigration(config: ProfileConfig) {
init { config.apply {
@ -21,4 +21,4 @@ class ProfileConfigMigration(config: ProfileConfig) {
dataVersion = 1
}
}}
}
}

View File

@ -14,7 +14,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZI
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
@ -90,7 +95,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
val columnName = info.child(4).text().trim()
val comment = info.ownText()
val description = columnName + if (comment.isNotBlank()) " - $comment" else ""
val description = columnName + if (comment.isNotBlank()) " - $comment" else null
val teacherName = info.child(1).text()
val teacher = data.getTeacherByLastFirst(teacherName)
@ -109,20 +114,20 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
} ?: -1
val gradeObject = Grade(
profileId,
id,
fullName,
color,
description,
name,
value,
if (gradeCountToAverage) weight else 0f,
semester,
teacher.id,
subject.id
).apply {
type = gradeType
}
profileId = profileId,
id = id,
name = name,
type = gradeType,
value = value,
weight = if (gradeCountToAverage) weight else 0f,
color = color,
category = fullName,
description = description,
comment = null,
semester = semester,
teacherId = teacher.id,
subjectId = subject.id
)
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
@ -139,23 +144,23 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
if (proposed != null && proposed.isNotBlank()) {
val proposedGradeObject = Grade(
profileId,
(-1 * subject.id) - 1,
"",
-1,
"",
proposed,
proposed.toFloatOrNull() ?: 0f,
0f,
semester,
-1,
subject.id
).apply {
type = when (semester) {
1 -> TYPE_SEMESTER1_PROPOSED
else -> TYPE_SEMESTER2_PROPOSED
}
}
profileId = profileId,
id = (-1 * subject.id) - 1,
name = proposed,
type = when (semester) {
1 -> TYPE_SEMESTER1_PROPOSED
else -> TYPE_SEMESTER2_PROPOSED
},
value = proposed.toFloatOrNull() ?: 0f,
weight = 0f,
color = -1,
category = null,
description = null,
comment = null,
semester = semester,
teacherId = -1,
subjectId = subject.id
)
data.gradeList.add(proposedGradeObject)
data.metadataList.add(Metadata(
@ -172,23 +177,23 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
if (final != null && final.isNotBlank()) {
val finalGradeObject = Grade(
profileId,
(-1 * subject.id) - 2,
"",
-1,
"",
final,
final.toFloatOrNull() ?: 0f,
0f,
semester,
-1,
subject.id
).apply {
type = when (semester) {
1 -> TYPE_SEMESTER1_FINAL
else -> TYPE_SEMESTER2_FINAL
}
}
profileId = profileId,
id = (-1 * subject.id) - 2,
name = final,
type = when (semester) {
1 -> TYPE_SEMESTER1_FINAL
else -> TYPE_SEMESTER2_FINAL
},
value = final.toFloatOrNull() ?: 0f,
weight = 0f,
color = -1,
category = null,
description = null,
comment = null,
semester = semester,
teacherId = -1,
subjectId = subject.id
)
data.gradeList.add(finalGradeObject)
data.metadataList.add(Metadata(

View File

@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date
@ -63,17 +64,19 @@ class IdziennikWebGrades(override val data: DataIdziennik,
}
val gradeObject = Grade(
profileId,
id,
category,
colorInt,
"",
name,
value,
weight,
semester,
teacher.id,
subject.id)
profileId = profileId,
id = id,
name = name,
type = TYPE_NORMAL,
value = value,
weight = weight,
color = colorInt,
category = category,
description = null,
comment = null,
semester = semester,
teacherId = teacher.id,
subjectId = subject.id)
when (grade.getInt("Typ")) {
0 -> {
@ -98,17 +101,19 @@ class IdziennikWebGrades(override val data: DataIdziennik,
}
val historyObject = Grade(
profileId,
gradeObject.id * -1,
historyItem.get("Kategoria").asString,
colorInt,
historyItem.get("Uzasadnienie").asString,
historyItem.get("Ocena").asString,
value,
if (value > 0f && countToTheAverage) weight * -1f else 0f,
historyItem.get("Semestr").asInt,
teacher.id,
subject.id)
profileId = profileId,
id = gradeObject.id * -1,
name = historyItem.getString("Ocena") ?: "",
type = TYPE_NORMAL,
value = value,
weight = if (value > 0f && countToTheAverage) weight * -1f else 0f,
color = colorInt,
category = historyItem.getString("Kategoria"),
description = historyItem.getString("Uzasadnienie"),
comment = null,
semester = historyItem.getInt("Semestr") ?: 1,
teacherId = teacher.id,
subjectId = subject.id)
historyObject.parentId = gradeObject.id
val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()

View File

@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonArray
@ -54,20 +54,20 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
if (semester1Proposed != "") {
val gradeObject = Grade(
profileId,
semester1Id,
"",
-1,
"",
semester1Value.toString(),
semester1Value.toFloat(),
0f,
1,
-1,
subjectObject.id
).apply {
type = TYPE_SEMESTER1_PROPOSED
}
profileId = profileId,
id = semester1Id,
name = semester1Value.toString(),
type = TYPE_SEMESTER1_PROPOSED,
value = semester1Value.toFloat(),
weight = 0f,
color = -1,
category = null,
description = null,
comment = null,
semester = 1,
teacherId = -1,
subjectId = subjectObject.id
)
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
@ -82,20 +82,20 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
if (semester2Proposed != "") {
val gradeObject = Grade(
profileId,
semester2Id,
"",
-1,
"",
semester2Value.toString(),
semester2Value.toFloat(),
0f,
2,
-1,
subjectObject.id
).apply {
type = TYPE_YEAR_PROPOSED
}
profileId = profileId,
id = semester2Id,
name = semester2Value.toString(),
type = TYPE_YEAR_PROPOSED,
value = semester2Value.toFloat(),
weight = 0f,
color = -1,
category = null,
description = null,
comment = null,
semester = 2,
teacherId = -1,
subjectId = subjectObject.id
)
val addedDate = if (data.profile.empty)
data.profile.dateSemester1Start.inMillis

View File

@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -31,18 +32,20 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
if (data.startPointsSemester1 > 0) {
val semester1StartGradeObject = Grade(
profileId,
-101,
data.app.getString(R.string.grade_start_points),
0xffbdbdbd.toInt(),
data.app.getString(R.string.grade_start_points_format, 1),
nameFormat.format(data.startPointsSemester1),
data.startPointsSemester1.toFloat(),
-1f,
1,
-1,
1
).apply { type = Grade.TYPE_POINT_SUM }
profileId = profileId,
id = -101,
name = nameFormat.format(data.startPointsSemester1),
type = TYPE_POINT_SUM,
value = data.startPointsSemester1.toFloat(),
weight = 0f,
color = 0xffbdbdbd.toInt(),
category = data.app.getString(R.string.grade_start_points),
description = data.app.getString(R.string.grade_start_points_format, 1),
comment = null,
semester = 1,
teacherId = -1,
subjectId = 1
)
data.gradeList.add(semester1StartGradeObject)
data.metadataList.add(Metadata(
@ -57,18 +60,20 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
if (data.startPointsSemester2 > 0) {
val semester2StartGradeObject = Grade(
profileId,
-102,
data.app.getString(R.string.grade_start_points),
0xffbdbdbd.toInt(),
data.app.getString(R.string.grade_start_points_format, 2),
nameFormat.format(data.startPointsSemester2),
data.startPointsSemester2.toFloat(),
-1f,
2,
-1,
1
).apply { type = Grade.TYPE_POINT_SUM }
profileId = profileId,
id = -102,
name = nameFormat.format(data.startPointsSemester2),
type = TYPE_POINT_SUM,
value = data.startPointsSemester2.toFloat(),
weight = -1f,
color = 0xffbdbdbd.toInt(),
category = data.app.getString(R.string.grade_start_points),
description = data.app.getString(R.string.grade_start_points_format, 2),
comment = null,
semester = 2,
teacherId = -1,
subjectId = 1
)
data.gradeList.add(semester2StartGradeObject)
data.metadataList.add(Metadata(
@ -123,19 +128,20 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
val valueTo = category?.valueTo ?: 0f
val gradeObject = Grade(
profileId,
id,
categoryName,
color,
description,
name,
valueFrom,
-1f,
semester,
teacherId,
1
profileId = profileId,
id = id,
name = name,
type = TYPE_POINT_SUM,
value = valueFrom,
weight = -1f,
color = color,
category = categoryName,
description = description,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = 1
).apply {
type = Grade.TYPE_POINT_SUM
valueMax = valueTo
}

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_DESCRIPTIVE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -53,20 +53,20 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
val gradeObject = Grade(
profileId,
id,
category?.text ?: "",
category?.color ?: -1,
description,
" ",
0f,
0f,
semester,
teacherId,
subjectId
).apply {
this.type = type
}
profileId = profileId,
id = id,
name = " ",
type = type,
value = 0f,
weight = 0f,
color = category?.color ?: -1,
category = category?.text,
description = description,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = subjectId
)
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(

View File

@ -6,7 +6,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -54,32 +60,28 @@ class LibrusApiGrades(override val data: DataLibrus,
} ?: ""
val gradeObject = Grade(
profileId,
id,
category?.text ?: "",
category?.color ?: -1,
description,
name,
value,
weight,
semester,
teacherId,
subjectId
profileId = profileId,
id = id,
name = name,
type = when {
grade.getBoolean("IsConstituent") ?: false -> TYPE_NORMAL
grade.getBoolean("IsSemester") ?: false -> if (semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
grade.getBoolean("IsSemesterProposition") ?: false -> if (semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
grade.getBoolean("IsFinal") ?: false -> TYPE_YEAR_FINAL
grade.getBoolean("IsFinalProposition") ?: false -> TYPE_YEAR_PROPOSED
else -> TYPE_NORMAL
},
value = value,
weight = weight,
color = category?.color ?: -1,
category = category?.text ?: "",
description = description,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = subjectId
)
when {
grade.getBoolean("IsConstituent") ?: false ->
gradeObject.type = TYPE_NORMAL
grade.getBoolean("IsSemester") ?: false -> // semester final
gradeObject.type = if (gradeObject.semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
grade.getBoolean("IsSemesterProposition") ?: false -> // semester proposed
gradeObject.type = if (gradeObject.semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
grade.getBoolean("IsFinal") ?: false -> // year final
gradeObject.type = TYPE_YEAR_FINAL
grade.getBoolean("IsFinalProposition") ?: false -> // year final
gradeObject.type = TYPE_YEAR_PROPOSED
}
grade.getJsonObject("Improvement")?.also {
val historicalId = it.getLong("Id")
data.gradeList.firstOrNull { grade -> grade.id == historicalId }?.also { grade ->

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_POINT_AVG
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_AVG
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -44,19 +44,20 @@ class LibrusApiPointGrades(override val data: DataLibrus,
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
val gradeObject = Grade(
profileId,
id,
category?.text ?: "",
category?.color ?: -1,
"",
name,
value,
category?.weight ?: 0f,
semester,
teacherId,
subjectId
profileId = profileId,
id = id,
name = name,
type = TYPE_POINT_AVG,
value = value,
weight = category?.weight ?: 0f,
color = category?.color ?: -1,
category = category?.text ?: "",
description = null,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = subjectId
).apply {
type = TYPE_POINT_AVG
valueMax = category?.valueTo ?: 0f
}

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_DESCRIPTIVE
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -48,20 +48,20 @@ class LibrusApiTextGrades(override val data: DataLibrus,
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
val gradeObject = Grade(
profileId,
id,
category?.text ?: "",
category?.color ?: -1,
description,
name,
0f,
0f,
semester,
teacherId,
subjectId
).apply {
type = TYPE_DESCRIPTIVE
}
profileId = profileId,
id = id,
name = name,
type = TYPE_DESCRIPTIVE,
value = 0f,
weight = 0f,
color = category?.color ?: -1,
category = category?.text ?: "",
description = description,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = subjectId
)
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(

View File

@ -7,7 +7,13 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
class MobidziennikApiGrades(val data: DataMobidziennik, rows: List<String>) {
@ -61,18 +67,19 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List<String>) {
}
val gradeObject = Grade(
data.profileId,
id,
category,
color,
description,
name,
value,
weight,
semester,
teacherId,
subjectId)
gradeObject.type = type
profileId = data.profileId,
id = id,
name = name,
type = type,
value = value,
weight = weight,
color = color,
category = category,
description = description,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = subjectId)
if (data.profile?.empty == true) {
addedDate = data.profile.dateSemester1Start.inMillis

View File

@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.fixWhiteSpaces
@ -112,17 +113,19 @@ class MobidziennikWebGrades(override val data: DataMobidziennik,
}
val gradeObject = Grade(
profileId,
gradeId,
gradeCategory,
gradeColor,
"NLDŚR, $gradeDescription",
gradeName,
gradeValue,
0f,
gradeSemester,
teacherId,
subjectId
profileId = profileId,
id = gradeId,
name = gradeName,
type = TYPE_NORMAL,
value = gradeValue,
weight = 0f,
color = gradeColor,
category = gradeCategory,
description = "NLDŚR, $gradeDescription",
comment = null,
semester = gradeSemester,
teacherId = teacherId,
subjectId = subjectId
)
gradeObject.classAverage = gradeClassAverage

View File

@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import java.text.DecimalFormat
@ -88,17 +89,19 @@ class VulcanApiGrades(override val data: DataVulcan,
}.toInt()
val gradeObject = Grade(
profileId,
id,
category,
color,
finalDescription,
name,
value ?: 0.0f,
weight,
data.studentSemesterNumber,
teacherId,
subjectId
profileId = profileId,
id = id,
name = name,
type = TYPE_NORMAL,
value = value ?: 0.0f,
weight = weight,
color = color,
category = category,
description = finalDescription,
comment = null,
semester = data.studentSemesterNumber,
teacherId = teacherId,
subjectId = subjectId
)
data.gradeList.add(gradeObject)

View File

@ -8,6 +8,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_GRADES_SUMMARY
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject
@ -53,23 +57,25 @@ class VulcanApiProposedGrades(override val data: DataVulcan,
val color = Utils.getVulcanGradeColor(name)
val gradeObject = Grade(
profileId,
id,
"",
color,
"",
name,
value,
0f,
data.studentSemesterNumber,
-1,
subjectId
profileId = profileId,
id = id,
name = name,
type = if (data.studentSemesterNumber == 1) {
if (isFinal) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER1_PROPOSED
} else {
if (isFinal) TYPE_SEMESTER2_FINAL else TYPE_SEMESTER2_PROPOSED
},
value = value,
weight = 0f,
color = color,
category = "",
description = null,
comment = null,
semester = data.studentSemesterNumber,
teacherId = -1,
subjectId = subjectId
)
if (data.studentSemesterNumber == 1) {
gradeObject.type = if (isFinal) Grade.TYPE_SEMESTER1_FINAL else Grade.TYPE_SEMESTER1_PROPOSED
} else {
gradeObject.type = if (isFinal) Grade.TYPE_SEMESTER2_FINAL else Grade.TYPE_SEMESTER2_PROPOSED
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,

View File

@ -42,7 +42,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
ConfigEntry::class,
LibrusLesson::class,
Metadata::class
], version = 77)
], version = 78)
@TypeConverters(
ConverterTime::class,
ConverterDate::class,
@ -160,7 +160,8 @@ abstract class AppDb : RoomDatabase() {
Migration74(),
Migration75(),
Migration76(),
Migration77()
Migration77(),
Migration78()
).allowMainThreadQueries().build()
}
}

View File

@ -63,8 +63,8 @@ public abstract class MetadataDao {
@Transaction
public void setSeen(int profileId, Object o, boolean seen) {
if (o instanceof Grade) {
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).id, seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_GRADE, ((Grade) o).id, seen);
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_GRADE, ((Grade) o).getId(), seen);
}
}
if (o instanceof Attendance) {
@ -102,8 +102,8 @@ public abstract class MetadataDao {
@Transaction
public void setNotified(int profileId, Object o, boolean notified) {
if (o instanceof Grade) {
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).id, false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_GRADE, ((Grade) o).id, notified);
if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_GRADE, ((Grade) o).getId(), notified);
}
}
if (o instanceof Attendance) {

View File

@ -1,104 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.entity;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.Index;
@Entity(tableName = "grades",
primaryKeys = {"profileId", "gradeId"},
indices = {@Index(value = {"profileId"})})
public class Grade {
public int profileId;
@ColumnInfo(name = "gradeId")
public long id;
@ColumnInfo(name = "gradeCategory")
public String category;
@ColumnInfo(name = "gradeColor")
public int color;
@ColumnInfo(name = "gradeDescription")
public String description;
@ColumnInfo(name = "gradeComment")
public String comment;
@ColumnInfo(name = "gradeName")
public String name;
@ColumnInfo(name = "gradeValue")
public float value;
@ColumnInfo(name = "gradeValueMax")
public float valueMax;
@ColumnInfo(name = "gradeWeight")
public float weight;
@ColumnInfo(name = "gradeSemester")
public int semester;
@ColumnInfo(name = "gradeClassAverage")
public float classAverage = -1;
public static final int TYPE_NORMAL = 0;
public static final int TYPE_SEMESTER1_PROPOSED = 1;
public static final int TYPE_SEMESTER1_FINAL = 2;
public static final int TYPE_SEMESTER2_PROPOSED = 3;
public static final int TYPE_SEMESTER2_FINAL = 4;
public static final int TYPE_YEAR_PROPOSED = 5;
public static final int TYPE_YEAR_FINAL = 6;
public static final int TYPE_POINT_AVG = 10;
public static final int TYPE_POINT_SUM = 20;
public static final int TYPE_DESCRIPTIVE = 30;
public static final int TYPE_DESCRIPTIVE_TEXT = 31;
public static final int TYPE_TEXT = 40;
@ColumnInfo(name = "gradeType")
public int type = TYPE_NORMAL;
@ColumnInfo(name = "gradePointGrade")
public boolean pointGrade = false;
/**
* Applies for historical grades. It's the new/replacement grade's ID.
*/
@ColumnInfo(name = "gradeParentId")
public long parentId = -1;
/**
* Applies for current grades. If the grade was worse and this is the improved one.
*/
@ColumnInfo(name = "gradeIsImprovement")
public boolean isImprovement = false;
public long teacherId;
public long subjectId;
@Ignore
public Grade() {}
public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
this.profileId = profileId;
this.id = id;
this.category = category;
this.color = color;
this.description = description;
this.name = name;
this.value = value;
this.weight = weight;
this.semester = semester;
this.teacherId = teacherId;
this.subjectId = subjectId;
}
/*@Ignore
public Grade(int profileId, long id, String description, String name, float value, float weight, int semester, long teacherId, long categoryId, long subjectId) {
this.profileId = profileId;
this.id = id;
this.description = description;
this.name = name;
this.value = value;
this.weight = weight;
this.semester = semester;
this.teacherId = teacherId;
//this.categoryId = categoryId;
this.subjectId = subjectId;
}*/
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
/*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
this.profileId = profileId;
this.id = id;
this.category = category;
this.color = color;
this.description = description;
this.name = name;
this.value = value;
this.weight = weight;
this.semester = semester;
this.teacherId = teacherId;
this.subjectId = subjectId;
}*/
@Entity(tableName = "grades",
primaryKeys = ["profileId", "gradeId"],
indices = [Index(value = ["profileId"])])
open class Grade(
val profileId: Int,
@ColumnInfo(name = "gradeId")
val id: Long,
@ColumnInfo(name = "gradeName")
var name: String,
@ColumnInfo(name = "gradeType")
var type: Int,
@ColumnInfo(name = "gradeValue")
var value: Float,
@ColumnInfo(name = "gradeWeight")
var weight: Float,
@ColumnInfo(name = "gradeColor")
var color: Int,
@ColumnInfo(name = "gradeCategory")
var category: String?,
@ColumnInfo(name = "gradeDescription")
var description: String?,
@ColumnInfo(name = "gradeComment")
var comment: String?,
@ColumnInfo(name = "gradeSemester")
val semester: Int,
val teacherId: Long,
val subjectId: Long
) {
companion object {
const val TYPE_NORMAL = 0
const val TYPE_SEMESTER1_PROPOSED = 1
const val TYPE_SEMESTER1_FINAL = 2
const val TYPE_SEMESTER2_PROPOSED = 3
const val TYPE_SEMESTER2_FINAL = 4
const val TYPE_YEAR_PROPOSED = 5
const val TYPE_YEAR_FINAL = 6
const val TYPE_POINT_AVG = 10
const val TYPE_POINT_SUM = 20
const val TYPE_DESCRIPTIVE = 30
const val TYPE_DESCRIPTIVE_TEXT = 31
const val TYPE_TEXT = 40
}
@ColumnInfo(name = "gradeValueMax")
var valueMax: Float? = null
@ColumnInfo(name = "gradeClassAverage")
var classAverage: Float? = null
/**
* Applies for historical grades. It's the new/replacement grade's ID.
*/
@ColumnInfo(name = "gradeParentId")
var parentId: Long? = null
/**
* Applies for current grades. If the grade was worse and this is the improved one.
*/
@ColumnInfo(name = "gradeIsImprovement")
var isImprovement = false
}

View File

@ -53,15 +53,8 @@ open class Profile(
const val REGISTRATION_UNSPECIFIED = 0
const val REGISTRATION_DISABLED = 1
const val REGISTRATION_ENABLED = 2
const val COLOR_MODE_DEFAULT = 0
const val COLOR_MODE_WEIGHTED = 1
const val AGENDA_DEFAULT = 0
const val AGENDA_CALENDAR = 1
const val YEAR_1_AVG_2_AVG = 0
const val YEAR_1_SEM_2_AVG = 1
const val YEAR_1_AVG_2_SEM = 2
const val YEAR_1_SEM_2_SEM = 3
const val YEAR_ALL_GRADES = 4
}
override var image: String? = null

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.full;
import pl.szczodrzynski.edziennik.data.db.entity.Grade;
public class GradeFull extends Grade {
//public String category = "";
//public int color;
public String subjectLongName = "";
public String subjectShortName = "";
public String teacherFullName = "";
// metadata
public boolean seen;
public boolean notified;
public long addedDate;
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-6
*/
package pl.szczodrzynski.edziennik.data.db.full
import pl.szczodrzynski.edziennik.data.db.entity.Grade
class GradeFull(
profileId: Int, id: Long, name: String, type: Int,
value: Float, weight: Float, color: Int,
category: String?, description: String?, comment: String?,
semester: Int, teacherId: Long, subjectId: Long
) : Grade(
profileId, id, name, type,
value, weight, color,
category, description, comment,
semester, teacherId, subjectId
) {
var subjectLongName: String? = null
var subjectShortName: String? = null
var teacherFullName: String? = null
// metadata
var seen = false
var notified = false
var addedDate: Long = 0
}

View File

@ -0,0 +1,44 @@
package pl.szczodrzynski.edziennik.data.db.migration
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration78 : Migration(77, 78) {
override fun migrate(database: SupportSQLiteDatabase) {
// grades migration to kotlin
database.execSQL("ALTER TABLE grades RENAME TO _grades;")
database.execSQL("""CREATE TABLE grades (
profileId INTEGER NOT NULL,
gradeId INTEGER NOT NULL,
gradeName TEXT NOT NULL,
gradeType INTEGER NOT NULL,
gradeValue REAL NOT NULL,
gradeWeight REAL NOT NULL,
gradeColor INTEGER NOT NULL,
gradeCategory TEXT,
gradeDescription TEXT,
gradeComment TEXT,
gradeSemester INTEGER NOT NULL,
teacherId INTEGER NOT NULL,
subjectId INTEGER NOT NULL,
gradeValueMax REAL DEFAULT NULL,
gradeClassAverage REAL DEFAULT NULL,
gradeParentId INTEGER DEFAULT NULL,
gradeIsImprovement INTEGER NOT NULL,
PRIMARY KEY(profileId, gradeId)
);""")
database.execSQL("DROP INDEX IF EXISTS index_grades_profileId;")
database.execSQL("CREATE INDEX index_grades_profileId ON grades (profileId);")
database.execSQL("""INSERT INTO grades (profileId, gradeId, gradeName, gradeType, gradeValue, gradeWeight, gradeColor, gradeCategory, gradeDescription, gradeComment, gradeSemester, teacherId, subjectId, gradeValueMax, gradeClassAverage, gradeParentId, gradeIsImprovement)
SELECT profileId, gradeId, gradeName, gradeType, gradeValue, gradeWeight, gradeColor,
CASE gradeCategory WHEN '' THEN NULL WHEN ' ' THEN NULL ELSE gradeCategory END,
CASE gradeDescription WHEN '' THEN NULL WHEN ' ' THEN NULL ELSE gradeDescription END,
CASE gradeComment WHEN '' THEN NULL WHEN ' ' THEN NULL ELSE gradeComment END,
gradeSemester, teacherId, subjectId,
CASE gradeValueMax WHEN 0 THEN NULL ELSE gradeValueMax END,
CASE gradeClassAverage WHEN -1 THEN NULL ELSE gradeClassAverage END,
CASE gradeParentId WHEN -1 THEN NULL ELSE gradeParentId END,
gradeIsImprovement FROM _grades;""")
database.execSQL("DROP TABLE _grades;")
}
}

View File

@ -1,129 +0,0 @@
package pl.szczodrzynski.edziennik.ui.dialogs.grade;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
import android.view.View;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog;
import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.entity.Grade;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding;
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListAdapter;
import pl.szczodrzynski.edziennik.utils.Colors;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.COLOR_MODE_DEFAULT;
public class GradeDetailsDialog {
private App app;
private Context context;
private int profileId;
public GradeDetailsDialog(Context context) {
this.context = context;
this.profileId = App.Companion.getProfileId();
}
public GradeDetailsDialog(Context context, int profileId) {
this.context = context;
this.profileId = profileId;
}
public MaterialDialog dialog;
private DialogGradeDetailsBinding b;
private DialogInterface.OnDismissListener dismissListener;
public boolean callDismissListener = true;
public GradeDetailsDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
return this;
}
public void performDismiss(DialogInterface dialogInterface) {
if (callDismissListener && dismissListener != null) {
dismissListener.onDismiss(dialogInterface);
}
callDismissListener = true;
}
public void show(App _app, GradeFull grade)
{
this.app = _app;
dialog = new MaterialDialog.Builder(context)
.customView(R.layout.dialog_grade_details, true)
.positiveText(R.string.close)
.autoDismiss(false)
.onPositive((dialog, which) -> dialog.dismiss())
.dismissListener(this::performDismiss)
.show();
View root = dialog.getCustomView();
assert root != null;
b = DialogGradeDetailsBinding.bind(root);
b.setGrade(grade);
int gradeColor;
if (App.Companion.getConfig().getFor(profileId).getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {
gradeColor = Colors.gradeToColor(grade);
}
DecimalFormat format = new DecimalFormat("#.##");
if (grade.weight < 0) {
grade.weight *= -1;
}
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_DESCRIPTIVE_TEXT || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_POINT_SUM) {
b.setWeightText(null);
grade.weight = 0;
}
else {
if (grade.type == Grade.TYPE_POINT_AVG) {
b.setWeightText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
}
else if (grade.weight == 0) {
b.setWeightText(app.getString(R.string.grades_weight_not_counted));
}
else {
b.setWeightText(app.getString(R.string.grades_weight_format, format.format(grade.weight)));
}
}
b.setCommentVisible(false);
b.setDevMode(App.Companion.getDevMode());
b.gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.3 ? 0xff000000 : 0xffffffff);
b.gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
AsyncTask.execute(() -> {
List<GradeFull> historyList = app.db.gradeDao().getAllWithParentIdNow(profileId, grade.id);
if (historyList.size() == 0) {
b.setHistoryVisible(false);
return;
}
b.setHistoryVisible(true);
b.gradeHistoryNest.post(() -> {
b.gradeHistoryNest.setNestedScrollingEnabled(false);
b.gradeHistoryList.setHasFixedSize(false);
b.gradeHistoryList.setNestedScrollingEnabled(false);
b.gradeHistoryList.setLayoutManager(new LinearLayoutManager(context));
b.gradeHistoryList.setAdapter(new GradesListAdapter(context, historyList));
});
});
}
}

View File

@ -0,0 +1,83 @@
package pl.szczodrzynski.edziennik.ui.dialogs.grade
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class GradeDetailsDialog(
val activity: AppCompatActivity,
val grade: GradeFull,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "GradeDetailsDialog"
}
private lateinit var app: App
private lateinit var b: DialogGradeDetailsBinding
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogGradeDetailsBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
val manager = app.gradesManager
val gradeColor = manager.getColor(grade)
b.grade = grade
b.weightText = manager.getWeightString(app, grade)
b.commentVisible = false
b.devMode = App.debugMode
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) -0x1000000 else -0x1)
b.gradeName.background.setTintColor(gradeColor)
b.gradeValue = if (grade.weight == 0f || grade.value < 0f) -1f else manager.getGradeValue(grade)
launch {
val historyList = withContext(Dispatchers.Default) {
app.db.gradeDao().getAllWithParentIdNow(App.profileId, grade.id)
}
if (historyList.isEmpty()) {
b.historyVisible = false
return@launch
}
b.historyVisible = true
//b.gradeHistoryNest.isNestedScrollingEnabled = false
b.gradeHistoryList.adapter = GradesAdapter(activity, {
GradeDetailsDialog(activity, it)
}).also { it.items = historyList.toMutableList() }
b.gradeHistoryList.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
}
}
}}
}

View File

@ -6,14 +6,20 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.config.ConfigGrades
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import it.sephiroth.android.library.numberpicker.doOnStopTrackingTouch
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.DialogConfigGradesBinding
import pl.szczodrzynski.edziennik.setOnSelectedListener
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_DEFAULT
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_SUBJECT_ASC
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class GradesConfigDialog(
val activity: AppCompatActivity,
@ -42,6 +48,7 @@ class GradesConfigDialog(
.setView(b.root)
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
saveConfig()
onDismissListener?.invoke(TAG)
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
}
@ -52,42 +59,71 @@ class GradesConfigDialog(
}}
private fun loadConfig() {
b.customPlusCheckBox.isChecked = profileConfig.plusValue != null
b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked
b.customMinusCheckBox.isChecked = profileConfig.minusValue != null
b.customMinusValue.isVisible = b.customMinusCheckBox.isChecked
b.customPlusValue.progress = profileConfig.plusValue ?: 0.5f
b.customMinusValue.progress = profileConfig.minusValue ?: 0.25f
when (config.orderBy) {
ConfigGrades.ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio
ConfigGrades.ORDER_BY_SUBJECT_ASC -> b.sortGradesBySubjectRadio
ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio
ORDER_BY_SUBJECT_ASC -> b.sortGradesBySubjectRadio
else -> null
}?.isChecked = true
when (profileConfig.colorMode) {
Profile.COLOR_MODE_DEFAULT -> b.gradeColorFromERegister
Profile.COLOR_MODE_WEIGHTED -> b.gradeColorByValue
COLOR_MODE_DEFAULT -> b.gradeColorFromERegister
COLOR_MODE_WEIGHTED -> b.gradeColorByValue
else -> null
}?.isChecked = true
when (profileConfig.yearAverageMode) {
Profile.YEAR_ALL_GRADES -> b.gradeAverageMode4
Profile.YEAR_1_AVG_2_AVG -> b.gradeAverageMode0
Profile.YEAR_1_SEM_2_AVG -> b.gradeAverageMode1
Profile.YEAR_1_AVG_2_SEM -> b.gradeAverageMode2
Profile.YEAR_1_SEM_2_SEM -> b.gradeAverageMode3
YEAR_ALL_GRADES -> b.gradeAverageMode4
YEAR_1_AVG_2_AVG -> b.gradeAverageMode0
YEAR_1_SEM_2_AVG -> b.gradeAverageMode1
YEAR_1_AVG_2_SEM -> b.gradeAverageMode2
YEAR_1_SEM_2_SEM -> b.gradeAverageMode3
else -> null
}?.isChecked = true
b.dontCountZeroToAverage.isChecked = !profileConfig.countZeroToAvg
}
private fun saveConfig() {
profileConfig.plusValue = if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
profileConfig.minusValue = if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
}
private fun initView() {
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ConfigGrades.ORDER_BY_DATE_DESC }
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ConfigGrades.ORDER_BY_SUBJECT_ASC }
b.customPlusCheckBox.onChange { _, isChecked ->
b.customPlusValue.isVisible = isChecked
}
b.customMinusCheckBox.onChange { _, isChecked ->
b.customMinusValue.isVisible = isChecked
}
b.gradeColorFromERegister.setOnSelectedListener { profileConfig.colorMode = Profile.COLOR_MODE_DEFAULT }
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = Profile.COLOR_MODE_WEIGHTED }
// who the hell named those methods
// THIS SHIT DOES NOT EVEN WORK
b.customPlusValue.doOnStopTrackingTouch {
profileConfig.plusValue = it.progress
}
b.customMinusValue.doOnStopTrackingTouch {
profileConfig.minusValue = it.progress
}
b.gradeAverageMode4.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_ALL_GRADES }
b.gradeAverageMode0.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_AVG_2_AVG }
b.gradeAverageMode1.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_SEM_2_AVG }
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_AVG_2_SEM }
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = Profile.YEAR_1_SEM_2_SEM }
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC }
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC }
b.gradeColorFromERegister.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_DEFAULT }
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_WEIGHTED }
b.gradeAverageMode4.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_ALL_GRADES }
b.gradeAverageMode0.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_AVG }
b.gradeAverageMode1.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_AVG }
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM }
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM }
b.dontCountZeroToAverage.setOnCheckedChangeListener { _, isChecked -> profileConfig.countZeroToAvg = !isChecked }
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Typeface
import android.text.TextUtils
import android.util.AttributeSet
import android.util.TypedValue.COMPLEX_UNIT_SP
import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.graphics.ColorUtils
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.resolveAttr
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
class GradeView : AppCompatTextView {
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr)
constructor(context: Context, grade: Grade, manager: GradesManager, periodGradesTextual: Boolean = false) : this(context, null) {
setGrade(grade, manager, false, periodGradesTextual)
}
@SuppressLint("RestrictedApi")
fun setGrade(grade: Grade?, manager: GradesManager, bigView: Boolean = false, periodGradesTextual: Boolean = false) {
if (grade == null) {
visibility = View.GONE
return
}
visibility = View.VISIBLE
val gradeName = grade.name
val gradeColor = manager.getColor(grade)
text = if (periodGradesTextual)
when (grade.type) {
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> context.getString(
R.string.grade_semester_proposed_format,
gradeName
)
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> context.getString(
R.string.grade_semester_final_format,
gradeName
)
TYPE_YEAR_PROPOSED -> context.getString(
R.string.grade_year_proposed_format,
gradeName
)
TYPE_YEAR_FINAL -> context.getString(
R.string.grade_year_final_format,
gradeName
)
else -> gradeName
}
else
gradeName
setTextColor(when (grade.type) {
TYPE_SEMESTER1_PROPOSED,
TYPE_SEMESTER2_PROPOSED,
TYPE_YEAR_PROPOSED -> android.R.attr.textColorPrimary.resolveAttr(context)
else -> if (ColorUtils.calculateLuminance(gradeColor) > 0.3)
0x99000000.toInt()
else
0x99ffffff.toInt()
})
typeface = Typeface.create("serif-monospace", Typeface.BOLD)
setBackgroundResource(when (grade.type) {
TYPE_SEMESTER1_PROPOSED,
TYPE_SEMESTER2_PROPOSED,
TYPE_YEAR_PROPOSED -> if (bigView) R.drawable.bg_rounded_8dp_outline else R.drawable.bg_rounded_4dp_outline
else -> if (bigView) R.drawable.bg_rounded_8dp else R.drawable.bg_rounded_4dp
})
background.setTintColor(gradeColor)
gravity = Gravity.CENTER
if (bigView) {
setTextSize(COMPLEX_UNIT_SP, 24f)
setAutoSizeTextTypeUniformWithConfiguration(
14,
32,
1,
COMPLEX_UNIT_SP
)
setPadding(2.dp, 2.dp, 2.dp, 2.dp)
}
else {
setTextSize(COMPLEX_UNIT_SP, 16f)
setPadding(5.dp, 0, 5.dp, 0)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, 0, 5.dp, 0)
}
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
measure(WRAP_CONTENT, WRAP_CONTENT)
}
}
}

View File

@ -0,0 +1,187 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades
import android.animation.ObjectAnimator
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.ui.modules.grades.models.*
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.*
class GradesAdapter(
val activity: AppCompatActivity,
var onGradeClick: ((item: GradeFull) -> Unit)? = null,
var onGradesEditorClick: ((subjectId: Long, semesterNumber: Int) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val TAG = "GradesAdapter"
private const val ITEM_TYPE_SUBJECT = 0
private const val ITEM_TYPE_SEMESTER = 1
private const val ITEM_TYPE_EMPTY = 2
private const val ITEM_TYPE_GRADE = 3
private const val ITEM_TYPE_STATS = 4
const val STATE_CLOSED = 0
const val STATE_OPENED = 1
}
var items = mutableListOf<Any>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
ITEM_TYPE_SUBJECT -> SubjectViewHolder(inflater, parent)
ITEM_TYPE_SEMESTER -> SemesterViewHolder(inflater, parent)
ITEM_TYPE_EMPTY -> EmptyViewHolder(inflater, parent)
ITEM_TYPE_GRADE -> GradeViewHolder(inflater, parent)
ITEM_TYPE_STATS -> StatsViewHolder(inflater, parent)
else -> throw IllegalArgumentException("Incorrect viewType")
}
}
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is GradesSubject -> ITEM_TYPE_SUBJECT
is GradesSemester -> ITEM_TYPE_SEMESTER
is GradesEmpty -> ITEM_TYPE_EMPTY
is Grade -> ITEM_TYPE_GRADE
is GradesStats -> ITEM_TYPE_STATS
else -> throw IllegalArgumentException("Incorrect viewType")
}
}
private val onClickListener = View.OnClickListener { view ->
val model = view.getTag(R.string.tag_key_model)
if (model is GradeFull) {
onGradeClick?.invoke(model)
return@OnClickListener
}
if (model !is ExpandableItemModel<*>)
return@OnClickListener
val position = items.indexOf(model)
if (position == -1)
return@OnClickListener
//val position = it.getTag(R.string.tag_key_position) as? Int ?: return@OnClickListener
if (model is GradesSubject || model is GradesSemester) {
view.findViewById<View>(R.id.dropdownIcon)?.let { dropdownIcon ->
ObjectAnimator.ofFloat(
dropdownIcon,
View.ROTATION,
if (model.state == STATE_CLOSED) 0f else 180f,
if (model.state == STATE_CLOSED) 180f else 0f
).setDuration(200).start();
}
}
if (model is GradesSubject) {
val preview = view.findViewById<View>(R.id.previewContainer)
val summary = view.findViewById<View>(R.id.yearSummary)
preview?.visibility = if (model.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
summary?.visibility = if (model.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
}
if (model.state == STATE_CLOSED) {
val subItems = if (model is GradesSemester && model.grades.isEmpty())
listOf(GradesEmpty())
else
model.items
model.state = STATE_OPENED
items.addAll(position + 1, subItems.filterNotNull())
notifyItemRangeInserted(position + 1, subItems.size)
/*notifyItemRangeChanged(
position + subItems.size,
items.size - (position + subItems.size)
)*/
//notifyItemRangeChanged(position, items.size - position)
if (model is GradesSubject) {
// auto expand first semester
if (model.semesters.isNotEmpty()) {
val semester = model.semesters.firstOrNull { it.grades.isNotEmpty() } ?: model.semesters.first()
val semesterIndex = model.semesters.indexOf(semester)
val grades = if (semester.grades.isEmpty())
listOf(GradesEmpty())
else
semester.grades
semester.state = STATE_OPENED
items.addAll(position + 2 + semesterIndex, grades)
notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
}
}
}
else {
val start = position + 1
var end: Int = items.size
for (i in start until items.size) {
val model1 = items[i]
val level = if (model1 is GradesStats) 0 else (model1 as? ExpandableItemModel<*>)?.level ?: 3
if (level <= model.level) {
end = i
break
} else {
if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) {
model1.state = STATE_CLOSED
}
}
}
if (end != -1) {
items.subList(start, end).clear()
notifyItemRangeRemoved(start, end - start)
//notifyItemRangeChanged(start, end - start)
//notifyItemRangeChanged(position, items.size - position)
}
model.state = STATE_CLOSED
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
if (holder !is BindableViewHolder<*>)
return
val app = activity.applicationContext as App
val viewType = when (holder) {
is SubjectViewHolder -> ITEM_TYPE_SUBJECT
is SemesterViewHolder -> ITEM_TYPE_SEMESTER
is EmptyViewHolder -> ITEM_TYPE_EMPTY
is GradeViewHolder -> ITEM_TYPE_GRADE
is StatsViewHolder -> ITEM_TYPE_STATS
else -> throw IllegalArgumentException("Incorrect viewType")
}
holder.itemView.setTag(R.string.tag_key_view_type, viewType)
holder.itemView.setTag(R.string.tag_key_position, position)
holder.itemView.setTag(R.string.tag_key_model, item)
when {
holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position)
holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position)
holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position)
holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position)
holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position)
}
if (holder is SemesterViewHolder && item is GradesSemester) {
holder.b.editButton.onClick {
onGradesEditorClick?.invoke(item.subjectId, item.number)
}
}
holder.itemView.setOnClickListener(onClickListener)
}
override fun getItemCount() = items.size
}

View File

@ -1,513 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.grades;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.config.ProfileConfigGrades;
import pl.szczodrzynski.edziennik.data.db.entity.Grade;
import pl.szczodrzynski.edziennik.data.db.entity.Subject;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.databinding.FragmentGradesBinding;
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_DATE_ASC;
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_DATE_DESC;
import static pl.szczodrzynski.edziennik.config.ConfigGrades.ORDER_BY_SUBJECT_ASC;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_ALL_GRADES;
public class GradesFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private FragmentGradesBinding b = null;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
activity = (MainActivity) getActivity();
if (getActivity() == null || getContext() == null)
return null;
app = (App) activity.getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
// activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_grades, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
b.refreshLayout.setNestedScrollingEnabled(true);
return b.getRoot();
}
ListView listView;
List<ItemGradesSubjectModel> subjectList;
private String getRegisterCardAverageModeSubText() {
switch (App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()) {
default:
case YEAR_1_AVG_2_AVG:
return getString(R.string.settings_register_avg_mode_0_short);
case YEAR_1_SEM_2_AVG:
return getString(R.string.settings_register_avg_mode_1_short);
case YEAR_1_AVG_2_SEM:
return getString(R.string.settings_register_avg_mode_2_short);
case YEAR_1_SEM_2_SEM:
return getString(R.string.settings_register_avg_mode_3_short);
case YEAR_ALL_GRADES:
return getString(R.string.settings_register_avg_mode_4_short);
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || activity == null || b == null || !isAdded())
return;
/*activity.getBottomSheet().setToggleGroupEnabled(true);
activity.getBottomSheet().toggleGroupRemoveItems();
activity.getBottomSheet().setToggleGroupSelectionMode(NavBottomSheet.TOGGLE_GROUP_SORTING_ORDER);
activity.getBottomSheet().toggleGroupAddItem(0, getString(R.string.sort_by_date), (Drawable)null, SORT_MODE_DESCENDING);
activity.getBottomSheet().toggleGroupAddItem(1, getString(R.string.sort_by_subject), (Drawable)null, SORT_MODE_ASCENDING);
activity.getBottomSheet().setToggleGroupSortingOrderListener((id, sortMode) -> {
sortModeChanged = true;
if (id == 0 && sortMode == SORT_MODE_ASCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_DATE_ASC;
}
else if (id == 1 && sortMode == SORT_MODE_ASCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_SUBJECT_ASC;
}
else if (id == 0 && sortMode == SORT_MODE_DESCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_DATE_DESC;
}
else if (id == 1 && sortMode == SORT_MODE_DESCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_SUBJECT_DESC;
}
return null;
});
activity.getBottomSheet().setToggleGroupTitle("Sortowanie");
activity.getBottomSheet().toggleGroupCheck(0);
activity.getBottomSheet().setOnCloseListener(() -> {
if (sortModeChanged) {
sortModeChanged = false;
activity.reloadTarget();
}
return null;
});*/
activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_averages)
.withDescription(R.string.menu_grades_averages_desc)
.withIcon(CommunityMaterial.Icon.cmd_chart_line)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
showAverages();
}),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_config)
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
new GradesConfigDialog(activity, true, null, null);
}),
new BottomSheetSeparatorItem(true),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_GRADE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
})
);
activity.gainAttention();
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_GRADES, b.refreshLayout);
});*/
listView = b.gradesRecyclerView;
//listView.setHasFixedSize(true);
//listView.setLayoutManager(new LinearLayoutManager(getContext()));
long expandSubjectId = -1;
if (getArguments() != null) {
expandSubjectId = getArguments().getLong("gradesSubjectId", -1);
}
/*b.gradesSwipeLayout.setOnRefreshListener(() -> {
Toast.makeText(activity, "Works!", Toast.LENGTH_LONG).show();
// To keep animation for 4 seconds
new Handler().postDelayed(() -> {
// Stop animation (This will be after 3 seconds)
b.gradesSwipeLayout.setRefreshing(false);
}, 3000);
});*/
long finalExpandSubjectId = expandSubjectId;
String orderBy;
if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_SUBJECT_ASC) {
orderBy = "subjectLongName ASC, addedDate DESC";
}
else if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_DATE_DESC) {
orderBy = "addedDate DESC";
}
else if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_DATE_ASC) {
orderBy = "addedDate ASC";
}
else {
orderBy = "subjectLongName DESC, addedDate DESC";
}
App.db.gradeDao().getAllOrderBy(App.Companion.getProfileId(), orderBy).observe(this, grades -> {
if (app == null || activity == null || b == null || !isAdded())
return;
subjectList = new ArrayList<>();
ProfileConfigGrades config = app.getConfig().getFor(App.Companion.getProfileId()).getGrades();
// now we have all grades from the newest to the oldest
for (GradeFull grade: grades) {
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
if (model == null) {
model = new ItemGradesSubjectModel(app.getProfile(),
new Subject(App.Companion.getProfileId(), grade.subjectId, grade.subjectLongName, grade.subjectShortName),
new ArrayList<>(),
new ArrayList<>());
subjectList.add(model);
if (model.subject != null && model.subject.id == finalExpandSubjectId) {
model.expandView = true;
}
model.colorMode = App.Companion.getConfig().forProfile().getGrades().getColorMode();
model.yearAverageMode = App.Companion.getConfig().forProfile().getGrades().getYearAverageMode();
}
if (!grade.seen && grade.semester == 1) {
model.semester1Unread++;
}
if (!grade.seen && grade.semester == 2) {
model.semester2Unread++;
}
// COUNT POINT GRADES
if (grade.type == Grade.TYPE_POINT_AVG) {
model.isPointSubject = true;
if (grade.semester == 1) {
model.gradeSumOverall += grade.value;
model.gradeCountOverall += grade.valueMax;
model.gradeSumSemester1 += grade.value;
model.gradeCountSemester1 += grade.valueMax;
model.semester1Average = model.gradeSumSemester1 / model.gradeCountSemester1 * 100;
model.grades1.add(grade);
}
if (grade.semester == 2) {
model.gradeSumOverall += grade.value;
model.gradeCountOverall += grade.valueMax;
model.gradeSumSemester2 += grade.value;
model.gradeCountSemester2 += grade.valueMax;
model.semester2Average = model.gradeSumSemester2 / model.gradeCountSemester2 * 100;
model.grades2.add(grade);
}
}
else if (grade.type == Grade.TYPE_POINT_SUM) {
model.isBehaviourSubject = true;
if (grade.semester == 1) {
model.semester1Average += grade.value;
model.yearAverage += grade.value;
model.grades1.add(grade);
}
if (grade.semester == 2) {
model.semester2Average += grade.value;
model.yearAverage += grade.value;
model.grades2.add(grade);
}
}
else if (grade.type == Grade.TYPE_NORMAL) {
model.isNormalSubject = true;
float weight = grade.weight;
if (weight < 0) {
// do not show *normal* grades with negative weight - these are historical grades - Iuczniowie
continue;
}
if (!config.getCountZeroToAvg() && grade.name.equals("0")) {
weight = 0;
}
float valueWeighted = grade.value * weight;
if (grade.semester == 1) {
model.gradeSumOverall += valueWeighted;
model.gradeCountOverall += weight;
model.gradeSumSemester1 += valueWeighted;
model.gradeCountSemester1 += weight;
model.semester1Average = model.gradeSumSemester1 / model.gradeCountSemester1;
if (grade.parentId == -1) {
// show only "current" grades - these which are not historical
model.grades1.add(grade);
}
}
if (grade.semester == 2) {
model.gradeSumOverall += valueWeighted;
model.gradeCountOverall += weight;
model.gradeSumSemester2 += valueWeighted;
model.gradeCountSemester2 += weight;
model.semester2Average = model.gradeSumSemester2 / model.gradeCountSemester2;
if (grade.parentId == -1) {
// show only "current" grades - these which are not historical
model.grades2.add(grade);
}
}
}
else if (grade.type == Grade.TYPE_SEMESTER1_PROPOSED) {
model.semester1Proposed = grade;
}
else if (grade.type == Grade.TYPE_SEMESTER1_FINAL) {
model.semester1Final = grade;
}
else if (grade.type == Grade.TYPE_SEMESTER2_PROPOSED) {
model.semester2Proposed = grade;
}
else if (grade.type == Grade.TYPE_SEMESTER2_FINAL) {
model.semester2Final = grade;
}
else if (grade.type == Grade.TYPE_YEAR_PROPOSED) {
model.yearProposed = grade;
}
else if (grade.type == Grade.TYPE_YEAR_FINAL) {
model.yearFinal = grade;
}
else {
// descriptive grades, text grades
model.isDescriptiveSubject = true;
if (grade.semester == 1) {
model.grades1.add(grade);
}
if (grade.semester == 2) {
model.grades2.add(grade);
}
}
}
for (ItemGradesSubjectModel model: subjectList) {
if (model.isPointSubject) {
model.yearAverage = model.gradeSumOverall / model.gradeCountOverall * 100.0f; // map the point grade "average" % value from 0.0f-1.0f to 0%-100%
}
/*else if (model.isDescriptiveSubject && !model.isNormalSubject) {
// applies for only descriptive grades - do nothing. average is hidden
//model.isDescriptiveSubject = false;
//model.semester1Average = -1;
//model.semester2Average = -1;
//model.yearAverage = -1;
}*/
else if (!model.isBehaviourSubject && model.isNormalSubject) {
// applies for normal grades & normal+descriptive grades
// calculate the normal grade average based on the user's setting
switch (App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()) {
case YEAR_1_AVG_2_AVG:
model.yearAverage = (model.semester1Average + model.semester2Average) / 2;
break;
case YEAR_1_SEM_2_AVG:
model.yearAverage = model.semester1Final != null ? (model.semester1Final.value + model.semester2Average) / 2 : 0.0f;
break;
case YEAR_1_AVG_2_SEM:
model.yearAverage = model.semester2Final != null ? (model.semester1Average + model.semester2Final.value) / 2 : 0.0f;
break;
case YEAR_1_SEM_2_SEM:
model.yearAverage = model.semester1Final != null && model.semester2Final != null ? (model.semester1Final.value + model.semester2Final.value) / 2 : 0.0f;
break;
default:
case YEAR_ALL_GRADES:
model.yearAverage = model.gradeSumOverall / model.gradeCountOverall;
break;
}
}
}
if (subjectList.size() > 0) {
GradesSubjectAdapter adapter;
if ((adapter = (GradesSubjectAdapter) listView.getAdapter()) != null) {
adapter.subjectList = subjectList;
adapter.notifyDataSetChanged();
return;
}
adapter = new GradesSubjectAdapter(subjectList, activity);
listView.setAdapter(adapter);
listView.setVisibility(View.VISIBLE);
b.gradesNoData.setVisibility(View.GONE);
if (finalExpandSubjectId != -1) {
int subjectIndex = subjectList.indexOf(ItemGradesSubjectModel.searchModelBySubjectId(subjectList, finalExpandSubjectId));
listView.setSelection(subjectIndex > 0 ? subjectIndex - 1 : subjectIndex);
}
}
else {
listView.setVisibility(View.GONE);
b.gradesNoData.setVisibility(View.VISIBLE);
}
});
}
private int gradeFromAverage(float value) {
int grade = (int)Math.floor(value);
if (value % 1.0f >= 0.75f)
grade++;
return grade;
}
public void showAverages() {
if (app == null || activity == null || b == null || !isAdded() || subjectList == null)
return;
float semester1Sum = 0;
float semester1Count = 0;
float semester1ProposedSum = 0;
float semester1ProposedCount = 0;
float semester1FinalSum = 0;
float semester1FinalCount = 0;
float semester2Sum = 0;
float semester2Count = 0;
float semester2ProposedSum = 0;
float semester2ProposedCount = 0;
float semester2FinalSum = 0;
float semester2FinalCount = 0;
float yearSum = 0;
float yearCount = 0;
float yearProposedSum = 0;
float yearProposedCount = 0;
float yearFinalSum = 0;
float yearFinalCount = 0;
for (ItemGradesSubjectModel subject: subjectList) {
// we cannot skip non-normal subjects because a point subject may also have a final grade
if (subject.isBehaviourSubject)
continue;
// SEMESTER 1 GRADES & AVERAGES
if (subject.semester1Final != null && subject.semester1Final.value > 0) { // if final available, add to final grades & expected grades
semester1FinalSum += subject.semester1Final.value;
semester1FinalCount++;
semester1Sum += subject.semester1Final.value;
semester1Count++;
}
else if (subject.semester1Proposed != null && subject.semester1Proposed.value > 0) { // if final not available, add proposed to expected grades
semester1Sum += subject.semester1Proposed.value;
semester1Count++;
}
else if (!Float.isNaN(subject.semester1Average)
&& subject.semester1Average > 0
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
semester1Sum += gradeFromAverage(subject.semester1Average);
semester1Count++;
}
if (subject.semester1Proposed != null && subject.semester1Proposed.value > 0) { // add proposed to proposed grades even if final is available
semester1ProposedSum += subject.semester1Proposed.value;
semester1ProposedCount++;
}
// SEMESTER 2 GRADES & AVERAGES
if (subject.semester2Final != null && subject.semester2Final.value > 0) { // if final available, add to final grades & expected grades
semester2FinalSum += subject.semester2Final.value;
semester2FinalCount++;
semester2Sum += subject.semester2Final.value;
semester2Count++;
}
else if (subject.semester2Proposed != null && subject.semester2Proposed.value > 0) { // if final not available, add proposed to expected grades
semester2Sum += subject.semester2Proposed.value;
semester2Count++;
}
else if (!Float.isNaN(subject.semester2Average)
&& subject.semester2Average > 0
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
semester2Sum += gradeFromAverage(subject.semester2Average);
semester2Count++;
}
if (subject.semester2Proposed != null && subject.semester2Proposed.value > 0) { // add proposed to proposed grades even if final is available
semester2ProposedSum += subject.semester2Proposed.value;
semester2ProposedCount++;
}
// YEAR GRADES & AVERAGES
if (subject.yearFinal != null && subject.yearFinal.value > 0) { // if final available, add to final grades & expected grades
yearFinalSum += subject.yearFinal.value;
yearFinalCount++;
yearSum += subject.yearFinal.value;
yearCount++;
}
else if (subject.yearProposed != null && subject.yearProposed.value > 0) { // if final not available, add proposed to expected grades
yearSum += subject.yearProposed.value;
yearCount++;
}
else if (!Float.isNaN(subject.yearAverage)
&& subject.yearAverage > 0
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
yearSum += gradeFromAverage(subject.yearAverage);
yearCount++;
}
if (subject.yearProposed != null && subject.yearProposed.value > 0) { // add proposed to proposed grades even if final is available
yearProposedSum += subject.yearProposed.value;
yearProposedCount++;
}
}
String semester1ExpectedAverageStr = semester1Count > semester1ProposedCount || semester1Count > semester1FinalCount ? getString(R.string.dialog_averages_expected_format, 1, semester1Sum / semester1Count) : "";
String semester1ProposedAverageStr = semester1ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 1, semester1ProposedSum / semester1ProposedCount) : "";
String semester1FinalAverageStr = semester1FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 1, semester1FinalSum / semester1FinalCount) : "";
String semester2ExpectedAverageStr = semester2Count > semester2ProposedCount || semester2Count > semester2FinalCount ? getString(R.string.dialog_averages_expected_format, 2, semester2Sum / semester2Count) : "";
String semester2ProposedAverageStr = semester2ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 2, semester2ProposedSum / semester2ProposedCount) : "";
String semester2FinalAverageStr = semester2FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 2, semester2FinalSum / semester2FinalCount) : "";
String yearExpectedAverageStr = yearCount > yearProposedCount || yearCount > yearFinalCount ? getString(R.string.dialog_averages_expected_yearly_format, yearSum / yearCount) : "";
String yearProposedAverageStr = yearProposedCount > 0 ? getString(R.string.dialog_averages_proposed_yearly_format, yearProposedSum / yearProposedCount) : "";
String yearFinalAverageStr = yearFinalCount > 0 ? getString(R.string.dialog_averages_final_yearly_format, yearFinalSum / yearFinalCount) : "";
if (semester1ExpectedAverageStr.isEmpty() && semester1ProposedAverageStr.isEmpty() && semester1FinalAverageStr.isEmpty()) {
semester1ExpectedAverageStr = getString(R.string.dialog_averages_unavailable_format, 1);
}
if (semester2ExpectedAverageStr.isEmpty() && semester2ProposedAverageStr.isEmpty() && semester2FinalAverageStr.isEmpty()) {
semester2ExpectedAverageStr = getString(R.string.dialog_averages_unavailable_format, 2);
}
if (yearExpectedAverageStr.isEmpty() && yearProposedAverageStr.isEmpty() && yearFinalAverageStr.isEmpty()) {
yearExpectedAverageStr = getString(R.string.dialog_averages_unavailable_yearly);
}
new MaterialDialog.Builder(activity)
.title(R.string.dialog_averages_title)
.content(getString(
R.string.dialog_averages_format,
semester1ExpectedAverageStr,
semester1ProposedAverageStr,
semester1FinalAverageStr,
semester2ExpectedAverageStr,
semester2ProposedAverageStr,
semester2FinalAverageStr,
yearExpectedAverageStr,
yearProposedAverageStr,
yearFinalAverageStr))
.positiveText(R.string.ok)
.show();
}
}

View File

@ -0,0 +1,245 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-4.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.averageOrNull
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
import kotlin.coroutines.CoroutineContext
import kotlin.math.max
class GradesFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "GradesFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: GradesFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local/private variables go here
private val adapter by lazy {
GradesAdapter(activity)
}
private val manager by lazy { app.gradesManager }
private val dontCountGrades by lazy { manager.dontCountGrades }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
b = GradesFragmentBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded)
return
app.db.gradeDao()
.getAllOrderBy(App.profileId, app.gradesManager.getOrderByString())
.observe(this, Observer { grades ->
if (b.gradesRecyclerView.adapter == null) {
b.gradesRecyclerView.adapter = adapter
b.gradesRecyclerView.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
//addItemDecoration(SimpleDividerItemDecoration(context))
}
}
launch(Dispatchers.Default) {
processGrades(grades)
}
if (grades != null && grades.isNotEmpty()) {
b.gradesRecyclerView.visibility = View.VISIBLE
b.gradesNoData.visibility = View.GONE
} else {
b.gradesRecyclerView.visibility = View.GONE
b.gradesNoData.visibility = View.VISIBLE
}
})
adapter.onGradeClick = {
GradeDetailsDialog(activity, it)
}
adapter.onGradesEditorClick = { subjectId, semesterNumber ->
}
}
private suspend fun processGrades(grades: List<GradeFull>) {
val items = mutableListOf<GradesSubject>()
var subjectId = -1L
var semesterNumber = 0
var subject = GradesSubject(subjectId, "")
var semester = GradesSemester(0, 1)
// grades returned by the query are ordered
// by the subject ID, so it's easier and probably
// a bit faster to build all the models
for (grade in grades) {
/*if (grade.parentId != null && grade.parentId != -1L)
continue // the grade is hidden as a new, improved one is available*/
if (grade.subjectId != subjectId) {
subjectId = grade.subjectId
semesterNumber = 0
subject = items.firstOrNull { it.subjectId == subjectId }
?: GradesSubject(grade.subjectId, grade.subjectLongName ?: "").also {
items += it
it.semester = 2
}
}
if (grade.semester != semesterNumber) {
semesterNumber = grade.semester
semester = subject.semesters.firstOrNull { it.number == semesterNumber }
?: GradesSemester(subject.subjectId, grade.semester).also { subject.semesters += it }
}
when (grade.type) {
Grade.TYPE_SEMESTER1_PROPOSED,
Grade.TYPE_SEMESTER2_PROPOSED -> semester.proposedGrade = grade
Grade.TYPE_SEMESTER1_FINAL,
Grade.TYPE_SEMESTER2_FINAL -> semester.finalGrade = grade
Grade.TYPE_YEAR_PROPOSED -> subject.proposedGrade = grade
Grade.TYPE_YEAR_FINAL -> subject.finalGrade = grade
else -> {
semester.grades += grade
countGrade(grade, subject.averages)
countGrade(grade, semester.averages)
}
}
subject.lastAddedDate = max(subject.lastAddedDate, grade.addedDate)
}
val stats = GradesStats()
val sem1Expected = mutableListOf<Float>()
val sem2Expected = mutableListOf<Float>()
val yearlyExpected = mutableListOf<Float>()
val sem1Proposed = mutableListOf<Float>()
val sem2Proposed = mutableListOf<Float>()
val yearlyProposed = mutableListOf<Float>()
val sem1Final = mutableListOf<Float>()
val sem2Final = mutableListOf<Float>()
val yearlyFinal = mutableListOf<Float>()
val sem1Point = mutableListOf<Float>()
val sem2Point = mutableListOf<Float>()
val yearlyPoint = mutableListOf<Float>()
for (item in items) {
item.semesters.forEach { sem ->
manager.calculateAverages(sem.averages)
if (sem.number == 1) {
sem.proposedGrade?.value?.let { sem1Proposed += it }
sem.finalGrade?.value?.let {
sem1Final += it
sem1Expected += it
} ?: run {
sem.averages.normalAvg?.let { sem1Expected += manager.getRoundedGrade(it).toFloat() }
}
sem.averages.pointAvgPercent?.let { sem1Point += it }
}
if (sem.number == 2) {
sem.proposedGrade?.value?.let { sem2Proposed += it }
sem.finalGrade?.value?.let {
sem2Final += it
sem2Expected += it
} ?: run {
sem.averages.normalAvg?.let { sem2Expected += manager.getRoundedGrade(it).toFloat() }
}
sem.averages.pointAvgPercent?.let { sem2Point += it }
}
}
manager.calculateAverages(item.averages, item.semesters)
item.proposedGrade?.value?.let { yearlyProposed += it }
item.finalGrade?.value?.let {
yearlyFinal += it
yearlyExpected += it
} ?: run {
item.averages.normalAvg?.let { yearlyExpected += manager.getRoundedGrade(it).toFloat() }
}
item.averages.pointAvgPercent?.let { yearlyPoint += it }
}
stats.normalSem1 = sem1Expected.averageOrNull()?.toFloat() ?: 0f
stats.normalSem1Proposed = sem1Proposed.averageOrNull()?.toFloat() ?: 0f
stats.normalSem1Final = sem1Final.averageOrNull()?.toFloat() ?: 0f
stats.sem1NotAllFinal = sem1Final.size < sem1Expected.size
stats.normalSem2 = sem2Expected.averageOrNull()?.toFloat() ?: 0f
stats.normalSem2Proposed = sem2Proposed.averageOrNull()?.toFloat() ?: 0f
stats.normalSem2Final = sem2Final.averageOrNull()?.toFloat() ?: 0f
stats.sem2NotAllFinal = sem2Final.size < sem2Expected.size
stats.normalYearly = yearlyExpected.averageOrNull()?.toFloat() ?: 0f
stats.normalYearlyProposed = yearlyProposed.averageOrNull()?.toFloat() ?: 0f
stats.normalYearlyFinal = yearlyFinal.averageOrNull()?.toFloat() ?: 0f
stats.yearlyNotAllFinal = yearlyFinal.size < yearlyExpected.size
stats.pointSem1 = sem1Point.averageOrNull()?.toFloat() ?: 0f
stats.pointSem2 = sem2Point.averageOrNull()?.toFloat() ?: 0f
stats.pointYearly = yearlyPoint.averageOrNull()?.toFloat() ?: 0f
when (manager.orderBy) {
GradesManager.ORDER_BY_DATE_DESC -> items.sortByDescending { it.lastAddedDate }
GradesManager.ORDER_BY_DATE_ASC -> items.sortBy { it.lastAddedDate }
}
adapter.items = items.toMutableList()
adapter.items.add(stats)
withContext(Dispatchers.Main) {
adapter.notifyDataSetChanged()
}
}
private fun countGrade(grade: Grade, averages: GradesAverages) {
val value = manager.getGradeValue(grade)
val weight = manager.getGradeWeight(dontCountGrades, grade)
when (grade.type) {
Grade.TYPE_NORMAL -> {
averages.normalSum += value
averages.normalCount ++
averages.normalWeightedSum += value * weight
averages.normalWeightedCount += weight
}
Grade.TYPE_POINT_AVG -> {
averages.pointAvgSum += grade.value
averages.pointAvgMax += grade.valueMax ?: value
}
Grade.TYPE_POINT_SUM -> {
averages.pointSum += grade.value
}
}
}
}

View File

@ -1,143 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.grades;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.entity.Grade;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.COLOR_MODE_DEFAULT;
public class GradesListAdapter extends RecyclerView.Adapter<GradesListAdapter.ViewHolder> {
private Context mContext;
private List<GradeFull> gradeList;
//getting the context and product list with constructor
public GradesListAdapter(Context mCtx, List<GradeFull> gradeList) {
this.mContext = mCtx;
this.gradeList = gradeList;
}
@NonNull
@Override
public GradesListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.row_grades_list_item, parent, false);
return new GradesListAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull GradesListAdapter.ViewHolder holder, int position) {
App app = (App) mContext.getApplicationContext();
GradeFull grade = gradeList.get(position);
holder.root.setOnClickListener((v -> {
new GradeDetailsDialog(v.getContext(), App.Companion.getProfileId()).show(app, grade);
}));
int gradeColor;
if (App.Companion.getConfig().forProfile().getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {
gradeColor = Colors.gradeToColor(grade);
}
holder.gradesListName.setText(grade.name);
holder.gradesListName.setSelected(true);
//holder.gradesListName.setTypeface(null, Typeface.BOLD);
holder.gradesListName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.3 ? 0xff000000 : 0xffffffff);
holder.gradesListName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
if (grade.description.trim().isEmpty()) {
holder.gradesListDescription.setText(grade.category);
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, "") : "");
}
else {
holder.gradesListDescription.setText(grade.description);
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, grade.category) : grade.category);
}
DecimalFormat format = new DecimalFormat("#.##");
DecimalFormat formatWithZeroes = new DecimalFormat("#.00");
if (grade.weight < 0) {
grade.weight *= -1;
}
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_DESCRIPTIVE_TEXT || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_POINT_SUM) {
holder.gradesListWeight.setVisibility(View.GONE);
grade.weight = 0;
}
else {
holder.gradesListWeight.setVisibility(View.VISIBLE);
if (grade.type == Grade.TYPE_POINT_AVG) {
holder.gradesListWeight.setText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
}
else if (grade.weight == 0) {
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_not_counted));
}
else {
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_format, format.format(grade.weight) + (grade.classAverage != -1 ? ", " + formatWithZeroes.format(grade.classAverage) : "")));
}
}
holder.gradesListTeacher.setText(grade.teacherFullName);
holder.gradesListAddedDate.setText(Date.fromMillis(grade.addedDate).getFormattedStringShort());
if (!grade.seen) {
holder.gradesListDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesListDescription.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesListDescription.setBackground(null);
}
}
@Override
public int getItemCount() {
return gradeList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
View root;
TextView gradesListName;
TextView gradesListDescription;
TextView gradesListCategory;
TextView gradesListWeight;
TextView gradesListTeacher;
TextView gradesListAddedDate;
ViewHolder(View itemView) {
super(itemView);
root = itemView.getRootView();
gradesListName = itemView.findViewById(R.id.gradesListName);
gradesListDescription = itemView.findViewById(R.id.gradesListCategoryColumn);
gradesListCategory = itemView.findViewById(R.id.gradesListCategoryDescription);
gradesListWeight = itemView.findViewById(R.id.gradesListWeight);
gradesListTeacher = itemView.findViewById(R.id.gradesListTeacher);
gradesListAddedDate = itemView.findViewById(R.id.gradesListAddedDate);
}
}
}

View File

@ -1,787 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.grades;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.graphics.ColorUtils;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.mikepenz.iconics.view.IconicsImageView;
import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.entity.Subject;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static pl.szczodrzynski.edziennik.MainActivity.TARGET_GRADES_EDITOR;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.COLOR_MODE_DEFAULT;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_SEM;
public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> implements View.OnClickListener {
private static final String TAG = "GradesSubjectAdapter";
private MainActivity activity;
public List<ItemGradesSubjectModel> subjectList;
private void updateBadges(Context context, ItemGradesSubjectModel model) {
// do not need this since we have an Observer for unread counters..
//((App)getContext().getApplicationContext()).saveRegister(); // I don't like this.
}
private boolean invalidAvg(float avg) {
return avg == 0.0f || Float.isNaN(avg);
}
private void updateSubjectSemesterBadges(ViewHolder holder, ItemGradesSubjectModel model) {
if (model.semester1Unread > 0 || model.semester2Unread > 0) {
holder.gradesSubjectTitle.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesSubjectTitle.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesSubjectTitle.setBackground(null);
}
if (model.semester1Unread > 0) {
holder.gradesSubjectSemester1Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesSubjectSemester1Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesSubjectSemester1Title.setBackground(null);
}
if (model.semester2Unread > 0) {
holder.gradesSubjectSemester2Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesSubjectSemester2Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesSubjectSemester2Title.setBackground(null);
}
}
private boolean gradesSetAsRead(ViewHolder holder, ItemGradesSubjectModel model, int semester) {
boolean somethingChanged = false;
AppDb db = App.Companion.getDb();
if (semester == 1) {
model.semester1Unread = 0;
for (GradeFull grade : model.grades1) {
if (!grade.seen) {
db.metadataDao().setSeen(App.Companion.getProfileId(), grade, somethingChanged = true);
}
}
if (model.semester1Proposed != null && !model.semester1Proposed.seen)
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester1Proposed, somethingChanged = true);
if (model.semester1Final != null && !model.semester1Final.seen)
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester1Final, somethingChanged = true);
}
else if (semester == 2) {
model.semester2Unread = 0;
for (GradeFull grade : model.grades2) {
if (!grade.seen) {
db.metadataDao().setSeen(App.Companion.getProfileId(), grade, somethingChanged = true);
}
}
if (model.semester2Proposed != null && !model.semester2Proposed.seen)
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester2Proposed, somethingChanged = true);
if (model.semester2Final != null && !model.semester2Final.seen)
db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester2Final, somethingChanged = true);
if (model.yearProposed != null && !model.yearProposed.seen)
db.metadataDao().setSeen(App.Companion.getProfileId(), model.yearProposed, somethingChanged = true);
if (model.yearFinal != null && !model.yearFinal.seen)
db.metadataDao().setSeen(App.Companion.getProfileId(), model.yearFinal, somethingChanged = true);
}
if (somethingChanged) updateSubjectSemesterBadges(holder, model);
return somethingChanged;
}
private void expandSubject(ViewHolder holder, ItemGradesSubjectModel model) {
Anim.fadeOut(holder.gradesSubjectPreviewContainer, 200, new Animation.AnimationListener() {
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) {
boolean somethingChanged = false;
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
somethingChanged = gradesSetAsRead(holder, model, 1);
}
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
somethingChanged = gradesSetAsRead(holder, model, 2);
}
if (somethingChanged) updateBadges(getContext(), model);
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
Anim.expand(holder.gradesSubjectContent, 500, null);
}
});
}
private void collapseSubject(ViewHolder holder, ItemGradesSubjectModel model) {
Anim.collapse(holder.gradesSubjectContent, 500, new Animation.AnimationListener() {
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) {
//holder.gradesSubjectPreviewContent.setVisibility(View.VISIBLE);
Anim.fadeIn(holder.gradesSubjectPreviewContainer, 200, null);
}
});
}
class BuildGradeViews extends AsyncTask<Void, Void, Void> {
boolean findViews;
ItemGradesSubjectModel model;
//ViewGroup parent;
//int position;
ViewHolder holder;
BuildGradeViews(ItemGradesSubjectModel model, ViewHolder holder, ViewGroup parent, int position, boolean findViews) {
this.model = model;
this.holder = holder;
this.findViews = findViews;
//this.parent = parent;
//this.position = position;
}
protected Void doInBackground(Void... params) {
if (this.findViews) {
findViews(holder, holder.gradesSubjectRoot);
}
return null;
}
protected void onPostExecute(Void aVoid) {
DecimalFormat df = new DecimalFormat("#.00");
if (this.findViews) {
// TODO NIE WIEM CO TO ROBI XD
//this.viewHolder.semestrTitle1.setText(C0193R.string.semestr1);
//this.viewHolder.semestrTitle2.setText(C0193R.string.semestr2);
/*if (GradesSubjectAdapter.this.columns == 0) {
DisplayMetrics metrics = GradeListAdapterBySubject.this.context.getResources().getDisplayMetrics();
if (GradeListAdapterBySubject.this.context.getResources().getBoolean(C0193R.bool.tablet)) {
GradeListAdapterBySubject.this.columns = ((int) ((((float) (this.parent.getWidth() / 2)) / metrics.density) - 30.0f)) / 58;
} else {
GradeListAdapterBySubject.this.columns = ((int) ((((float) this.parent.getWidth()) / metrics.density) - 30.0f)) / 58;
}
}
this.viewHolder.semestrGridView1.setColumnCount(GradeListAdapterBySubject.this.columns);
this.viewHolder.semestrGridView2.setColumnCount(GradeListAdapterBySubject.this.columns);*/
}
if (model != null && model.subject != null && model.subject.id != holder.lastSubject) {
holder.gradesSubjectRoot.setBackground(Colors.getAdaptiveBackgroundDrawable(model.subject.color, model.subject.color));
updateSubjectSemesterBadges(holder, model);
holder.gradesSubjectTitle.setText(model.subject.longName);
holder.gradesSubjectPreviewContent.removeAllViews();
if (model.expandView) {
// commented is without animations, do not use now (with unread badges)
//holder.gradesSubjectContent.setVisibility(View.VISIBLE);
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
expandSubject(holder, model);
model.expandView = false;
}
int showSemester = model.profile.getCurrentSemester();
List<GradeFull> gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
if (gradeList.size() == 0) {
showSemester = (showSemester == 1 ? 2 : 1);
gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
}
App app = (App) getContext().getApplicationContext();
float scale = getContext().getResources().getDisplayMetrics().density;
int _5dp = (int) (5 * scale + 0.5f);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
layoutParams.setMargins(0, 0, _5dp, 0);
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.getConfig().getUi().getMiniMenuVisible() ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
int totalWidthPx = 0;
boolean ellipsized = false;
if (showSemester != model.profile.getCurrentSemester()) {
// showing different semester, because of no grades in the selected one
holder.gradesSubjectPreviewSemester.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewSemester.setText(getContext().getString(R.string.grades_semester_header_format, showSemester));
// decrease the max preview width. DONE below
/*holder.gradesSubjectPreviewSemester.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewSemester.getMeasuredWidth();
maxWidthPx -= _5dp;*/
}
else {
holder.gradesSubjectPreviewSemester.setVisibility(View.GONE);
}
if (model.grades1.size() > 0) {
holder.gradesSubjectSemester1Nest.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester1Content.setHasFixedSize(false);
holder.gradesSubjectSemester1Content.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester1Content.setLayoutManager(new LinearLayoutManager(activity));
holder.gradesSubjectSemester1Content.setAdapter(new GradesListAdapter(activity, model.grades1));
holder.gradesSubjectSemester1Header.setVisibility(View.VISIBLE);
if (showSemester == 1) {
holder.gradesSubjectSemester1Container.setVisibility(View.VISIBLE);
}
else {
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
}
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
holder.gradesSubjectSemester1Average.setVisibility(View.GONE);
}
else {
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1Average.setVisibility(View.VISIBLE);
int formatSingle = (model.isPointSubject ? R.string.grades_average_single_percent_format : model.isBehaviourSubject ? R.string.grades_average_single_point_format : R.string.grades_average_single_format);
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
// PREVIEW AVERAGE
if (showSemester == 1) {
holder.gradesSubjectPreviewAverage.setText(
getContext().getString(
formatSingle,
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
)
);
}
// AVERAGE value
holder.gradesSubjectSemester1Average.setText(
getContext().getString(
format,
1,
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
)
);
}
// PROPOSED grade
if (model.semester1Proposed != null) {
holder.gradesSubjectSemester1Proposed.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1Proposed.setText(model.semester1Proposed.name);
holder.gradesSubjectSemester1Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectSemester1Proposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
if (showSemester == 1) {
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewProposed.setText(model.semester1Proposed.name);
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
}
}
else {
holder.gradesSubjectSemester1Proposed.setVisibility(View.GONE);
if (showSemester == 1) {
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
}
}
// FINAL grade
if (model.semester1Final != null) {
holder.gradesSubjectSemester1Final.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1Final.setText(model.semester1Final.name);
holder.gradesSubjectSemester1Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
if (showSemester == 1) {
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewFinal.setText(model.semester1Final.name);
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
}
}
else {
holder.gradesSubjectSemester1Final.setVisibility(View.GONE);
if (showSemester == 1) {
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
}
}
}
else {
holder.gradesSubjectSemester1Header.setVisibility(View.GONE);
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
}
if (model.grades2.size() > 0) {
holder.gradesSubjectSemester2Nest.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester2Content.setHasFixedSize(false);
holder.gradesSubjectSemester2Content.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester2Content.setLayoutManager(new LinearLayoutManager(activity));
holder.gradesSubjectSemester2Content.setAdapter(new GradesListAdapter(activity, model.grades2));
holder.gradesSubjectSemester2Header.setVisibility(View.VISIBLE);
if (showSemester == 2) {
holder.gradesSubjectSemester2Container.setVisibility(View.VISIBLE);
}
else {
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
}
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
holder.gradesSubjectSemester2Average.setVisibility(View.GONE);
}
else {
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2Average.setVisibility(View.VISIBLE);
// PREVIEW AVERAGE
int formatDouble = (model.isPointSubject ? R.string.grades_average_double_percent_format : model.isBehaviourSubject ? R.string.grades_average_double_point_format : R.string.grades_average_double_format);
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
if (showSemester == 2) {
if (model.semester2Proposed != null || model.semester2Final != null) {
holder.gradesSubjectPreviewAverage.setText(invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average));
holder.gradesSubjectPreviewYearAverage.setText(invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage));
holder.gradesSubjectPreviewYearAverage.setVisibility(View.VISIBLE);
}
else {
holder.gradesSubjectPreviewAverage.setText(
getContext().getString(
formatDouble,
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average),
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
)
);
holder.gradesSubjectPreviewYearAverage.setVisibility(View.GONE);
}
}
// AVERAGE value
holder.gradesSubjectSemester2Average.setText(
getContext().getString(
format,
2,
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average)
)
);
}
// PROPOSED grade
if (model.semester2Proposed != null) {
holder.gradesSubjectSemester2Proposed.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2Proposed.setText(model.semester2Proposed.name);
holder.gradesSubjectSemester2Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectSemester2Proposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
if (showSemester == 2) {
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewProposed.setText(model.semester2Proposed.name);
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
}
}
else {
holder.gradesSubjectSemester2Proposed.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
}
}
// FINAL grade
if (model.semester2Final != null) {
holder.gradesSubjectSemester2Final.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2Final.setText(model.semester2Final.name);
holder.gradesSubjectSemester2Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
if (showSemester == 2) {
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewFinal.setText(model.semester2Final.name);
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
}
}
else {
holder.gradesSubjectSemester2Final.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
}
}
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
holder.gradesSubjectYearAverage.setVisibility(View.GONE);
}
else {
holder.gradesSubjectYearAverage.setVisibility(View.VISIBLE);
// AVERAGE value
int format = (model.isPointSubject ? R.string.grades_year_average_percent_format : model.isBehaviourSubject ? R.string.grades_year_average_point_format : R.string.grades_year_average_format);
holder.gradesSubjectYearAverage.setText(
getContext().getString(
format,
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
)
);
}
// PROPOSED grade
if (model.yearProposed != null) {
holder.gradesSubjectYearProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectYearProposed.setText(model.yearProposed.name);
holder.gradesSubjectYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
if (showSemester == 2) {
holder.gradesSubjectPreviewYearProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewYearProposed.setText(model.yearProposed.name);
holder.gradesSubjectPreviewYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectPreviewYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
}
}
else {
holder.gradesSubjectYearProposed.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewYearProposed.setVisibility(View.GONE);
}
}
// FINAL grade
if (model.yearFinal != null) {
holder.gradesSubjectYearFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectYearFinal.setText(model.yearFinal.name);
holder.gradesSubjectYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
if (showSemester == 2) {
holder.gradesSubjectPreviewYearFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewYearFinal.setText(model.yearFinal.name);
holder.gradesSubjectPreviewYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
}
}
else {
holder.gradesSubjectYearFinal.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewYearFinal.setVisibility(View.GONE);
}
}
}
else {
holder.gradesSubjectSemester2Header.setVisibility(View.GONE);
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
}
// decrease the width by average, proposed, final and semester TextViews
holder.gradesSubjectPreviewContainer.measure(WRAP_CONTENT, MATCH_PARENT);
//Log.d(TAG, "gradesSubjectPreviewContainer "+holder.gradesSubjectPreviewContainer.getMeasuredWidth());
/*holder.gradesSubjectPreviewAverage.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewAverage.getMeasuredWidth();
maxWidthPx -= 2*_5dp;
if (holder.gradesSubjectPreviewProposed.getVisibility() == View.VISIBLE) {
holder.gradesSubjectPreviewProposed.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewProposed.getMeasuredWidth();
maxWidthPx -= _5dp;
}
if (holder.gradesSubjectPreviewFinal.getVisibility() == View.VISIBLE) {
holder.gradesSubjectPreviewFinal.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewFinal.getMeasuredWidth();
maxWidthPx -= _5dp;
}*/
maxWidthPx -= holder.gradesSubjectPreviewContainer.getMeasuredWidth();
maxWidthPx -= _5dp;
for (GradeFull grade: gradeList) {
if (grade.semester != showSemester)
continue;
int gradeColor;
if (model.colorMode == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
} else {
gradeColor = Colors.gradeToColor(grade);
}
TextView gradeName = new TextView(activity);
gradeName.setText(grade.name);
gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
gradeName.setPadding(_5dp, 0, _5dp, 0);
gradeName.setBackgroundResource(R.drawable.bg_rounded_4dp);
gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
gradeName.setTypeface(null, Typeface.BOLD);
gradeName.measure(WRAP_CONTENT, WRAP_CONTENT);
totalWidthPx += gradeName.getMeasuredWidth() + _5dp;
//Log.d(TAG, "totalWidthPx " + totalWidthPx);
if (totalWidthPx >= maxWidthPx) {
if (ellipsized)
continue;
ellipsized = true;
TextView ellipsisText = new TextView(activity);
ellipsisText.setText(R.string.ellipsis);
ellipsisText.setTextAppearance(activity, R.style.NavView_TextView);
ellipsisText.setTypeface(null, Typeface.BOLD);
ellipsisText.setPadding(0, 0, 0, 0);
holder.gradesSubjectPreviewContent.addView(ellipsisText, layoutParams);
}
else {
holder.gradesSubjectPreviewContent.addView(gradeName, layoutParams);
}
}
}
if (findViews) {
//Log.d("GradesSubjectAdapter", "runOnUiThread");
//this.viewHolder.gradesSubjectContent.setTag(View.VISIBLE);
//this.viewHolder.gradesSubjectPreviewContainer.setTag(View.VISIBLE);
activity.runOnUiThread(() -> {
holder.gradesSubjectRoot.setOnClickListener(v -> {
if (holder.gradesSubjectContent.getVisibility() == View.GONE) {
expandSubject(holder, model);
}
else {
collapseSubject(holder, model);
}
});
holder.gradesSubjectSemester1Header.setOnClickListener(v -> {
if (holder.gradesSubjectSemester1Container.getVisibility() == View.GONE) {
if (gradesSetAsRead(holder, model, 1)) updateBadges(getContext(), model);
Anim.expand(holder.gradesSubjectSemester1Container, 500, null);
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
}
}
else {
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
}
});
holder.gradesSubjectSemester2Header.setOnClickListener(v -> {
if (holder.gradesSubjectSemester2Container.getVisibility() == View.GONE) {
if (gradesSetAsRead(holder, model, 2)) updateBadges(getContext(), model);
Anim.expand(holder.gradesSubjectSemester2Container, 500, null);
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
}
}
else {
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
}
});
// hide the grade simulator when there are point, behaviour or descriptive grades
if (model.isPointSubject || model.isBehaviourSubject || (model.isDescriptiveSubject && !model.isNormalSubject)) {
holder.gradesSubjectSemester1EditButton.setVisibility(View.GONE);
holder.gradesSubjectSemester2EditButton.setVisibility(View.GONE);
}
else {
holder.gradesSubjectSemester1EditButton.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1EditButton.setOnClickListener(v -> {
Bundle arguments = new Bundle();
if (model.subject != null) {
arguments.putLong("subjectId", model.subject.id);
}
arguments.putInt("semester", 1);
//d(TAG, "Model is " + model);
switch (model.yearAverageMode) {
case YEAR_1_SEM_2_AVG:
case YEAR_1_SEM_2_SEM:
arguments.putInt("averageMode", -1);
break;
default:
arguments.putInt("averageMode", model.semester2Final == null && model.yearAverageMode == YEAR_1_AVG_2_SEM ? -1 : model.yearAverageMode);
arguments.putFloat("yearAverageBefore", model.yearAverage);
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester2);
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester2);
arguments.putFloat("averageOtherSemester", model.semester2Average);
arguments.putFloat("finalOtherSemester", model.semester2Final == null ? -1 : model.semester2Final.value);
break;
}
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
});
holder.gradesSubjectSemester2EditButton.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2EditButton.setOnClickListener(v -> {
Bundle arguments = new Bundle();
if (model.subject != null) {
arguments.putLong("subjectId", model.subject.id);
}
arguments.putInt("semester", 2);
//d(TAG, "Model is " + model);
switch (model.yearAverageMode) {
case YEAR_1_AVG_2_SEM:
case YEAR_1_SEM_2_SEM:
arguments.putInt("averageMode", -1);
break;
default:
arguments.putInt("averageMode", model.semester1Final == null && model.yearAverageMode == YEAR_1_SEM_2_AVG ? -1 : model.yearAverageMode);
arguments.putFloat("yearAverageBefore", model.yearAverage);
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester1);
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester1);
arguments.putFloat("averageOtherSemester", model.semester1Average);
arguments.putFloat("finalOtherSemester", model.semester1Final == null ? -1 : model.semester1Final.value);
break;
}
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
});
}
});
}
if (model != null && model.subject != null) {
holder.lastSubject = model.subject.id;
}
super.onPostExecute(aVoid);
}
}
//getting the context and product list with constructor
public GradesSubjectAdapter(List<ItemGradesSubjectModel> data, MainActivity context) {
super(context, R.layout.row_grades_subject_item, data);
this.activity = context;
this.subjectList = data;
}
@Override
public void onClick(View v) {
int position = (Integer) v.getTag();
Object object = getItem(position);
ItemGradesSubjectModel dataModel = (ItemGradesSubjectModel)object;
}
private void findViews(ViewHolder holder, View root) {
//holder.gradesSubjectRoot = root.findViewById(R.id.gradesSubjectRoot);
holder.gradesSubjectTitle = root.findViewById(R.id.gradesSubjectTitle);
holder.gradesSubjectExpandIndicator = root.findViewById(R.id.gradesSubjectExpandIndicator);
holder.gradesSubjectPreviewContainer = root.findViewById(R.id.gradesSubjectPreviewContainer);
holder.gradesSubjectPreviewSemester = root.findViewById(R.id.gradesSubjectPreviewSemester);
holder.gradesSubjectPreviewContent = root.findViewById(R.id.gradesSubjectPreviewContent);
holder.gradesSubjectPreviewAverage = root.findViewById(R.id.gradesSubjectPreviewAverage);
holder.gradesSubjectPreviewProposed = root.findViewById(R.id.gradesSubjectPreviewProposed);
holder.gradesSubjectPreviewFinal = root.findViewById(R.id.gradesSubjectPreviewFinal);
holder.gradesSubjectPreviewYearAverage = root.findViewById(R.id.gradesSubjectPreviewYearAverage);
holder.gradesSubjectPreviewYearProposed = root.findViewById(R.id.gradesSubjectPreviewYearProposed);
holder.gradesSubjectPreviewYearFinal = root.findViewById(R.id.gradesSubjectPreviewYearFinal);
holder.gradesSubjectContent = root.findViewById(R.id.gradesSubjectContent);
holder.gradesSubjectSemester1Header = root.findViewById(R.id.gradesSubjectSemester1Header);
holder.gradesSubjectSemester1Title = root.findViewById(R.id.gradesSubjectSemester1Title);
holder.gradesSubjectSemester1ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester1ExpandIndicator);
holder.gradesSubjectSemester1Average = root.findViewById(R.id.gradesSubjectSemester1Average);
holder.gradesSubjectSemester1Proposed = root.findViewById(R.id.gradesSubjectSemester1Proposed);
holder.gradesSubjectSemester1Final = root.findViewById(R.id.gradesSubjectSemester1Final);
holder.gradesSubjectSemester1EditButton = root.findViewById(R.id.gradesSubjectSemester1EditButton);
holder.gradesSubjectSemester1Container = root.findViewById(R.id.gradesSubjectSemester1Container);
holder.gradesSubjectSemester1Nest = root.findViewById(R.id.gradesSubjectSemester1Nest);
holder.gradesSubjectSemester1Content = root.findViewById(R.id.gradesSubjectSemester1Content);
holder.gradesSubjectSemester2Header = root.findViewById(R.id.gradesSubjectSemester2Header);
holder.gradesSubjectSemester2Title = root.findViewById(R.id.gradesSubjectSemester2Title);
holder.gradesSubjectSemester2ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester2ExpandIndicator);
holder.gradesSubjectSemester2Average = root.findViewById(R.id.gradesSubjectSemester2Average);
holder.gradesSubjectSemester2Proposed = root.findViewById(R.id.gradesSubjectSemester2Proposed);
holder.gradesSubjectSemester2Final = root.findViewById(R.id.gradesSubjectSemester2Final);
holder.gradesSubjectSemester2EditButton = root.findViewById(R.id.gradesSubjectSemester2EditButton);
holder.gradesSubjectSemester2Container = root.findViewById(R.id.gradesSubjectSemester2Container);
holder.gradesSubjectSemester2Nest = root.findViewById(R.id.gradesSubjectSemester2Nest);
holder.gradesSubjectSemester2Content = root.findViewById(R.id.gradesSubjectSemester2Content);
holder.gradesSubjectYearAverage = root.findViewById(R.id.gradesSubjectYearAverage);
holder.gradesSubjectYearProposed = root.findViewById(R.id.gradesSubjectYearProposed);
holder.gradesSubjectYearFinal = root.findViewById(R.id.gradesSubjectYearFinal);
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
if (position >= subjectList.size()) {
return convertView;
}
ItemGradesSubjectModel model = subjectList.get(position);
if (model == null) {
//Toast.makeText(activity, "return convertView;", Toast.LENGTH_SHORT).show();
return convertView;
}
ViewHolder holder;
if (convertView == null) {
try {
convertView = LayoutInflater.from(activity).inflate(R.layout.row_grades_subject_item, parent, false);
holder = new ViewHolder();
holder.gradesSubjectRoot = convertView.findViewById(R.id.gradesSubjectRoot);
convertView.setTag(holder);
new BuildGradeViews(model, holder, parent, position, true).execute();
return convertView;
} catch (Exception e) {
return new View(getContext());
}
}
holder = (ViewHolder) convertView.getTag();
Subject subject = model.subject;
holder.gradesSubjectTitle.setText(subject != null ? subject.longName : "");
/*if (model.getNotSeen() > 0) {
viewHolder.notseen.setVisibility(0);
viewHolder.notseen.setText(model.getNotSeen() + "");
} else {
viewHolder.notseen.setVisibility(8);
}*/
new BuildGradeViews(model, holder, parent, position, false).execute();
return convertView;
}
public int getViewTypeCount() {
return getCount();
}
public int getItemViewType(int position) {
return position;
}
class ViewHolder {
long lastSubject;
TextView gradesSubjectTitle;
ConstraintLayout gradesSubjectRoot;
IconicsImageView gradesSubjectExpandIndicator;
LinearLayout gradesSubjectPreviewContainer;
TextView gradesSubjectPreviewSemester;
LinearLayout gradesSubjectPreviewContent;
TextView gradesSubjectPreviewAverage;
TextView gradesSubjectPreviewProposed;
TextView gradesSubjectPreviewFinal;
TextView gradesSubjectPreviewYearAverage;
TextView gradesSubjectPreviewYearProposed;
TextView gradesSubjectPreviewYearFinal;
LinearLayout gradesSubjectContent;
LinearLayout gradesSubjectSemester1Header;
TextView gradesSubjectSemester1Title;
IconicsImageView gradesSubjectSemester1ExpandIndicator;
TextView gradesSubjectSemester1Average;
TextView gradesSubjectSemester1Proposed;
TextView gradesSubjectSemester1Final;
IconicsImageView gradesSubjectSemester1EditButton;
LinearLayout gradesSubjectSemester1Container;
NestedScrollView gradesSubjectSemester1Nest;
RecyclerView gradesSubjectSemester1Content;
LinearLayout gradesSubjectSemester2Header;
TextView gradesSubjectSemester2Title;
IconicsImageView gradesSubjectSemester2ExpandIndicator;
TextView gradesSubjectSemester2Average;
TextView gradesSubjectSemester2Proposed;
TextView gradesSubjectSemester2Final;
IconicsImageView gradesSubjectSemester2EditButton;
LinearLayout gradesSubjectSemester2Container;
NestedScrollView gradesSubjectSemester2Nest;
RecyclerView gradesSubjectSemester2Content;
TextView gradesSubjectYearAverage;
TextView gradesSubjectYearProposed;
TextView gradesSubjectYearFinal;
}
}

View File

@ -14,13 +14,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
import java.text.DecimalFormat
import java.util.*
import kotlin.math.floor

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.models
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED
abstract class ExpandableItemModel<T>(val items: MutableList<T>) {
open var level: Int = 3
var state: Int = STATE_CLOSED
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.models
class GradesAverages {
var normalSum = 0f
var normalCount = 0
var normalWeightedSum = 0f
var normalWeightedCount = 0f
var pointSum = 0f
var pointAvgSum = 0f
var pointAvgMax = 0f
var normalAvg: Float? = null
var pointAvgPercent: Float? = null
}

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.models
class GradesEmpty

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.models
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
data class GradesSemester(
val subjectId: Long,
val number: Int,
val grades: MutableList<Grade> = mutableListOf()
) : ExpandableItemModel<Grade>(grades) {
override var level = 2
val averages = GradesAverages()
var proposedGrade: GradeFull? = null
var finalGrade: GradeFull? = null
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-3.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.models
class GradesStats {
var normalSem1 = 0f
var normalSem1Proposed = 0f
var normalSem1Final = 0f
var normalSem2 = 0f
var normalSem2Proposed = 0f
var normalSem2Final = 0f
var normalYearly = 0f
var normalYearlyProposed = 0f
var normalYearlyFinal = 0f
var sem1NotAllFinal = false
var sem2NotAllFinal = false
var yearlyNotAllFinal = false
var pointSem1 = 0f
var pointSem2 = 0f
var pointYearly = 0f
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.models
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
data class GradesSubject(
val subjectId: Long,
val subjectName: String,
val semesters: MutableList<GradesSemester> = mutableListOf()
) : ExpandableItemModel<GradesSemester>(semesters) {
override var level = 1
var lastAddedDate = 0L
var semester: Int = 1
val averages = GradesAverages()
var proposedGrade: GradeFull? = null
var finalGrade: GradeFull? = null
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import androidx.appcompat.app.AppCompatActivity
import pl.szczodrzynski.edziennik.App
interface BindableViewHolder<T> {
fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int)
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.databinding.GradesItemEmptyBinding
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesEmpty
class EmptyViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemEmptyBinding = GradesItemEmptyBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesEmpty> {
companion object {
private const val TAG = "EmptyViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: GradesEmpty, position: Int) {
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesItemGradeBinding
import pl.szczodrzynski.edziennik.utils.models.Date
class GradeViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemGradeBinding = GradesItemGradeBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradeFull> {
companion object {
private const val TAG = "GradeViewHolder"
}
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun onBind(activity: AppCompatActivity, app: App, grade: GradeFull, position: Int) {
val manager = app.gradesManager
b.gradeName.setGrade(grade, manager, bigView = true)
if (grade.description.isNullOrBlank()) {
b.gradeDescription.text = grade.category
b.gradeCategory.text =
if (grade.isImprovement)
app.getString(R.string.grades_improvement_category_format, "")
else
""
} else {
b.gradeDescription.text = grade.description
b.gradeCategory.text =
if (grade.isImprovement)
app.getString(R.string.grades_improvement_category_format, grade.category)
else
grade.category
}
b.gradeWeight.text = manager.getWeightString(activity, grade, showClassAverage = true)
b.gradeTeacherName.text = grade.teacherFullName
b.gradeAddedDate.text = Date.fromMillis(grade.addedDate).let {
it.getRelativeString(app, 5) ?: it.formattedStringShort
}
/*if (!grade.seen) {
b.gradeDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp))
b.gradeDescription.getBackground()
.setColorFilter(PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY))
} else {
b.gradeDescription.setBackground(null)
}*/
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.GradesItemSemesterBinding
import pl.szczodrzynski.edziennik.setText
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
class SemesterViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemSemesterBinding = GradesItemSemesterBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSemester> {
companion object {
private const val TAG = "SemesterViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSemester, position: Int) {
val manager = app.gradesManager
b.semesterName.setText(R.string.grades_semester_format, item.number)
b.dropdownIcon.rotation = when (item.state) {
GradesAdapter.STATE_CLOSED -> 0f
else -> 180f
}
b.average.text = manager.getAverageString(app, item.averages)
b.proposedGrade.setGrade(item.proposedGrade, manager)
b.finalGrade.setGrade(item.finalGrade, manager)
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-3.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.GradesItemStatsBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
import java.text.DecimalFormat
class StatsViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemStatsBinding = GradesItemStatsBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesStats> {
companion object {
private const val TAG = "StatsViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: GradesStats, position: Int) {
val manager = app.gradesManager
val showAverages = mutableListOf<Int>()
val showPoint = mutableListOf<Int>()
getSemesterString(app, item.normalSem1, item.normalSem1Proposed, item.normalSem1Final, item.sem1NotAllFinal).let { (average, notice) ->
b.normalSemester1Layout.isVisible = average != null
b.normalSemester1Notice.isVisible = notice != null
b.normalSemester1.text = average
b.normalSemester1Notice.text = notice
if (average != null)
showAverages += 1
}
getSemesterString(app, item.normalSem2, item.normalSem2Proposed, item.normalSem2Final, item.sem2NotAllFinal).let { (average, notice) ->
b.normalSemester2Layout.isVisible = average != null
b.normalSemester2Notice.isVisible = notice != null
b.normalSemester2.text = average
b.normalSemester2Notice.text = notice
if (average != null)
showAverages += 2
}
getSemesterString(app, item.normalYearly, item.normalYearlyProposed, item.normalYearlyFinal, item.yearlyNotAllFinal).let { (average, notice) ->
b.normalYearlyLayout.isVisible = average != null
b.normalYearlyNotice.isVisible = notice != null
b.normalYearly.text = average
b.normalYearlyNotice.text = notice
if (average != null)
showAverages += 3
}
b.normalTitle.isVisible = showAverages.size > 0
b.normalLayout.isVisible = showAverages.size > 0
b.normalDivider.isVisible = showAverages.size > 0
b.helpButton.isVisible = showAverages.size > 0
b.normalDiv1.isVisible = showAverages.contains(1) && showAverages.contains(2) || showAverages.contains(1) && showAverages.contains(3)
b.normalDiv2.isVisible = showAverages.contains(2) && showAverages.contains(3)
getSemesterString(app, 0f, 0f, item.pointSem1, false).let { (average, _) ->
b.pointSemester1Layout.isVisible = average != null
b.pointSemester1.text = average
if (average != null)
showPoint += 1
}
getSemesterString(app, 0f, 0f, item.pointSem2, false).let { (average, _) ->
b.pointSemester2Layout.isVisible = average != null
b.pointSemester2.text = average
if (average != null)
showPoint += 2
}
getSemesterString(app, 0f, 0f, item.pointYearly, false).let { (average, _) ->
b.pointYearlyLayout.isVisible = average != null
b.pointYearly.text = average
if (average != null)
showPoint += 3
}
b.pointTitle.isVisible = showPoint.size > 0
b.pointLayout.isVisible = showPoint.size > 0
b.pointDivider.isVisible = showPoint.size > 0
b.pointDiv1.isVisible = showPoint.contains(1) && showPoint.contains(2)
b.pointDiv2.isVisible = showPoint.contains(2) && showPoint.contains(3)
b.noData.isVisible = showAverages.isEmpty() && showPoint.isEmpty()
b.disclaimer.isVisible = !b.noData.isVisible
b.helpButton.onClick {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.grades_stats_help_title)
.setMessage(R.string.grades_stats_help_text)
.setPositiveButton(R.string.ok, null)
.show()
}
b.customValueDivider.isVisible = manager.plusValue != null || manager.minusValue != null
b.customValueLayout.isVisible = b.customValueDivider.isVisible
b.customValueButton.onClick {
GradesConfigDialog(activity, reloadOnDismiss = true)
}
}
private fun getSemesterString(context: Context, expected: Float, proposed: Float, final: Float, notAllFinal: Boolean) : Pair<String?, String?> {
val format = DecimalFormat("#.##")
val average = when {
final != 0f -> final
proposed != 0f -> proposed
expected != 0f -> expected
else -> null
}?.let {
format.format(it)
}
val notice = when {
final != 0f -> when {
notAllFinal -> if (expected != 0f)
context.getString(R.string.grades_stats_from_final, format.format(expected))
else
context.getString(R.string.grades_stats_from_final_no_expected)
proposed != 0f -> context.getString(R.string.grades_stats_proposed_avg, format.format(proposed))
else -> null
}
proposed != 0f -> if (expected != 0f)
context.getString(R.string.grades_stats_from_proposed, format.format(expected))
else
context.getString(R.string.grades_stats_from_proposed_no_expected)
expected != 0f -> context.getString(R.string.grades_stats_expected)
else -> null
}
return average to notice
}
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-29.
*/
package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.get
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.GradesItemSubjectBinding
import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.setText
import pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
import pl.szczodrzynski.edziennik.utils.Themes
class SubjectViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemSubjectBinding = GradesItemSubjectBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesSubject> {
companion object {
private const val TAG = "SubjectViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: GradesSubject, position: Int) {
val manager = app.gradesManager
val contextWrapper = ContextThemeWrapper(activity, Themes.themeInt)
b.subjectName.text = item.subjectName
b.dropdownIcon.rotation = when (item.state) {
STATE_CLOSED -> 0f
else -> 180f
}
b.previewContainer.visibility = if (item.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE
b.yearSummary.visibility = if (item.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE
val gradesContainer = b.previewContainer[0]
b.previewContainer.removeAllViews()
b.gradesContainer.removeAllViews()
b.previewContainer.addView(gradesContainer)
val firstSemester = item.semesters.firstOrNull() ?: return
b.yearSummary.text = manager.getYearSummaryString(app, item.semesters.map { it.grades.size }.sum(), item.averages)
if (firstSemester.number != item.semester) {
b.gradesContainer.addView(TextView(contextWrapper).apply {
setText(R.string.grades_preview_other_semester, firstSemester.number)
setPadding(0, 0, 5.dp, 0)
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
})
}
/*if (firstSemester.grades.isEmpty()) {
b.previewContainer.addView(TextView(app).apply {
setText(R.string.grades_no_grades_in_semester, firstSemester.number)
})
}*/
for (grade in firstSemester.grades) {
b.gradesContainer.addView(GradeView(
contextWrapper,
grade,
manager,
periodGradesTextual = false
))
}
b.previewContainer.addView(TextView(contextWrapper).apply {
text = manager.getAverageString(app, firstSemester.averages, nameSemester = true, showSemester = firstSemester.number)
//gravity = Gravity.END
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, 0, 8.dp, 0)
}
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
})
firstSemester.proposedGrade?.let {
b.previewContainer.addView(GradeView(
contextWrapper,
it,
manager
))
}
firstSemester.finalGrade?.let {
b.previewContainer.addView(GradeView(
contextWrapper,
it,
manager
))
}
if (firstSemester.number == item.semester) {
b.previewContainer.addView(TextView(contextWrapper).apply {
text = manager.getAverageString(app, item.averages, nameSemester = true)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, 0, 8.dp, 0)
}
maxLines = 1
ellipsize = TextUtils.TruncateAt.END
})
item.proposedGrade?.let {
b.previewContainer.addView(GradeView(
contextWrapper,
it,
manager
))
}
item.finalGrade?.let {
b.previewContainer.addView(GradeView(
contextWrapper,
it,
manager
))
}
}
}
}

View File

@ -28,7 +28,12 @@ import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
@ -39,6 +44,7 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_DEFAULT
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel
import kotlin.coroutines.CoroutineContext
@ -122,7 +128,7 @@ class HomeGradesCard(
subject.grades1.onEach { grade ->
val gradeColor = when (App.config.forProfile().grades.colorMode) {
Profile.COLOR_MODE_DEFAULT -> grade.color
COLOR_MODE_DEFAULT -> grade.color
else -> Colors.gradeToColor(grade)
}

View File

@ -64,14 +64,14 @@ import static android.app.Activity.RESULT_OK;
import static pl.szczodrzynski.edziennik.ExtensionsKt.initDefaultLocale;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.REGISTRATION_DISABLED;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.REGISTRATION_ENABLED;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_1_SEM_2_SEM;
import static pl.szczodrzynski.edziennik.data.db.entity.Profile.YEAR_ALL_GRADES;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
import static pl.szczodrzynski.edziennik.utils.Utils.getRealPathFromURI;
import static pl.szczodrzynski.edziennik.utils.Utils.getResizedBitmap;
import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_AVG_2_AVG;
import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_SEM_2_SEM;
import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_ALL_GRADES;
public class SettingsNewFragment extends MaterialAboutFragment {

View File

@ -117,17 +117,17 @@ public class Colors {
public static int gradeToColor(Grade grade)
{
if (grade.type == Grade.TYPE_POINT_SUM) {
return grade.value < 0 ? 0xfff44336 : grade.value > 0 ? 0xff4caf50 : 0xffbdbdbd;
if (grade.getType() == Grade.TYPE_POINT_SUM) {
return grade.getValue() < 0 ? 0xfff44336 : grade.getValue() > 0 ? 0xff4caf50 : 0xffbdbdbd;
}
else if (grade.type == Grade.TYPE_POINT_AVG) {
return Color.parseColor("#"+gradeValueToColorStr(grade.value/grade.valueMax*100));
else if (grade.getType() == Grade.TYPE_POINT_AVG) {
return Color.parseColor("#"+gradeValueToColorStr(grade.getValue()/grade.getValueMax()*100));
}
else if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_DESCRIPTIVE_TEXT || grade.type == Grade.TYPE_TEXT) {
return grade.color;
else if (grade.getType() == Grade.TYPE_DESCRIPTIVE || grade.getType() == Grade.TYPE_DESCRIPTIVE_TEXT || grade.getType() == Grade.TYPE_TEXT) {
return grade.getColor();
}
else {
return Color.parseColor("#"+gradeNameToColorStr(grade.name));
return Color.parseColor("#"+gradeNameToColorStr(grade.getName()));
}
}

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-1.
*/
package pl.szczodrzynski.edziennik.utils.managers
import android.content.Context
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_AVG
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
import pl.szczodrzynski.edziennik.utils.Colors
import java.text.DecimalFormat
import kotlin.math.floor
class GradesManager(val app: App) {
companion object {
const val ORDER_BY_DATE_DESC = 0
const val ORDER_BY_SUBJECT_ASC = 1
const val ORDER_BY_DATE_ASC = 2
const val ORDER_BY_SUBJECT_DESC = 3
const val YEAR_1_AVG_2_AVG = 0
const val YEAR_1_SEM_2_AVG = 1
const val YEAR_1_AVG_2_SEM = 2
const val YEAR_1_SEM_2_SEM = 3
const val YEAR_ALL_GRADES = 4
const val COLOR_MODE_DEFAULT = 0
const val COLOR_MODE_WEIGHTED = 1
}
private val gradeRegex by lazy { """([0-6])([+-])?""".toRegex() }
private val format = DecimalFormat("#.##")
val orderBy
get() = app.config.grades.orderBy
val yearAverageMode
get() = app.config.forProfile().grades.yearAverageMode
val colorMode
get() = app.config.forProfile().grades.colorMode
val plusValue
get() = app.config.forProfile().grades.plusValue
val minusValue
get() = app.config.forProfile().grades.minusValue
val dontCountGrades
get() = app.config.forProfile().grades.dontCountGrades
fun getOrderByString() = when (orderBy) {
ORDER_BY_SUBJECT_ASC -> "subjectLongName ASC, gradeSemester DESC, addedDate DESC"
ORDER_BY_DATE_ASC -> "subjectId ASC, gradeSemester DESC, addedDate ASC"
ORDER_BY_SUBJECT_DESC -> "subjectLongName DESC, gradeSemester DESC, addedDate DESC"
else -> "subjectId ASC, gradeSemester DESC, addedDate DESC"
}
fun getWeightString(context: Context, grade: Grade, showClassAverage: Boolean = false) = when (grade.type) {
TYPE_NORMAL -> if (grade.weight == 0f)
context.getString(R.string.grades_weight_not_counted)
else
if (showClassAverage && (grade.classAverage ?: 0f) != 0f)
context.getString(R.string.grades_weight_format, format.format(grade.weight)+", "+format.format(grade.classAverage))
else
context.getString(R.string.grades_weight_format, format.format(grade.weight))
TYPE_POINT_AVG -> context.getString(R.string.grades_max_points_format, format.format(grade.valueMax))
else -> null
}
private fun gradeNameToColorStr(grade: String): String? {
when (grade.toLowerCase()) {
"+", "++", "+++" ->
return "4caf50"
"-", "-,", "-,-,", "np", "np.", "npnp", "np,", "np,np,", "bs", "nk" ->
return "ff7043"
"1-", "1", "f" ->
return "ff0000"
"1+", "ef" ->
return "ff3d00"
"2-", "2", "e" ->
return "ff9100"
"2+", "de" ->
return "ffab00"
"3-", "3", "d" ->
return "ffff00"
"3+", "cd" ->
return "c6ff00"
"4-", "4", "c" ->
return "76ff03"
"4+", "bc" ->
return "64dd17"
"5-", "5", "b" ->
return "00c853"
"5+", "ab" ->
return "00bfa5"
"6-", "6", "a" ->
return "2196f3"
"6+", "a+" ->
return "0091ea"
}
return "bdbdbd"
}
fun getRoundedGrade(value: Float): Int {
return floor(value.toDouble()).toInt() + if (value % 1.0f >= 0.75) 1 else 0
}
fun getGradeValue(grade: Grade): Float {
gradeRegex.find(grade.name)?.let {
var value = it[1].toFloatOrNull() ?: return grade.value
if (it[2] == "+")
value += plusValue ?: return grade.value
if (it[2] == "-")
value -= minusValue ?: return grade.value
return value
}
return grade.value
}
fun getGradeWeight(dontCountGrades: List<String>, grade: Grade): Float {
if (grade.name.toLowerCase() in dontCountGrades)
return 0f
return grade.weight
}
fun getColor(grade: Grade): Int {
return Colors.gradeToColor(grade)
}
fun calculateAverages(averages: GradesAverages, semesters: List<GradesSemester>? = null) {
if (averages.pointAvgMax != 0f)
averages.pointAvgPercent = averages.pointAvgSum / averages.pointAvgMax * 100f
if (averages.normalCount <= 0f) {
// no grades to count
return
}
if (semesters == null || yearAverageMode == YEAR_ALL_GRADES) {
// counting average for one semester
// or yearly, but just averaging every grade
averages.normalAvg = when {
averages.normalWeightedCount > 0f -> {
averages.normalWeightedSum / averages.normalWeightedCount
}
averages.normalSum > 0f && averages.normalCount > 0f -> {
averages.normalSum / averages.normalCount
}
else -> null
}
return
}
val yearAverageMode = yearAverageMode
averages.normalAvg = when (yearAverageMode) {
YEAR_1_AVG_2_AVG -> {
semesters.mapNotNull { it.averages.normalAvg }.average().toFloat()
}
else -> {
if (semesters.size >= 2) {
val sem1 = semesters.firstOrNull { it.number == 1 }
val sem2 = semesters.firstOrNull { it.number == 2 }
when (yearAverageMode) {
YEAR_1_SEM_2_AVG -> {
ifNotNull(sem1?.finalGrade?.value, sem2?.averages?.normalAvg) { s1, s2 ->
(s1 + s2) / 2f
}
}
YEAR_1_AVG_2_SEM -> {
ifNotNull(sem1?.averages?.normalAvg, sem2?.finalGrade?.value) { s1, s2 ->
(s1 + s2) / 2f
}
}
YEAR_1_SEM_2_SEM -> {
ifNotNull(sem1?.finalGrade?.value, sem2?.finalGrade?.value) { s1, s2 ->
(s1 + s2) / 2f
}
}
else -> null
}
}
else null
}
}
}
fun getAverageString(context: Context, averages: GradesAverages, nameSemester: Boolean = false, showSemester: Int? = null, noAverageRes: Int? = null): CharSequence? {
val averageString = when {
averages.normalAvg != null -> String.format("%.2f", averages.normalAvg)
averages.pointSum != 0f -> context.getString(R.string.grades_average_value_sum_format, format.format(averages.pointSum))
averages.pointAvgPercent != null -> context.getString(
R.string.grades_average_value_point_format,
format.format(averages.pointAvgSum),
format.format(averages.pointAvgMax),
format.format(averages.pointAvgPercent)
)
else -> return noAverageRes?.let { context.getString(it) }
}.asColoredSpannable(android.R.attr.textColorSecondary.resolveAttr(context))
return if (nameSemester) {
context.getString(
if (showSemester == null)
R.string.grades_average_year_format
else
R.string.grades_average_semester_format,
showSemester ?: 0,
averageString
)
}
else {
when {
averages.normalAvg != null -> context.getString(R.string.grades_average_normal_format, averageString)
averages.pointSum != 0f -> context.getString(R.string.grades_average_sum_format, averageString)
averages.pointAvgPercent != null -> context.getString(R.string.grades_average_point_format, averageString)
else -> null
}
}
}
fun getYearSummaryString(context: Context, gradeCount: Int, averages: GradesAverages): CharSequence {
val gradeCountString = context.plural(R.plurals.grades_format, gradeCount)
return context.getString(
R.string.grades_year_summary_format,
gradeCountString,
getAverageString(context, averages, noAverageRes = R.string.grades_average_no)
)
}
}

View File

@ -313,7 +313,7 @@ public class Date implements Comparable<Date> {
@Nullable
public String getRelativeString(Context context, int maxDiff) {
int diffDays = Date.diffDays(this, Date.getToday());
if (maxDiff != 0 && diffDays > maxDiff) {
if (maxDiff != 0 && Math.abs(diffDays) > maxDiff) {
return null;
}
return dayDiffString(context, diffDays);

View File

@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#00000000" />
<corners android:radius="4dp" />
<!--<solid android:color="?attr/windowBackground" />-->
<stroke android:color="#ffffff" android:width="2dp" />
</shape>
</shape>

View File

@ -5,4 +5,4 @@
<corners android:radius="8dp" />
<solid android:color="#ffffff" />
</shape>
</shape>

View File

@ -0,0 +1,16 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-4.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:pathData="m114,62c0,27.6 -22.4,50 -50,50s-50,-22.4 -50,-50 22.4,-50 50,-50 50,22.4 50,50z"
android:fillColor="#795bfa"/>
<path
android:pathData="m67.9,36.8 l4.46,12.6c0.57,1.61 2.07,2.71 3.77,2.76l11.9,0.375c3.94,0.121 5.49,5.18 2.31,7.51l-9.39,6.88c-1.45,1.06 -2.05,2.93 -1.5,4.64l4.39,11c1.2,3.71 -2.9,6.89 -6.17,4.77l-11.4,-6.81c-1.36,-0.883 -3.11,-0.883 -4.48,0l-11.4,6.81c-3.27,2.11 -7.37,-1.06 -6.17,-4.77l4.39,-11c0.551,-1.71 -0.055,-3.58 -1.5,-4.64l-9.39,-6.88c-3.18,-2.33 -1.63,-7.39 2.31,-7.51l11.9,-0.375c1.7,-0.051 3.2,-1.15 3.77,-2.76l4.46,-12.6c1.3,-3.69 6.49,-3.69 7.8,0z"
android:fillColor="#fff"/>
</vector>

View File

@ -3,7 +3,8 @@
~ Copyright (c) Kacper Ziubryniewicz 2020-1-20
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
@ -14,10 +15,62 @@
android:orientation="vertical"
android:padding="24dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<CheckBox
android:id="@+id/customPlusCheckBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="0dp"
android:text="Własna wartość plusa" />
<it.sephiroth.android.library.numberpicker.NumberPicker
android:id="@+id/customPlusValue"
android:layout_width="wrap_content"
android:layout_height="32dp"
app:picker_min="0.0"
app:picker_max="1.0"
app:picker_stepSize="0.01"
app:picker_disableGestures="true"
app:picker_orientation="horizontal" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<CheckBox
android:id="@+id/customMinusCheckBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="0dp"
android:text="Własna wartość minusa" />
<it.sephiroth.android.library.numberpicker.NumberPicker
android:id="@+id/customMinusValue"
android:layout_width="wrap_content"
android:layout_height="32dp"
app:picker_min="0.0"
app:picker_max="1.0"
app:picker_stepSize="0.01"
app:picker_disableGestures="true"
app:picker_orientation="horizontal" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginBottom="4dp"
style="@style/TextAppearance.AppCompat.Small"
android:text="@string/menu_grades_sort_mode"/>
@ -29,20 +82,22 @@
android:id="@+id/sortGradesByDateRadio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dialog_grades_config_sort_by_date"/>
android:minHeight="0dp"
android:text="@string/dialog_grades_config_sort_by_date" />
<RadioButton
android:id="@+id/sortGradesBySubjectRadio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/dialog_grades_config_sort_by_subject"/>
</RadioGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
style="@style/TextAppearance.AppCompat.Small"
android:text="@string/menu_grades_color_mode"/>
@ -54,20 +109,22 @@
android:id="@+id/gradeColorFromERegister"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/dialog_grades_config_color_from_eregister"/>
<RadioButton
android:id="@+id/gradeColorByValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/dialog_grades_config_color_by_value"/>
</RadioGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
style="@style/TextAppearance.AppCompat.Small"
android:text="@string/menu_grades_average_mode"/>
@ -79,30 +136,35 @@
android:id="@+id/gradeAverageMode4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/settings_register_avg_mode_4"/>
<RadioButton
android:id="@+id/gradeAverageMode0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/settings_register_avg_mode_0"/>
<RadioButton
android:id="@+id/gradeAverageMode1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/settings_register_avg_mode_1"/>
<RadioButton
android:id="@+id/gradeAverageMode2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/settings_register_avg_mode_2"/>
<RadioButton
android:id="@+id/gradeAverageMode3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/settings_register_avg_mode_3"/>
</RadioGroup>
@ -118,6 +180,7 @@
android:id="@+id/dontCountZeroToAverage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/settings_register_dont_count_zero_text"/>
</LinearLayout>
</ScrollView>

View File

@ -28,238 +28,252 @@
<variable
name="devMode"
type="boolean" />
<variable
name="gradeValue"
type="float" />
</data>
<LinearLayout
android:orientation="vertical"
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeName"
android:layout_width="72dp"
android:layout_height="72dp"
android:background="@drawable/bg_rounded_16dp"
android:fontFamily="serif-monospace"
android:gravity="center"
android:padding="8dp"
android:text="@{grade.name}"
android:textIsSelectable="true"
android:textSize="36sp"
android:textStyle="bold"
app:autoSizeMaxTextSize="56sp"
app:autoSizeTextType="uniform"
tools:background="#ff4caf50"
tools:text="1-" />
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_gravity="center_vertical"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeSemester"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="@style/NavView.TextView.Helper"
android:textIsSelectable="true"
android:text="@{@string/dialog_grade_details_semester_format(grade.semester)}"
tools:text="semestr 1"/>
</LinearLayout>
<TextView
android:id="@+id/gradeSubjectName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{grade.subjectLongName}"
android:textIsSelectable="true"
android:textAppearance="@style/NavView.TextView.Title"
tools:text="pracownia urządzeń techniki komputerowej" />
<TextView
android:id="@+id/gradeWeight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{weightText}"
android:textIsSelectable="true"
android:textAppearance="@style/NavView.TextView.Subtitle"
android:visibility="@{weightText != null ? View.VISIBLE : View.GONE}"
tools:text="waga 3"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_grade_details_teacher" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{grade.teacherFullName}"
android:textIsSelectable="true"
tools:text="Janósz Kowalski" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_grade_details_category" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Utils.ns(@string/dialog_grade_details_no_category, grade.category)}"
android:textIsSelectable="true"
tools:text="@string/dialog_grade_details_no_category" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_grade_details_description" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Utils.ns(@string/dialog_grade_details_no_description, grade.description)}"
android:textIsSelectable="true"
tools:text="@string/dialog_grade_details_no_description" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{grade.classAverage > -1 ? View.VISIBLE : View.GONE}"
android:text="@string/dialog_grade_details_class_average" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{@string/dialog_grade_details_class_average_format(grade.classAverage)}"
android:textIsSelectable="true"
android:visibility="@{grade.classAverage > -1 ? View.VISIBLE : View.GONE}"
tools:text="1.72" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{commentVisible ? View.VISIBLE : View.GONE}"
android:text="@string/dialog_grade_details_comment" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
tools:text="Uczeń jest idiotą i nie umie robić nic w excelu. Niestety nie zdał tej klasy więc ta ocena i tak mu nic nie da, więc a cotam, dam mu jedynkę kolejną.. XDD"
android:textIsSelectable="true"
android:visibility="@{commentVisible ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_grade_details_value"
android:visibility="@{grade.value > -1 ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{@string/dialog_grade_details_class_average_format(grade.value)}"
android:textIsSelectable="true"
android:visibility="@{grade.value > -1 ? View.VISIBLE : View.GONE}"
tools:text="4.75" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_grade_details_added_date" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{@string/dialog_grade_details_date_format(Date.fromMillis(grade.addedDate).getFormattedString(), Time.fromMillis(grade.addedDate).getStringHM())}"
android:textIsSelectable="true"
tools:text="May 10, 12:03" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{devMode ? View.VISIBLE : View.GONE}"
android:text="@string/dialog_grade_details_id" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Long.toString(grade.id)}"
android:textIsSelectable="true"
android:visibility="@{devMode ? View.VISIBLE : View.GONE}"
tools:text="12345" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Small"
android:visibility="@{grade.parentId != -1 ? View.VISIBLE : View.GONE}"
android:text="@string/dialog_grade_details_improved" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{historyVisible ? View.VISIBLE : View.GONE}"
android:text="@string/dialog_grade_details_history" />
<androidx.core.widget.NestedScrollView
android:id="@+id/gradeHistoryNest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{historyVisible ? View.VISIBLE : View.GONE}">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradeHistoryList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/row_grades_list_item" />
android:orientation="horizontal">
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/gradeName"
android:layout_width="72dp"
android:layout_height="72dp"
android:background="@drawable/bg_rounded_16dp"
android:fontFamily="serif-monospace"
android:gravity="center"
android:padding="8dp"
android:text="@{grade.name}"
android:textIsSelectable="true"
android:textSize="36sp"
android:textStyle="bold"
app:autoSizeMaxTextSize="56sp"
app:autoSizeTextType="uniform"
tools:background="#ff4caf50"
tools:text="1-" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeSemester"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@{@string/dialog_grade_details_semester_format(grade.semester)}"
android:textAppearance="@style/NavView.TextView.Helper"
android:textIsSelectable="true"
tools:text="semestr 1" />
</LinearLayout>
<TextView
android:id="@+id/gradeSubjectName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{grade.subjectLongName}"
android:textAppearance="@style/NavView.TextView.Title"
android:textIsSelectable="true"
tools:text="pracownia urządzeń techniki komputerowej" />
<TextView
android:id="@+id/gradeWeight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{weightText}"
android:textAppearance="@style/NavView.TextView.Subtitle"
android:textIsSelectable="true"
android:visibility="@{weightText != null ? View.VISIBLE : View.GONE}"
tools:text="waga 3"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/dialog_grade_details_teacher"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{grade.teacherFullName}"
android:textIsSelectable="true"
tools:text="Janósz Kowalski" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_category"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Utils.ns(@string/dialog_grade_details_no_category, grade.category)}"
android:textIsSelectable="true"
tools:text="@string/dialog_grade_details_no_category" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_description"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Utils.ns(@string/dialog_grade_details_no_description, grade.description)}"
android:textIsSelectable="true"
tools:text="@string/dialog_grade_details_no_description" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_class_average"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{grade.classAverage != null &amp;&amp; grade.classAverage != -1 ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{@string/dialog_grade_details_class_average_format(grade.classAverage)}"
android:textIsSelectable="true"
android:visibility="@{grade.classAverage != null &amp;&amp; grade.classAverage != -1 ? View.VISIBLE : View.GONE}"
tools:text="1.72" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_comment"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{commentVisible ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:textIsSelectable="true"
android:visibility="@{commentVisible ? View.VISIBLE : View.GONE}"
tools:text="Uczeń jest idiotą i nie umie robić nic w excelu. Niestety nie zdał tej klasy więc ta ocena i tak mu nic nie da, więc a cotam, dam mu jedynkę kolejną.. XDD" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_value"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{gradeValue != -1 ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{@string/dialog_grade_details_class_average_format(gradeValue)}"
android:textIsSelectable="true"
android:visibility="@{gradeValue != -1 ? View.VISIBLE : View.GONE}"
tools:text="4.75" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_added_date"
android:textAppearance="@style/NavView.TextView.Helper" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{@string/dialog_grade_details_date_format(Date.fromMillis(grade.addedDate).getFormattedString(), Time.fromMillis(grade.addedDate).getStringHM())}"
android:textIsSelectable="true"
tools:text="May 10, 12:03" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_id"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{devMode ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@{Long.toString(grade.id)}"
android:textIsSelectable="true"
android:visibility="@{devMode ? View.VISIBLE : View.GONE}"
tools:text="12345" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_improved"
android:textAppearance="@style/NavView.TextView.Small"
android:visibility="@{grade.parentId instanceof Long ? View.VISIBLE : View.GONE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/dialog_grade_details_history"
android:textAppearance="@style/NavView.TextView.Helper"
android:visibility="@{historyVisible ? View.VISIBLE : View.GONE}" />
<androidx.core.widget.NestedScrollView
android:id="@+id/gradeHistoryNest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-8dp"
android:layout_marginRight="-8dp"
android:visibility="@{historyVisible ? View.VISIBLE : View.GONE}">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradeHistoryList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/row_grades_list_item" />
</androidx.core.widget.NestedScrollView>
</LinearLayout>
</ScrollView>
</layout>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-4.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/gradesNoData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
android:text="@string/grades_no_data"
android:textSize="24sp"
app:drawableTopCompat="@drawable/ic_no_grades" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradesRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/grades_item_subject" />
</FrameLayout>
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
</layout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-1.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="center"
android:textSize="18sp"
android:textColor="?android:textColorSecondary"
android:textStyle="italic"
android:text="Nie ma ocen w tym semestrze."/>
</LinearLayout>
</layout>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-2-29.
-->
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
android:id="@+id/gradeName"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
tools:text="5+" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
tools:text="kraje hehe no jak zwykle jedynka z geografii. to jest baaardzo długi tekst ale szkoda że się nie scrolluje." />
<TextView
android:id="@+id/gradeAddedDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:text="14.10.2015" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/gradeWeight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:textStyle="bold"
tools:text="waga 30"
tools:visibility="visible" />
<TextView
android:id="@+id/gradeCategory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:ellipsize="end"
android:maxWidth="200dp"
android:maxLines="1"
tools:text="Kartkówki - K1 123456789 12345678" />
<TextView
android:id="@+id/gradeTeacherName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:text="Anna Jakaśtam-Cośtam" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</layout>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-2-29.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/dropdownIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:scaleType="centerInside"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-chevron-down"
app:iiv_size="16dp"
tools:src="@android:drawable/ic_menu_more" />
<TextView
android:id="@+id/semesterName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:textColor="?android:textColorPrimary"
android:textSize="18sp"
tools:text="Semestr 1" />
<TextView
android:id="@+id/average"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:gravity="end"
android:textSize="14sp"
tools:text="punkty: 18.75/20\n(95,67%)"
tools:text1="suma: 175 pkt"
tools:text2="średnia: 4,78" />
<pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
android:id="@+id/proposedGrade"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
tools:layout_marginRight="5dp"
tools:visibility="visible" />
<pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
android:id="@+id/finalGrade"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
tools:layout_marginRight="5dp"
tools:visibility="visible" />
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/editButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:background="?selectableItemBackgroundBorderless"
android:scaleType="centerInside"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-playlist-edit"
app:iiv_size="16dp"
tools:src="@android:drawable/ic_menu_edit" />
</LinearLayout>
</layout>

View File

@ -0,0 +1,334 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-3-3.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="32dp"
android:background="@drawable/divider" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/grades_stats_title"
android:textAppearance="@style/NavView.TextView.Title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/noData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:fontFamily="sans-serif-light"
android:text="@string/grades_stats_no_data"
android:textSize="16sp"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/normalTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="@string/grades_stats_normal"
android:textAppearance="@style/NavView.TextView.Subtitle" />
<LinearLayout
android:id="@+id/normalLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp">
<LinearLayout
android:id="@+id/normalSemester1Layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_semester_1" />
<TextView
android:id="@+id/normalSemester1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="4,56" />
<TextView
android:id="@+id/normalSemester1Notice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="@style/NavView.TextView.Helper"
tools:text="*z ocen proponowanych" />
</LinearLayout>
<View
android:id="@+id/normalDiv1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<LinearLayout
android:id="@+id/normalSemester2Layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_semester_2" />
<TextView
android:id="@+id/normalSemester2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="4,67" />
<TextView
android:id="@+id/normalSemester2Notice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="@style/NavView.TextView.Helper"
tools:text="*przewidywana średnia" />
</LinearLayout>
<View
android:id="@+id/normalDiv2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<LinearLayout
android:id="@+id/normalYearlyLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_yearly" />
<TextView
android:id="@+id/normalYearly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="3,75" />
<TextView
android:id="@+id/normalYearlyNotice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="@style/NavView.TextView.Helper"
tools:text="*przewidywana średnia" />
</LinearLayout>
</LinearLayout>
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/helpButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end"
android:background="?selectableItemBackgroundBorderless"
android:scaleType="centerInside"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-help-circle-outline"
app:iiv_size="16dp"
tools:src="@android:drawable/ic_menu_help" />
<View
android:id="@+id/normalDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<TextView
android:id="@+id/pointTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/grades_stats_point"
android:textAppearance="@style/NavView.TextView.Subtitle" />
<LinearLayout
android:id="@+id/pointLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp">
<LinearLayout
android:id="@+id/pointSemester1Layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_semester_1" />
<TextView
android:id="@+id/pointSemester1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="95%" />
</LinearLayout>
<View
android:id="@+id/pointDiv1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<LinearLayout
android:id="@+id/pointSemester2Layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_semester_2" />
<TextView
android:id="@+id/pointSemester2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="93,27%" />
</LinearLayout>
<View
android:id="@+id/pointDiv2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<LinearLayout
android:id="@+id/pointYearlyLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grades_stats_yearly" />
<TextView
android:id="@+id/pointYearly"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textSize="24sp"
tools:text="94,50%" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/pointDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<TextView
android:id="@+id/disclaimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/grades_stats_disclaimer"
android:textAppearance="@style/NavView.TextView.Helper"
android:textSize="12sp"
android:textStyle="italic" />
<View
android:id="@+id/customValueDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="@drawable/divider" />
<LinearLayout
android:id="@+id/customValueLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:text="@string/grades_stats_custom_value_notice"
android:textAppearance="@style/NavView.TextView.Helper"
android:textSize="12sp"
android:textStyle="italic" />
<Button
android:id="@+id/customValueButton"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:minHeight="0dp"
android:text="@string/configure" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-2-29.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?selectableItemBackground">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/divider"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/subjectName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:maxLines="2"
android:textColor="?android:textColorPrimary"
android:textSize="20sp"
tools:text="systemy operacyjne\n1234" />
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/dropdownIcon"
android:layout_width="24dp"
android:layout_height="36dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:scaleType="centerInside"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-chevron-down"
app:iiv_size="18dp"
tools:src="@android:drawable/ic_menu_more" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp">
<LinearLayout
android:id="@+id/previewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:visibility="visible"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/gradesContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:orientation="horizontal" />
</LinearLayout>
<TextView
android:id="@+id/yearSummary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="14sp"
android:visibility="gone"
tools:text1="Cały rok: 3 oceny • suma: 320 pkt"
tools:text2="Cały rok: 15 ocen • średnia: 2,62"
tools:text="Cały rok: 6 ocen • punkty: 34.20/40 (87.5%)"
tools:visibility="gone"/>
</FrameLayout>
</LinearLayout>
</layout>

View File

@ -175,7 +175,7 @@
<string name="dialog_grade_details_description">Description</string>
<string name="dialog_grade_details_history">Grade history</string>
<string name="dialog_grade_details_id">Grade ID</string>
<string name="dialog_grade_details_improved">Grade is hidden - it was improved</string>
<string name="dialog_grade_details_improved">The grade is improved by another one</string>
<string name="dialog_grade_details_no_category">(no category)</string>
<string name="dialog_grade_details_no_description">(no description)</string>
<string name="dialog_grade_details_semester_format">semester %d</string>
@ -273,7 +273,7 @@
<string name="grades_editor_weight_other">other weight</string>
<string name="grades_improvement_category_format">(resit) %s</string>
<string name="grades_max_points_format">max %s pts</string>
<string name="grades_no_data">No subjects to show</string>
<string name="grades_no_data">No grades.</string>
<string name="grades_semester1_header">Semester 1</string>
<string name="grades_semester2_header">Semester 2</string>
<string name="grades_semester_average_format">semester %d: %s</string>

View File

@ -153,4 +153,10 @@
<item quantity="few">%d inne powiadomienia</item> <!-- 2, 3, 4, 32, 33, 34 -->
<item quantity="other">%d innych powiadomień</item> <!-- 5, 10, 12, 13, 21, 25 -->
</plurals>
</resources>
<plurals name="grades_format">
<item quantity="one">%d ocena</item> <!-- 1 -->
<item quantity="few">%d oceny</item> <!-- 2, 3, 4, 32, 33, 34 -->
<item quantity="other">%d ocen</item> <!-- 5, 10, 12, 13, 21, 25 -->
</plurals>
</resources>

View File

@ -208,7 +208,7 @@
<string name="dialog_grade_details_description">Opis</string>
<string name="dialog_grade_details_history">Historia oceny</string>
<string name="dialog_grade_details_id">ID oceny</string>
<string name="dialog_grade_details_improved">Ocena jest ukryta - została poprawiona</string>
<string name="dialog_grade_details_improved">Ocena została poprawiona</string>
<string name="dialog_grade_details_no_category">(brak kategorii)</string>
<string name="dialog_grade_details_no_description">(brak opisu)</string>
<string name="dialog_grade_details_semester_format">semestr %d</string>
@ -311,7 +311,7 @@
<string name="grades_editor_weight_other">inna waga</string>
<string name="grades_improvement_category_format">(poprawa) %s</string>
<string name="grades_max_points_format">max %s pkt</string>
<string name="grades_no_data">Brak przedmiotów do wyświetlenia</string>
<string name="grades_no_data">Brak ocen w dzienniku.</string>
<string name="grades_semester1_header">Semestr 1</string>
<string name="grades_semester2_header">Semestr 2</string>
<string name="grades_semester_average_format">semestr %d: %s</string>
@ -1195,4 +1195,42 @@
<string name="hint_save_in_calendar">Zapisz do kalendarza</string>
<string name="hint_edit_event">Edytuj wydarzenie</string>
<string name="hint_go_to_timetable">Idź do planu lekcji</string>
<string name="today">dzisiaj</string>
<string name="tomorrow">jutro</string>
<string name="tag_key_model">model</string>
<string name="tag_key_view_type">viewType</string>
<string name="tag_key_position">position</string>
<string name="grades_semester_format">Semestr %d</string>
<string name="grades_preview_other_semester">Semestr %d</string>
<!--<string name="grades_average_value_point_format">%s/%s (%s%%)</string>-->
<string name="grades_average_value_point_format">%3$s%%</string>
<string name="grades_average_value_sum_format">%s pkt</string>
<string name="grades_average_normal_format">średnia: %s</string>
<string name="grades_average_sum_format">suma: %s</string>
<string name="grades_average_point_format">punkty: %s</string>
<string name="grades_average_semester_format">sem. %1$d: %2$s</string>
<string name="grades_average_year_format">roczna: %2$s</string>
<string name="grades_year_summary_format">Cały rok: %s • %s</string>
<string name="grades_average_no">brak średniej</string>
<string name="grades_stats_semester_1">semestr 1</string>
<string name="grades_stats_semester_2">semestr 2</string>
<string name="grades_stats_yearly">całoroczna</string>
<string name="grades_stats_disclaimer">* średnie ocen są poglądowe i mogą się różnić, w zależności od ustawień szkoły</string>
<string name="grades_stats_title">Statystyka ocen</string>
<string name="grades_stats_normal">Średnia zwykłych ocen</string>
<string name="grades_stats_point">Średnia przedmiotów punktowych</string>
<string name="grades_stats_no_data">Zbyt mało danych do wyliczenia średniej.</string>
<string name="grades_stats_expected">*przewidywana średnia</string>
<string name="grades_stats_from_proposed">*z ocen proponowanych\nPrzewidywana: %s</string>
<string name="grades_stats_from_proposed_no_expected">*z ocen proponowanych</string>
<string name="grades_stats_from_final">*z ocen końcowych\nPrzewidywana: %s</string>
<string name="grades_stats_from_final_no_expected">*z ocen końcowych</string>
<string name="grades_stats_proposed_avg">Śr. ocen proponowanych:\n%s</string>
<string name="grades_stats_help_title">Obliczanie średniej wszystkich przedmiotów</string>
<string name="grades_stats_help_text">Ocena przewidywana z danego przedmiotu jest obliczana na podstawie aktualnej średniej ważonej.\n\nOcena jest liczbą całkowitą, jaką wystawił by nauczyciel bazując na średniej. Liczba zaokrąglona jest w górę jeśli część po przecinku przekroczy ,75.\nPrzykładowo: średnie 3,75 jak również 4,74 dają w wyniku ocenę dobrą (4).\n\nŚrednia przewidywana ze wszystkich przedmiotów obejmuje obliczone w ten sposób oceny końcowe.</string>
<string name="grades_stats_custom_value_notice">Została ustawiona własna wartość plusa/minusa. Jeśli uważasz, że średnia się nie zgadza, kliknij Konfiguruj.</string>
<string name="configure">Konfiguruj</string>
</resources>