1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 00:39:08 -05:00

Refactor attendance module (#161)

This commit is contained in:
Mikołaj Pich 2018-10-01 19:41:09 +02:00 committed by Rafał Borcz
parent a1f64baca4
commit 357b2350cb
35 changed files with 1151 additions and 571 deletions

View File

@ -68,12 +68,13 @@ play {
uploadImages = true
}
ext.supportVersion = "28.0.0-rc02"
ext.supportVersion = "28.0.0"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'com.github.wulkanowy:api:ad57669'
implementation('com.github.wulkanowy:api:07201a4') {
exclude module: "threetenbp"
}
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:design:$supportVersion"
implementation "com.android.support:cardview-v7:$supportVersion"
@ -116,6 +117,7 @@ dependencies {
debugImplementation "com.amitshekhar.android:debug-db:1.0.4"
testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.8.8"
testImplementation "org.mockito:mockito-inline:2.21.0"
androidTestImplementation 'com.android.support.test:runner:1.0.2'

View File

@ -0,0 +1,52 @@
package io.github.wulkanowy.data.repositories.local
import android.arch.persistence.room.Room
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Attendance
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 AttendanceLocalTest {
private lateinit var attendanceLocal: AttendanceLocal
private lateinit var testDb: AppDatabase
@Before
fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(), AppDatabase::class.java).build()
attendanceLocal = AttendanceLocal(testDb.attendanceDao())
}
@After
fun closeDb() {
testDb.close()
}
@Test
fun saveAndReadTest() {
attendanceLocal.saveAttendance(listOf(
Attendance(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 10)),
Attendance(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 14)),
Attendance(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 17)) // in next week
))
val attendance = attendanceLocal
.getAttendance(Semester(studentId = "1", diaryId = "2", semesterId = "3"),
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 14)
)
.blockingGet()
assertEquals(2, attendance.size)
assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10))
assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14))
}
}

View File

@ -11,7 +11,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.threeten.bp.LocalDate
import java.sql.Date
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
@ -35,16 +34,19 @@ class ExamLocalTest {
@Test
fun saveAndReadTest() {
examLocal.saveExams(listOf(
Exam(studentId = "1", diaryId = "2", date = Date.valueOf("2018-09-10")),
Exam(studentId = "1", diaryId = "2", date = Date.valueOf("2018-09-14")),
Exam(studentId = "1", diaryId = "2", date = Date.valueOf("2018-09-17")) // in next week
Exam(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 10)),
Exam(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 14)),
Exam(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 17)) // in next week
))
val exams = examLocal
.getExams(Semester(studentId = "1", diaryId = "2", semesterId = "3"), LocalDate.of(2018, 9, 10))
.getExams(Semester(studentId = "1", diaryId = "2", semesterId = "3"),
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 14)
)
.blockingGet()
assertEquals(2, exams.size)
assertEquals(exams[0].date, Date.valueOf("2018-09-10"))
assertEquals(exams[1].date, Date.valueOf("2018-09-14"))
assertEquals(exams[0].date, LocalDate.of(2018, 9, 10))
assertEquals(exams[1].date, LocalDate.of(2018, 9, 14))
}
}

View File

@ -52,4 +52,8 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideExamDao(database: AppDatabase) = database.examsDao()
@Singleton
@Provides
fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao()
}

View File

@ -3,9 +3,11 @@ package io.github.wulkanowy.data.db
import android.arch.persistence.room.Database
import android.arch.persistence.room.RoomDatabase
import android.arch.persistence.room.TypeConverters
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
@ -16,7 +18,8 @@ import javax.inject.Singleton
entities = [
Student::class,
Semester::class,
Exam::class
Exam::class,
Attendance::class
],
version = 1,
exportSchema = false
@ -29,4 +32,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun semesterDao(): SemesterDao
abstract fun examsDao(): ExamDao
abstract fun attendanceDao(): AttendanceDao
}

View File

@ -1,14 +1,18 @@
package io.github.wulkanowy.data.db
import android.arch.persistence.room.TypeConverter
import org.threeten.bp.*
import java.util.*
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? = value?.run { Date(value) }
fun fromTimestamp(value: Long?): LocalDate? = value?.run {
DateTimeUtils.toInstant(Date(value)).atZone(ZoneOffset.UTC).toLocalDate()
}
@TypeConverter
fun dateToTimestamp(date: Date?): Long? = date?.time
fun dateToTimestamp(date: LocalDate?): Long? {
return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
}
}

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.db.dao
import android.arch.persistence.room.Dao
import android.arch.persistence.room.Delete
import android.arch.persistence.room.Insert
import android.arch.persistence.room.Query
import io.github.wulkanowy.data.db.entities.Attendance
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
@Dao
interface AttendanceDao {
@Insert
fun insertAll(exams: List<Attendance>): List<Long>
@Delete
fun deleteAll(exams: List<Attendance>)
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun getExams(diaryId: String, studentId: String, from: LocalDate, end: LocalDate): Maybe<List<Attendance>>
}

View File

@ -6,7 +6,7 @@ import android.arch.persistence.room.Insert
import android.arch.persistence.room.Query
import io.github.wulkanowy.data.db.entities.Exam
import io.reactivex.Maybe
import java.util.*
import org.threeten.bp.LocalDate
@Dao
interface ExamDao {
@ -18,5 +18,5 @@ interface ExamDao {
fun deleteAll(exams: List<Exam>)
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun getExams(diaryId: String, studentId: String, from: Date, end: Date): Maybe<List<Exam>>
fun getExams(diaryId: String, studentId: String, from: LocalDate, end: LocalDate): Maybe<List<Exam>>
}

View File

@ -0,0 +1,40 @@
package io.github.wulkanowy.data.db.entities
import android.arch.persistence.room.ColumnInfo
import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable
@Entity(tableName = "Attendance")
data class Attendance(
@PrimaryKey(autoGenerate = true)
var id: Long = 0,
@ColumnInfo(name = "student_id")
var studentId: String = "",
@ColumnInfo(name = "diary_id")
var diaryId: String = "",
var date: LocalDate,
var number: Int = 0,
var subject: String = "",
var name: String = "",
var presence: Boolean = false,
var absence: Boolean = false,
var exemption: Boolean = false,
var lateness: Boolean = false,
var excused: Boolean = false,
var deleted: Boolean = false
) : Serializable

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import android.arch.persistence.room.ColumnInfo
import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable
import java.util.*
@Entity(tableName = "Exams")
data class Exam(
@ -18,10 +18,10 @@ data class Exam(
@ColumnInfo(name = "diary_id")
var diaryId: String = "",
var date: Date,
var date: LocalDate,
@ColumnInfo(name = "entry_date")
var entryDate: Date = Date(),
var entryDate: LocalDate = LocalDate.now(),
var subject: String = "",

View File

@ -0,0 +1,43 @@
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.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.local.AttendanceLocal
import io.github.wulkanowy.data.repositories.remote.AttendanceRemote
import io.github.wulkanowy.utils.extension.getWeekFirstDayAlwaysCurrent
import io.reactivex.Single
import org.threeten.bp.DayOfWeek
import org.threeten.bp.LocalDate
import org.threeten.bp.temporal.TemporalAdjusters
import java.net.UnknownHostException
import javax.inject.Inject
class AttendanceRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: AttendanceLocal,
private val remote: AttendanceRemote
) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Attendance>> {
val start = startDate.getWeekFirstDayAlwaysCurrent()
val end = endDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY))
return local.getAttendance(semester, start, end).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getAttendance(semester, start, end)
else Single.error(UnknownHostException())
}.flatMap { newLessons ->
local.getAttendance(semester, start, end).toSingle(emptyList()).map { grades ->
local.deleteAttendance(grades - newLessons)
local.saveAttendance(newLessons - grades)
newLessons
}
}).map { list ->
list.asSequence().filter {
it.date in startDate..endDate
}.toList()
}
}
}

