Improve the display of changes in the timetable (#275)

Closes #264
This commit is contained in:
Mikołaj Pich 2019-03-11 20:56:47 +01:00 committed by Rafał Borcz
parent 475e7dd6a3
commit 2621e5680d
13 changed files with 224 additions and 83 deletions

View File

@ -77,7 +77,7 @@ play {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation('com.github.wulkanowy:api:a875c1de3b') { exclude module: "threetenbp" } implementation('com.github.wulkanowy:api:29d24ca1ff') { exclude module: "threetenbp" }
implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.0.2" implementation "androidx.appcompat:appcompat:1.0.2"

View File

@ -16,9 +16,12 @@ fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", s
end = now(), end = now(),
date = start.toLocalDate(), date = start.toLocalDate(),
subject = subject, subject = subject,
subjectOld = "",
group = "", group = "",
room = room, room = room,
roomOld = "",
teacher = "", teacher = "",
teacherOld = "",
info = "", info = "",
changes = false, changes = false,
canceled = false canceled = false

View File

@ -46,6 +46,7 @@ import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5 import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6 import io.github.wulkanowy.data.db.migrations.Migration6
import io.github.wulkanowy.data.db.migrations.Migration7 import io.github.wulkanowy.data.db.migrations.Migration7
import io.github.wulkanowy.data.db.migrations.Migration8
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -76,7 +77,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 7 const val VERSION_SCHEMA = 8
fun newInstance(context: Context): AppDatabase { fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
@ -89,7 +90,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration4(), Migration4(),
Migration5(), Migration5(),
Migration6(), Migration6(),
Migration7() Migration7(),
Migration8()
) )
.build() .build()
} }

View File

@ -26,12 +26,18 @@ data class Timetable(
val subject: String, val subject: String,
val subjectOld: String,
val group: String, val group: String,
val room: String, val room: String,
val roomOld: String,
val teacher: String, val teacher: String,
val teacherOld: String,
val info: String, val info: String,
val changes: Boolean, val changes: Boolean,

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration8 : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE `Timetable` ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL")
database.execSQL("ALTER TABLE `Timetable` ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL")
database.execSQL("ALTER TABLE `Timetable` ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL")
}
}

View File

@ -26,9 +26,12 @@ class TimetableRemote @Inject constructor(private val api: Api) {
end = it.end.toLocalDateTime(), end = it.end.toLocalDateTime(),
date = it.date.toLocalDate(), date = it.date.toLocalDate(),
subject = it.subject, subject = it.subject,
subjectOld = it.subjectOld,
group = it.group, group = it.group,
room = it.room, room = it.room,
roomOld = it.roomOld,
teacher = it.teacher, teacher = it.teacher,
teacherOld = it.teacherOld,
info = it.info, info = it.info,
changes = it.changes, changes = it.changes,
canceled = it.canceled canceled = it.canceled

View File

@ -19,33 +19,31 @@ class TimetableRepository @Inject constructor(
private val remote: TimetableRemote private val remote: TimetableRemote
) { ) {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false) fun getTimetable(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Timetable>> {
: Single<List<Timetable>> { return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) ->
return Single.fromCallable { startDate.monday to endDate.friday } local.getTimetable(semester, monday, friday).filter { !forceRefresh }
.flatMap { dates -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
local.getTimetable(semester, dates.first, dates.second).filter { !forceRefresh } if (it) remote.getTimetable(semester, monday, friday)
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getTimetable(semester, dates.first, dates.second)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { newTimetable -> }.flatMap { newTimetable ->
local.getTimetable(semester, dates.first, dates.second) local.getTimetable(semester, monday, friday)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { oldTimetable -> .doOnSuccess { oldTimetable ->
local.deleteTimetable(oldTimetable - newTimetable) local.deleteTimetable(oldTimetable - newTimetable)
local.saveTimetable((newTimetable - oldTimetable).map { item -> local.saveTimetable((newTimetable - oldTimetable).map { item ->
item.apply { item.apply {
if (room.isEmpty()) { oldTimetable.singleOrNull { this.start == it.start }?.let {
oldTimetable.singleOrNull { it.start == this.start && it.room.isNotEmpty() } return@map copy(
?.let { return@map copy(room = it.room) } room = if (room.isEmpty()) it.room else room,
teacher = if (teacher.isEmpty()) it.teacher else teacher
)
} }
} }
}) })
} }
}.flatMap { }.flatMap {
local.getTimetable(semester, dates.first, dates.second) local.getTimetable(semester, monday, friday).toSingle(emptyList())
.toSingle(emptyList()) }).map { list -> list.filter { it.date in start..end } }
}).map { list -> list.filter { it.date in startDate..endDate } }
} }
} }
} }

