mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2024-11-23 17:36:00 -06:00
Add completed lessons (#236)
This commit is contained in:
parent
52ed7dcb6c
commit
297502056c
@ -11,9 +11,9 @@ cache:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
#branches:
|
||||
# only:
|
||||
# - master
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
android:
|
||||
licenses:
|
||||
|
@ -73,7 +73,7 @@ play {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation('com.github.wulkanowy:api:f941c4b1c7') { exclude module: "threetenbp" }
|
||||
implementation('com.github.wulkanowy:api:0bbd246778') { exclude module: "threetenbp" }
|
||||
|
||||
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||
implementation "androidx.appcompat:appcompat:1.0.2"
|
||||
|
@ -0,0 +1,57 @@
|
||||
package io.github.wulkanowy.data.repositories.local
|
||||
|
||||
import androidx.room.Room
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CompletedLessonsLocalTest {
|
||||
|
||||
private lateinit var completedLessonsLocal: CompletedLessonsLocal
|
||||
|
||||
private lateinit var testDb: AppDatabase
|
||||
|
||||
@Before
|
||||
fun createDb() {
|
||||
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
|
||||
.build()
|
||||
completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao)
|
||||
}
|
||||
|
||||
@After
|
||||
fun closeDb() {
|
||||
testDb.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
completedLessonsLocal.saveCompletedLessons(listOf(
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 10), 1),
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 14), 2),
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 17), 3)
|
||||
))
|
||||
|
||||
val completed = completedLessonsLocal
|
||||
.getCompletedLessons(Semester(1, 1, 2, "", 3, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, completed.size)
|
||||
assertEquals(completed[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(completed[1].date, LocalDate.of(2018, 9, 14))
|
||||
}
|
||||
|
||||
private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson {
|
||||
return CompletedLesson(1, 2, date, number, "", "", "", "", "", "", "")
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
9
app/src/main/res/drawable/ic_all_close_circle_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_all_close_circle_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M14.59,8L12,10.59L9.41,8L8,9.41L10.59,12L8,14.59L9.41,16L12,13.41L14.59,16L16,14.59L13.41,12L16,9.41L14.59,8Z" />
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19,19H5V8H19M19,3H18V1H16V3H8V1H6V3H5C3.89,3 3,3.9 3,5V19A2,2 0,0 0,5 21H19A2,2 0,0 0,21 19V5A2,2 0,0 0,19 3M16.53,11.06L15.47,10L10.59,14.88L8.47,12.76L7.41,13.82L10.59,17L16.53,11.06Z" />
|
||||
</vector>
|
138
app/src/main/res/layout/dialog_lesson_completed.xml
Normal file
138
app/src/main/res/layout/dialog_lesson_completed.xml
Normal file
@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.modules.timetable.completed.CompletedLessonDialog">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/all_details"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/timetable_lesson"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogSubject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3dp"
|
||||
android:text="@string/all_no_data"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/completed_lessons_topic"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogTopic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3dp"
|
||||
android:text="@string/all_no_data"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogTeacherTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/all_teacher"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogTeacher"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3dp"
|
||||
android:text="@string/all_no_data"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogChangesTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/timetable_changes"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogChanges"
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogAbsenceTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/completed_lessons_absence"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogAbsence"
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogResourcesTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/completed_lessons_resources"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonDialogResources"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3dp"
|
||||
android:autoLink="web"
|
||||
android:text="@string/all_no_data"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/completedLessonDialogClose"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@string/all_close"
|
||||
android:textAllCaps="true"
|
||||
android:textSize="15sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
99
app/src/main/res/layout/fragment_timetable_completed.xml
Normal file
99
app/src/main/res/layout/fragment_timetable_completed.xml
Normal file
@ -0,0 +1,99 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.modules.timetable.completed.CompletedLessonsFragment">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="50dp">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/completedLessonsProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/completedLessonsSwipe"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/completedLessonsRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/completedLessonsEmpty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="100dp"
|
||||
android:minHeight="100dp"
|
||||
android:id="@+id/completedLessonsInfoImage"
|
||||
app:srcCompat="@drawable/ic_menu_main_lessons_completed_24dp"
|
||||
app:tint="?android:attr/textColorPrimary"
|
||||
tools:ignore="contentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonsInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/completed_lessons_no_items"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:elevation="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/completedLessonsPreviousButton"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="start|center"
|
||||
android:text="@string/all_prev"
|
||||
android:textAlignment="gravity" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonsNavDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/app_name" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/completedLessonsNextButton"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="end|center"
|
||||
android:text="@string/all_next"
|
||||
android:textAlignment="gravity" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
72
app/src/main/res/layout/item_completed_lesson.xml
Normal file
72
app/src/main/res/layout/item_completed_lesson.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<RelativeLayout 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/completedLesson_subitem_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/ic_all_divider"
|
||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="7dp"
|
||||
tools:context=".ui.modules.timetable.completed.CompletedLessonItem">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonItemNumber"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"
|
||||
android:maxLength="2"
|
||||
android:textSize="32sp"
|
||||
tools:ignore="all"
|
||||
tools:text="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonItemSubject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginEnd="40dp"
|
||||
android:layout_marginRight="40dp"
|
||||
android:layout_toEndOf="@+id/completedLessonItemNumber"
|
||||
android:layout_toRightOf="@+id/completedLessonItemNumber"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="17sp"
|
||||
tools:ignore="RelativeOverlap"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/completedLessonItemTopic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@id/completedLessonItemSubject"
|
||||
android:layout_alignLeft="@id/completedLessonItemSubject"
|
||||
android:layout_alignEnd="@+id/completedLessonItemAlert"
|
||||
android:layout_alignRight="@+id/completedLessonItemAlert"
|
||||
android:layout_alignBottom="@+id/completedLessonItemNumber"
|
||||
android:layout_marginEnd="40dp"
|
||||
android:layout_marginRight="40dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/completedLessonItemAlert"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="10dp"
|
||||
app:srcCompat="@drawable/ic_timetable_swap_30dp"
|
||||
tools:ignore="contentDescription" />
|
||||
|
||||
</RelativeLayout>
|
10
app/src/main/res/menu/action_menu_timetable.xml
Normal file
10
app/src/main/res/menu/action_menu_timetable.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/timetableMenuCompletedLessons"
|
||||
android:icon="@drawable/ic_menu_main_lessons_completed_24dp"
|
||||
android:orderInCategory="1"
|
||||
android:title="@string/completed_lessons_button"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
@ -102,6 +102,13 @@
|
||||
<string name="timetable_changes">Zmiany</string>
|
||||
<string name="timetable_no_items">Brak lekcji w tym dniu</string>
|
||||
|
||||
<!--CompletedLesson-->
|
||||
<string name="completed_lessons_title">Lekcje zrealizowane</string>
|
||||
<string name="completed_lessons_button">Zobacz lekcje zrealizowane</string>
|
||||
<string name="completed_lessons_no_items">Brak informacji o lekcjach zrealizowanych</string>
|
||||
<string name="completed_lessons_topic">Temat</string>
|
||||
<string name="completed_lessons_absence">Nieobecność</string>
|
||||
<string name="completed_lessons_resources">Zasoby</string>
|
||||
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Podsumowanie frekwencji</string>
|
||||
@ -263,4 +270,5 @@
|
||||
<string name="error_login_failed">Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację</string>
|
||||
<string name="error_service_unavailable">Dziennik jest niedostępny. Spróbuj ponownie później</string>
|
||||
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
|
||||
<string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string>
|
||||
</resources>
|
||||
|
@ -93,6 +93,13 @@
|
||||
<string name="timetable_changes">Changes</string>
|
||||
<string name="timetable_no_items">No lessons this day</string>
|
||||
|
||||
<!--Completed lessons-->
|
||||
<string name="completed_lessons_title">Completed lessons</string>
|
||||
<string name="completed_lessons_button">Show completed lessons</string>
|
||||
<string name="completed_lessons_no_items">No info about completed lessons</string>
|
||||
<string name="completed_lessons_topic">Topic</string>
|
||||
<string name="completed_lessons_absence">Absence</string>
|
||||
<string name="completed_lessons_resources">Resources</string>
|
||||
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Attendance summary</string>
|
||||
@ -246,4 +253,5 @@
|
||||
<string name="error_login_failed">Login is failed. Try again or restart the app</string>
|
||||
<string name="error_service_unavailable">The log is not available. Try again later</string>
|
||||
<string name="error_unknown">An unexpected error occurred</string>
|
||||
<string name="error_feature_disabled">Feature disabled by your school</string>
|
||||
</resources>
|
||||
|
@ -0,0 +1,56 @@
|
||||
package io.github.wulkanowy.data.repositories.remote
|
||||
|
||||
import io.github.wulkanowy.api.Api
|
||||
import io.github.wulkanowy.api.timetable.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.reactivex.Single
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.threeten.bp.LocalDate
|
||||
import java.sql.Date
|
||||
|
||||
class CompletedLessonsRemoteTest {
|
||||
|
||||
@SpyK
|
||||
private var mockApi = Api()
|
||||
|
||||
@MockK
|
||||
private lateinit var semesterMock: Semester
|
||||
|
||||
@Before
|
||||
fun initApi() {
|
||||
MockKAnnotations.init(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCompletedLessonsTest() {
|
||||
every {
|
||||
mockApi.getCompletedLessons(
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 15)
|
||||
)
|
||||
} returns Single.just(listOf(
|
||||
getCompletedLesson("2018-09-10"),
|
||||
getCompletedLesson("2018-09-17")
|
||||
))
|
||||
|
||||
every { mockApi.diaryId } returns 1
|
||||
every { semesterMock.studentId } returns 1
|
||||
every { semesterMock.diaryId } returns 1
|
||||
|
||||
val completed = CompletedLessonsRemote(mockApi).getCompletedLessons(semesterMock,
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 15)
|
||||
).blockingGet()
|
||||
Assert.assertEquals(2, completed.size)
|
||||
}
|
||||
|
||||
private fun getCompletedLesson(dateString: String): CompletedLesson {
|
||||
return CompletedLesson().apply { date = Date.valueOf(dateString) }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user