Add option to show empty tiles in the timetable (#2236)

Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
This commit is contained in:
Antoni Paduch 2023-08-22 23:47:12 +02:00 committed by GitHub
parent 024ca89708
commit 533157709b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 363 additions and 47 deletions

View File

@ -50,5 +50,9 @@
{ {
"displayName": "Tomasz F.", "displayName": "Tomasz F.",
"githubUsername": "Pengwius" "githubUsername": "Pengwius"
},
{
"displayName": "Antoni Paduch",
"githubUsername": "janAte1"
} }
] ]

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data.enums
enum class TimetableGapsMode(val value: String) {
NO_GAPS("no_gaps"),
BETWEEN_LESSONS("between"),
BETWEEN_AND_BEFORE_LESSONS("before_and_between");
companion object {
fun getByValue(value: String) = entries.find { it.value == value } ?: BETWEEN_LESSONS
}
}

View File

@ -15,7 +15,6 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.time.Instant import java.time.Instant
@ -201,6 +200,14 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_timetable_show_timers R.bool.pref_default_timetable_show_timers
) )
val showTimetableGaps: TimetableGapsMode
get() = TimetableGapsMode.getByValue(
getString(
R.string.pref_key_timetable_show_gaps,
R.string.pref_default_timetable_show_gaps
)
)
val showSubjectsWithoutGrades: Boolean val showSubjectsWithoutGrades: Boolean
get() = getBoolean( get() = getBoolean(
R.string.pref_key_subjects_without_grades, R.string.pref_key_subjects_without_grades,

View File

@ -4,6 +4,7 @@ import android.content.Intent
import android.widget.RemoteViewsService import android.widget.RemoteViewsService
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
@ -26,10 +27,19 @@ class TimetableWidgetService : RemoteViewsService() {
@Inject @Inject
lateinit var sharedPref: SharedPrefProvider lateinit var sharedPref: SharedPrefProvider
@Inject
lateinit var prefRepository: PreferencesRepository
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
Timber.d("TimetableWidgetFactory created") Timber.d("TimetableWidgetFactory created")
return TimetableWidgetFactory( return TimetableWidgetFactory(
timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent timetableRepository = timetableRepo,
studentRepository = studentRepo,
semesterRepository = semesterRepo,
sharedPref = sharedPref,
prefRepository = prefRepository,
context = applicationContext,
intent = intent,
) )
} }
} }

View File