View File

@ -1,16 +1,19 @@
package io.github.wulkanowy.ui.modules.timetable package io.github.wulkanowy.ui.modules.timetable
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
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.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.synthetic.main.dialog_timetable.* import kotlinx.android.synthetic.main.dialog_timetable.*
import org.threeten.bp.LocalDateTime
class TimetableDialog : DialogFragment() { class TimetableDialog : DialogFragment() {
@ -38,41 +41,110 @@ class TimetableDialog : DialogFragment() {
return inflater.inflate(R.layout.dialog_timetable, container, false) return inflater.inflate(R.layout.dialog_timetable, container, false)
} }
@SuppressLint("SetTextI18n")
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
timetableDialogSubject.text = lesson.subject lesson.run {
timetableDialogTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}" setInfo(info, teacher, canceled, changes)
setSubject(subject, subjectOld)
lesson.group.let { setTeacher(teacher, teacherOld)
if (it.isBlank()) { setGroup(group)
timetableDialogGroupTitle.visibility = GONE setRoom(room, roomOld)
timetableDialogGroup.visibility = GONE setTime(start, end)
} else timetableDialogGroup.text = it
}
lesson.room.let {
if (it.isBlank()) {
timetableDialogRoomTitle.visibility = GONE
timetableDialogRoom.visibility = GONE
} else timetableDialogRoom.text = it
}
lesson.teacher.let {
if (it.isBlank()) {
timetableDialogTeacherTitle.visibility = GONE
timetableDialogTeacher.visibility = GONE
} else timetableDialogTeacher.text = it
}
lesson.info.let {
if (it.isBlank()) {
timetableDialogChangesTitle.visibility = GONE
timetableDialogChanges.visibility = GONE
} else timetableDialogChanges.text = it
} }
timetableDialogClose.setOnClickListener { dismiss() } timetableDialogClose.setOnClickListener { dismiss() }
} }
private fun setSubject(subject: String, subjectOld: String) {
timetableDialogSubject.text = subject
if (subjectOld.isNotBlank() && subjectOld != subject) {
timetableDialogSubject.run {
paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG
text = subjectOld
}
timetableDialogSubjectNew.run {
visibility = VISIBLE
text = subject
}
}
}
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 -> {
timetableDialogChangesTitle.visibility = GONE
timetableDialogChanges.visibility = GONE
}
}
}
private fun setTeacher(teacher: String, teacherOld: String) {
when {
teacherOld.isNotBlank() && teacherOld != teacher -> {
timetableDialogTeacher.run {
visibility = VISIBLE
paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG
text = teacherOld
}
if (teacher.isNotBlank()) {
timetableDialogTeacherNew.run {
visibility = VISIBLE
text = teacher
}
}
}
teacher.isNotBlank() -> timetableDialogTeacher.text = teacher
else -> {
timetableDialogTeacherTitle.visibility = GONE
timetableDialogTeacher.visibility = GONE
}
}
}
private fun setGroup(group: String) {
group.let {
when {
it.isNotBlank() -> timetableDialogGroup.text = it
else -> {
timetableDialogGroupTitle.visibility = GONE
timetableDialogGroup.visibility = GONE
}
}
}
}
private fun setRoom(room: String, roomOld: String) {
when {
roomOld.isNotBlank() && roomOld != room -> {
timetableDialogRoom.run {
visibility = VISIBLE
paintFlags = paintFlags or STRIKE_THRU_TEXT_FLAG
text = roomOld
}
if (room.isNotBlank()) {
timetableDialogRoomNew.run {
visibility = VISIBLE
text = room
}
}
}
room.isNotBlank() -> timetableDialogRoom.text = room
else -> {
timetableDialogRoomTitle.visibility = GONE
timetableDialogRoom.visibility = GONE
}
}
}
@SuppressLint("SetTextI18n")
private fun setTime(start: LocalDateTime, end: LocalDateTime) {
timetableDialogTime.text = "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}"
}
} }

