forked from github/wulkanowy-mirror
Add completed lessons (#236)
This commit is contained in:

committed by
Rafał Borcz

parent
52ed7dcb6c
commit
297502056c
@ -117,4 +117,8 @@ internal class RepositoryModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||
@ -28,11 +29,13 @@ import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.Subject
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@ -50,9 +53,10 @@ import javax.inject.Singleton
|
||||
Note::class,
|
||||
Homework::class,
|
||||
Subject::class,
|
||||
LuckyNumber::class
|
||||
LuckyNumber::class,
|
||||
CompletedLesson::class
|
||||
],
|
||||
version = 2,
|
||||
version = 3,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
@ -63,7 +67,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
|
||||
.setJournalMode(TRUNCATE)
|
||||
.addMigrations(
|
||||
Migration2()
|
||||
Migration2(),
|
||||
Migration3()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
@ -94,4 +99,6 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract val subjectDao: SubjectDao
|
||||
|
||||
abstract val luckyNumberDao: LuckyNumberDao
|
||||
|
||||
abstract val completedLessonsDao: CompletedLessonsDao
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
interface CompletedLessonsDao {
|
||||
|
||||
@Insert
|
||||
fun insertAll(exams: List<CompletedLesson>): List<Long>
|
||||
|
||||
@Delete
|
||||
fun deleteAll(exams: List<CompletedLesson>)
|
||||
|
||||
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<CompletedLesson>>
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.io.Serializable
|
||||
|
||||
@Entity(tableName = "CompletedLesson")
|
||||
data class CompletedLesson(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
var studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "diary_id")
|
||||
var diaryId: Int,
|
||||
|
||||
var date: LocalDate,
|
||||
|
||||
var number: Int,
|
||||
|
||||
var subject: String,
|
||||
|
||||
var topic: String,
|
||||
|
||||
var teacher: String,
|
||||
|
||||
@ColumnInfo(name = "teacher_symbol")
|
||||
var teacherSymbol: String,
|
||||
|
||||
var substitution: String,
|
||||
|
||||
var absence: String,
|
||||
|
||||
var resources: String
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration3 : Migration(2, 3) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE CompletedLesson (" +
|
||||
"id INTEGER NOT NULL PRIMARY KEY, " +
|
||||
"student_id INTEGER NOT NULL, " +
|
||||
"diary_id INTEGER NOT NULL, " +
|
||||
"date INTEGER NOT NULL, " +
|
||||
"number INTEGER NOT NULL, " +
|
||||
"subject TEXT NOT NULL, " +
|
||||
"topic TEXT NOT NULL, " +
|
||||
"teacher TEXT NOT NULL, " +
|
||||
"teacher_symbol TEXT NOT NULL, " +
|
||||
"substitution TEXT NOT NULL, " +
|
||||
"absence TEXT NOT NULL, " +
|
||||
"resources TEXT NOT NULL)")
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.repositories.local.CompletedLessonsLocal
|
||||
import io.github.wulkanowy.data.repositories.remote.CompletedLessonsRemote
|
||||
import io.github.wulkanowy.utils.friday
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class CompletedLessonsRepository @Inject constructor(
|
||||
private val settings: InternetObservingSettings,
|
||||
private val local: CompletedLessonsLocal,
|
||||
private val remote: CompletedLessonsRemote
|
||||
) {
|
||||
|
||||
fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<CompletedLesson>> {
|
||||
return Single.fromCallable { startDate.monday to endDate.friday }
|
||||
.flatMap { dates ->
|
||||
local.getCompletedLessons(semester, dates.first, dates.second).filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.getCompletedLessons(semester, dates.first, dates.second)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
local.getCompletedLessons(semester, dates.first, dates.second)
|
||||
.toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteCompleteLessons(old - new)
|
||||
local.saveCompletedLessons(new - old)
|
||||
}
|
||||
}.flatMap {
|
||||
local.getCompletedLessons(semester, dates.first, dates.second)
|
||||
.toSingle(emptyList())
|
||||
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.github.wulkanowy.data.repositories.local
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) {
|
||||
|
||||
fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Maybe<List<CompletedLesson>> {
|
||||
return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end).filter { !it.isEmpty() }
|
||||
}
|
||||
|
||||
fun saveCompletedLessons(completedLessons: List<CompletedLesson>) {
|
||||
completedLessonsDb.insertAll(completedLessons)
|
||||
}
|
||||
|
||||
fun deleteCompleteLessons(completedLessons: List<CompletedLesson>) {
|
||||
completedLessonsDb.deleteAll(completedLessons)
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package io.github.wulkanowy.data.repositories.remote
|
||||
|
||||
import io.github.wulkanowy.api.Api
|
||||
import io.github.wulkanowy.api.toLocalDate
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class CompletedLessonsRemote @Inject constructor(private val api: Api) {
|
||||
|
||||
fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<CompletedLesson>> {
|
||||
return Single.just(api.apply { diaryId = semester.diaryId })
|
||||
.flatMap { it.getCompletedLessons(startDate, endDate) }
|
||||
.map { lessons ->
|
||||
lessons.map {
|
||||
it.absence
|
||||
CompletedLesson(
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
date = it.date.toLocalDate(),
|
||||
number = it.number,
|
||||
subject = it.subject,
|
||||
topic = it.topic,
|
||||
teacher = it.teacher,
|
||||
teacherSymbol = it.teacherSymbol,
|
||||
substitution = it.substitution,
|
||||
absence = it.absence,
|
||||
resources = it.resources
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import com.firebase.jobdispatcher.JobParameters
|
||||
import com.firebase.jobdispatcher.SimpleJobService
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
||||
import io.github.wulkanowy.data.repositories.CompletedLessonsRepository
|
||||
import io.github.wulkanowy.data.repositories.ExamRepository
|
||||
import io.github.wulkanowy.data.repositories.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.GradeSummaryRepository
|
||||
@ -64,6 +65,9 @@ class SyncWorker : SimpleJobService() {
|
||||
@Inject
|
||||
lateinit var luckyNumber: LuckyNumberRepository
|
||||
|
||||
@Inject
|
||||
lateinit var completedLessons: CompletedLessonsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var prefRepository: PreferencesRepository
|
||||
|
||||
@ -105,7 +109,8 @@ class SyncWorker : SimpleJobService() {
|
||||
note.getNotes(it.first, true, notify).ignoreElement(),
|
||||
homework.getHomework(it.first, LocalDate.now(), true).ignoreElement(),
|
||||
homework.getHomework(it.first, LocalDate.now().plusDays(1), true).ignoreElement(),
|
||||
luckyNumber.getLuckyNumber(it.first, true, notify).ignoreElement()
|
||||
luckyNumber.getLuckyNumber(it.first, true, notify).ignoreElement(),
|
||||
completedLessons.getCompletedLessons(it.first, start, end, true).ignoreElement()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.base
|
||||
import android.content.res.Resources
|
||||
import com.readystatesoftware.chuck.api.ChuckCollector
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
|
||||
import io.github.wulkanowy.api.interceptor.ServiceUnavailableException
|
||||
import io.github.wulkanowy.api.login.NotLoggedInException
|
||||
import timber.log.Timber
|
||||
@ -26,6 +27,7 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
|
||||
is SocketTimeoutException -> resources.getString(R.string.error_timeout)
|
||||
is NotLoggedInException -> resources.getString(R.string.error_login_failed)
|
||||
is ServiceUnavailableException -> resources.getString(R.string.error_service_unavailable)
|
||||
is FeatureDisabledException -> resources.getString(R.string.error_feature_disabled)
|
||||
else -> resources.getString(R.string.error_unknown)
|
||||
}), error)
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.security.ScramblerException
|
||||
import javax.inject.Inject
|
||||
|
||||
class SessionErrorHandler @Inject constructor(resources: Resources, chuckCollector: ChuckCollector) : ErrorHandler(resources, chuckCollector) {
|
||||
open class SessionErrorHandler @Inject constructor(
|
||||
resources: Resources,
|
||||
chuckCollector: ChuckCollector
|
||||
) : ErrorHandler(resources, chuckCollector) {
|
||||
|
||||
var onDecryptionFail: () -> Unit = {}
|
||||
|
||||
|
@ -8,7 +8,10 @@ import io.github.wulkanowy.api.login.BadCredentialsException
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoginErrorHandler @Inject constructor(resources: Resources, chuckCollector: ChuckCollector) : ErrorHandler(resources, chuckCollector) {
|
||||
class LoginErrorHandler @Inject constructor(
|
||||
resources: Resources,
|
||||
chuckCollector: ChuckCollector
|
||||
) : ErrorHandler(resources, chuckCollector) {
|
||||
|
||||
var onBadCredentials: () -> Unit = {}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
|
||||
|
||||
@Module
|
||||
abstract class MainModule {
|
||||
@ -93,5 +94,9 @@ abstract class MainModule {
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindsAccountDialog(): AccountDialog
|
||||
abstract fun bindAccountDialog(): AccountDialog
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment
|
||||
}
|
||||
|
@ -2,6 +2,9 @@ package io.github.wulkanowy.ui.modules.timetable
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
@ -12,6 +15,7 @@ import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_timetable.*
|
||||
import javax.inject.Inject
|
||||
@ -39,6 +43,14 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
|
||||
override val isViewEmpty: Boolean
|
||||
get() = timetableAdapter.isEmpty
|
||||
|
||||
override val currentStackSize: Int?
|
||||
get() = (activity as? MainActivity)?.currentStackSize
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_timetable, container, false)
|
||||
}
|
||||
@ -63,6 +75,15 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
|
||||
timetableNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||
inflater?.inflate(R.menu.action_menu_timetable, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
override fun updateData(data: List<TimetableItem>) {
|
||||
timetableAdapter.updateDataSet(data, true)
|
||||
}
|
||||
@ -87,6 +108,10 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
|
||||
presenter.onViewReselected()
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
(activity as? MainActivity)?.popView()
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
@ -111,6 +136,10 @@ class TimetableFragment : BaseSessionFragment(), TimetableView, MainView.MainChi
|
||||
(activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson))
|
||||
}
|
||||
|
||||
override fun openCompletedLessonsView() {
|
||||
(activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||
|
@ -57,12 +57,16 @@ class TimetablePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onViewReselected() {
|
||||
Timber.i("Exam view is reselected")
|
||||
now().nextOrSameSchoolDay.also {
|
||||
if (currentDate != it) {
|
||||
loadData(it)
|
||||
reloadView()
|
||||
} else if (view?.isViewEmpty == false) view?.resetView()
|
||||
Timber.i("Timetable view is reselected")
|
||||
view?.also { view ->
|
||||
if (view.currentStackSize == 1) {
|
||||
now().nextOrSameSchoolDay.also {
|
||||
if (currentDate != it) {
|
||||
loadData(it)
|
||||
reloadView()
|
||||
} else if (!view.isViewEmpty) view.resetView()
|
||||
}
|
||||
} else view.popView()
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +77,11 @@ class TimetablePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onCompletedLessonsSwitchSelected(): Boolean {
|
||||
view?.openCompletedLessonsView()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
||||
Timber.i("Loading timetable data started")
|
||||
currentDate = date
|
||||
|
@ -9,6 +9,8 @@ interface TimetableView : BaseSessionView {
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
val currentStackSize: Int?
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<TimetableItem>)
|
||||
@ -32,4 +34,8 @@ interface TimetableView : BaseSessionView {
|
||||
fun showNextButton(show: Boolean)
|
||||
|
||||
fun showTimetableDialog(lesson: Timetable)
|
||||
|
||||
fun popView()
|
||||
|
||||
fun openCompletedLessonsView()
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.completed
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.CompletedLesson
|
||||
import kotlinx.android.synthetic.main.dialog_lesson_completed.*
|
||||
|
||||
class CompletedLessonDialog : DialogFragment() {
|
||||
|
||||
private lateinit var completedLesson: CompletedLesson
|
||||
|
||||
companion object {
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: CompletedLesson): CompletedLessonDialog {
|
||||
return CompletedLessonDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
arguments?.run {
|
||||
completedLesson = getSerializable(CompletedLessonDialog.ARGUMENT_KEY) as CompletedLesson
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_lesson_completed, container, false)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
completedLessonDialogSubject.text = completedLesson.subject
|
||||
completedLessonDialogTopic.text = completedLesson.topic
|
||||
completedLessonDialogTeacher.text = completedLesson.teacher
|
||||
completedLessonDialogAbsence.text = completedLesson.absence
|
||||
completedLessonDialogChanges.text = completedLesson.substitution
|
||||
completedLessonDialogResources.text = completedLesson.resources
|
||||
|
||||
completedLesson.substitution.let {
|
||||
if (it.isBlank()) {
|
||||
completedLessonDialogChangesTitle.visibility = View.GONE
|
||||
completedLessonDialogChanges.visibility = View.GONE
|
||||
} else completedLessonDialogChanges.text = it
|
||||
}
|
||||
|
||||
completedLesson.absence.let {
|
||||
if (it.isBlank()) {
|
||||
completedLessonDialogAbsenceTitle.visibility = View.GONE
|
||||
completedLessonDialogAbsence.visibility = View.GONE
|
||||
} else completedLessonDialogAbsence.text = it
|
||||
}
|
||||
|
||||
completedLesson.resources.let {
|
||||
if (it.isBlank()) {
|
||||
completedLessonDialogResourcesTitle.visibility = View.GONE
|
||||
completedLessonDialogResources.visibility = View.GONE
|
||||
} else completedLessonDialogResources.text = it
|
||||
}
|
||||
|
||||
completedLessonDialogClose.setOnClickListener { dismiss() }
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.completed
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
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 kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_completed_lesson.*
|
||||
|
||||
class CompletedLessonItem(val completedLesson: CompletedLesson) : AbstractFlexibleItem<CompletedLessonItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_completed_lesson
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): CompletedLessonItem.ViewHolder {
|
||||
return CompletedLessonItem.ViewHolder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: CompletedLessonItem.ViewHolder?, position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.apply {
|
||||
completedLessonItemNumber.text = completedLesson.number.toString()
|
||||
completedLessonItemSubject.text = completedLesson.subject
|
||||
completedLessonItemTopic.text = completedLesson.topic
|
||||
completedLessonItemAlert.visibility = if (completedLesson.substitution.isNotEmpty()) VISIBLE else GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CompletedLessonItem
|
||||
|
||||
if (completedLesson != other.completedLesson) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return completedLesson.hashCode()
|
||||
}
|
||||
|
||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.completed
|
||||
|
||||
import android.content.res.Resources
|
||||
import com.readystatesoftware.chuck.api.ChuckCollector
|
||||
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
|
||||
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
|
||||
import javax.inject.Inject
|
||||
|
||||
class CompletedLessonsErrorHandler @Inject constructor(
|
||||
resources: Resources,
|
||||
chuckCollector: ChuckCollector
|
||||
) : SessionErrorHandler(resources, chuckCollector) {
|
||||
|
||||
var onFeatureDisabled: () -> Unit = {}
|
||||
|
||||
override fun proceed(error: Throwable) {
|
||||
when (error) {
|
||||
is FeatureDisabledException -> onFeatureDisabled()
|
||||
else -> super.proceed(error)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
super.clear()
|
||||
onFeatureDisabled = {}
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.completed
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_timetable_completed.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class CompletedLessonsFragment : BaseSessionFragment(), CompletedLessonsView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: CompletedLessonsPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var completedLessonsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
companion object {
|
||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||
|
||||
fun newInstance() = CompletedLessonsFragment()
|
||||
}
|
||||
|
||||
override val titleStringId: Int
|
||||
get() = R.string.completed_lessons_title
|
||||
|
||||
override val isViewEmpty
|
||||
get() = completedLessonsAdapter.isEmpty
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_timetable_completed, container, false)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
messageContainer = completedLessonsRecycler
|
||||
presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY))
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
completedLessonsAdapter.run {
|
||||
setOnItemClickListener { presenter.onCompletedLessonsItemSelected(it) }
|
||||
}
|
||||
|
||||
completedLessonsRecycler.run {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
adapter = completedLessonsAdapter
|
||||
}
|
||||
completedLessonsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() }
|
||||
completedLessonsNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<CompletedLessonItem>) {
|
||||
completedLessonsAdapter.updateDataSet(data, true)
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
completedLessonsAdapter.clear()
|
||||
}
|
||||
|
||||
override fun updateNavigationDay(date: String) {
|
||||
completedLessonsNavDate.text = date
|
||||
}
|
||||
|
||||
override fun hideRefresh() {
|
||||
completedLessonsSwipe.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
completedLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun showFeatureDisabled() {
|
||||
context?.let {
|
||||
completedLessonsInfo.text = getString(R.string.error_feature_disabled)
|
||||
completedLessonsInfoImage.setImageDrawable(ContextCompat.getDrawable(it, R.drawable.ic_all_close_circle_24dp))
|
||||
}
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
completedLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
completedLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun showPreButton(show: Boolean) {
|
||||
completedLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun showNextButton(show: Boolean) {
|
||||
completedLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun showCompletedLessonDialog(completedLesson: CompletedLesson) {
|
||||
(activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson))
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(CompletedLessonsFragment.SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.completed
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.repositories.CompletedLessonsRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
|
||||
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.github.wulkanowy.utils.isHolidays
|
||||
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 org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.ofEpochDay
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class CompletedLessonsPresenter @Inject constructor(
|
||||
private val schedulers: SchedulersProvider,
|
||||
private val errorHandler: CompletedLessonsErrorHandler,
|
||||
private val studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val completedLessonsRepository: CompletedLessonsRepository,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BaseSessionPresenter<CompletedLessonsView>(errorHandler) {
|
||||
|
||||
lateinit var currentDate: LocalDate
|
||||
private set
|
||||
|
||||
fun onAttachView(view: CompletedLessonsView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
Timber.i("Completed lessons is attached")
|
||||
view.initView()
|
||||
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||
reloadView()
|
||||
errorHandler.onFeatureDisabled = {
|
||||
this.view?.showFeatureDisabled()
|
||||
Timber.i("Completed lessons feature disabled by school")
|
||||
}
|
||||
}
|
||||
|
||||
fun onPreviousDay() {
|
||||
loadData(currentDate.previousSchoolDay)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
fun onNextDay() {
|
||||
loadData(currentDate.nextSchoolDay)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
fun onSwipeRefresh() {
|
||||
Timber.i("Force refreshing the completed lessons")
|
||||
loadData(currentDate, true)
|
||||
}
|
||||
|
||||
fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is CompletedLessonItem) {
|
||||
Timber.i("Select completed lessons item ${item.completedLesson.id}")
|
||||
view?.showCompletedLessonDialog(item.completedLesson)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
||||
Timber.i("Loading completed lessons data started")
|
||||
currentDate = date
|
||||
disposable.apply {
|
||||
clear()
|
||||
add(studentRepository.getCurrentStudent()
|
||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||
.delay(200, TimeUnit.MILLISECONDS)
|
||||
.flatMap { completedLessonsRepository.getCompletedLessons(it, currentDate, currentDate, forceRefresh) }
|
||||
.map { items -> items.map { CompletedLessonItem(it) } }
|
||||
.map { items -> items.sortedBy { it.completedLesson.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally {
|
||||
view?.run {
|
||||
hideRefresh()
|
||||
showProgress(false)
|
||||
}
|
||||
}
|
||||
.subscribe({
|
||||
Timber.i("Loading completed lessons lessons result: Success")
|
||||
view?.apply {
|
||||
updateData(it)
|
||||
showEmpty(it.isEmpty())
|
||||
showContent(it.isNotEmpty())
|
||||
}
|
||||
analytics.logEvent("load_completed_lessons", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))
|
||||
}) {
|
||||
Timber.i("Loading completed lessons result: An exception occurred")
|
||||
view?.run { showEmpty(isViewEmpty) }
|
||||
errorHandler.dispatch(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun reloadView() {
|
||||
Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}")
|
||||
view?.apply {
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
showEmpty(false)
|
||||
clearData()
|
||||
showNextButton(!currentDate.plusDays(1).isHolidays)
|
||||
showPreButton(!currentDate.minusDays(1).isHolidays)
|
||||
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.completed
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.ui.base.session.BaseSessionView
|
||||
|
||||
interface CompletedLessonsView : BaseSessionView {
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<CompletedLessonItem>)
|
||||
|
||||
fun clearData()
|
||||
|
||||
fun updateNavigationDay(date: String)
|
||||
|
||||
fun hideRefresh()
|
||||
|
||||
fun showEmpty(show: Boolean)
|
||||
|
||||
fun showFeatureDisabled()
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showPreButton(show: Boolean)
|
||||
|
||||
fun showNextButton(show: Boolean)
|
||||
|
||||
fun showCompletedLessonDialog(completedLesson: CompletedLesson)
|
||||
}
|
Reference in New Issue
Block a user