Add mark as done feature in homework (#743)

This commit is contained in:
Mikołaj Pich 2020-04-03 17:39:36 +02:00 committed by GitHub
parent 651be69ad2
commit 18c1153e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 2060 additions and 82 deletions

View File

@ -128,7 +128,7 @@ configurations.all {
}
dependencies {
implementation "io.github.wulkanowy:sdk:44725a9"
implementation "io.github.wulkanowy:sdk:bff34b1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0"

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,7 @@ import io.github.wulkanowy.data.db.migrations.Migration21
import io.github.wulkanowy.data.db.migrations.Migration22
import io.github.wulkanowy.data.db.migrations.Migration23
import io.github.wulkanowy.data.db.migrations.Migration24
import io.github.wulkanowy.data.db.migrations.Migration25
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5
@ -109,7 +110,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 24
const val VERSION_SCHEMA = 25
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf(
@ -135,7 +136,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration21(),
Migration22(),
Migration23(),
Migration24()
Migration24(),
Migration25()
)
}

View File

@ -48,4 +48,14 @@ class Converters {
fun gsonToIntList(value: String): List<Int> {
return Gson().fromJson(value, object : TypeToken<List<Int>>() {}.type)
}
@TypeConverter
fun stringPairListToGson(list: List<Pair<String, String>>): String {
return Gson().toJson(list)
}
@TypeConverter
fun gsonToStringPairList(value: String): List<Pair<String, String>> {
return Gson().fromJson(value, object : TypeToken<List<Pair<String, String>>>() {}.type)
}
}

View File

@ -27,10 +27,14 @@ data class Homework(
val teacher: String,
@ColumnInfo(name = "teacher_symbol")
val teacherSymbol: String
val teacherSymbol: String,
val attachments: List<Pair<String, String>>
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
@ColumnInfo(name = "is_done")
var isDone: Boolean = false
}

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration25 : Migration(24, 25) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"")
}
}

View File

@ -19,6 +19,10 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
homeworkDb.deleteAll(homework)
}
fun updateHomework(homework: List<Homework>) {
homeworkDb.updateAll(homework)
}
fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Homework>> {
return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate)
.filter { it.isNotEmpty() }

View File

@ -23,7 +23,8 @@ class HomeworkRemote @Inject constructor(private val sdk: Sdk) {
subject = it.subject,
content = it.content,
teacher = it.teacher,
teacherSymbol = it.teacherSymbol
teacherSymbol = it.teacherSymbol,
attachments = it.attachments.map { attachment -> attachment.url to attachment.name }
)
}
}

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable
import io.reactivex.Single
import org.threeten.bp.LocalDate
import java.net.UnknownHostException
@ -36,4 +37,12 @@ class HomeworkRepository @Inject constructor(
}.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) })
}
}
fun toggleDone(homework: Homework): Completable {
return Completable.fromCallable {
local.updateHomework(listOf(homework.apply {
isDone = !isDone
}))
}
}
}

View File

@ -1,49 +0,0 @@
package io.github.wulkanowy.ui.modules.homework
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.synthetic.main.dialog_homework.*
class HomeworkDialog : DialogFragment() {
private lateinit var homework: Homework
companion object {
private const val ARGUMENT_KEY = "Item"
fun newInstance(homework: Homework): HomeworkDialog {
return HomeworkDialog().apply {
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) }
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0)
arguments?.run {
homework = getSerializable(HomeworkDialog.ARGUMENT_KEY) as Homework
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_homework, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
homeworkDialogDate.text = homework.date.toFormattedString()
homeworkDialogEntryDate.text = homework.entryDate.toFormattedString()
homeworkDialogSubject.text = homework.subject
homeworkDialogTeacher.text = homework.teacher
homeworkDialogContent.text = homework.content
homeworkDialogClose.setOnClickListener { dismiss() }
}
}

View File

