Add better timetable changes display (#513)

This commit is contained in:
Dominik Korsa
2019-10-03 21:13:01 +02:00
committed by Rafał Borcz
parent 736d570f26
commit 0162c8bbee
17 changed files with 514 additions and 215 deletions

View File

@ -11,6 +11,7 @@ import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.synthetic.main.dialog_timetable.*
import org.threeten.bp.LocalDateTime
@ -72,13 +73,22 @@ class TimetableDialog : DialogFragment() {
private fun setInfo(info: String, teacher: String, canceled: Boolean, changes: Boolean) {
when {
info.isNotBlank() -> timetableDialogChanges.text = when {
canceled && !changes -> "Lekcja odwołana: $info"
changes && teacher.isNotBlank() -> "Zastępstwo: $teacher"
changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}"
else -> info.capitalize()
}
else -> {
info.isNotBlank() -> {
if (canceled) {
timetableDialogChangesTitle.setTextColor(requireContext().getThemeAttrColor(R.attr.colorPrimary))
timetableDialogChanges.setTextColor(requireContext().getThemeAttrColor(R.attr.colorPrimary))
} else {
timetableDialogChangesTitle.setTextColor(requireContext().getThemeAttrColor(R.attr.colorTimetableChange))
timetableDialogChanges.setTextColor(requireContext().getThemeAttrColor(R.attr.colorTimetableChange))
}
timetableDialogChanges.text = when {
canceled && !changes -> "Lekcja odwołana: $info"
changes && teacher.isNotBlank() -> "Zastępstwo: $teacher"
changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}"
else -> info.capitalize()
}
} else -> {
timetableDialogChangesTitle.visibility = GONE
timetableDialogChanges.visibility = GONE
}

View File

@ -39,8 +39,6 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
override val titleStringId get() = R.string.timetable_title
override val roomString get() = getString(R.string.timetable_room)
override val isViewEmpty get() = timetableAdapter.isEmpty
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize

View File

@ -11,11 +11,12 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_timetable.*
class TimetableItem(val lesson: Timetable, private val roomText: String) :
class TimetableItem(val lesson: Timetable) :
AbstractFlexibleItem<TimetableItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_timetable
@ -26,16 +27,97 @@ class TimetableItem(val lesson: Timetable, private val roomText: String) :
@SuppressLint("SetTextI18n")
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
timetableItemNumber.text = lesson.number.toString()
timetableItemSubject.text = lesson.subject
timetableItemRoom.text = if (lesson.room.isNotBlank()) "$roomText ${lesson.room}" else ""
timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}"
timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE
updateFields(holder)
with(holder) {
timetableItemSubject.paintFlags =
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
updateDescription(holder)
updateColors(holder)
}
private fun updateFields(holder: ViewHolder) {
with(holder) {
timetableItemNumber.text = lesson.number.toString()
timetableItemSubject.text = lesson.subject
timetableItemRoom.text = lesson.room
timetableItemTeacher.text = lesson.teacher
timetableItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm")
}
}
private fun updateDescription(holder: ViewHolder) {
with(holder) {
if (lesson.info.isNotBlank() && !lesson.changes) {
updateDescriptionNoChanges(this)
} else {
timetableItemDescription.visibility = GONE
timetableItemRoom.visibility = VISIBLE
timetableItemTeacher.visibility = VISIBLE
}
}
}
private fun updateDescriptionNoChanges(holder: ViewHolder) {
with(holder) {
timetableItemDescription.visibility = VISIBLE
timetableItemDescription.text = lesson.info
timetableItemRoom.visibility = GONE
timetableItemTeacher.visibility = GONE
timetableItemDescription.setTextColor(holder.view.context.getThemeAttrColor(
if (lesson.canceled) R.attr.colorPrimary
else R.attr.colorTimetableChange
))
}
}
private fun updateColors(holder: ViewHolder) {
with(holder) {
if (lesson.canceled) {
timetableItemNumber.setTextColor(holder.view.context.getThemeAttrColor(R.attr.colorPrimary))
timetableItemSubject.setTextColor(holder.view.context.getThemeAttrColor(R.attr.colorPrimary))
} else {
updateNumberColor(this)
updateSubjectColor(this)
updateRoomColor(this)
updateTeacherColor(this)
}
}
}
private fun updateNumberColor(holder: ViewHolder) {
holder.timetableItemNumber.setTextColor(holder.view.context.getThemeAttrColor(
if (lesson.changes || lesson.info.isNotBlank()) R.attr.colorTimetableChange
else android.R.attr.textColorPrimary
))
}
private fun updateSubjectColor(holder: ViewHolder) {
holder.timetableItemSubject.setTextColor(holder.view.context.getThemeAttrColor(
if (lesson.subjectOld.isNotBlank() && lesson.subjectOld != lesson.subject) R.attr.colorTimetableChange
else android.R.attr.textColorPrimary
))
}
private fun updateRoomColor(holder: ViewHolder) {
holder.timetableItemRoom.setTextColor(holder.view.context.getThemeAttrColor(
if (lesson.roomOld.isNotBlank() && lesson.roomOld != lesson.room) R.attr.colorTimetableChange
else android.R.attr.textColorSecondary
))
}
private fun updateTeacherColor(holder: ViewHolder) {
holder.timetableItemTeacher.setTextColor(holder.view.context.getThemeAttrColor(
if (lesson.teacherOld.isNotBlank() && lesson.teacherOld != lesson.teacher) R.attr.colorTimetableChange
else android.R.attr.textColorSecondary
))
}
override fun equals(other: Any?): Boolean {

View File

@ -109,7 +109,7 @@ class TimetablePresenter @Inject constructor(
.flatMap { semesterRepository.getCurrentSemester(it) }
.delay(200, MILLISECONDS)
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
.map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } }
.map { items -> items.map { TimetableItem(it) } }
.map { items -> items.sortedBy { it.lesson.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)

View File

@ -5,8 +5,6 @@ import io.github.wulkanowy.ui.base.BaseView
interface TimetableView : BaseView {
val roomString: String
val isViewEmpty: Boolean
val currentStackSize: Int?

View File

@ -9,6 +9,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.utils.getThemeAttrColor
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_completed_lesson.*
@ -23,6 +24,10 @@ class CompletedLessonItem(val completedLesson: CompletedLesson) : AbstractFlexib
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: CompletedLessonItem.ViewHolder?, position: Int, payloads: MutableList<Any>?) {
holder?.apply {
completedLessonItemNumber.text = completedLesson.number.toString()
completedLessonItemNumber.setTextColor(holder.contentView.context.getThemeAttrColor(
if (completedLesson.substitution.isNotEmpty()) R.attr.colorTimetableChange
else android.R.attr.textColorPrimary
))
completedLessonItemSubject.text = completedLesson.subject
completedLessonItemTopic.text = completedLesson.topic
completedLessonItemAlert.visibility = if (completedLesson.substitution.isNotEmpty()) VISIBLE else GONE

View File

@ -21,6 +21,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.getThemeWidgetKey
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
@ -40,6 +41,12 @@ class TimetableWidgetFactory(
private var layoutId: Int? = null
private var primaryColor: Int? = null
private var textColor: Int? = null
private var timetableChangeColor: Int? = null
override fun getLoadingView() = null
override fun hasStableIds() = true
@ -59,28 +66,40 @@ class TimetableWidgetFactory(
val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0))
val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0)
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
layoutId = if (savedTheme == 0L) R.layout.item_widget_timetable else R.layout.item_widget_timetable_dark
updateTheme(appWidgetId)
lessons = try {
studentRepository.isStudentSaved()
.filter { true }
.flatMap { studentRepository.getSavedStudents().toMaybe() }
.flatMap {
val student = it.singleOrNull { student -> student.id == studentId }
updateLessons(date, studentId)
}
}
if (student != null) Maybe.just(student)
else Maybe.empty()
}
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
.flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() }
.map { item -> item.sortedBy { it.number } }
.subscribeOn(schedulers.backgroundThread)
.blockingGet(emptyList())
} catch (e: Exception) {
Timber.e(e, "An error has occurred in timetable widget factory")
emptyList()
}
private fun updateTheme(appWidgetId: Int) {
val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
layoutId = if (savedTheme == 0L) R.layout.item_widget_timetable else R.layout.item_widget_timetable_dark
primaryColor = if (savedTheme == 0L) R.color.colorPrimary else R.color.colorPrimaryLight
textColor = if (savedTheme == 0L) android.R.color.black else android.R.color.white
timetableChangeColor = if (savedTheme == 0L) R.color.timetable_change_dark else R.color.timetable_change_light
}
private fun updateLessons(date: LocalDate, studentId: Long) {
lessons = try {
studentRepository.isStudentSaved()
.filter { true }
.flatMap { studentRepository.getSavedStudents().toMaybe() }
.flatMap {
val student = it.singleOrNull { student -> student.id == studentId }
if (student != null) Maybe.just(student)
else Maybe.empty()
}
.flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
.flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() }
.map { item -> item.sortedBy { it.number } }
.subscribeOn(schedulers.backgroundThread)
.blockingGet(emptyList())
} catch (e: Exception) {
Timber.e(e, "An error has occurred in timetable widget factory")
emptyList()
}
}
@ -93,34 +112,95 @@ class TimetableWidgetFactory(
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
setTextViewText(R.id.timetableWidgetItemTime, lesson.start.toFormattedString("HH:mm") +
" - ${lesson.end.toFormattedString("HH:mm")}")
setTextViewText(R.id.timetableWidgetItemTimeStart, lesson.start.toFormattedString("HH:mm"))
setTextViewText(R.id.timetableWidgetItemTimeFinish, lesson.end.toFormattedString("HH:mm"))
if (lesson.room.isNotBlank()) {
setTextViewText(R.id.timetableWidgetItemRoom, "${context.getString(R.string.timetable_room)} ${lesson.room}")
} else setTextViewText(R.id.timetableWidgetItemRoom, "")
if (lesson.info.isNotBlank()) {
setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
setTextViewText(R.id.timetableWidgetItemDescription,
with(lesson) {
when (true) {
canceled && !changes -> "Lekcja odwołana: ${lesson.info}"
changes && teacher.isNotBlank() -> "Zastępstwo: ${lesson.teacher}"
changes && teacher.isBlank() -> "Zastępstwo, ${lesson.info.decapitalize()}"
else -> info.capitalize()
}
})
} else setViewVisibility(R.id.timetableWidgetItemDescription, GONE)
updateDescription(this, lesson)
if (lesson.canceled) {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags",
STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG)
updateStylesCanceled(this)
} else {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG)
updateStylesNotCanceled(this, lesson)
}
setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent())
}
}
private fun updateDescription(remoteViews: RemoteViews, lesson: Timetable) {
with(remoteViews) {
if (lesson.info.isNotBlank() && !lesson.changes) {
setTextViewText(R.id.timetableWidgetItemDescription, lesson.info)
setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
setViewVisibility(R.id.timetableWidgetItemRoom, GONE)
setViewVisibility(R.id.timetableWidgetItemTeacher, GONE)
} else {
setViewVisibility(R.id.timetableWidgetItemDescription, GONE)
setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE)
setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE)
}
}
}
private fun updateStylesCanceled(remoteViews: RemoteViews) {
with(remoteViews) {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags",
STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG)
setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor(primaryColor!!))
setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(primaryColor!!))
setTextColor(R.id.timetableWidgetItemDescription, context.getCompatColor(primaryColor!!))
}
}
private fun updateStylesNotCanceled(remoteViews: RemoteViews, lesson: Timetable) {
with(remoteViews) {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG)
setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(textColor!!))
setTextColor(R.id.timetableWidgetItemDescription, context.getCompatColor(timetableChangeColor!!))
updateNotCanceledLessonNumberColor(this, lesson)
updateNotCanceledSubjectColor(this, lesson)
val teacherChange = lesson.teacherOld.isNotBlank() && lesson.teacher != lesson.teacherOld
updateNotCanceledRoom(this, lesson, teacherChange)
updateNotCanceledTeacher(this, lesson, teacherChange)
}
}
private fun updateNotCanceledLessonNumberColor(remoteViews: RemoteViews, lesson: Timetable) {
remoteViews.setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor(
if (lesson.changes || (lesson.info.isNotBlank() && !lesson.canceled)) timetableChangeColor!!
else textColor!!
))
}
private fun updateNotCanceledSubjectColor(remoteViews: RemoteViews, lesson: Timetable) {
remoteViews.setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(
if (lesson.subjectOld.isNotBlank() && lesson.subject != lesson.subjectOld) timetableChangeColor!!
else textColor!!
))
}
private fun updateNotCanceledRoom(remoteViews: RemoteViews, lesson: Timetable, teacherChange: Boolean) {
with(remoteViews) {
if (lesson.room.isNotBlank()) {
setTextViewText(R.id.timetableWidgetItemRoom,
if (teacherChange) lesson.room
else "${context.getString(R.string.timetable_room)} ${lesson.room}"
)
setTextColor(R.id.timetableWidgetItemRoom, context.getCompatColor(
if (lesson.roomOld.isNotBlank() && lesson.room != lesson.roomOld) timetableChangeColor!!
else textColor!!
))
} else setTextViewText(R.id.timetableWidgetItemRoom, "")
}
}
private fun updateNotCanceledTeacher(remoteViews: RemoteViews, lesson: Timetable, teacherChange: Boolean) {
remoteViews.setTextViewText(R.id.timetableWidgetItemTeacher,
if (teacherChange) lesson.teacher
else ""
)
}
}