View File

@ -6,8 +6,12 @@ import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.local.ExamLocal
import io.github.wulkanowy.data.repositories.remote.ExamRemote
import io.github.wulkanowy.utils.extension.getWeekFirstDayAlwaysCurrent
import io.github.wulkanowy.utils.extension.toDate
import io.reactivex.Single
import org.threeten.bp.DayOfWeek
import org.threeten.bp.LocalDate
import org.threeten.bp.temporal.TemporalAdjusters
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@ -19,21 +23,24 @@ class ExamRepository @Inject constructor(
private val remote: ExamRemote
) {
fun getExams(semester: Semester, date: LocalDate, forceRefresh: Boolean = false): Single<List<Exam>> {
return local.getExams(semester, date).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getExams(semester, date)
else Single.error(UnknownHostException())
}.flatMap { newExams ->
local.getExams(semester, date).toSingle(emptyList())
.map {
local.deleteExams(it - newExams)
local.saveExams(newExams - it)
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Exam>> {
val start = startDate.getWeekFirstDayAlwaysCurrent()
val end = endDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY))
newExams
}
}
)
return local.getExams(semester, start, end).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getExams(semester, start, end)
else Single.error(UnknownHostException())
}.flatMap { newExams ->
local.getExams(semester, start, end).toSingle(emptyList()).map { grades ->
local.deleteExams(grades - newExams)
local.saveExams(newExams - grades)
newExams
}
}).map { list ->
list.asSequence().filter {
it.date in startDate..endDate
}.toList()
}
}
}

View File

@ -0,0 +1,24 @@
package io.github.wulkanowy.data.repositories.local
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import javax.inject.Inject
class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Attendance>> {
return attendanceDb.getExams(semester.diaryId, semester.studentId, startDate, endDate)
.filter { !it.isEmpty() }
}
fun saveAttendance(attendance: List<Attendance>) {
attendanceDb.insertAll(attendance)
}
fun deleteAttendance(attendance: List<Attendance>) {
attendanceDb.deleteAll(attendance)
}
}

View File

