diff --git a/app/build.gradle b/app/build.gradle index 9a870db5..5acb3931 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ apply from: 'sonarqube.gradle' apply plugin: 'com.github.triplet.play' android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.2' playAccountConfigs { defaultAccountConfig { @@ -22,7 +22,7 @@ android { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 16 versionName "0.5.2" multiDexEnabled true @@ -68,11 +68,11 @@ play { uploadImages = true } -ext.supportVersion = "27.1.1" +ext.supportVersion = "28.0.0-rc02" dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation 'com.github.wulkanowy:api:88ede83149' + implementation 'com.github.wulkanowy:api:ad57669' implementation "com.android.support:support-v4:$supportVersion" implementation "com.android.support:design:$supportVersion" @@ -98,7 +98,7 @@ dependencies { implementation 'com.github.pwittchen:reactivenetwork-rx2:2.1.0' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' - implementation "io.reactivex.rxjava2:rxjava:2.2.0" + implementation "io.reactivex.rxjava2:rxjava:2.2.1" implementation "org.apache.commons:commons-lang3:3.8" implementation "org.apache.commons:commons-collections4:4.2" 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 new file mode 100644 index 00000000..01163273 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt @@ -0,0 +1,50 @@ +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.Exam +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 java.sql.Date +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class ExamLocalTest { + + private lateinit var examLocal: ExamLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(), AppDatabase::class.java).build() + examLocal = ExamLocal(testDb.examsDao()) + } + + @After + fun closeDb() { + testDb.close() + } + + @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 + )) + + val exams = examLocal + .getExams(Semester(studentId = "1", diaryId = "2", semesterId = "3"), LocalDate.of(2018, 9, 10)) + .blockingGet() + assertEquals(2, exams.size) + assertEquals(exams[0].date, Date.valueOf("2018-09-10")) + assertEquals(exams[1].date, Date.valueOf("2018-09-14")) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/SessionLocalTest.kt similarity index 69% rename from app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt rename to app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/SessionLocalTest.kt index 7103d478..17264c95 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/StudentLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/SessionLocalTest.kt @@ -14,9 +14,9 @@ import org.junit.runner.RunWith import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) -class StudentLocalTest { +class SessionLocalTest { - private lateinit var studentLocal: StudentLocal + private lateinit var studentLocal: SessionLocal private lateinit var testDb: AppDatabase @@ -28,7 +28,7 @@ class StudentLocalTest { testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) .build() sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE)) - studentLocal = StudentLocal(testDb.studentDao(), sharedHelper, context) + studentLocal = SessionLocal(testDb.studentDao(), testDb.semesterDao(), sharedHelper, context) } @After @@ -38,12 +38,12 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.save(Student(email = "test", password = "test123", schoolId = "23")).blockingAwait() - assert(sharedHelper.getLong(StudentLocal.CURRENT_USER_KEY, 0) == 1L) + studentLocal.saveStudent(Student(email = "test", password = "test123", schoolId = "23")).blockingAwait() + assert(sharedHelper.getLong(SessionLocal.LAST_USER_KEY, 0) == 1L) - assert(studentLocal.isStudentLoggedIn) + assert(studentLocal.isSessionSaved) - val student = studentLocal.getCurrentStudent().blockingGet() + val student = studentLocal.getLastStudent().blockingGet() assertEquals("23", student.schoolId) } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 47e6e013..21c7bc99 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -13,7 +14,9 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" - android:theme="@style/WulkanowyTheme"> + android:theme="@style/WulkanowyTheme" + android:usesCleartextTraffic="true" + tools:targetApi="m"> ): List + + @Delete + 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> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt index a8ebb478..363da09b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt @@ -3,11 +3,16 @@ package io.github.wulkanowy.data.db.dao import android.arch.persistence.room.Dao import android.arch.persistence.room.Insert import android.arch.persistence.room.OnConflictStrategy.REPLACE +import android.arch.persistence.room.Query import io.github.wulkanowy.data.db.entities.Semester +import io.reactivex.Single @Dao interface SemesterDao { @Insert(onConflict = REPLACE) - fun insert(semester: Semester): Long + fun insertAll(semester: List) + + @Query("SELECT * FROM Semesters WHERE student_id = :studentId") + fun getSemester(studentId: String): Single> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt index fb3cb4e7..08ca48d2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -5,7 +5,7 @@ import android.arch.persistence.room.Insert import android.arch.persistence.room.OnConflictStrategy.REPLACE import android.arch.persistence.room.Query import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single +import io.reactivex.Maybe @Dao interface StudentDao { @@ -14,5 +14,5 @@ interface StudentDao { fun insert(student: Student): Long @Query("SELECT * FROM Students WHERE id = :id") - fun load(id: Long): Single + fun load(id: Long): Maybe } 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 new file mode 100644 index 00000000..64f9857d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt @@ -0,0 +1,38 @@ +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 java.io.Serializable +import java.util.* + +@Entity(tableName = "Exams") +data class Exam( + + @PrimaryKey(autoGenerate = true) + var id: Long = 0, + + @ColumnInfo(name = "student_id") + var studentId: String = "", + + @ColumnInfo(name = "diary_id") + var diaryId: String = "", + + var date: Date, + + @ColumnInfo(name = "entry_date") + var entryDate: Date = Date(), + + var subject: String = "", + + var group: String = "", + + var type: String = "", + + var description: String = "", + + var teacher: String = "", + + @ColumnInfo(name = "teacher_symbol") + var teacherSymbol: String = "" +) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt index dec731d7..5ef1f359 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt @@ -6,12 +6,15 @@ import android.arch.persistence.room.Index import android.arch.persistence.room.PrimaryKey @Entity(tableName = "Semesters", - indices = [Index(value = ["diary_id", "semester_id"], unique = true)]) + indices = [Index(value = ["semester_id", "diary_id", "student_id"], unique = true)]) data class Semester( - @PrimaryKey + @PrimaryKey(autoGenerate = true) var id: Long = 0, + @ColumnInfo(name = "student_id") + var studentId: String, + @ColumnInfo(name = "diary_id") var diaryId: String, @@ -22,5 +25,8 @@ data class Semester( var semesterId: String, @ColumnInfo(name = "semester_name") - var semesterName: String = "" + var semesterName: Int = 0, + + @ColumnInfo(name = "is_current") + var current: Boolean = false ) 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 new file mode 100644 index 00000000..9cb4961e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -0,0 +1,39 @@ +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.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.reactivex.Single +import org.threeten.bp.LocalDate +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ExamRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: ExamLocal, + 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) + + newExams + } + } + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SessionRepository.kt similarity index 59% rename from app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/SessionRepository.kt index 316c482c..6d158d29 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SessionRepository.kt @@ -2,9 +2,10 @@ 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.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.local.StudentLocal -import io.github.wulkanowy.data.repositories.remote.StudentRemote +import io.github.wulkanowy.data.repositories.local.SessionLocal +import io.github.wulkanowy.data.repositories.remote.SessionRemote import io.reactivex.Completable import io.reactivex.Single import java.net.UnknownHostException @@ -12,17 +13,17 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class StudentRepository @Inject constructor( - private val local: StudentLocal, - private val remote: StudentRemote, +class SessionRepository @Inject constructor( + private val local: SessionLocal, + private val remote: SessionRemote, private val settings: InternetObservingSettings) { + val isSessionSaved + get() = local.isSessionSaved + lateinit var cachedStudents: Single> private set - val isStudentLoggedIn: Boolean - get() = local.isStudentLoggedIn - fun getConnectedStudents(email: String, password: String, symbol: String): Single> { cachedStudents = ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { isConnected -> @@ -32,9 +33,19 @@ class StudentRepository @Inject constructor( return cachedStudents } - fun save(student: Student): Completable = local.save(student) + fun getSemesters(): Single> { + return local.getLastStudent() + .flatMapSingle { + remote.initApi(it, true) + local.getSemesters(it) + } + } - fun getCurrentStudent(): Single = local.getCurrentStudent() + fun saveStudent(student: Student): Completable { + return remote.getSemesters(student).flatMapCompletable { + local.saveSemesters(it) + }.concatWith(local.saveStudent(student)) + } fun clearCache() { cachedStudents = Single.just(emptyList()) 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 new file mode 100644 index 00000000..2ca12411 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/local/ExamLocal.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.repositories.local + +import io.github.wulkanowy.data.db.dao.ExamDao +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.reactivex.Maybe +import org.threeten.bp.DayOfWeek +import org.threeten.bp.LocalDate +import org.threeten.bp.temporal.TemporalAdjusters +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 saveExams(exams: List) { + examDb.insertAll(exams) + } + + fun deleteExams(exams: List) { + examDb.deleteAll(exams) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/SessionLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/SessionLocal.kt new file mode 100644 index 00000000..e507899a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/local/SessionLocal.kt @@ -0,0 +1,49 @@ +package io.github.wulkanowy.data.repositories.local + +import android.content.Context +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.dao.StudentDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.security.Scrambler.decrypt +import io.github.wulkanowy.utils.security.Scrambler.encrypt +import io.reactivex.Completable +import io.reactivex.Maybe +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SessionLocal @Inject constructor( + private val studentDb: StudentDao, + private val semesterDb: SemesterDao, + private val sharedPref: SharedPrefHelper, + private val context: Context) { + + companion object { + const val LAST_USER_KEY: String = "last_user_id" + } + + val isSessionSaved + get() = sharedPref.getLong(LAST_USER_KEY, defaultValue = 0L) != 0L + + fun saveStudent(student: Student): Completable { + return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) } + .map { sharedPref.putLong(LAST_USER_KEY, it) } + .ignoreElement() + } + + fun getLastStudent(): Maybe { + return studentDb.load(sharedPref.getLong(LAST_USER_KEY, defaultValue = 0)) + .map { it.apply { password = decrypt(password) } } + } + + fun saveSemesters(semesters: List): Completable { + return Single.fromCallable { semesterDb.insertAll(semesters) }.ignoreElement() + } + + fun getSemesters(student: Student): Single> { + return semesterDb.getSemester(student.studentId) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt deleted file mode 100644 index a500141c..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/local/StudentLocal.kt +++ /dev/null @@ -1,37 +0,0 @@ -package io.github.wulkanowy.data.repositories.local - -import android.content.Context -import io.github.wulkanowy.data.db.SharedPrefHelper -import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.security.Scrambler.decrypt -import io.github.wulkanowy.utils.security.Scrambler.encrypt -import io.reactivex.Completable -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentLocal @Inject constructor( - private val studentDb: StudentDao, - private val sharedPref: SharedPrefHelper, - private val context: Context) { - - companion object { - const val CURRENT_USER_KEY: String = "current_user_id" - } - - val isStudentLoggedIn: Boolean - get() = sharedPref.getLong(CURRENT_USER_KEY, 0) != 0L - - fun save(student: Student): Completable { - return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) } - .map { sharedPref.putLong(CURRENT_USER_KEY, it) } - .ignoreElement() - } - - fun getCurrentStudent(): Single { - return studentDb.load(sharedPref.getLong(CURRENT_USER_KEY, defaultValue = 0)) - .map { it.apply { password = decrypt(password) } } - } -} 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 new file mode 100644 index 00000000..df179afd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/ExamRemote.kt @@ -0,0 +1,37 @@ +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.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> { + 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 + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SessionRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SessionRemote.kt new file mode 100644 index 00000000..8fdb8632 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/SessionRemote.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.data.repositories.remote + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SessionRemote @Inject constructor(private val api: Api) { + + fun getConnectedStudents(email: String, password: String, symbol: String): Single> { + return Single.just(initApi(Student(email = email, password = password, symbol = symbol))) + .flatMap { _ -> + api.getPupils().map { students -> + students.map { + Student(email = email, + password = password, + symbol = it.symbol, + studentId = it.studentId, + studentName = it.studentName, + schoolId = it.schoolId, + schoolName = it.schoolName) + } + } + } + } + + fun getSemesters(student: Student): Single> { + return Single.just(initApi(student)).flatMap { _ -> + api.getSemesters().map { semesters -> + semesters.map { + Semester(studentId = student.studentId, + diaryId = it.diaryId, + diaryName = it.diaryName, + semesterId = it.semesterId.toString(), + semesterName = it.semesterNumber, + current = it.current) + } + + } + } + } + + fun initApi(student: Student, checkInit: Boolean = false) { + if (if (checkInit) api.studentId.isEmpty() else true) { + api.run { + email = student.email + password = student.password + symbol = student.symbol + host = "vulcan.net.pl" + schoolId = student.schoolId + studentId = student.studentId + notifyDataChanged() + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt deleted file mode 100644 index 02b44723..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/remote/StudentRemote.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.repositories.remote - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import io.reactivex.Single -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRemote @Inject constructor(private val api: Api) { - - fun getConnectedStudents(email: String, password: String, symbol: String): Single> { - api.let { - it.email = email - it.password = password - it.symbol = symbol - it.host = "vulcan.net.pl" - it.onConfigChange() - } - return api.getPupils().map { students -> - students.map { - Student(email = email, - password = password, - symbol = it.symbol, - studentId = it.studentId, - studentName = it.studentName, - schoolId = it.schoolId, - schoolName = it.schoolName) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt index 612f837f..6eff9e97 100644 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt @@ -3,8 +3,9 @@ package io.github.wulkanowy.di import android.content.Context import dagger.Module import dagger.Provides +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.WulkanowyApp -import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.utils.schedulers.SchedulersManager import io.github.wulkanowy.utils.schedulers.SchedulersProvider @@ -18,5 +19,5 @@ internal class AppModule { fun provideSchedulers(): SchedulersManager = SchedulersProvider() @Provides - fun provideErrorHandler(context: Context): ErrorHandler = ErrorHandler(context.resources) + fun provideFlexibleAdapter() = FlexibleAdapter>(null, null, true) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/login/form/LoginFormPresenter.kt index 27ddb540..3cc25a84 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/login/form/LoginFormPresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.login.form -import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.login.LoginErrorHandler import io.github.wulkanowy.utils.DEFAULT_SYMBOL @@ -10,7 +10,7 @@ import javax.inject.Inject class LoginFormPresenter @Inject constructor( private val schedulers: SchedulersManager, private val errorHandler: LoginErrorHandler, - private val studentRepository: StudentRepository) + private val sessionRepository: SessionRepository) : BasePresenter(errorHandler) { private var wasEmpty = false @@ -22,7 +22,7 @@ class LoginFormPresenter @Inject constructor( fun attemptLogin(email: String, password: String, symbol: String) { if (!validateCredentials(email, password, symbol)) return - disposable.add(studentRepository.getConnectedStudents(email, password, normalizeSymbol(symbol)) + disposable.add(sessionRepository.getConnectedStudents(email, password, normalizeSymbol(symbol)) .observeOn(schedulers.mainThread()) .subscribeOn(schedulers.backgroundThread()) .doOnSubscribe { @@ -34,7 +34,7 @@ class LoginFormPresenter @Inject constructor( showSoftKeyboard() } } - studentRepository.clearCache() + sessionRepository.clearCache() } .doFinally { view?.showLoginProgress(false) } .subscribe({ diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsFragment.kt index 382f9f47..cedf47f6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsFragment.kt @@ -9,6 +9,7 @@ import android.view.View.VISIBLE 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.ui.base.BaseFragment import io.github.wulkanowy.ui.main.MainActivity @@ -22,7 +23,11 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView { lateinit var presenter: LoginOptionsPresenter @Inject - lateinit var loginAdapter: FlexibleAdapter + lateinit var loginAdapter: FlexibleAdapter> + + companion object { + fun newInstance() = LoginOptionsFragment() + } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_login_options, container, false) @@ -34,7 +39,13 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView { } override fun initRecycler() { - loginAdapter.setOnItemClickListener { item -> item?.let { presenter.onSelectStudent(it.student) } } + loginAdapter.run { + setOnItemClickListener { position -> + (getItem(position) as? LoginOptionsItem)?.let { + presenter.onSelectStudent(it.student) + } + } + } loginOptionsRecycler.run { adapter = loginAdapter layoutManager = SmoothScrollLinearLayoutManager(context) @@ -47,7 +58,7 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView { override fun updateData(data: List) { loginAdapter.run { - updateDataSet(data) + updateDataSet(data, true) } } @@ -66,4 +77,9 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView { override fun showActionBar(show: Boolean) { (activity as AppCompatActivity?)?.supportActionBar?.run { if (show) show() else hide() } } + + override fun onDestroyView() { + super.onDestroyView() + presenter.detachView() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsPresenter.kt index 3221cf51..3fb3c13d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/login/options/LoginOptionsPresenter.kt @@ -2,14 +2,14 @@ package io.github.wulkanowy.ui.login.options import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.utils.schedulers.SchedulersManager import javax.inject.Inject class LoginOptionsPresenter @Inject constructor( private val errorHandler: ErrorHandler, - private val repository: StudentRepository, + private val repository: SessionRepository, private val schedulers: SchedulersManager) : BasePresenter(errorHandler) { @@ -32,7 +32,7 @@ class LoginOptionsPresenter @Inject constructor( } fun onSelectStudent(student: Student) { - disposable.add(repository.save(student) + disposable.add(repository.saveStudent(student) .subscribeOn(schedulers.backgroundThread()) .observeOn(schedulers.mainThread()) .doOnSubscribe { _ -> diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamDialog.kt new file mode 100644 index 00000000..7f15267d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamDialog.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.ui.main.exam + +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.Exam +import io.github.wulkanowy.utils.extension.toFormat +import kotlinx.android.synthetic.main.dialog_exam.* + +class ExamDialog : DialogFragment() { + + private lateinit var exam: Exam + + companion object { + private const val ARGUMENT_KEY = "Item" + + fun newInstance(exam: Exam): ExamDialog { + return ExamDialog().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 { + exam = getSerializable(ARGUMENT_KEY) as Exam + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + dialog.setTitle(getString(R.string.all_details)) + return inflater.inflate(R.layout.dialog_exam, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + examDialogSubjectValue.text = exam.subject + examDialogTypeValue.text = exam.type + examDialogTeacherValue.text = exam.teacher + examDialogDateValue.text = exam.entryDate.toFormat() + examDialogDescriptionValue.text = exam.description + + examDialogClose.setOnClickListener { dismiss() } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamFragment.kt index ca60fe88..957bb94f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamFragment.kt @@ -3,17 +3,104 @@ package io.github.wulkanowy.ui.main.exam import android.os.Bundle import android.view.LayoutInflater import android.view.View +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.Exam import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.utils.extension.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_exam.* +import javax.inject.Inject -class ExamFragment : BaseFragment() { +class ExamFragment : BaseFragment(), ExamView { + + @Inject + lateinit var presenter: ExamPresenter + + @Inject + lateinit var examAdapter: FlexibleAdapter> companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" fun newInstance() = ExamFragment() } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_exam, container, false) } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.run { + attachView(this@ExamFragment) + loadData(date = savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + } + + override fun initView() { + examAdapter.run { + setOnItemClickListener { presenter.onExamItemSelected(getItem(it)) } + } + examRecycler.run { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = examAdapter + } + examSwipe.setOnRefreshListener { presenter.loadData(date = null, forceRefresh = true) } + examPreviousButton.setOnClickListener { presenter.loadExamsForPreviousWeek() } + examNextButton.setOnClickListener { presenter.loadExamsForNextWeek()} + } + + override fun updateData(data: List) { + examAdapter.updateDataSet(data, true) + } + + override fun updateNavigationWeek(date: String) { + examNavDate.text = date + } + + override fun showEmpty(show: Boolean) { + examEmpty.visibility = if (show) VISIBLE else GONE + } + + override fun showProgress(show: Boolean) { + examProgress.visibility = if (show) VISIBLE else GONE + } + + override fun showContent(show: Boolean) { + examRecycler.visibility = if (show) VISIBLE else GONE + } + + override fun showRefresh(show: Boolean) { + examSwipe.isRefreshing = show + } + + override fun showPreButton(show: Boolean) { + examPreviousButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showNextButton(show: Boolean) { + examNextButton.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showExamDialog(exam: Exam) { + ExamDialog.newInstance(exam).show(fragmentManager, exam.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/exam/ExamHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamHeader.kt new file mode 100644 index 00000000..fce27172 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamHeader.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.ui.main.exam + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractHeaderItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.ExpandableViewHolder +import io.github.wulkanowy.R +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.* + +class ExamHeader : AbstractHeaderItem() { + + lateinit var date: Date + + override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun getLayoutRes() = R.layout.header_exam + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ExamHeader + + if (date != other.date) return false + + return true + } + + override fun hashCode(): Int { + return date.hashCode() + } + + override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder, + position: Int, payloads: MutableList?) { + holder.run { + examHeaderDay.text = StringUtils.capitalize(date.getWeekDayName()) + examHeaderDate.text = date.toFormat() + } + } + + class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : ExpandableViewHolder(view, adapter), + LayoutContainer { + + init { + contentView.setOnClickListener(this) + } + + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamItem.kt new file mode 100644 index 00000000..bb7a503d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamItem.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.ui.main.exam + +import android.support.v7.widget.RecyclerView +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractSectionableItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Exam +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_exam.* + +class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem(header) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ExamItem + + if (exam != other.exam) return false + + return true + } + + override fun hashCode(): Int { + return exam.hashCode() + } + + override fun getLayoutRes() = R.layout.item_exam + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, + position: Int, payloads: MutableList?) { + holder.run { + examItemSubject.text = exam.subject + examItemTeacher.text = exam.teacher + examItemType.text = exam.type + } + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), + LayoutContainer { + + override val containerView: View + get() = contentView + } +} 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 new file mode 100644 index 00000000..f00a0798 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamPresenter.kt @@ -0,0 +1,98 @@ +package io.github.wulkanowy.ui.main.exam + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.ErrorHandler +import io.github.wulkanowy.data.db.entities.Exam +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.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( + private val errorHandler: ErrorHandler, + private val schedulers: SchedulersManager, + private val examRepository: ExamRepository, + private val sessionRepository: SessionRepository +) : BasePresenter(errorHandler) { + + var currentDate: LocalDate = getNearMonday(LocalDate.now()) + private set + + override fun attachView(view: ExamView) { + super.attachView(view) + view.initView() + } + + fun loadExamsForPreviousWeek() = loadData(currentDate.minusDays(7).toEpochDay()) + + fun loadExamsForNextWeek() = loadData(currentDate.plusDays(7).toEpochDay()) + + fun loadData(date: Long?, forceRefresh: Boolean = false) { + this.currentDate = LocalDate.ofEpochDay(date ?: getNearMonday(currentDate).toEpochDay()) + if (currentDate.isHolidays()) return + + disposable.clear() + disposable.add(sessionRepository.getSemesters() + .map { selectSemester(it, -1) } + .flatMap { examRepository.getExams(it, currentDate, forceRefresh) } + .map { it.groupBy { exam -> exam.date }.toSortedMap() } + .map { createExamItems(it) } + .subscribeOn(schedulers.backgroundThread()) + .observeOn(schedulers.mainThread()) + .doOnSubscribe { + view?.run { + showRefresh(forceRefresh) + showProgress(!forceRefresh) + if (!forceRefresh) showEmpty(false) + showContent(null == date && forceRefresh) + showPreButton(!currentDate.minusDays(7).isHolidays()) + showNextButton(!currentDate.plusDays(7).isHolidays()) + updateNavigationWeek("${currentDate.toFormat("dd.MM")}-${currentDate.plusDays(4).toFormat("dd.MM")}") + } + } + .doAfterSuccess { + view?.run { + showEmpty(it.isEmpty()) + showContent(it.isNotEmpty()) + } + } + .doFinally { + view?.run { + showRefresh(false) + showProgress(false) + } + } + .subscribe({ view?.updateData(it) }) { errorHandler.proceed(it) }) + } + + private fun createExamItems(items: Map>): List { + return items.flatMap { + val header = ExamHeader().apply { date = it.key } + it.value.reversed().map { item -> + ExamItem(header, item) + } + } + } + + fun onExamItemSelected(item: AbstractFlexibleItem<*>?) { + if (item is ExamItem) view?.showExamDialog(item.exam) + } + + 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/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamView.kt new file mode 100644 index 00000000..64ee9517 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/main/exam/ExamView.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.ui.main.exam + +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate + +interface ExamView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun showEmpty(show: Boolean) + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showRefresh(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showExamDialog(exam: Exam) + + fun updateNavigationWeek(date: String) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.kt index 6ea0ff0f..f3cf93d9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashActivity.kt @@ -21,12 +21,12 @@ class SplashActivity : BaseActivity(), SplashView { presenter.detachView() } - override fun openLoginActivity() { + override fun openLoginView() { startActivity(LoginActivity.getStartIntent(this)) finish() } - override fun openMainActivity() { + override fun openMainView() { startActivity(MainActivity.getStartIntent(this)) finish() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.kt index ba69ecbc..0a22284c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashPresenter.kt @@ -1,16 +1,16 @@ package io.github.wulkanowy.ui.splash import io.github.wulkanowy.data.ErrorHandler -import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.ui.base.BasePresenter import javax.inject.Inject -class SplashPresenter @Inject constructor(private val studentRepository: StudentRepository, +class SplashPresenter @Inject constructor(private val sessionRepository: SessionRepository, errorHandler: ErrorHandler) : BasePresenter(errorHandler) { override fun attachView(view: SplashView) { super.attachView(view) - view.run { if (studentRepository.isStudentLoggedIn) openMainActivity() else openLoginActivity() } + view.run { if (sessionRepository.isSessionSaved) openMainView() else openLoginView() } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashView.kt b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashView.kt index 7cdde3b3..34073933 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/splash/SplashView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/splash/SplashView.kt @@ -4,7 +4,7 @@ import io.github.wulkanowy.ui.base.BaseView interface SplashView : BaseView { - fun openLoginActivity() + fun openLoginView() - fun openMainActivity() + fun openMainView() } diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt index dc45d87d..bb9bebf3 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeUtils.kt @@ -4,6 +4,7 @@ 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.* @@ -68,29 +69,35 @@ 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(), Year.now().value) +fun isHolidays(): Boolean = isHolidays(LocalDate.now()) -fun isHolidays(day: LocalDate, year: Int): Boolean { - return day.isAfter(getLastSchoolDay(year)) && day.isBefore(getFirstSchoolDay(year)) +fun isHolidays(day: LocalDate): Boolean { + return day.isAfter(getLastSchoolDay(day.year)) && day.isBefore(getFirstSchoolDay(day.year)) } -fun getFirstSchoolDay(year: Int): LocalDate? { +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 - } + else -> firstSeptember } } -fun getLastSchoolDay(year: Int): LocalDate? { +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 new file mode 100644 index 00000000..db5965ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/extension/DateExtension.kt @@ -0,0 +1,25 @@ +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/FlexibleAdapterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/extension/FlexibleAdapterExtension.kt index 779a66fd..b8906003 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/extension/FlexibleAdapterExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/extension/FlexibleAdapterExtension.kt @@ -3,9 +3,13 @@ package io.github.wulkanowy.utils.extension import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -fun , T : FlexibleAdapter> T.setOnItemClickListener(listener: (K?) -> Unit) { +fun FlexibleAdapter<*>.setOnItemClickListener(listener: (position: Int) -> Unit) { addListener(FlexibleAdapter.OnItemClickListener { _, position -> - listener(getItem(position)) + listener(position) true }) } + +fun FlexibleAdapter<*>.setOnUpdateListener(listener: (size: Int) -> Unit) { + addListener(FlexibleAdapter.OnUpdateListener { listener(it) }) +} diff --git a/app/src/main/res/layout/dialog_exam.xml b/app/src/main/res/layout/dialog_exam.xml index e59956ef..a1562660 100644 --- a/app/src/main/res/layout/dialog_exam.xml +++ b/app/src/main/res/layout/dialog_exam.xml @@ -1,6 +1,5 @@ @@ -8,171 +7,106 @@ 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" /> - + - + - + - + - + - + - + - + - + - +