Allow expanding multiple subject' grades at once (#1584)

This commit is contained in:
Michael 2021-10-24 01:23:36 +02:00 committed by GitHub
parent 94fd303f8e
commit 0f800b61f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 187 additions and 65 deletions

View File

@ -193,7 +193,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration38(), Migration38(),
Migration39(), Migration39(),
Migration40(), Migration40(),
Migration41(), Migration41(sharedPrefProvider),
Migration42() Migration42()
) )

View File

@ -2,10 +2,20 @@ package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
class Migration41 : Migration(40, 41) { class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
migrateSharedPreferences()
database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0") database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0")
} }
private fun migrateSharedPreferences() {
if (sharedPrefProvider.getBoolean("pref_key_expand_grade", false)) {
sharedPrefProvider.putString("pref_key_expand_grade_mode", GradeExpandMode.ALWAYS_EXPANDED.value)
}
sharedPrefProvider.delete("pref_key_expand_grade")
}
} }

View File

@ -10,6 +10,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.sdk.toLocalDate import io.github.wulkanowy.sdk.toLocalDate
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode import io.github.wulkanowy.ui.modules.grade.GradeSortingMode
import io.github.wulkanowy.utils.toLocalDateTime import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp import io.github.wulkanowy.utils.toTimestamp
@ -19,6 +20,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.lang.ClassCastException
import java.lang.IllegalStateException
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import javax.inject.Inject import javax.inject.Inject
@ -56,8 +59,13 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_grade_average_force_calc R.bool.pref_default_grade_average_force_calc
) )
val isGradeExpandable: Boolean val gradeExpandMode: GradeExpandMode
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade) get() = GradeExpandMode.getByValue(
getString(
R.string.pref_key_expand_grade_mode,
R.string.pref_default_expand_grade_mode
)
)
val showAllSubjectsOnStatisticsList: Boolean val showAllSubjectsOnStatisticsList: Boolean
get() = getBoolean( get() = getBoolean(
@ -265,6 +273,9 @@ class PreferencesRepository @Inject constructor(
private fun getBoolean(id: String, default: Int) = private fun getBoolean(id: String, default: Int) =
sharedPref.getBoolean(id, context.resources.getBoolean(default)) sharedPref.getBoolean(id, context.resources.getBoolean(default))
private fun getBoolean(id: Int, default: Boolean) =
sharedPref.getBoolean(context.getString(id), default)
private companion object { private companion object {
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position" private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"

View File

@ -0,0 +1,9 @@
package io.github.wulkanowy.ui.modules.grade
enum class GradeExpandMode(val value: String) {
ONE("one"), UNLIMITED("any"), ALWAYS_EXPANDED("always");
companion object {
fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ONE
}
}

View File

@ -5,6 +5,7 @@ import android.content.res.Resources
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.NO_POSITION import androidx.recyclerview.widget.RecyclerView.NO_POSITION
@ -13,9 +14,11 @@ import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding
import io.github.wulkanowy.databinding.ItemGradeDetailsBinding import io.github.wulkanowy.databinding.ItemGradeDetailsBinding
import io.github.wulkanowy.ui.base.BaseExpandableAdapter import io.github.wulkanowy.ui.base.BaseExpandableAdapter
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
import io.github.wulkanowy.utils.getBackgroundColor import io.github.wulkanowy.utils.getBackgroundColor
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import timber.log.Timber import timber.log.Timber
import java.util.BitSet
import javax.inject.Inject import javax.inject.Inject
class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<RecyclerView.ViewHolder>() { class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<RecyclerView.ViewHolder>() {
@ -24,19 +27,20 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
private var items = mutableListOf<GradeDetailsItem>() private var items = mutableListOf<GradeDetailsItem>()
private var expandedPosition = NO_POSITION private val expandedPositions = BitSet(items.size)
private var isExpandable = false private var expandMode = GradeExpandMode.ONE
var onClickListener: (Grade, position: Int) -> Unit = { _, _ -> } var onClickListener: (Grade, position: Int) -> Unit = { _, _ -> }
var colorTheme = "" var colorTheme = ""
fun setDataItems(data: List<GradeDetailsItem>, isExpanded: Boolean = isExpandable) { fun setDataItems(data: List<GradeDetailsItem>, expandMode: GradeExpandMode = this.expandMode) {
headers = data.filter { it.viewType == ViewType.HEADER }.toMutableList() headers = data.filter { it.viewType == ViewType.HEADER }.toMutableList()
items = if (isExpanded) headers else data.toMutableList() items =
isExpandable = isExpanded (if (expandMode != GradeExpandMode.ALWAYS_EXPANDED) headers else data).toMutableList()
expandedPosition = NO_POSITION this.expandMode = expandMode
expandedPositions.clear()
} }
fun updateDetailsItem(position: Int, grade: Grade) { fun updateDetailsItem(position: Int, grade: Grade) {
@ -48,7 +52,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
val candidates = headers.filter { (it.value as GradeDetailsHeader).subject == subject } val candidates = headers.filter { (it.value as GradeDetailsHeader).subject == subject }
if (candidates.size > 1) { if (candidates.size > 1) {
Timber.e("Header with subject $subject found ${candidates.size} times! Expanded: $expandedPosition. Items: $candidates") Timber.e("Header with subject $subject found ${candidates.size} times! Expanded: $expandedPositions. Items: $candidates")
} }
return candidates.first() return candidates.first()
@ -64,9 +68,9 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
} }
fun collapseAll() { fun collapseAll() {
if (expandedPosition != -1) { if (!expandedPositions.isEmpty) {
refreshList(headers) refreshList(headers.toMutableList())
expandedPosition = NO_POSITION expandedPositions.clear()
} }
} }
@ -86,8 +90,12 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return when (viewType) { return when (viewType) {
ViewType.HEADER.id -> HeaderViewHolder(HeaderGradeDetailsBinding.inflate(inflater, parent, false)) ViewType.HEADER.id -> HeaderViewHolder(
ViewType.ITEM.id -> ItemViewHolder(ItemGradeDetailsBinding.inflate(inflater, parent, false)) HeaderGradeDetailsBinding.inflate(inflater, parent, false)
)
ViewType.ITEM.id -> ItemViewHolder(
ItemGradeDetailsBinding.inflate(inflater, parent, false)
)
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
@ -106,46 +114,91 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
} }
} }
private fun bindHeaderViewHolder(holder: HeaderViewHolder, header: GradeDetailsHeader, position: Int) { private fun bindHeaderViewHolder(
val headerPosition = headers.indexOf(items[position]) holder: HeaderViewHolder,
val adapterPosition = holder.bindingAdapterPosition header: GradeDetailsHeader,
position: Int
) {
val context = holder.binding.root.context
val item = items[position]
val headerPosition = headers.indexOf(item)
with(holder.binding) { with(holder.binding) {
gradeHeaderDivider.visibility = if (adapterPosition == 0) View.GONE else View.VISIBLE gradeHeaderDivider.isVisible = holder.bindingAdapterPosition != 0
with(gradeHeaderSubject) { with(gradeHeaderSubject) {
text = header.subject text = header.subject
maxLines = if (headerPosition == expandedPosition) 2 else 1 maxLines = if (expandedPositions[headerPosition]) 2 else 1
} }
gradeHeaderAverage.text = formatAverage(header.average, root.context.resources) gradeHeaderAverage.text = formatAverage(header.average, root.context.resources)
gradeHeaderPointsSum.text = root.context.getString(R.string.grade_points_sum, header.pointsSum) gradeHeaderPointsSum.text =
gradeHeaderPointsSum.visibility = if (!header.pointsSum.isNullOrEmpty()) View.VISIBLE else View.GONE context.getString(R.string.grade_points_sum, header.pointsSum)
gradeHeaderNumber.text = root.context.resources.getQuantityString(R.plurals.grade_number_item, header.grades.size, header.grades.size) gradeHeaderPointsSum.isVisible = !header.pointsSum.isNullOrEmpty()
gradeHeaderNote.visibility = if (header.newGrades > 0) View.VISIBLE else View.GONE gradeHeaderNumber.text = context.resources.getQuantityString(
if (header.newGrades > 0) gradeHeaderNote.text = header.newGrades.toString(10) R.plurals.grade_number_item,
header.grades.size,
header.grades.size
)
gradeHeaderNote.isVisible = header.newGrades > 0
gradeHeaderContainer.isEnabled = isExpandable if (header.newGrades > 0) {
gradeHeaderNote.text = header.newGrades.toString()
}
gradeHeaderContainer.isEnabled = expandMode != GradeExpandMode.ALWAYS_EXPANDED
gradeHeaderContainer.setOnClickListener { gradeHeaderContainer.setOnClickListener {
expandedPosition = if (expandedPosition == adapterPosition) -1 else adapterPosition expandGradeHeader(headerPosition, header, holder)
if (expandedPosition != NO_POSITION) {
refreshList(headers.toMutableList().apply {
addAll(headerPosition + 1, header.grades)
})
scrollToHeaderWithSubItems(headerPosition, header.grades.size)
} else {
refreshList(headers)
}
} }
} }
} }
private fun formatAverage(average: Double?, resources: Resources): String { private fun expandGradeHeader(
return if (average == null || average == .0) resources.getString(R.string.grade_no_average) headerPosition: Int,
else resources.getString(R.string.grade_average, average) header: GradeDetailsHeader,
holder: HeaderViewHolder
) {
if (expandMode == GradeExpandMode.ONE) {
val isHeaderExpanded = expandedPositions[headerPosition]
expandedPositions.clear()
if (!isHeaderExpanded) {
val updatedItemList = headers.toMutableList()
.apply { addAll(headerPosition + 1, header.grades) }
expandedPositions.set(headerPosition)
refreshList(updatedItemList)
scrollToHeaderWithSubItems(headerPosition, header.grades.size)
} else {
refreshList(headers.toMutableList())
}
} else if (expandMode == GradeExpandMode.UNLIMITED) {
val headerAdapterPosition = holder.bindingAdapterPosition
val isHeaderExpanded = expandedPositions[headerPosition]
expandedPositions.flip(headerPosition)
if (!isHeaderExpanded) {
val updatedList = items.toMutableList()
.apply { addAll(headerAdapterPosition + 1, header.grades) }
refreshList(updatedList)
scrollToHeaderWithSubItems(headerAdapterPosition, header.grades.size)
} else {
val startPosition = headerAdapterPosition + 1
val updatedList = items.toMutableList()
.apply {
subList(startPosition, startPosition + header.grades.size).clear()
}
refreshList(updatedList)
}
}
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun bindItemViewHolder(holder: ItemViewHolder, grade: Grade) { private fun bindItemViewHolder(holder: ItemViewHolder, grade: Grade) {
val context = holder.binding.root.context
with(holder.binding) { with(holder.binding) {
gradeItemValue.run { gradeItemValue.run {
text = grade.entry text = grade.entry
@ -154,26 +207,37 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
gradeItemDescription.text = when { gradeItemDescription.text = when {
grade.description.isNotBlank() -> grade.description grade.description.isNotBlank() -> grade.description
grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol
else -> root.context.getString(R.string.all_no_description) else -> context.getString(R.string.all_no_description)
} }
gradeItemDate.text = grade.date.toFormattedString() gradeItemDate.text = grade.date.toFormattedString()
gradeItemWeight.text = "${root.context.getString(R.string.grade_weight)}: ${grade.weight}" gradeItemWeight.text = "${context.getString(R.string.grade_weight)}: ${grade.weight}"
gradeItemNote.visibility = if (!grade.isRead) View.VISIBLE else View.GONE gradeItemNote.visibility = if (!grade.isRead) View.VISIBLE else View.GONE
root.setOnClickListener { root.setOnClickListener {
holder.bindingAdapterPosition.let { if (it != NO_POSITION) onClickListener(grade, it) } holder.bindingAdapterPosition.let {
if (it != NO_POSITION) onClickListener(grade, it)
}
} }
} }
} }
private fun formatAverage(average: Double?, resources: Resources) =
if (average == null || average == .0) {
resources.getString(R.string.grade_no_average)
} else {
resources.getString(R.string.grade_average, average)
}
private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) : private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
private class ItemViewHolder(val binding: ItemGradeDetailsBinding) : private class ItemViewHolder(val binding: ItemGradeDetailsBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
class GradeDetailsDiffUtil(private val old: List<GradeDetailsItem>, private val new: List<GradeDetailsItem>) : private class GradeDetailsDiffUtil(
DiffUtil.Callback() { private val old: List<GradeDetailsItem>,
private val new: List<GradeDetailsItem>
) : DiffUtil.Callback() {
override fun getOldListSize() = old.size override fun getOldListSize() = old.size

View File

@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
import io.github.wulkanowy.databinding.FragmentGradeDetailsBinding import io.github.wulkanowy.databinding.FragmentGradeDetailsBinding
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
@ -79,10 +80,10 @@ class GradeDetailsFragment :
else false else false
} }
override fun updateData(data: List<GradeDetailsItem>, isGradeExpandable: Boolean, gradeColorTheme: String) { override fun updateData(data: List<GradeDetailsItem>, expandMode: GradeExpandMode, gradeColorTheme: String) {
with(gradeDetailsAdapter) { with(gradeDetailsAdapter) {
colorTheme = gradeColorTheme colorTheme = gradeColorTheme
setDataItems(data, isGradeExpandable) setDataItems(data, expandMode)
notifyDataSetChanged() notifyDataSetChanged()
} }
} }

View File

@ -9,6 +9,7 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE
import io.github.wulkanowy.ui.modules.grade.GradeSubject import io.github.wulkanowy.ui.modules.grade.GradeSubject
@ -113,7 +114,7 @@ class GradeDetailsPresenter @Inject constructor(
fun onParentViewReselected() { fun onParentViewReselected() {
view?.run { view?.run {
if (!isViewEmpty) { if (!isViewEmpty) {
if (preferencesRepository.isGradeExpandable) collapseAllItems() if (preferencesRepository.gradeExpandMode != GradeExpandMode.ALWAYS_EXPANDED) collapseAllItems()
scrollToStart() scrollToStart()
} }
} }
@ -157,7 +158,7 @@ class GradeDetailsPresenter @Inject constructor(
showContent(true) showContent(true)
updateData( updateData(
data = items, data = items,
isGradeExpandable = preferencesRepository.isGradeExpandable, expandMode = preferencesRepository.gradeExpandMode,
gradeColorTheme = preferencesRepository.gradeColorTheme gradeColorTheme = preferencesRepository.gradeColorTheme
) )
notifyParentDataLoaded(semesterId) notifyParentDataLoaded(semesterId)
@ -175,7 +176,7 @@ class GradeDetailsPresenter @Inject constructor(
showContent(items.isNotEmpty()) showContent(items.isNotEmpty())
updateData( updateData(
data = items, data = items,
isGradeExpandable = preferencesRepository.isGradeExpandable, expandMode = preferencesRepository.gradeExpandMode,
gradeColorTheme = preferencesRepository.gradeColorTheme gradeColorTheme = preferencesRepository.gradeColorTheme
) )
} }
@ -235,14 +236,24 @@ class GradeDetailsPresenter @Inject constructor(
.sortedByDescending { it.date } .sortedByDescending { it.date }
.map { GradeDetailsItem(it, ViewType.ITEM) } .map { GradeDetailsItem(it, ViewType.ITEM) }
listOf(GradeDetailsItem(GradeDetailsHeader( val gradeDetailsItems = listOf(
subject = subject, GradeDetailsItem(
average = average, GradeDetailsHeader(
pointsSum = points, subject = subject,
grades = subItems average = average,
).apply { pointsSum = points,
newGrades = grades.filter { grade -> !grade.isRead }.size grades = subItems
}, ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems ).apply {
newGrades = grades.filter { grade -> !grade.isRead }.size
}, ViewType.HEADER
)
)
if (preferencesRepository.gradeExpandMode == GradeExpandMode.ALWAYS_EXPANDED) {
gradeDetailsItems + subItems
} else {
gradeDetailsItems
}
}.flatten() }.flatten()
} }

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.grade.details package io.github.wulkanowy.ui.modules.grade.details
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.base.BaseView
interface GradeDetailsView : BaseView { interface GradeDetailsView : BaseView {
@ -9,7 +10,7 @@ interface GradeDetailsView : BaseView {
fun initView() fun initView()
fun updateData(data: List<GradeDetailsItem>, isGradeExpandable: Boolean, gradeColorTheme: String) fun updateData(data: List<GradeDetailsItem>, expandMode: GradeExpandMode, gradeColorTheme: String)
fun updateItem(item: Grade, position: Int) fun updateItem(item: Grade, position: Int)

View File

@ -4,7 +4,7 @@
<bool name="pref_default_attendance_present">true</bool> <bool name="pref_default_attendance_present">true</bool>
<string name="pref_default_grade_average_mode">only_one_semester</string> <string name="pref_default_grade_average_mode">only_one_semester</string>
<bool name="pref_default_grade_average_force_calc">false</bool> <bool name="pref_default_grade_average_force_calc">false</bool>
<bool name="pref_default_expand_grade">false</bool> <string name="pref_default_expand_grade_mode">one</string>
<bool name="pref_default_grade_statistics_list">false</bool> <bool name="pref_default_grade_statistics_list">false</bool>
<string name="pref_default_app_theme">light</string> <string name="pref_default_app_theme">light</string>
<string name="pref_default_grade_color_scheme">vulcan</string> <string name="pref_default_grade_color_scheme">vulcan</string>

View File

@ -5,7 +5,8 @@
<string name="pref_key_app_theme">app_theme</string> <string name="pref_key_app_theme">app_theme</string>
<string name="pref_key_dashboard_tiles">dashboard_tiles</string> <string name="pref_key_dashboard_tiles">dashboard_tiles</string>
<string name="pref_key_grade_color_scheme">grade_color_scheme</string> <string name="pref_key_grade_color_scheme">grade_color_scheme</string>
<string name="pref_key_expand_grade">expand_grade</string> <string name="pref_key_expand_grade">expand_grade</string> <!-- replaced by expand_grade_mode -->
<string name="pref_key_expand_grade_mode">expand_grade_mode</string>
<string name="pref_key_grade_average_mode">grade_average_mode</string> <string name="pref_key_grade_average_mode">grade_average_mode</string>
<string name="pref_key_grade_average_force_calc">grade_average_always_calc</string> <string name="pref_key_grade_average_force_calc">grade_average_always_calc</string>
<string name="pref_key_grade_statistics_list">grade_statistics_list</string> <string name="pref_key_grade_statistics_list">grade_statistics_list</string>

View File

@ -99,6 +99,17 @@
<item>grade_color</item> <item>grade_color</item>
</string-array> </string-array>
<string-array name="default_expand_grade_entries">
<item>Up to 1 at once</item>
<item>Always expanded</item>
<item>Unlimited expansions</item>
</string-array>
<string-array name="default_expand_grade_values" translatable="false">
<item>one</item>
<item>always</item>
<item>any</item>
</string-array>
<string-array name="grade_average_mode_entries"> <string-array name="grade_average_mode_entries">
<item>Average of grades only from selected semester</item> <item>Average of grades only from selected semester</item>
<item>Average of averages from both semesters</item> <item>Average of averages from both semesters</item>

View File

@ -50,11 +50,14 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_color_scheme" app:key="@string/pref_key_grade_color_scheme"
app:title="@string/pref_view_grade_color_scheme" /> app:title="@string/pref_view_grade_color_scheme" />
<SwitchPreferenceCompat <ListPreference
app:defaultValue="@bool/pref_default_expand_grade" app:defaultValue="@string/pref_default_expand_grade_mode"
app:entries="@array/default_expand_grade_entries"
app:entryValues="@array/default_expand_grade_values"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_expand_grade" app:key="@string/pref_key_expand_grade_mode"
app:title="@string/pref_view_expand_grade" /> app:title="@string/pref_view_expand_grade"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_subjects_without_grades" app:defaultValue="@bool/pref_default_subjects_without_grades"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"