@ -12,7 +12,9 @@ import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.databinding.ItemTimetableBinding import io.github.wulkanowy.databinding.ItemTimetableBinding
import io.github.wulkanowy.databinding.ItemTimetableEmptyBinding
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject import javax.inject.Inject
@ -29,9 +31,14 @@ class TimetableAdapter @Inject constructor() :
TimetableItemType.SMALL -> SmallViewHolder( TimetableItemType.SMALL -> SmallViewHolder(
ItemTimetableSmallBinding.inflate(inflater, parent, false) ItemTimetableSmallBinding.inflate(inflater, parent, false)
) )
TimetableItemType.NORMAL -> NormalViewHolder( TimetableItemType.NORMAL -> NormalViewHolder(
ItemTimetableBinding.inflate(inflater, parent, false) ItemTimetableBinding.inflate(inflater, parent, false)
) )
TimetableItemType.EMPTY -> EmptyViewHolder(
ItemTimetableEmptyBinding.inflate(inflater, parent, false)
)
} }
} }
@ -40,12 +47,12 @@ class TimetableAdapter @Inject constructor() :
position: Int, position: Int,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads) if (payloads.isNotEmpty() && holder is NormalViewHolder) {
updateTimeLeft(
if (holder is NormalViewHolder) updateTimeLeft( binding = holder.binding,
binding = holder.binding, timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft,
timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft, )
) } else super.onBindViewHolder(holder, position, payloads)
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
@ -54,10 +61,16 @@ class TimetableAdapter @Inject constructor() :
binding = holder.binding, binding = holder.binding,
item = getItem(position) as TimetableItem.Small, item = getItem(position) as TimetableItem.Small,
) )
is NormalViewHolder -> bindNormalView( is NormalViewHolder -> bindNormalView(
binding = holder.binding, binding = holder.binding,
item = getItem(position) as TimetableItem.Normal, item = getItem(position) as TimetableItem.Normal,
) )
is EmptyViewHolder -> bindEmptyView(
binding = holder.binding,
item = getItem(position) as TimetableItem.Empty,
)
} }
} }
@ -100,6 +113,19 @@ class TimetableAdapter @Inject constructor() :
} }
} }
private fun bindEmptyView(binding: ItemTimetableEmptyBinding, item: TimetableItem.Empty) {
with(binding) {
timetableEmptyItemNumber.text = when (item.numFrom) {
item.numTo -> item.numFrom.toString()
else -> "${item.numFrom}-${item.numTo}"
}
timetableEmptyItemSubject.text = timetableEmptyItemSubject.context.getPlural(
R.plurals.timetable_no_lesson,
item.numTo - item.numFrom + 1
)
}
}
private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) { private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) {
with(binding) { with(binding) {
when { when {
@ -137,6 +163,7 @@ class TimetableAdapter @Inject constructor() :
timetableItemTimeLeft.visibility = VISIBLE timetableItemTimeLeft.visibility = VISIBLE
timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished) timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished)
} }
else -> { else -> {
timetableItemTimeUntil.visibility = GONE timetableItemTimeUntil.visibility = GONE
timetableItemTimeLeft.visibility = GONE timetableItemTimeLeft.visibility = GONE
@ -191,7 +218,8 @@ class TimetableAdapter @Inject constructor() :
) )
} else { } else {
timetableItemDescription.visibility = GONE timetableItemDescription.visibility = GONE
timetableItemRoom.isVisible = lesson.room.isNotBlank() || lesson.roomOld.isNotBlank() timetableItemRoom.isVisible =
lesson.room.isNotBlank() || lesson.roomOld.isNotBlank()
timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank() timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank()
timetableItemTeacher.visibility = VISIBLE timetableItemTeacher.visibility = VISIBLE
} }
@ -274,6 +302,9 @@ class TimetableAdapter @Inject constructor() :
private class SmallViewHolder(val binding: ItemTimetableSmallBinding) : private class SmallViewHolder(val binding: ItemTimetableSmallBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
private class EmptyViewHolder(val binding: ItemTimetableEmptyBinding) :
RecyclerView.ViewHolder(binding.root)
companion object { companion object {
private val differ = object : DiffUtil.ItemCallback<TimetableItem>() { private val differ = object : DiffUtil.ItemCallback<TimetableItem>() {
override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean = override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean =
@ -281,9 +312,11 @@ class TimetableAdapter @Inject constructor() :
oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> { oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> {
oldItem.lesson.start == newItem.lesson.start oldItem.lesson.start == newItem.lesson.start
} }
oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> { oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> {
oldItem.lesson.start == newItem.lesson.start oldItem.lesson.start == newItem.lesson.start
} }
else -> oldItem == newItem else -> oldItem == newItem
} }

View File

@ -16,6 +16,11 @@ sealed class TimetableItem(val type: TimetableItemType) {
val timeLeft: TimeLeft?, val timeLeft: TimeLeft?,
val onClick: (Timetable) -> Unit, val onClick: (Timetable) -> Unit,
) : TimetableItem(TimetableItemType.NORMAL) ) : TimetableItem(TimetableItemType.NORMAL)
data class Empty(
val numFrom: Int,
val numTo: Int
) : TimetableItem(TimetableItemType.EMPTY)
} }
data class TimeLeft( data class TimeLeft(
@ -27,4 +32,5 @@ data class TimeLeft(
enum class TimetableItemType { enum class TimetableItemType {
SMALL, SMALL,
NORMAL, NORMAL,
EMPTY
} }

View File

@ -1,23 +1,44 @@
package io.github.wulkanowy.ui.modules.timetable package io.github.wulkanowy.ui.modules.timetable
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
import io.github.wulkanowy.data.enums.TimetableMode import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.data.flatResourceFlow
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceData
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceIntermediate
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
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.utils.* import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.capitalise
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.isJustFinished
import io.github.wulkanowy.utils.isShowTimeUntil
import io.github.wulkanowy.utils.left
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.until
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDate.* import java.time.LocalDate.now
import java.util.* import java.time.LocalDate.of
import java.time.LocalDate.ofEpochDay
import java.util.Timer
import javax.inject.Inject import javax.inject.Inject
import kotlin.concurrent.timer import kotlin.concurrent.timer
@ -192,16 +213,38 @@ class TimetablePresenter @Inject constructor(
compareBy({ item -> item.number }, { item -> !item.isStudentPlan }) compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
) )
return filteredItems.mapIndexed { i, it -> var prevNum = when (prefRepository.showTimetableGaps) {
if (it.isStudentPlan) TimetableItem.Normal( BETWEEN_AND_BEFORE_LESSONS -> 0
lesson = it, else -> null
showGroupsInPlan = prefRepository.showGroupsInPlan, }
timeLeft = filteredItems.getTimeLeftForLesson(it, i), return buildList {
onClick = ::onTimetableItemSelected filteredItems.forEachIndexed { i, it ->
) else TimetableItem.Small( if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) {
lesson = it, val emptyLesson = TimetableItem.Empty(
onClick = ::onTimetableItemSelected numFrom = prevNum!! + 1,
) numTo = it.number - 1
)
add(emptyLesson)
}
if (it.isStudentPlan) {
val normalLesson = TimetableItem.Normal(
lesson = it,
showGroupsInPlan = prefRepository.showGroupsInPlan,
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
onClick = ::onTimetableItemSelected
)
add(normalLesson)
} else {
val smallLesson = TimetableItem.Small(
lesson = it,
onClick = ::onTimetableItemSelected
)
add(smallLesson)
}
prevNum = it.number
}
} }
} }