View File

@ -79,7 +79,14 @@ class TimetableWidgetFactory(
if (it.info.isNotBlank()) { if (it.info.isNotBlank()) {
setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
setTextViewText(R.id.timetableWidgetItemDescription, it.info.capitalize()) setTextViewText(R.id.timetableWidgetItemDescription, it.run {
when (true) {
canceled && !changes -> "Lekcja odwołana: $info"
changes && teacher.isNotBlank() -> "Zastępstwo: $teacher"
changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalize()}"
else -> it.info.capitalize()
}
})
} else setViewVisibility(R.id.timetableWidgetItemDescription, GONE) } else setViewVisibility(R.id.timetableWidgetItemDescription, GONE)
if (it.canceled) { if (it.canceled) {

View File

@ -121,6 +121,3 @@ class TimetableWidgetProvider : BroadcastReceiver() {
}, FLAG_UPDATE_CURRENT) }, FLAG_UPDATE_CURRENT)
} }
} }

View File

@ -51,6 +51,17 @@
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView
android:id="@+id/timetableDialogSubjectNew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textColor="@color/colorPrimary"
android:textIsSelectable="true"
android:textSize="12sp"
android:visibility="gone" />
<TextView <TextView
android:id="@+id/timetableDialogTeacherTitle" android:id="@+id/timetableDialogTeacherTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -68,6 +79,17 @@
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView
android:id="@+id/timetableDialogTeacherNew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textColor="@color/colorPrimary"
android:textIsSelectable="true"
android:textSize="12sp"
android:visibility="gone" />
<TextView <TextView
android:id="@+id/timetableDialogGroupTitle" android:id="@+id/timetableDialogGroupTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -102,6 +124,17 @@
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="12sp" /> android:textSize="12sp" />
<TextView
android:id="@+id/timetableDialogRoomNew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textColor="@color/colorPrimary"
android:textIsSelectable="true"
android:textSize="12sp"
android:visibility="gone" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -6,7 +6,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/ic_all_divider" android:background="@drawable/ic_all_divider"
android:minHeight="45dp" android:minHeight="45dp"
android:orientation="horizontal"> android:orientation="horizontal"
tools:context=".ui.widgets.timetable.TimetableWidgetFactory">
<TextView <TextView
android:id="@+id/timetableWidgetItemNumber" android:id="@+id/timetableWidgetItemNumber"
@ -19,7 +20,7 @@
android:maxLines="1" android:maxLines="1"
android:textColor="@android:color/black" android:textColor="@android:color/black"
android:textSize="25sp" android:textSize="25sp"
tools:text="0" /> tools:text="1" />
<TextView <TextView
android:id="@+id/timetableWidgetItemTime" android:id="@+id/timetableWidgetItemTime"
@ -31,9 +32,9 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_toEndOf="@id/timetableWidgetItemNumber" android:layout_toEndOf="@id/timetableWidgetItemNumber"
android:layout_toRightOf="@id/timetableWidgetItemNumber" android:layout_toRightOf="@id/timetableWidgetItemNumber"
android:text="@string/app_name"
android:textColor="@android:color/secondary_text_light" android:textColor="@android:color/secondary_text_light"
android:textSize="13sp" /> android:textSize="13sp"
tools:text="08:00 - 08:45" />
<TextView <TextView
android:id="@+id/timetableWidgetItemSubject" android:id="@+id/timetableWidgetItemSubject"
@ -46,9 +47,9 @@
android:layout_marginRight="25dp" android:layout_marginRight="25dp"
android:layout_toEndOf="@id/timetableWidgetItemTime" android:layout_toEndOf="@id/timetableWidgetItemTime"
android:layout_toRightOf="@id/timetableWidgetItemTime" android:layout_toRightOf="@id/timetableWidgetItemTime"
android:text="@string/app_name"
android:textColor="@android:color/secondary_text_light" android:textColor="@android:color/secondary_text_light"
android:textSize="13sp" /> android:textSize="13sp"
tools:text="Język polski" />
<TextView <TextView
android:id="@+id/timetableWidgetItemDescription" android:id="@+id/timetableWidgetItemDescription"
@ -63,9 +64,9 @@
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_toEndOf="@id/timetableWidgetItemTime" android:layout_toEndOf="@id/timetableWidgetItemTime"
android:layout_toRightOf="@id/timetableWidgetItemTime" android:layout_toRightOf="@id/timetableWidgetItemTime"
android:text="@string/app_name"
android:textColor="@color/colorPrimaryDark" android:textColor="@color/colorPrimaryDark"
android:textSize="12sp" /> android:textSize="12sp"
tools:text="Lekcja odwołana: uczniowie zwolnieni do domu" />
<TextView <TextView
android:id="@+id/timetableWidgetItemRoom" android:id="@+id/timetableWidgetItemRoom"
@ -78,7 +79,7 @@
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_toEndOf="@id/timetableWidgetItemNumber" android:layout_toEndOf="@id/timetableWidgetItemNumber"
android:layout_toRightOf="@id/timetableWidgetItemNumber" android:layout_toRightOf="@id/timetableWidgetItemNumber"
android:text="@string/app_name"
android:textColor="@android:color/secondary_text_light" android:textColor="@android:color/secondary_text_light"
android:textSize="12sp" /> android:textSize="12sp"
tools:text="Sala 25" />
</RelativeLayout> </RelativeLayout>

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/white"> android:background="@android:color/white"
tools:context=".ui.widgets.timetable.TimetableWidgetProvider">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -18,7 +20,8 @@
android:layout_marginLeft="5dp" android:layout_marginLeft="5dp"
android:backgroundTint="@color/colorPrimaryDark" android:backgroundTint="@color/colorPrimaryDark"
android:contentDescription="@string/all_prev" android:contentDescription="@string/all_prev"
android:src="@drawable/ic_widget_chevron_24dp" /> android:src="@drawable/ic_widget_chevron_24dp"
tools:targetApi="lollipop" />
<TextView <TextView
android:id="@+id/timetableWidgetDay" android:id="@+id/timetableWidgetDay"
@ -32,9 +35,9 @@
android:layout_toRightOf="@id/timetableWidgetPrev" android:layout_toRightOf="@id/timetableWidgetPrev"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:maxLines="1" android:maxLines="1"
android:text="@string/app_name"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="15sp" /> android:textSize="15sp"
tools:text="Poniedziałek" />
<TextView <TextView
android:id="@+id/timetableWidgetDate" android:id="@+id/timetableWidgetDate"
@ -49,9 +52,9 @@
android:layout_toRightOf="@id/timetableWidgetPrev" android:layout_toRightOf="@id/timetableWidgetPrev"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:maxLines="1" android:maxLines="1"
android:text="@string/app_name"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="14sp" /> android:textSize="14sp"
tools:text="12.03.2019" />
<ImageButton <ImageButton
android:id="@+id/timetableWidgetNext" android:id="@+id/timetableWidgetNext"
@ -65,7 +68,8 @@
android:backgroundTint="@color/colorPrimaryDark" android:backgroundTint="@color/colorPrimaryDark"
android:contentDescription="@string/all_next" android:contentDescription="@string/all_next"
android:rotation="180" android:rotation="180"
android:src="@drawable/ic_widget_chevron_24dp" /> android:src="@drawable/ic_widget_chevron_24dp"
tools:targetApi="lollipop" />
</RelativeLayout> </RelativeLayout>
@ -73,7 +77,8 @@
android:id="@+id/timetableWidgetList" android:id="@+id/timetableWidgetList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="@dimen/timetable_widget_bar_height" /> android:layout_marginTop="@dimen/timetable_widget_bar_height"
tools:listitem="@layout/item_widget_timetable" />
<TextView <TextView
android:id="@+id/timetableWidgetEmpty" android:id="@+id/timetableWidgetEmpty"
@ -82,5 +87,6 @@
android:layout_gravity="center" android:layout_gravity="center"
android:text="@string/widget_timetable_no_items" android:text="@string/widget_timetable_no_items"
android:textColor="@android:color/black" android:textColor="@android:color/black"
android:textSize="20sp" /> android:textSize="20sp"
tools:visibility="invisible" />
</FrameLayout> </FrameLayout>