@ -13,6 +13,7 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.dpToPx
@ -73,6 +74,10 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
homeworkAdapter.updateDataSet(data, true)
}
fun onReloadList() {
presenter.reloadData()
}
override fun clearData() {
homeworkAdapter.clear()
}
@ -118,7 +123,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
}
override fun showTimetableDialog(homework: Homework) {
(activity as? MainActivity)?.showDialogFragment(HomeworkDialog.newInstance(homework))
(activity as? MainActivity)?.showDialogFragment(HomeworkDetailsDialog.newInstance(homework))
}
override fun onSaveInstanceState(outState: Bundle) {

View File

@ -24,6 +24,8 @@ class HomeworkItem(header: HomeworkHeader, val homework: Homework) :
homeworkItemSubject.text = homework.subject
homeworkItemTeacher.text = homework.teacher
homeworkItemContent.text = homework.content
homeworkItemCheckImage.visibility = if (homework.isDone) View.VISIBLE else View.GONE
homeworkItemAttachmentImage.visibility = if (!homework.isDone && homework.attachments.isNotEmpty()) View.VISIBLE else View.GONE
}
}
@ -43,7 +45,8 @@ class HomeworkItem(header: HomeworkHeader, val homework: Homework) :
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) :
FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -95,6 +95,10 @@ class HomeworkPresenter @Inject constructor(
})
}
fun reloadData() {
loadData(currentDate, false)
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading homework data started")
currentDate = date

View File

@ -0,0 +1,79 @@
package io.github.wulkanowy.ui.modules.homework.details
import android.annotation.SuppressLint
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.text.HtmlCompat
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.synthetic.main.dialog_homework.*
import javax.inject.Inject
class HomeworkDetailsDialog : BaseDialogFragment(), HomeworkDetailsView {
@Inject
lateinit var presenter: HomeworkDetailsPresenter
private lateinit var homework: Homework
companion object {
private const val ARGUMENT_KEY = "Item"
fun newInstance(homework: Homework): HomeworkDetailsDialog {
return HomeworkDetailsDialog().apply {
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) }
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0)
arguments?.run {
homework = getSerializable(ARGUMENT_KEY) as Homework
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_homework, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
@SuppressLint("SetTextI18n")
override fun initView() {
homeworkDialogDate.text = homework.date.toFormattedString()
homeworkDialogEntryDate.text = homework.entryDate.toFormattedString()
homeworkDialogSubject.text = homework.subject
homeworkDialogTeacher.text = homework.teacher
homeworkDialogContent.movementMethod = LinkMovementMethod.getInstance()
homeworkDialogContent.text = homework.content
homeworkDialogAttachments.movementMethod = LinkMovementMethod.getInstance()
homeworkDialogAttachments.text = HtmlCompat.fromHtml(homework.attachments.joinToString("<br>") {
"<a href='${it.first}'>${it.second}</a>"
}, HtmlCompat.FROM_HTML_MODE_COMPACT).ifBlank { getString(R.string.all_no_data) }
homeworkDialogRead.text = view?.context?.getString(if (homework.isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
homeworkDialogRead.setOnClickListener { presenter.toggleDone(homework) }
homeworkDialogClose.setOnClickListener { dismiss() }
}
override fun updateMarkAsDoneLabel(isDone: Boolean) {
(parentFragment as? HomeworkFragment)?.onReloadList()
homeworkDialogRead.text = view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,44 @@
package io.github.wulkanowy.ui.modules.homework.details
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class HomeworkDetailsPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val homeworkRepository: HomeworkRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: HomeworkDetailsView) {
super.onAttachView(view)
view.initView()
Timber.i("Homework details view was initialized")
}
fun toggleDone(homework: Homework) {
Timber.i("Homework details update start")
disposable.add(homeworkRepository.toggleDone(homework)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Homework details update: Success")
view?.run {
updateMarkAsDoneLabel(homework.isDone)
}
analytics.logEvent("homework_mark_as_done")
}) {
Timber.i("Homework details update result: An exception occurred")
errorHandler.dispatch(it)
}
)
}
}

View File

@ -0,0 +1,10 @@
package io.github.wulkanowy.ui.modules.homework.details
import io.github.wulkanowy.ui.base.BaseView
interface HomeworkDetailsView : BaseView {
fun initView()
fun updateMarkAsDoneLabel(isDone: Boolean)
}

View File

@ -20,6 +20,7 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeModule
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageModule
@ -98,6 +99,10 @@ abstract class MainModule {
@ContributesAndroidInjector
abstract fun bindHomeworkFragment(): HomeworkFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindHomeworkDetailsDialog(): HomeworkDetailsDialog
@PerFragment
@ContributesAndroidInjector
abstract fun bindLuckyNumberFragment(): LuckyNumberFragment

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -97,13 +98,46 @@
android:textIsSelectable="true"
android:textSize="12sp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkDialogClose"
style="@style/Widget.MaterialComponents.Button.TextButton"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="15dp"
android:text="@string/all_close" />
android:layout_marginTop="10dp"
android:text="@string/homework_attachments"
android:textSize="17sp" />
<TextView
android:id="@+id/homeworkDialogAttachments"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:lineSpacingMultiplier="1.2"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="14sp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkDialogRead"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/homework_mark_as_done"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkDialogClose"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -1,4 +1,5 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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:id="@+id/homework_subitem_container"
android:layout_width="match_parent"
@ -8,43 +9,75 @@
<TextView
android:id="@+id/homeworkItemSubject"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:textSize="17sp"
tools:text="@tools:sample/lorem" />
android:ellipsize="end"
android:singleLine="true"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@+id/homeworkItemTeacher"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/homeworkItemTeacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:layout_toEndOf="@id/homeworkItemSubject"
android:layout_toRightOf="@id/homeworkItemSubject"
android:ellipsize="end"
android:gravity="end"
android:maxWidth="200dp"
android:minWidth="80dp"
android:singleLine="true"
android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/homeworkItemSubject"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/homeworkItemContent"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/homeworkItemSubject"
android:layout_alignStart="@id/homeworkItemSubject"
android:layout_alignLeft="@id/homeworkItemSubject"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="50dp"
android:layout_marginBottom="15dp"
android:ellipsize="end"
android:lineSpacingMultiplier="1.2"
android:maxLines="2"
android:textSize="14sp"
tools:text="@tools:sample/lorem" />
</RelativeLayout>
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/homeworkItemSubject"
app:layout_constraintTop_toBottomOf="@id/homeworkItemSubject"
tools:text="@tools:sample/lorem/random" />
<ImageView
android:id="@+id/homeworkItemCheckImage"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@id/homeworkItemTeacher"
app:layout_constraintTop_toBottomOf="@id/homeworkItemTeacher"
app:srcCompat="@drawable/ic_check"
app:tint="?android:textColorSecondary"
tools:ignore="ContentDescription"
tools:visibility="gone" />
<ImageView
android:id="@+id/homeworkItemAttachmentImage"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@id/homeworkItemTeacher"
app:layout_constraintTop_toBottomOf="@id/homeworkItemTeacher"
app:srcCompat="@drawable/ic_attachment"
app:tint="?android:textColorSecondary"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -219,6 +219,9 @@
<!--Homework-->
<string name="homework_no_items">Keine Informationen über Hausaufgaben</string>
<string name="homework_mark_as_done">Gemacht</string>
<string name="homework_mark_as_undone">Unvollständig</string>
<string name="homework_attachments">Anhänge</string>
<!--Lucky number-->

View File

@ -244,6 +244,9 @@
<!--Homework-->
<string name="homework_no_items">Brak zadań domowych</string>
<string name="homework_mark_as_done">Wykonane</string>
<string name="homework_mark_as_undone">Niewykonane</string>
<string name="homework_attachments">Załączniki</string>
<!--Lucky number-->

View File

@ -240,6 +240,9 @@
<!--Homework-->
<string name="homework_no_items">Нет домашних заданий</string>
<string name="homework_mark_as_done">сделанный</string>
<string name="homework_mark_as_undone">Не сделано</string>
<string name="homework_attachments">Вложения</string>
<!--Lucky number-->

View File

@ -241,6 +241,9 @@
<!--Homework-->
<string name="homework_no_items">Немає домашніх завдань</string>
<string name="homework_mark_as_done">зроблений</string>
<string name="homework_mark_as_undone">Не зроблено</string>
<string name="homework_attachments">Вкладення</string>
<!--Lucky number-->

View File

@ -28,6 +28,6 @@
<item>lublin</item>
<item>tarnow</item>
<item>rzeszow</item>
<item>Default</item>
<item>powiatwulkanowy</item>
</string-array>
</resources>

View File

@ -227,6 +227,9 @@
<!--Homework-->
<string name="homework_no_items">No info about homework</string>
<string name="homework_mark_as_done">Mark as done</string>
<string name="homework_mark_as_undone">Mark as undone</string>
<string name="homework_attachments">Attachments</string>
<!--Lucky number-->