View File

@ -16,6 +16,9 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
@ -24,6 +27,7 @@ import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Co
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import timber.log.Timber import timber.log.Timber
@ -35,11 +39,12 @@ class TimetableWidgetFactory(
private val studentRepository: StudentRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val sharedPref: SharedPrefProvider, private val sharedPref: SharedPrefProvider,
private val prefRepository: PreferencesRepository,
private val context: Context, private val context: Context,
private val intent: Intent? private val intent: Intent?
) : RemoteViewsService.RemoteViewsFactory { ) : RemoteViewsService.RemoteViewsFactory {
private var lessons = emptyList<Timetable>() private var items = emptyList<TimetableWidgetItem>()
private var timetableCanceledColor: Int? = null private var timetableCanceledColor: Int? = null
@ -47,18 +52,13 @@ class TimetableWidgetFactory(
private var timetableChangeColor: Int? = null private var timetableChangeColor: Int? = null
private var lastSyncInstant: Instant? = null
override fun getLoadingView() = null override fun getLoadingView() = null
override fun hasStableIds() = true override fun hasStableIds() = true
override fun getCount() = when { override fun getCount() = items.size
lessons.isEmpty() -> 0
else -> lessons.size + 1
}
override fun getViewTypeCount() = 2 override fun getViewTypeCount() = 3
override fun getItemId(position: Int) = position.toLong() override fun getItemId(position: Int) = position.toLong()
@ -75,9 +75,10 @@ class TimetableWidgetFactory(
runBlocking { runBlocking {
val student = getStudent(studentId) ?: return@runBlocking val student = getStudent(studentId) ?: return@runBlocking
val semester = semesterRepository.getCurrentSemester(student) val semester = semesterRepository.getCurrentSemester(student)
lessons = getLessons(student, semester, date) items = createItems(
lastSyncInstant = lessons = getLessons(student, semester, date),
timetableRepository.getLastRefreshTimestamp(semester, date, date) lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date)
)
if (date == LocalDate.now()) { if (date == LocalDate.now()) {
updateTodayLastLessonEnd(appWidgetId) updateTodayLastLessonEnd(appWidgetId)
} }
@ -101,8 +102,33 @@ class TimetableWidgetFactory(
return lessons.sortedBy { it.number } return lessons.sortedBy { it.number }
} }
private fun createItems(
lessons: List<Timetable>,
lastSync: Instant?,
): List<TimetableWidgetItem> {
var prevNum = when (prefRepository.showTimetableGaps) {
BETWEEN_AND_BEFORE_LESSONS -> 0
else -> null
}
return buildList {
lessons.forEach {
if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) {
val emptyItem = TimetableWidgetItem.Empty(
numFrom = prevNum!! + 1,
numTo = it.number - 1
)
add(emptyItem)
}
add(TimetableWidgetItem.Normal(it))
prevNum = it.number
}
add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN))
}
}
private fun updateTodayLastLessonEnd(appWidgetId: Int) { private fun updateTodayLastLessonEnd(appWidgetId: Int) {
val todayLastLessonEnd = lessons.maxOfOrNull { it.end } ?: return val todayLastLessonEnd = items.filterIsInstance<TimetableWidgetItem.Normal>()
.maxOfOrNull { it.lesson.end } ?: return
val key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId) val key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId)
sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true) sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true)
} }
@ -112,15 +138,15 @@ class TimetableWidgetFactory(
} }
override fun getViewAt(position: Int): RemoteViews? { override fun getViewAt(position: Int): RemoteViews? {
if (position == lessons.size) { return when (val item = items.getOrNull(position) ?: return null) {
val synchronizationInstant = lastSyncInstant ?: Instant.MIN is TimetableWidgetItem.Normal -> getNormalItemRemoteView(item)
val synchronizationText = getSynchronizationInfoText(synchronizationInstant) is TimetableWidgetItem.Empty -> getEmptyItemRemoteView(item)
return RemoteViews(context.packageName, R.layout.item_widget_timetable_footer).apply { is TimetableWidgetItem.Synchronized -> getSynchronizedItemRemoteView(item)
setTextViewText(R.id.timetableWidgetSynchronizationTime, synchronizationText)
}
} }
}
val lesson = lessons.getOrNull(position) ?: return null private fun getNormalItemRemoteView(item: TimetableWidgetItem.Normal): RemoteViews {
val lesson = item.lesson
val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE) val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE) val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE)
@ -130,30 +156,63 @@ class TimetableWidgetFactory(
setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime) setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime) setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject) setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
setTextViewText(R.id.timetableWidgetItemTeacher, lesson.teacher) setTextViewText(R.id.timetableWidgetItemTeacher, lesson.teacher)
setTextViewText(R.id.timetableWidgetItemDescription, lesson.info) setTextViewText(R.id.timetableWidgetItemDescription, lesson.info)
setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent()) setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent())
} }
updateTheme() updateTheme()
clearLessonStyles(remoteViews) clearLessonStyles(remoteViews)
if (lesson.room.isBlank()) { if (lesson.room.isBlank()) {
remoteViews.setViewVisibility(R.id.timetableWidgetItemRoom, GONE) remoteViews.setViewVisibility(R.id.timetableWidgetItemRoom, GONE)
} else { } else {
remoteViews.setTextViewText(R.id.timetableWidgetItemRoom, lesson.room) remoteViews.setTextViewText(R.id.timetableWidgetItemRoom, lesson.room)
} }
when { when {
lesson.canceled -> applyCancelledLessonStyles(remoteViews) lesson.canceled -> applyCancelledLessonStyles(remoteViews)
lesson.changes or lesson.info.isNotBlank() -> applyChangedLessonStyles( lesson.changes or lesson.info.isNotBlank() -> applyChangedLessonStyles(
remoteViews, lesson remoteViews = remoteViews,
lesson = lesson,
) )
} }
return remoteViews return remoteViews
} }
private fun getEmptyItemRemoteView(item: TimetableWidgetItem.Empty): RemoteViews {
return RemoteViews(
context.packageName,
R.layout.item_widget_timetable_empty
).apply {
setTextViewText(
R.id.timetableWidgetEmptyItemNumber,
when (item.numFrom) {
item.numTo -> item.numFrom.toString()
else -> "${item.numFrom}-${item.numTo}"
}
)
setTextViewText(
R.id.timetableWidgetEmptyItemText,
context.getPlural(
R.plurals.timetable_no_lesson,
item.numTo - item.numFrom + 1
)
)
setOnClickFillInIntent(R.id.timetableWidgetEmptyItemContainer, Intent())
}
}
private fun getSynchronizedItemRemoteView(item: TimetableWidgetItem.Synchronized): RemoteViews {
return RemoteViews(
context.packageName,
R.layout.item_widget_timetable_footer
).apply {
setTextViewText(
R.id.timetableWidgetSynchronizationTime,
getSynchronizationInfoText(item.timestamp)
)
}
}
private fun updateTheme() { private fun updateTheme() {
when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES -> { Configuration.UI_MODE_NIGHT_YES -> {

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.ui.modules.timetablewidget
import io.github.wulkanowy.data.db.entities.Timetable
import java.time.Instant
sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) {
data class Normal(
val lesson: Timetable,
) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL)
data class Empty(
val numFrom: Int,
val numTo: Int
) : TimetableWidgetItem(TimetableWidgetItemType.EMPTY)
data class Synchronized(
val timestamp: Instant,
) : TimetableWidgetItem(TimetableWidgetItemType.SYNCHRONIZED)
}
enum class TimetableWidgetItemType {
NORMAL,
EMPTY,
SYNCHRONIZED,
}

View File

@ -0,0 +1,43 @@
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:paddingStart="8dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
android:paddingBottom="6dp"
tools:context=".ui.modules.timetable.TimetableAdapter">
<TextView
android:id="@+id/timetableEmptyItemNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:includeFontPadding="false"
android:maxLength="5"
android:minWidth="40dp"
android:minHeight="40dp"
android:textColor="?android:textColorHint"
android:textSize="32sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1-4" />
<TextView
android:id="@+id/timetableEmptyItemSubject"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorHint"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="@+id/timetableEmptyItemNumber"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/timetableEmptyItemNumber"
app:layout_constraintTop_toTopOf="@+id/timetableEmptyItemNumber"
tools:text="No lessons" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/timetableWidgetEmptyItemContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_widget_item_timetable"
android:backgroundTint="?attr/colorSurface"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="12dp"
android:paddingVertical="8dp"
android:theme="@style/Wulkanowy.Widget.Theme"
tools:context=".ui.modules.timetablewidget.TimetableWidgetFactory">
<TextView
android:id="@+id/timetableWidgetEmptyItemNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceHeadline6"
android:textColor="?android:textColorHint"
android:textSize="22sp"
tools:text="1-4" />
<TextView
android:id="@+id/timetableWidgetEmptyItemText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="end"
android:lines="1"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:textColor="?android:textColorHint"
tools:text="No lessons" />
</LinearLayout>

View File

@ -51,6 +51,11 @@
<item>Średnia ze średnich z obu semestrów</item> <item>Średnia ze średnich z obu semestrów</item>
<item>Średnia wszystkich ocen z całego roku</item> <item>Średnia wszystkich ocen z całego roku</item>
</string-array> </string-array>
<string-array name="timetable_show_gaps_entries">
<item>Nie pokauj</item>
<item>Tylko między lekcjami</item>
<item>Przed i między lekcjami</item>
</string-array>
<string-array name="dashboard_tile_entries"> <string-array name="dashboard_tile_entries">
<item>Szczęśliwy numerek</item> <item>Szczęśliwy numerek</item>
<item>Nieprzeczytane wiadomości</item> <item>Nieprzeczytane wiadomości</item>

View File

@ -185,6 +185,12 @@
<string name="timetable_notify_change_room">Zmiana sali z %1$s na %2$s</string> <string name="timetable_notify_change_room">Zmiana sali z %1$s na %2$s</string>
<string name="timetable_notify_change_teacher">Zmiana nauczyciela z %1$s na %2$s</string> <string name="timetable_notify_change_teacher">Zmiana nauczyciela z %1$s na %2$s</string>
<string name="timetable_notify_change_subject">Zmiana przedmiotu z %1$s na %2$s</string> <string name="timetable_notify_change_subject">Zmiana przedmiotu z %1$s na %2$s</string>
<plurals name="timetable_no_lesson">
<item quantity="one">Brak lekcji</item>
<item quantity="few">Brak lekcji</item>
<item quantity="many">Brak lekcji</item>
<item quantity="other">Brak lekcji</item>
</plurals>
<plurals name="timetable_notify_new_items_title"> <plurals name="timetable_notify_new_items_title">
<item quantity="one">Zmiana planu lekcji</item> <item quantity="one">Zmiana planu lekcji</item>
<item quantity="few">Zmiany planu lekcji</item> <item quantity="few">Zmiany planu lekcji</item>
@ -700,6 +706,7 @@
<string name="pref_view_expand_grade">Rozwijanie ocen</string> <string name="pref_view_expand_grade">Rozwijanie ocen</string>
<string name="pref_view_timetable_show_timers">Oznaczaj bieżącą lekcję</string> <string name="pref_view_timetable_show_timers">Oznaczaj bieżącą lekcję</string>
<string name="pref_view_timetable_show_groups">Pokazuj grupę obok przedmiotu</string> <string name="pref_view_timetable_show_groups">Pokazuj grupę obok przedmiotu</string>
<string name="pref_view_timetable_show_gaps">Pokazuj puste kafelki gdzie nie ma lekcji</string>
<string name="pref_view_grade_statistics_list">Pokazuj listę wykresów w ocenach klasy</string> <string name="pref_view_grade_statistics_list">Pokazuj listę wykresów w ocenach klasy</string>
<string name="pref_view_subjects_without_grades">Pokazuj przedmioty bez ocen</string> <string name="pref_view_subjects_without_grades">Pokazuj przedmioty bez ocen</string>
<string name="pref_view_grade_color_scheme">Schemat kolorów ocen</string> <string name="pref_view_grade_color_scheme">Schemat kolorów ocen</string>

View File

@ -23,6 +23,7 @@
<string name="pref_default_timetable_show_whole_class">no</string> <string name="pref_default_timetable_show_whole_class">no</string>
<string name="pref_default_grade_sorting_mode">alphabetic</string> <string name="pref_default_grade_sorting_mode">alphabetic</string>
<bool name="pref_default_timetable_show_timers">false</bool> <bool name="pref_default_timetable_show_timers">false</bool>
<string name="pref_default_timetable_show_gaps">between</string>
<bool name="pref_default_subjects_without_grades">false</bool> <bool name="pref_default_subjects_without_grades">false</bool>
<bool name="pref_default_optional_arithmetic_average">false</bool> <bool name="pref_default_optional_arithmetic_average">false</bool>
<string name="pref_default_last_sync_date">0</string> <string name="pref_default_last_sync_date">0</string>

View File

@ -28,6 +28,7 @@
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string> <string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
<string name="pref_key_timetable_show_groups">show_groups_in_plan</string> <string name="pref_key_timetable_show_groups">show_groups_in_plan</string>
<string name="pref_key_timetable_show_timers">timetable_show_timers</string> <string name="pref_key_timetable_show_timers">timetable_show_timers</string>
<string name="pref_key_timetable_show_gaps">timetable_show_gaps</string>
<string name="pref_key_subjects_without_grades">subjects_without_grades</string> <string name="pref_key_subjects_without_grades">subjects_without_grades</string>
<string name="pref_key_optional_arithmetic_average">optional_arithmetic_average</string> <string name="pref_key_optional_arithmetic_average">optional_arithmetic_average</string>
<string name="pref_key_message_draft">message_draft</string> <string name="pref_key_message_draft">message_draft</string>

View File

@ -123,6 +123,17 @@
<item>all_year</item> <item>all_year</item>
</string-array> </string-array>
<string-array name="timetable_show_gaps_entries">
<item>Don\'t show</item>
<item>Only between lessons</item>
<item>Before and between lessons</item>
</string-array>
<string-array name="timetable_show_gaps_values" translatable="false">
<item>no_gaps</item>
<item>between</item>
<item>before_and_between</item>
</string-array>
<string-array name="dashboard_tile_entries"> <string-array name="dashboard_tile_entries">
<item>Lucky number</item> <item>Lucky number</item>
<item>Unread messages</item> <item>Unread messages</item>

View File

@ -186,6 +186,10 @@
<string name="timetable_notify_change_room">Change of room from %1$s to %2$s</string> <string name="timetable_notify_change_room">Change of room from %1$s to %2$s</string>
<string name="timetable_notify_change_teacher">Change of teacher from %1$s to %2$s</string> <string name="timetable_notify_change_teacher">Change of teacher from %1$s to %2$s</string>
<string name="timetable_notify_change_subject">Change of subject from %1$s to %2$s</string> <string name="timetable_notify_change_subject">Change of subject from %1$s to %2$s</string>
<plurals name="timetable_no_lesson">
<item quantity="one">No lesson</item>
<item quantity="other">No lessons</item>
</plurals>
<plurals name="timetable_notify_new_items_title"> <plurals name="timetable_notify_new_items_title">
<item quantity="one">Timetable change</item> <item quantity="one">Timetable change</item>
<item quantity="other">Timetable changes</item> <item quantity="other">Timetable changes</item>
@ -690,6 +694,7 @@
<string name="pref_view_expand_grade">Grades expanding</string> <string name="pref_view_expand_grade">Grades expanding</string>
<string name="pref_view_timetable_show_timers">Mark current lesson</string> <string name="pref_view_timetable_show_timers">Mark current lesson</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string> <string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string> <string name="pref_view_grade_statistics_list">Show chart list in class grades</string>
<string name="pref_view_subjects_without_grades">Show subjects without grades</string> <string name="pref_view_subjects_without_grades">Show subjects without grades</string>
<string name="pref_view_grade_color_scheme">Grades color scheme</string> <string name="pref_view_grade_color_scheme">Grades color scheme</string>

View File

@ -111,5 +111,13 @@
app:title="@string/pref_view_timetable_show_whole_class" app:title="@string/pref_view_timetable_show_whole_class"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
--> -->
<ListPreference
app:defaultValue="@string/pref_default_timetable_show_gaps"
app:entries="@array/timetable_show_gaps_entries"
app:entryValues="@array/timetable_show_gaps_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_gaps"
app:title="@string/pref_view_timetable_show_gaps"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>