@ -12,10 +12,9 @@ import javax.inject.Inject
class ExamLocal @Inject constructor(private val examDb: ExamDao) {
fun getExams(semester: Semester, startDate: LocalDate): Maybe<List<Exam>> {
return examDb.getExams(semester.diaryId, semester.studentId, startDate.toDate(),
startDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)).toDate()
).filter { !it.isEmpty() }
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> {
return examDb.getExams(semester.diaryId, semester.studentId, startDate, endDate)
.filter { !it.isEmpty() }
}
fun saveExams(exams: List<Exam>) {

View File

@ -0,0 +1,38 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.extension.toLocalDate
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
class AttendanceRemote @Inject constructor(private val api: Api) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
return Single.just(api.run {
if (diaryId != semester.diaryId) {
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getAttendance(startDate, endDate) }.map { attendance ->
attendance.map {
Attendance(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date.toLocalDate(),
number = it.number,
subject = it.subject,
name = it.name,
presence = it.presence,
absence = it.absence,
exemption = it.exemption,
lateness = it.lateness,
excused = it.excused,
deleted = it.deleted
)
}
}
}
}

View File

@ -3,35 +3,34 @@ package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.extension.toDate
import io.github.wulkanowy.utils.extension.toLocalDate
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
class ExamRemote @Inject constructor(private val api: Api) {
fun getExams(semester: Semester, startDate: LocalDate): Single<List<Exam>> {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Exam>> {
return Single.just(api.run {
if (diaryId != semester.diaryId) {
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getExams(startDate.toDate()) }
.map { exams ->
exams.map {
Exam(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date,
entryDate = it.entryDate,
subject = it.subject,
group = it.group,
type = it.type,
description = it.description,
teacher = it.teacher,
teacherSymbol = it.teacherSymbol
)
}
}
}).flatMap { api.getExams(startDate, endDate) }.map { exams ->
exams.map {
Exam(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date.toLocalDate(),
entryDate = it.entryDate.toLocalDate(),
subject = it.subject,
group = it.group,
type = it.type,
description = it.description,
teacher = it.teacher,
teacherSymbol = it.teacherSymbol
)
}
}
}
}

View File

@ -0,0 +1,49 @@
package io.github.wulkanowy.ui.main.attendance
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.utils.extension.toFormat
import kotlinx.android.synthetic.main.dialog_attendance.*
class AttendanceDialog : DialogFragment() {
private lateinit var attendance: Attendance
companion object {
private const val ARGUMENT_KEY = "Item"
fun newInstance(exam: Attendance): AttendanceDialog {
return AttendanceDialog().apply {
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragmentTheme)
arguments?.run {
attendance = getSerializable(ARGUMENT_KEY) as Attendance
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
dialog.setTitle(getString(R.string.all_details))
return inflater.inflate(R.layout.dialog_attendance, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
attendanceDialogSubject.text = attendance.subject
attendanceDialogDescription.text = attendance.name
attendanceDialogDate.text = attendance.date.toFormat()
attendanceDialogNumber.text = attendance.number.toString()
attendanceDialogClose.setOnClickListener { dismiss() }
}
}

View File

@ -4,17 +4,111 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.Attendance
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.utils.extension.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_attendance.*
import javax.inject.Inject
class AttendanceFragment : BaseFragment() {
class AttendanceFragment : BaseFragment(), AttendanceView {
@Inject
lateinit var presenter: AttendancePresenter
@Inject
lateinit var attendanceAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
companion object {
private const val SAVED_DATE_KEY = "CURRENT_DATE"
fun newInstance() = AttendanceFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_attendance, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.run {
attachView(this@AttendanceFragment)
loadData(date = savedInstanceState?.getLong(SAVED_DATE_KEY))
}
}
override fun initView() {
attendanceAdapter.run {
isAutoCollapseOnExpand = true
isAutoScrollOnExpand = true
setOnItemClickListener { presenter.onAttendanceItemSelected(getItem(it))}
}
attendanceRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = attendanceAdapter
}
attendanceSwipe.setOnRefreshListener { presenter.loadData(date = null, forceRefresh = true) }
attendancePreviousButton.setOnClickListener { presenter.loadAttendanceForPreviousDay() }
attendanceNextButton.setOnClickListener { presenter.loadAttendanceForNextDay() }
}
override fun updateData(data: List<AttendanceItem>) {
attendanceAdapter.updateDataSet(data, true)
}
override fun clearData() {
attendanceAdapter.clear()
}
override fun updateNavigationDay(date: String) {
attendanceNavDate.text = date
}
override fun isViewEmpty() = attendanceAdapter.isEmpty
override fun showEmpty(show: Boolean) {
attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showProgress(show: Boolean) {
attendanceProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showContent(show: Boolean) {
attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showRefresh(show: Boolean) {
attendanceSwipe.isRefreshing = show
}
override fun showPreButton(show: Boolean) {
attendancePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
}
override fun showNextButton(show: Boolean) {
attendanceNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
}
override fun showAttendanceDialog(lesson: Attendance) {
AttendanceDialog.newInstance(lesson).show(fragmentManager, lesson.toString())
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
presenter.loadData(date = savedInstanceState?.getLong(SAVED_DATE_KEY))
}
override fun onDestroyView() {
super.onDestroyView()
presenter.detachView()
}
}

View File

@ -0,0 +1,55 @@
package io.github.wulkanowy.ui.main.attendance
import android.view.View
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.Attendance
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_attendance.*
class AttendanceItem : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
lateinit var attendance: Attendance
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_attendance
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as AttendanceItem
if (attendance != other.attendance) return false
return true
}
override fun hashCode(): Int {
return attendance.hashCode()
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
holder.bind(attendance)
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View
get() = contentView
fun bind(lesson: Attendance) {
attendanceItemNumber.text = lesson.number.toString()
attendanceItemSubject.text = lesson.subject
attendanceItemDescription.text = lesson.name
attendanceItemAlert.visibility = if (lesson.absence && !lesson.excused) View.VISIBLE else View.INVISIBLE
}
}
}

View File

@ -0,0 +1,96 @@
package io.github.wulkanowy.ui.main.attendance
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.extension.*
import io.github.wulkanowy.utils.schedulers.SchedulersManager
import org.threeten.bp.LocalDate
import javax.inject.Inject
class AttendancePresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersManager,
private val attendanceRepository: AttendanceRepository,
private val sessionRepository: SessionRepository
) : BasePresenter<AttendanceView>(errorHandler) {
var currentDate: LocalDate = LocalDate.now().getNearSchoolDayPrevOnWeekEnd()
private set
override fun attachView(view: AttendanceView) {
super.attachView(view)
view.initView()
}
fun loadAttendanceForPreviousDay() = loadData(currentDate.getPreviousWorkDay().toEpochDay())
fun loadAttendanceForNextDay() = loadData(currentDate.getNextWorkDay().toEpochDay())
fun loadData(date: Long?, forceRefresh: Boolean = false) {
this.currentDate = LocalDate.ofEpochDay(date ?: currentDate.getNearSchoolDayPrevOnWeekEnd().toEpochDay())
if (currentDate.isHolidays()) return
disposable.clear()
disposable.add(sessionRepository.getSemesters()
.map { selectSemester(it, -1) }
.flatMap { attendanceRepository.getAttendance(it, currentDate, currentDate, forceRefresh) }
.map { createTimetableItems(it) }
.subscribeOn(schedulers.backgroundThread())
.observeOn(schedulers.mainThread())
.doOnSubscribe {
view?.run {
showRefresh(forceRefresh)
showProgress(!forceRefresh)
if (!forceRefresh) {
showEmpty(false)
clearData()
}
showPreButton(!currentDate.minusDays(1).isHolidays())
showNextButton(!currentDate.plusDays(1).isHolidays())
updateNavigationDay(currentDate.toFormat("EEEE \n dd.MM.YYYY").capitalize())
}
}
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
}
}
.subscribe({
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateData(it)
}
}) {
view?.run { showEmpty(isViewEmpty()) }
errorHandler.proceed(it)
})
}
private fun createTimetableItems(items: List<Attendance>): List<AttendanceItem> {
return items.map {
AttendanceItem().apply { attendance = it }
}
}
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance)
}
private fun selectSemester(semesters: List<Semester>, index: Int): Semester {
return semesters.single { it.current }.let { currentSemester ->
if (index == -1) currentSemester
else semesters.single { semester ->
semester.run {
semesterName - 1 == index && diaryId == currentSemester.diaryId
}
}
}
}
}

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.ui.main.attendance
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.ui.base.BaseView
interface AttendanceView : BaseView {
fun initView()
fun updateData(data: List<AttendanceItem>)
fun clearData()
fun updateNavigationDay(date: String)
fun isViewEmpty(): Boolean
fun showEmpty(show: Boolean)
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
fun showRefresh(show: Boolean)
fun showPreButton(show: Boolean)
fun showNextButton(show: Boolean)
fun showAttendanceDialog(lesson: Attendance)
}

View File

