From 357b2350cb30d4e0aad8cb5c9c750418b2458744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 1 Oct 2018 19:41:09 +0200 Subject: [PATCH] Refactor attendance module (#161) --- app/build.gradle | 8 +- .../repositories/local/AttendanceLocalTest.kt | 52 +++++ .../data/repositories/local/ExamLocalTest.kt | 16 +- .../github/wulkanowy/data/RepositoryModule.kt | 4 + .../github/wulkanowy/data/db/AppDatabase.kt | 7 +- .../io/github/wulkanowy/data/db/Converters.kt | 10 +- .../wulkanowy/data/db/dao/AttendanceDao.kt | 22 ++ .../github/wulkanowy/data/db/dao/ExamDao.kt | 4 +- .../wulkanowy/data/db/entities/Attendance.kt | 40 ++++ .../github/wulkanowy/data/db/entities/Exam.kt | 6 +- .../data/repositories/AttendanceRepository.kt | 43 ++++ .../data/repositories/ExamRepository.kt | 37 ++-- .../repositories/local/AttendanceLocal.kt | 24 +++ .../data/repositories/local/ExamLocal.kt | 7 +- .../repositories/remote/AttendanceRemote.kt | 38 ++++ .../data/repositories/remote/ExamRemote.kt | 37 ++-- .../ui/main/attendance/AttendanceDialog.kt | 49 +++++ .../ui/main/attendance/AttendanceFragment.kt | 96 ++++++++- .../ui/main/attendance/AttendanceItem.kt | 55 +++++ .../ui/main/attendance/AttendancePresenter.kt | 96 +++++++++ .../ui/main/attendance/AttendanceView.kt | 31 +++ .../wulkanowy/ui/main/exam/ExamHeader.kt | 7 +- .../wulkanowy/ui/main/exam/ExamPresenter.kt | 11 +- .../io/github/wulkanowy/utils/TimeUtils.kt | 104 --------- .../utils/extension/DateExtension.kt | 25 --- .../utils/extension/TimeExtension.kt | 79 +++++++ app/src/main/res/layout/dialog_attendance.xml | 199 ++++++------------ .../main/res/layout/fragment_attendance.xml | 111 ++++++++-- app/src/main/res/layout/item_attendance.xml | 122 +++++------ .../remote/AttendanceRemoteTest.kt | 56 +++++ .../repositories/remote/ExamRemoteTest.kt | 30 +-- .../github/wulkanowy/utils/TimeUtilsTest.kt | 140 ------------ .../utils/extension/TimeExtensionTest.kt | 150 +++++++++++++ build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 35 files changed, 1151 insertions(+), 571 deletions(-) create mode 100644 app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialog.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceItem.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceView.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/utils/extension/DateExtension.kt create mode 100644 app/src/main/java/io/github/wulkanowy/utils/extension/TimeExtension.kt create mode 100644 app/src/test/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemoteTest.kt delete mode 100644 app/src/test/java/io/github/wulkanowy/utils/TimeUtilsTest.kt create mode 100644 app/src/test/java/io/github/wulkanowy/utils/extension/TimeExtensionTest.kt diff --git a/app/build.gradle b/app/build.gradle index 5acb3931e..4f1b37b3d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt new file mode 100644 index 000000000..c8293b275 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt @@ -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)) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt index 011632735..52edf5593 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt @@ -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)) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt index db0423670..4cba8eda5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -52,4 +52,8 @@ internal class RepositoryModule { @Singleton @Provides fun provideExamDao(database: AppDatabase) = database.examsDao() + + @Singleton + @Provides + fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao() } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 2d51576a4..3e186f48d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt index 2e2d6cf74..8e92d6ebb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -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() + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt new file mode 100644 index 000000000..6f9f25364 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt @@ -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): List + + @Delete + fun deleteAll(exams: List) + + @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> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt index 30bb4a48f..8c5dd3d89 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ExamDao.kt @@ -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) @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> + fun getExams(diaryId: String, studentId: String, from: LocalDate, end: LocalDate): Maybe> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt new file mode 100644 index 000000000..a3bfa2aa4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt @@ -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 diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt index 64f9857df..9dd81a3d5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt @@ -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 = "", diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt new file mode 100644 index 000000000..4ed56d07c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -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> { + 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() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt index 9cb4961e6..e2932900b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -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> { - 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> { + 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() + } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt new file mode 100644 index 000000000..79ab5ec03 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/local/AttendanceLocal.kt @@ -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> { + return attendanceDb.getExams(semester.diaryId, semester.studentId, startDate, endDate) + .filter { !it.isEmpty() } + } + + fun saveAttendance(attendance: List) { + attendanceDb.insertAll(attendance) + } + + fun deleteAttendance(attendance: List) { + attendanceDb.deleteAll(attendance) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt index 2ca12411d..0074cd0fc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt @@ -12,10 +12,9 @@ import javax.inject.Inject class ExamLocal @Inject constructor(private val examDb: ExamDao) { - fun getExams(semester: Semester, startDate: LocalDate): Maybe> { - 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> { + return examDb.getExams(semester.diaryId, semester.studentId, startDate, endDate) + .filter { !it.isEmpty() } } fun saveExams(exams: List) { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt new file mode 100644 index 000000000..1a1657842 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemote.kt @@ -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> { + 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 + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt index df179afd5..ea4000a3c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt @@ -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> { + fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { 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 + ) + } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialog.kt new file mode 100644 index 000000000..91c36594d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceDialog.kt @@ -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() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.kt index 448817b19..179909aae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceFragment.kt @@ -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> 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) { + 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() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceItem.kt new file mode 100644 index 000000000..2fedeca10 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceItem.kt @@ -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() { + + lateinit var attendance: Attendance + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): 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>, holder: ViewHolder, + position: Int, payloads: MutableList?) { + 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 + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.kt new file mode 100644 index 000000000..7444055d2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendancePresenter.kt @@ -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(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): List { + return items.map { + AttendanceItem().apply { attendance = it } + } + } + + fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance) + } + + private fun selectSemester(semesters: List, 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 + } + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceView.kt new file mode 100644 index 000000000..7a5b537b6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/attendance/AttendanceView.kt @@ -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) + + 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) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamHeader.kt index fce271721..5c752b441 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamHeader.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamHeader.kt @@ -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() { - lateinit var date: Date + lateinit var date: LocalDate override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { return ViewHolder(view, adapter) @@ -41,7 +40,7 @@ class ExamHeader : AbstractHeaderItem() { override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.run { - examHeaderDay.text = StringUtils.capitalize(date.getWeekDayName()) + examHeaderDay.text = date.getWeekDayName().capitalize() examHeaderDate.text = date.toFormat() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamPresenter.kt index f00a07983..174cd2257 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamPresenter.kt @@ -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(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>): List { + private fun createExamItems(items: Map>): List { return items.flatMap { val header = ExamHeader().apply { date = it.key } it.value.reversed().map { item -> diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt deleted file mode 100644 index bb9bebf33..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt +++ /dev/null @@ -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 { - val startDate = getFirstSchoolDay(getSchoolYearForDate(date)) - ?.with(TemporalAdjusters.previousOrSame(MONDAY)) - val endDate = getFirstSchoolDay(getSchoolYearForDate(date) + 1) - ?.with(TemporalAdjusters.previousOrSame(MONDAY)) - - val dateList = ArrayList() - 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)) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/extension/DateExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/extension/DateExtension.kt deleted file mode 100644 index db5965abb..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/extension/DateExtension.kt +++ /dev/null @@ -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() diff --git a/app/src/main/java/io/github/wulkanowy/utils/extension/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/extension/TimeExtension.kt new file mode 100644 index 000000000..6b463c86b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/extension/TimeExtension.kt @@ -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)) +} diff --git a/app/src/main/res/layout/dialog_attendance.xml b/app/src/main/res/layout/dialog_attendance.xml index 730b632d7..8fd82d4d6 100644 --- a/app/src/main/res/layout/dialog_attendance.xml +++ b/app/src/main/res/layout/dialog_attendance.xml @@ -1,6 +1,5 @@ @@ -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"> - + android:text="@string/all_subject" + android:textIsSelectable="true" + android:textSize="17sp" /> - + - + - + - + - + - + - + - +