@ -10,12 +10,11 @@ import io.github.wulkanowy.utils.extension.getWeekDayName
import io.github.wulkanowy.utils.extension.toFormat
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.header_exam.*
import org.apache.commons.lang3.StringUtils
import java.util.*
import org.threeten.bp.LocalDate
class ExamHeader : AbstractHeaderItem<ExamHeader.ViewHolder>() {
lateinit var date: Date
lateinit var date: LocalDate
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
return ViewHolder(view, adapter)
@ -41,7 +40,7 @@ class ExamHeader : AbstractHeaderItem<ExamHeader.ViewHolder>() {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
holder.run {
examHeaderDay.text = StringUtils.capitalize(date.getWeekDayName())
examHeaderDay.text = date.getWeekDayName().capitalize()
examHeaderDate.text = date.toFormat()
}
}

View File

@ -7,12 +7,11 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.extension.getWeekFirstDayNextOnWeekEnd
import io.github.wulkanowy.utils.extension.isHolidays
import io.github.wulkanowy.utils.extension.toFormat
import io.github.wulkanowy.utils.getNearMonday
import io.github.wulkanowy.utils.schedulers.SchedulersManager
import org.threeten.bp.LocalDate
import java.util.*
import javax.inject.Inject
class ExamPresenter @Inject constructor(
@ -22,7 +21,7 @@ class ExamPresenter @Inject constructor(
private val sessionRepository: SessionRepository
) : BasePresenter<ExamView>(errorHandler) {
var currentDate: LocalDate = getNearMonday(LocalDate.now())
var currentDate: LocalDate = LocalDate.now().getWeekFirstDayNextOnWeekEnd()
private set
override fun attachView(view: ExamView) {
@ -35,13 +34,13 @@ class ExamPresenter @Inject constructor(
fun loadExamsForNextWeek() = loadData(currentDate.plusDays(7).toEpochDay())
fun loadData(date: Long?, forceRefresh: Boolean = false) {
this.currentDate = LocalDate.ofEpochDay(date ?: getNearMonday(currentDate).toEpochDay())
this.currentDate = LocalDate.ofEpochDay(date ?: currentDate.getWeekFirstDayNextOnWeekEnd().toEpochDay())
if (currentDate.isHolidays()) return
disposable.clear()
disposable.add(sessionRepository.getSemesters()
.map { selectSemester(it, -1) }
.flatMap { examRepository.getExams(it, currentDate, forceRefresh) }
.flatMap { examRepository.getExams(it, currentDate, currentDate.plusDays(4), forceRefresh) }
.map { it.groupBy { exam -> exam.date }.toSortedMap() }
.map { createExamItems(it) }
.subscribeOn(schedulers.backgroundThread())
@ -72,7 +71,7 @@ class ExamPresenter @Inject constructor(
.subscribe({ view?.updateData(it) }) { errorHandler.proceed(it) })
}
private fun createExamItems(items: Map<Date, List<Exam>>): List<ExamItem> {
private fun createExamItems(items: Map<LocalDate, List<Exam>>): List<ExamItem> {
return items.flatMap {
val header = ExamHeader().apply { date = it.key }
it.value.reversed().map { item ->

View File

@ -1,104 +0,0 @@
package io.github.wulkanowy.utils
import org.threeten.bp.DayOfWeek.*
import org.threeten.bp.LocalDate
import org.threeten.bp.Year
import org.threeten.bp.format.DateTimeFormatter
import org.threeten.bp.temporal.TemporalAdjuster
import org.threeten.bp.temporal.TemporalAdjusters
import java.util.*
private val formatter = DateTimeFormatter.ofPattern(DATE_PATTERN)
fun getParsedDate(dateString: String, dateFormat: String): LocalDate {
return LocalDate.parse(dateString, DateTimeFormatter.ofPattern(dateFormat))
}
fun getMondaysFromCurrentSchoolYear() = getMondaysFromCurrentSchoolYear(LocalDate.now())
fun getMondaysFromCurrentSchoolYear(date: LocalDate): List<String> {
val startDate = getFirstSchoolDay(getSchoolYearForDate(date))
?.with(TemporalAdjusters.previousOrSame(MONDAY))
val endDate = getFirstSchoolDay(getSchoolYearForDate(date) + 1)
?.with(TemporalAdjusters.previousOrSame(MONDAY))
val dateList = ArrayList<String>()
var monday = startDate as LocalDate
while (monday.isBefore(endDate)) {
dateList.add(monday.format(formatter))
monday = monday.plusWeeks(1)
}
return dateList
}
fun getSchoolYearForDate(date: LocalDate): Int {
return if (date.monthValue <= 8) date.year - 1 else date.year
}
fun getFirstDayOfCurrentWeek(): String = getFirstDayOfCurrentWeek(LocalDate.now())
fun getFirstDayOfCurrentWeek(date: LocalDate): String {
return when (date.dayOfWeek) {
SATURDAY -> date.plusDays(2)
SUNDAY -> date.plusDays(1)
else -> date.with(MONDAY)
}.format(formatter)
}
fun getTodayOrNextDayOrder(next: Boolean): Int = getTodayOrNextDayOrder(next, LocalDate.now())
fun getTodayOrNextDayOrder(next: Boolean, date: LocalDate): Int {
val day = date.dayOfWeek
return if (next) {
if (day == SUNDAY) {
0
} else day.value
} else day.value - 1
}
fun getTodayOrNextDay(next: Boolean): String? = getTodayOrNextDay(next, LocalDate.now())
fun getTodayOrNextDay(next: Boolean, date: LocalDate): String? {
return (if (next) {
date.plusDays(1)
} else date).format(formatter)
}
fun isDateInWeek(firstWeekDay: LocalDate, date: LocalDate): Boolean {
return date.isAfter(firstWeekDay.minusDays(1)) && date.isBefore(firstWeekDay.plusDays(5))
}
fun getNearMonday(date: LocalDate): LocalDate {
return when(date.dayOfWeek) {
MONDAY -> date
SATURDAY, SUNDAY -> date.with(TemporalAdjusters.next(MONDAY))
else -> date.with(TemporalAdjusters.previous(MONDAY))
}
}
/**
* [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335)
*/
fun isHolidays(): Boolean = isHolidays(LocalDate.now())
fun isHolidays(day: LocalDate): Boolean {
return day.isAfter(getLastSchoolDay(day.year)) && day.isBefore(getFirstSchoolDay(day.year))
}
fun getFirstSchoolDay(year: Int): LocalDate {
val firstSeptember = LocalDate.of(year, 9, 1)
return when (firstSeptember.dayOfWeek) {
FRIDAY,
SATURDAY,
SUNDAY -> firstSeptember.with(TemporalAdjusters.firstInMonth(MONDAY))
else -> firstSeptember
}
}
fun getLastSchoolDay(year: Int): LocalDate {
return LocalDate
.of(year, 6, 20)
.with(TemporalAdjusters.next(FRIDAY))
}

View File

@ -1,25 +0,0 @@
package io.github.wulkanowy.utils.extension
import io.github.wulkanowy.utils.DATE_PATTERN
import io.github.wulkanowy.utils.isHolidays
import org.threeten.bp.Instant
import org.threeten.bp.LocalDate
import org.threeten.bp.ZoneId
import org.threeten.bp.format.DateTimeFormatter
import java.util.*
private val formatter = DateTimeFormatter.ofPattern(DATE_PATTERN)
fun LocalDate.toDate(): Date = java.sql.Date.valueOf(this.format(formatter))
fun LocalDate.toFormat(format: String): String = this.format(DateTimeFormatter.ofPattern(format))
fun LocalDate.toFormat(): String = this.toFormat(DATE_PATTERN)
fun LocalDate.isHolidays(): Boolean = isHolidays(this)
fun Date.toLocalDate(): LocalDate = Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDate()
fun Date.getWeekDayName(): String = this.toLocalDate().format(DateTimeFormatter.ofPattern("EEEE", Locale.getDefault()))
fun Date.toFormat(): String = this.toLocalDate().toFormat()

View File

@ -0,0 +1,79 @@
package io.github.wulkanowy.utils.extension
import io.github.wulkanowy.utils.DATE_PATTERN
import org.threeten.bp.*
import org.threeten.bp.format.DateTimeFormatter
import org.threeten.bp.temporal.TemporalAdjusters
import java.text.SimpleDateFormat
import java.util.*
fun Date.toLocalDate(): LocalDate = LocalDate.parse(SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(this))
fun String.toDate(format: String = "yyyy-MM-dd"): LocalDate = LocalDate.parse(this, DateTimeFormatter.ofPattern(format))
fun LocalDate.toFormat(format: String): String = this.format(DateTimeFormatter.ofPattern(format))
fun LocalDate.toFormat(): String = this.toFormat(DATE_PATTERN)
fun LocalDate.getNextWorkDay(): LocalDate {
return when(this.dayOfWeek) {
DayOfWeek.FRIDAY, DayOfWeek.SATURDAY, DayOfWeek.SUNDAY -> this.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
else -> this.plusDays(1)
}
}
fun LocalDate.getPreviousWorkDay(): LocalDate {
return when(this.dayOfWeek) {
DayOfWeek.SATURDAY, DayOfWeek.SUNDAY, DayOfWeek.MONDAY -> this.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY))
else -> this.minusDays(1)
}
}
fun LocalDate.getNearSchoolDayPrevOnWeekEnd(): LocalDate {
return when(this.dayOfWeek) {
DayOfWeek.SATURDAY, DayOfWeek.SUNDAY -> this.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY))
else -> this
}
}
fun LocalDate.getNearSchoolDayNextOnWeekEnd(): LocalDate {
return when(this.dayOfWeek) {
DayOfWeek.SATURDAY, DayOfWeek.SUNDAY -> this.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
else -> this
}
}
fun LocalDate.getWeekDayName(): String = this.format(DateTimeFormatter.ofPattern("EEEE", Locale.getDefault()))
fun LocalDate.getWeekFirstDayAlwaysCurrent(): LocalDate {
return this.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
}
fun LocalDate.getWeekFirstDayNextOnWeekEnd(): LocalDate {
return when(this.dayOfWeek) {
DayOfWeek.SATURDAY, DayOfWeek.SUNDAY -> this.with(TemporalAdjusters.next(DayOfWeek.MONDAY))
else -> this.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
}
}
/**
* [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335)
*/
fun LocalDate.isHolidays(): Boolean = this.isAfter(this.getLastSchoolDay()) && this.isBefore(this.getFirstSchoolDay())
fun LocalDate.getSchoolYear(): Int = if (this.monthValue <= 8) this.year - 1 else this.year
fun LocalDate.getFirstSchoolDay(): LocalDate {
return LocalDate.of(this.year, 9, 1).run {
when (dayOfWeek) {
DayOfWeek.FRIDAY, DayOfWeek.SATURDAY, DayOfWeek.SUNDAY -> with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY))
else -> this
}
}
}
fun LocalDate.getLastSchoolDay(): LocalDate {
return LocalDate
.of(this.year, 6, 20)
.with(TemporalAdjusters.next(DayOfWeek.FRIDAY))
}

View File

@ -1,6 +1,5 @@
<?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">
@ -8,146 +7,84 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="300dp"
android:orientation="vertical">
android:orientation="vertical"
android:padding="20dp">
<RelativeLayout
android:id="@+id/attendance_dialog_relative_layout"
android:layout_width="match_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:paddingTop="10dp"
tools:ignore="UselessParent">
android:text="@string/all_subject"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_gravity="start"
android:gravity="center_vertical"
android:maxLines="5"
android:minHeight="60dp"
android:minLines="2"
android:paddingTop="10dp"
android:text="@string/all_details"
android:textIsSelectable="true"
android:textSize="20sp" />
<TextView
android:id="@+id/attendanceDialogSubject"
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/attendance_dialog_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_details"
android:layout_marginTop="10dp"
android:text="@string/all_subject"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/all_description"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_subject_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_subject"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/attendanceDialogDescription"
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/attendance_dialog_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_subject_value"
android:layout_marginTop="10dp"
android:text="@string/all_description"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/all_date"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_description_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_description"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/attendanceDialogDate"
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/attendance_dialog_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_description_value"
android:layout_marginTop="10dp"
android:text="@string/all_date"
android:textSize="17sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/attendance_number"
android:textSize="17sp" />
<TextView
android:id="@+id/attendance_dialog_date_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_date"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/attendanceDialogNumber"
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/attendance_dialog_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_date_value"
android:layout_marginTop="10dp"
android:text="@string/attendance_number"
android:textSize="17sp" />
<Button
android:id="@+id/attendanceDialogClose"
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:padding="0dp"
android:text="@string/all_close"
android:textAllCaps="true"
android:textSize="15sp" />
<TextView
android:id="@+id/attendance_dialog_number_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/attendance_dialog_number"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<Button
android:id="@+id/attendance_dialog_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/attendance_dialog_number_value"
android:layout_marginTop="25dp"
android:background="?attr/selectableItemBackground"
android:focusable="true"
android:text="@string/all_close"
android:textAllCaps="true"
android:textColor="?android:attr/android:textColorSecondary"
android:textSize="15sp" />
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -1,30 +1,99 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/attendanceContainer"
<LinearLayout 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"
android:layout_alignParentBottom="true">
android:orientation="vertical">
<TextView
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Attendance" />
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<!--<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/attendance_fragment_tab_layout"
android:layout_width="match_parent"
<ProgressBar
android:id="@+id/attendanceProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:tabMinWidth="125dp"
app:tabMode="scrollable"/>
android:layout_gravity="center"
android:indeterminate="true" />
<android.support.v4.view.ViewPager
android:id="@+id/attendance_fragment_viewpager"
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/attendanceSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/attendanceRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/attendanceEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/attendance_fragment_tab_layout" />
</RelativeLayout>-->
</android.support.design.widget.CoordinatorLayout>
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:visibility="gone">
<android.support.v7.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="100dp"
android:minWidth="100dp"
app:srcCompat="@drawable/ic_menu_main_attendance_24dp"
app:tint="?android:attr/textColorPrimary"
tools:ignore="contentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/attendance_no_items"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="horizontal">
<Button
android:id="@+id/attendancePreviousButton"
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/prev"
android:textAlignment="gravity" />
<TextView
android:id="@+id/attendanceNavDate"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/app_name" />
<Button
android:id="@+id/attendanceNextButton"
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/next"
android:textAlignment="gravity" />
</LinearLayout>
</LinearLayout>

View File

@ -1,77 +1,65 @@
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tool="http://schemas.android.com/tools"
android:id="@+id/attendance_subItem_cardView"
android:id="@+id/attendanceItemContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="7dp"
android:layout_marginEnd="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginStart="5dp"
android:foreground="?attr/selectableItemBackgroundBorderless"
card_view:cardElevation="0dp">
android:paddingStart="12dp"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="7dp">
<RelativeLayout
android:layout_width="match_parent"
<TextView
android:id="@+id/attendanceItemNumber"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:gravity="center"
android:maxLength="2"
android:text="0"
android:textSize="32sp"
tool:ignore="all" />
<TextView
android:id="@+id/attendanceItemSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="7dp"
android:layout_marginEnd="7dp"
android:layout_marginLeft="7dp"
android:layout_marginRight="7dp"
android:layout_marginStart="7dp"
android:layout_marginTop="7dp">
android:layout_alignParentTop="true"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginEnd="40dp"
android:layout_marginRight="40dp"
android:layout_toEndOf="@+id/attendanceItemNumber"
android:layout_toRightOf="@+id/attendanceItemNumber"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="17sp"
tool:ignore="all" />
<TextView
android:id="@+id/attendance_subItem_number"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:gravity="center"
android:maxLength="2"
android:text="0"
android:textSize="32sp"
tool:ignore="all" />
<TextView
android:id="@+id/attendanceItemDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/attendanceItemSubject"
android:layout_alignLeft="@id/attendanceItemSubject"
android:layout_alignBottom="@+id/attendanceItemNumber"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="?android:attr/android:textColorSecondary"
android:textSize="12sp" />
<TextView
android:id="@+id/attendance_subItem_lesson"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginEnd="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="40dp"
android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/attendance_subItem_number"
android:layout_toRightOf="@+id/attendance_subItem_number"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="17sp"
tool:ignore="all" />
<ImageView
android:id="@+id/attendanceItemAlert"
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_all_note_24dp"
tool:ignore="contentDescription" />
<TextView
android:id="@+id/attendance_subItem_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/attendance_subItem_number"
android:layout_alignLeft="@id/attendance_subItem_lesson"
android:layout_alignStart="@id/attendance_subItem_lesson"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="?android:attr/android:textColorSecondary"
android:textSize="12sp" />
<ImageView
android:id="@+id/attendance_subItem_alert_image"
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_all_note_24dp"
tool:ignore="contentDescription" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</RelativeLayout>

View File

@ -0,0 +1,56 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.attendance.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.threeten.bp.LocalDate
import java.sql.Date
class AttendanceRemoteTest {
@MockK
private lateinit var mockApi: Api
@MockK
private lateinit var semesterMock: Semester
@Before
fun initApi() {
MockKAnnotations.init(this)
}
@Test
fun getExamsTest() {
every { mockApi.getAttendance(
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
) } returns Single.just(listOf(
getAttendance("2018-09-10"),
getAttendance("2018-09-17")
))
every { mockApi.diaryId } returns "1"
every { semesterMock.studentId } returns "1"
every { semesterMock.diaryId } returns "1"
val attendance = AttendanceRemote(mockApi).getAttendance(semesterMock,
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)).blockingGet()
assertEquals(2, attendance.size)
}
private fun getAttendance(dateString: String): Attendance {
return Attendance().apply {
subject = "Fizyka"
name = "Obecność"
date = Date.valueOf(dateString)
}
}
}

View File

@ -3,41 +3,47 @@ package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.exams.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.doReturn
import org.mockito.MockitoAnnotations
import org.threeten.bp.LocalDate
import java.sql.Date
class ExamRemoteTest {
@Mock
@MockK
private lateinit var mockApi: Api
@Mock
@MockK
private lateinit var semesterMock: Semester
@Before
fun initApi() {
MockitoAnnotations.initMocks(this)
MockKAnnotations.init(this)
}
@Test
fun getExamsTest() {
doReturn(Single.just(listOf(
every { mockApi.getExams(
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
) } returns Single.just(listOf(
getExam("2018-09-10"),
getExam("2018-09-17")
))).`when`(mockApi).getExams(any())
))
doReturn("1").`when`(semesterMock).studentId
doReturn("1").`when`(semesterMock).diaryId
every { mockApi.diaryId } returns "1"
every { semesterMock.studentId } returns "1"
every { semesterMock.diaryId } returns "1"
val exams = ExamRemote(mockApi).getExams(semesterMock, LocalDate.of(2018, 9, 10)).blockingGet()
val exams = ExamRemote(mockApi).getExams(semesterMock,
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
).blockingGet()
assertEquals(2, exams.size)
}

View File

@ -1,140 +0,0 @@
package io.github.wulkanowy.utils
import org.junit.Test
import org.threeten.bp.LocalDate
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
class TimeUtilsTest {
@Test fun getParsedDateTest() {
assertEquals(LocalDate.of(1970, 1, 1),
getParsedDate("1970-01-01", "yyyy-MM-dd"))
}
@Test fun getMondaysFromCurrentSchoolYearTest() {
val y201718 = getMondaysFromCurrentSchoolYear(LocalDate.of(2018, 1, 1))
assertEquals("2017-09-04", y201718.first())
assertEquals("2018-08-27", y201718.last())
val y202122 = getMondaysFromCurrentSchoolYear(LocalDate.of(2022, 1, 1))
assertEquals("2021-08-30", y202122.first())
assertEquals("2022-08-22", y202122.last())
val y202223 = getMondaysFromCurrentSchoolYear(LocalDate.of(2023, 1, 1))
assertEquals("2022-08-29", y202223.first())
assertEquals("2023-08-28", y202223.last())
}
@Test fun getCurrentSchoolYearTest() {
assertEquals(2017, getSchoolYearForDate(LocalDate.of(2018, 8, 31)))
assertEquals(2018, getSchoolYearForDate(LocalDate.of(2018, 9, 1)))
}
@Test fun getFirstWeekDayTest() {
assertEquals("2018-06-18", getFirstDayOfCurrentWeek(LocalDate.of(2018, 6, 21)))
assertEquals("2018-06-18", getFirstDayOfCurrentWeek(LocalDate.of(2018, 6, 22)))
assertEquals("2018-06-25", getFirstDayOfCurrentWeek(LocalDate.of(2018, 6, 23)))
assertEquals("2018-06-25", getFirstDayOfCurrentWeek(LocalDate.of(2018, 6, 24)))
assertEquals("2018-06-25", getFirstDayOfCurrentWeek(LocalDate.of(2018, 6, 25)))
assertEquals("2018-06-25", getFirstDayOfCurrentWeek(LocalDate.of(2018, 6, 26)))
}
@Test fun getTodayOrNextDayOrderTest() {
assertEquals(0, getTodayOrNextDayOrder(true, LocalDate.of(2018, 6, 24))) // sunday
assertEquals(6, getTodayOrNextDayOrder(false, LocalDate.of(2018, 6, 24)))
assertEquals(1, getTodayOrNextDayOrder(true, LocalDate.of(2018, 6, 25)))
assertEquals(0, getTodayOrNextDayOrder(false, LocalDate.of(2018, 6, 25)))
}
@Test fun getTodayOrNextDayTest() {
assertEquals("2018-06-26", getTodayOrNextDay(false, LocalDate.of(2018, 6, 26)))
assertEquals("2018-06-27", getTodayOrNextDay(true, LocalDate.of(2018, 6, 26)))
}
@Test fun isDateInWeekInsideTest() {
assertTrue(isDateInWeek(
LocalDate.of(2018, 5, 28),
LocalDate.of(2018, 5, 31)
))
}
@Test fun isDateInWeekExtremesTest() {
assertTrue(isDateInWeek(
LocalDate.of(2018, 5, 28),
LocalDate.of(2018, 5, 28)
))
assertTrue(isDateInWeek(
LocalDate.of(2018, 5, 28),
LocalDate.of(2018, 6, 1)
))
}
@Test fun isDateInWeekOutOfTest() {
assertFalse(isDateInWeek(
LocalDate.of(2018, 5, 28),
LocalDate.of(2018, 6, 2)
))
assertFalse(isDateInWeek(
LocalDate.of(2018, 5, 28),
LocalDate.of(2018, 5, 27)
))
}
@Test fun isHolidaysInSchoolEndTest() {
assertFalse(isHolidays(LocalDate.of(2017, 6, 23)))
assertFalse(isHolidays(LocalDate.of(2018, 6, 22)))
assertFalse(isHolidays(LocalDate.of(2019, 6, 21)))
assertFalse(isHolidays(LocalDate.of(2020, 6, 26)))
assertFalse(isHolidays(LocalDate.of(2021, 6, 25)))
assertFalse(isHolidays(LocalDate.of(2022, 6, 24)))
assertFalse(isHolidays(LocalDate.of(2023, 6, 23)))
assertFalse(isHolidays(LocalDate.of(2024, 6, 21)))
assertFalse(isHolidays(LocalDate.of(2025, 6, 27)))
}
@Test fun isHolidaysInHolidaysStartTest() {
assertTrue(isHolidays(LocalDate.of(2017, 6, 24)))
assertTrue(isHolidays(LocalDate.of(2018, 6, 23)))
assertTrue(isHolidays(LocalDate.of(2019, 6, 22)))
assertTrue(isHolidays(LocalDate.of(2020, 6, 27)))
assertTrue(isHolidays(LocalDate.of(2021, 6, 26)))
assertTrue(isHolidays(LocalDate.of(2022, 6, 25)))
assertTrue(isHolidays(LocalDate.of(2023, 6, 24)))
assertTrue(isHolidays(LocalDate.of(2024, 6, 22)))
assertTrue(isHolidays(LocalDate.of(2025, 6, 28)))
}
@Test fun isHolidaysInHolidaysEndTest() {
assertTrue(isHolidays(LocalDate.of(2017, 9, 1))) // friday
assertTrue(isHolidays(LocalDate.of(2017, 9, 2))) // saturday
assertTrue(isHolidays(LocalDate.of(2017, 9, 3))) // sunday
assertTrue(isHolidays(LocalDate.of(2018, 9, 1))) // saturday
assertTrue(isHolidays(LocalDate.of(2018, 9, 2))) // sunday
assertTrue(isHolidays(LocalDate.of(2019, 9, 1))) // sunday
assertTrue(isHolidays(LocalDate.of(2020, 8, 31))) // monday
assertTrue(isHolidays(LocalDate.of(2021, 8, 31))) // tuesday
assertTrue(isHolidays(LocalDate.of(2022, 8, 31))) // wednesday
assertTrue(isHolidays(LocalDate.of(2023, 9, 1))) // friday
assertTrue(isHolidays(LocalDate.of(2023, 9, 2))) // saturday
assertTrue(isHolidays(LocalDate.of(2023, 9, 3))) // sunday
assertTrue(isHolidays(LocalDate.of(2024, 9, 1))) // sunday
assertTrue(isHolidays(LocalDate.of(2025, 8, 31))) // sunday
}
@Test fun isHolidaysInSchoolStartTest() {
assertFalse(isHolidays(LocalDate.of(2017, 9, 4))) // monday
assertFalse(isHolidays(LocalDate.of(2018, 9, 3))) // monday
assertFalse(isHolidays(LocalDate.of(2019, 9, 2))) // monday
assertFalse(isHolidays(LocalDate.of(2020, 9, 1))) // tuesday
assertFalse(isHolidays(LocalDate.of(2021, 9, 1))) // wednesday
assertFalse(isHolidays(LocalDate.of(2022, 9, 1))) // thursday
assertFalse(isHolidays(LocalDate.of(2023, 9, 4))) // monday
assertFalse(isHolidays(LocalDate.of(2024, 9, 2))) // monday
assertFalse(isHolidays(LocalDate.of(2025, 9, 1))) // monday
}
}

View File

@ -0,0 +1,150 @@
package io.github.wulkanowy.utils.extension
import org.junit.Assert.*
import org.junit.Test
import org.threeten.bp.LocalDate
import java.util.*
class TimeExtensionTest {
@Test
fun toDate() {
assertEquals(LocalDate.of(1970, 1, 1), "1970-01-01".toDate("yyyy-MM-dd"))
}
@Test
fun toFormat() {
assertEquals("2018-10-01", LocalDate.of(2018, 10, 1).toFormat())
assertEquals("2018-10.01", LocalDate.of(2018, 10, 1).toFormat("yyyy-MM.dd"))
}
@Test
fun getWeekFirstDayAlwaysCurrent() {
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 2).getWeekFirstDayAlwaysCurrent())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 5).getWeekFirstDayAlwaysCurrent())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 6).getWeekFirstDayAlwaysCurrent())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 7).getWeekFirstDayAlwaysCurrent())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 8).getWeekFirstDayAlwaysCurrent())
}
@Test
fun getWeekFirstDayNextOnWeekEnd() {
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 2).getWeekFirstDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 5).getWeekFirstDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 6).getWeekFirstDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 7).getWeekFirstDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 8).getWeekFirstDayNextOnWeekEnd())
}
@Test
fun getWeekDayName() {
Locale.setDefault(Locale.forLanguageTag("PL"))
assertEquals("poniedziałek", LocalDate.of(2018, 10, 1).getWeekDayName())
Locale.setDefault(Locale.forLanguageTag("US"))
assertEquals("Monday", LocalDate.of(2018, 10, 1).getWeekDayName())
}
@Test
fun getSchoolYear() {
assertEquals(2017, LocalDate.of(2018, 8, 31).getSchoolYear())
assertEquals(2018, LocalDate.of(2018, 9, 1).getSchoolYear())
}
@Test
fun getNextSchoolDay() {
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 1).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 2).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 4), LocalDate.of(2018, 10, 3).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 4).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 5).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 6).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 7).getNextWorkDay())
assertEquals(LocalDate.of(2018, 10, 9), LocalDate.of(2018, 10, 8).getNextWorkDay())
}
@Test
fun getPreviousSchoolDay() {
assertEquals(LocalDate.of(2018, 10, 9), LocalDate.of(2018, 10, 10).getPreviousWorkDay())
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 9).getPreviousWorkDay())
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 8).getPreviousWorkDay())
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 7).getPreviousWorkDay())
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 6).getPreviousWorkDay())
assertEquals(LocalDate.of(2018, 10, 4), LocalDate.of(2018, 10, 5).getPreviousWorkDay())
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 4).getPreviousWorkDay())
}
@Test
fun getNearSchoolDayPrevOnWeekEnd() {
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).getNearSchoolDayPrevOnWeekEnd())
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 29).getNearSchoolDayPrevOnWeekEnd())
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 30).getNearSchoolDayPrevOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 1).getNearSchoolDayPrevOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 2).getNearSchoolDayPrevOnWeekEnd())
}
@Test
fun getNearSchoolDayNextOnWeekEnd() {
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).getNearSchoolDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 9, 29).getNearSchoolDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 9, 30).getNearSchoolDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 1).getNearSchoolDayNextOnWeekEnd())
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 2).getNearSchoolDayNextOnWeekEnd())
}
@Test
fun isHolidays_schoolEnd() {
assertFalse(LocalDate.of(2017, 6, 23).isHolidays())
assertFalse(LocalDate.of(2018, 6, 22).isHolidays())
assertFalse(LocalDate.of(2019, 6, 21).isHolidays())
assertFalse(LocalDate.of(2020, 6, 26).isHolidays())
assertFalse(LocalDate.of(2021, 6, 25).isHolidays())
assertFalse(LocalDate.of(2022, 6, 24).isHolidays())
assertFalse(LocalDate.of(2023, 6, 23).isHolidays())
assertFalse(LocalDate.of(2024, 6, 21).isHolidays())
assertFalse(LocalDate.of(2025, 6, 27).isHolidays())
}
@Test
fun isHolidays_holidaysStart() {
assertTrue(LocalDate.of(2017, 6, 24).isHolidays())
assertTrue(LocalDate.of(2018, 6, 23).isHolidays())
assertTrue(LocalDate.of(2019, 6, 22).isHolidays())
assertTrue(LocalDate.of(2020, 6, 27).isHolidays())
assertTrue(LocalDate.of(2021, 6, 26).isHolidays())
assertTrue(LocalDate.of(2022, 6, 25).isHolidays())
assertTrue(LocalDate.of(2023, 6, 24).isHolidays())
assertTrue(LocalDate.of(2024, 6, 22).isHolidays())
assertTrue(LocalDate.of(2025, 6, 28).isHolidays())
}
@Test
fun isHolidays_holidaysEnd() {
assertTrue(LocalDate.of(2017, 9, 1).isHolidays()) // friday
assertTrue(LocalDate.of(2017, 9, 2).isHolidays()) // saturday
assertTrue(LocalDate.of(2017, 9, 3).isHolidays()) // sunday
assertTrue(LocalDate.of(2018, 9, 1).isHolidays()) // saturday
assertTrue(LocalDate.of(2018, 9, 2).isHolidays()) // sunday
assertTrue(LocalDate.of(2019, 9, 1).isHolidays()) // sunday
assertTrue(LocalDate.of(2020, 8, 31).isHolidays()) // monday
assertTrue(LocalDate.of(2021, 8, 31).isHolidays()) // tuesday
assertTrue(LocalDate.of(2022, 8, 31).isHolidays()) // wednesday
assertTrue(LocalDate.of(2023, 9, 1).isHolidays()) // friday
assertTrue(LocalDate.of(2023, 9, 2).isHolidays()) // saturday
assertTrue(LocalDate.of(2023, 9, 3).isHolidays()) // sunday
assertTrue(LocalDate.of(2024, 9, 1).isHolidays()) // sunday
assertTrue(LocalDate.of(2025, 8, 31).isHolidays()) // sunday
}
@Test
fun isHolidays_schoolStart() {
assertFalse(LocalDate.of(2017, 9, 4).isHolidays()) // monday
assertFalse(LocalDate.of(2018, 9, 3).isHolidays()) // monday
assertFalse(LocalDate.of(2019, 9, 2).isHolidays()) // monday
assertFalse(LocalDate.of(2020, 9, 1).isHolidays()) // tuesday
assertFalse(LocalDate.of(2021, 9, 1).isHolidays()) // wednesday
assertFalse(LocalDate.of(2022, 9, 1).isHolidays()) // thursday
assertFalse(LocalDate.of(2023, 9, 4).isHolidays()) // monday
assertFalse(LocalDate.of(2024, 9, 2).isHolidays()) // monday
assertFalse(LocalDate.of(2025, 9, 1).isHolidays()) // monday
}
}

View File

@ -9,7 +9,7 @@ buildscript {
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.2.0'
classpath "io.fabric.tools:gradle:1.25.4"
classpath "com.google.gms:oss-licenses:0.9.2"
classpath "com.github.triplet.gradle:play-publisher:1.2.2"

View File

@ -1,6 +1,6 @@
#Tue Mar 27 02:10:44 CEST 2018
#Sat Sep 29 23:00:14 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip