1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-19 23:09:09 -05:00

Refactor exam module (#157)

This commit is contained in:
Mikołaj Pich 2018-09-24 15:21:47 +02:00 committed by Rafał Borcz
parent b617957891
commit a1f64baca4
51 changed files with 1220 additions and 480 deletions

View File

@ -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"

View File

@ -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"))
}
}

View File

@ -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)
}
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.github.wulkanowy"
android:installLocation="internalOnly">
@ -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">
<activity
android:name=".ui.splash.SplashActivity"
android:configChanges="orientation|screenSize"

View File

@ -32,6 +32,9 @@ internal class RepositoryModule {
.build()
}
@Provides
fun provideErrorHandler(context: Context) = ErrorHandler(context.resources)
@Singleton
@Provides
fun provideSharedPref(context: Context): SharedPreferences {
@ -41,4 +44,12 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideStudentDao(database: AppDatabase) = database.studentDao()
@Singleton
@Provides
fun provideSemesterDao(database: AppDatabase) = database.semesterDao()
@Singleton
@Provides
fun provideExamDao(database: AppDatabase) = database.examsDao()
}

View File

@ -2,21 +2,31 @@ 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.ExamDao
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import javax.inject.Singleton
@Singleton
@Database(
entities = [Student::class, Semester::class],
entities = [
Student::class,
Semester::class,
Exam::class
],
version = 1,
exportSchema = false
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun studentDao(): StudentDao
abstract fun semesterDao(): SemesterDao
abstract fun examsDao(): ExamDao
}

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.data.db
import android.arch.persistence.room.TypeConverter
import java.util.*
class Converters {
@TypeConverter
fun fromTimestamp(value: Long?): Date? = value?.run { Date(value) }
@TypeConverter
fun dateToTimestamp(date: Date?): Long? = date?.time
}

View File

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

View File

@ -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<Semester>)
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
fun getSemester(studentId: String): Single<List<Semester>>
}

View File

@ -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<Student>
fun load(id: Long): Maybe<Student>
}

View File

@ -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

View File

@ -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
)

View File

@ -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<List<Exam>> {
return local.getExams(semester, date).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getExams(semester, date)
else Single.error(UnknownHostException())
}.flatMap { newExams ->
local.getExams(semester, date).toSingle(emptyList())
.map {
local.deleteExams(it - newExams)
local.saveExams(newExams - it)
newExams
}
}
)
}
}

View File

@ -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<List<Student>>
private set
val isStudentLoggedIn: Boolean
get() = local.isStudentLoggedIn
fun getConnectedStudents(email: String, password: String, symbol: String): Single<List<Student>> {
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<List<Semester>> {
return local.getLastStudent()
.flatMapSingle {
remote.initApi(it, true)
local.getSemesters(it)
}
}
fun getCurrentStudent(): Single<Student> = 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())

View File

@ -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<List<Exam>> {
return examDb.getExams(semester.diaryId, semester.studentId, startDate.toDate(),
startDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)).toDate()
).filter { !it.isEmpty() }
}
fun saveExams(exams: List<Exam>) {
examDb.insertAll(exams)
}
fun deleteExams(exams: List<Exam>) {
examDb.deleteAll(exams)
}
}

View File

@ -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<Student> {
return studentDb.load(sharedPref.getLong(LAST_USER_KEY, defaultValue = 0))
.map { it.apply { password = decrypt(password) } }
}
fun saveSemesters(semesters: List<Semester>): Completable {
return Single.fromCallable { semesterDb.insertAll(semesters) }.ignoreElement()
}
fun getSemesters(student: Student): Single<List<Semester>> {
return semesterDb.getSemester(student.studentId)
}
}

View File

@ -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<Student> {
return studentDb.load(sharedPref.getLong(CURRENT_USER_KEY, defaultValue = 0))
.map { it.apply { password = decrypt(password) } }
}
}

View File

@ -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<List<Exam>> {
return Single.just(api.run {
if (diaryId != semester.diaryId) {
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getExams(startDate.toDate()) }
.map { exams ->
exams.map {
Exam(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date,
entryDate = it.entryDate,
subject = it.subject,
group = it.group,
type = it.type,
description = it.description,
teacher = it.teacher,
teacherSymbol = it.teacherSymbol
)
}
}
}
}

View File

@ -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<List<Student>> {
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<List<Semester>> {
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()
}
}
}
}

View File

@ -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<List<Student>> {
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)
}
}
}
}

View File

@ -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<AbstractFlexibleItem<*>>(null, null, true)
}

View File

@ -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<LoginFormView>(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({

View File

@ -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<LoginOptionsItem>
lateinit var loginAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
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<LoginOptionsItem>) {
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()
}
}

View File

@ -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<LoginOptionsView>(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 { _ ->

View File

@ -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() }
}
}

View File

@ -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<AbstractFlexibleItem<*>>
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<ExamItem>) {
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()
}
}

View File

@ -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<ExamHeader.ViewHolder>() {
lateinit var date: Date
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): 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<IFlexible<*>>?, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
holder.run {
examHeaderDay.text = StringUtils.capitalize(date.getWeekDayName())
examHeaderDate.text = date.toFormat()
}
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : ExpandableViewHolder(view, adapter),
LayoutContainer {
init {
contentView.setOnClickListener(this)
}
override val containerView: View
get() = contentView
}
}

View File

@ -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<ExamItem.ViewHolder, ExamHeader>(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<IFlexible<RecyclerView.ViewHolder>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
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
}
}

View File

@ -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<ExamView>(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<Date, List<Exam>>): List<ExamItem> {
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<Semester>, index: Int): Semester {
return semesters.single { it.current }.let { currentSemester ->
if (index == -1) currentSemester
else semesters.single { semester ->
semester.run {
semesterName - 1 == index && diaryId == currentSemester.diaryId
}
}
}
}
}

View File

@ -0,0 +1,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<ExamItem>)
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)
}

View File

@ -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()
}

View File

@ -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<SplashView>(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() }
}
}

View File

@ -4,7 +4,7 @@ import io.github.wulkanowy.ui.base.BaseView
interface SplashView : BaseView {
fun openLoginActivity()
fun openLoginView()
fun openMainActivity()
fun openMainView()
}

View File

@ -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))

View File

@ -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()

View File

@ -3,9 +3,13 @@ package io.github.wulkanowy.utils.extension
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
fun <K : AbstractFlexibleItem<*>, T : FlexibleAdapter<K>> 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) })
}

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -8,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">
<RelativeLayout
android:id="@+id/timetable_dialog_relative_layout"
android:layout_width="match_parent"
<TextView
android:id="@+id/examDialogSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:paddingTop="10dp"
tools:ignore="UselessParent">
android:text="@string/all_subject"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:id="@+id/exams_dialog_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_gravity="start"
android:gravity="center_vertical"
android:maxLines="5"
android:minHeight="60dp"
android:minLines="2"
android:paddingTop="10dp"
android:text="@string/all_details"
android:textIsSelectable="true"
android:textSize="20sp" />
<TextView
android:id="@+id/examDialogSubjectValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/exams_dialog_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_details"
android:layout_marginTop="10dp"
android:text="@string/all_subject"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:id="@+id/examDialogType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/exam_type"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:id="@+id/exams_dialog_subject_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_subject"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/examDialogTypeValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/exams_dialog_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_subject_value"
android:layout_marginTop="10dp"
android:text="@string/exam_type"
android:textIsSelectable="true"
android:textSize="17sp" />
<TextView
android:id="@+id/examDialogTeacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/all_teacher"
android:textSize="17sp" />
<TextView
android:id="@+id/exams_dialog_type_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_type"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/examDialogTeacherValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/exams_dialog_teacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_type_value"
android:layout_marginTop="10dp"
android:text="@string/all_teacher"
android:textSize="17sp" />
<TextView
android:id="@+id/examDialogDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/exam_entry_date"
android:textSize="17sp" />
<TextView
android:id="@+id/exams_dialog_teacher_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_teacher"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/examDialogDateValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/exams_dialog_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_teacher_value"
android:layout_marginTop="10dp"
android:text="@string/exam_entry_date"
android:textSize="17sp" />
<TextView
android:id="@+id/examDialogDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/all_description"
android:textSize="17sp" />
<TextView
android:id="@+id/exams_dialog_date_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_date"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/examDialogDescriptionValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<TextView
android:id="@+id/exams_dialog_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_date_value"
android:layout_marginTop="10dp"
android:text="@string/all_description"
android:textSize="17sp" />
<Button
android:id="@+id/examDialogClose"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="15dp"
android:padding="0dp"
android:text="@string/all_close"
android:textAllCaps="true"
android:textSize="15sp" />
<TextView
android:id="@+id/exams_dialog_description_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/exams_dialog_description"
android:layout_marginTop="3dp"
android:text="@string/all_no_data"
android:textIsSelectable="true"
android:textSize="12sp" />
<Button
android:id="@+id/exams_dialog_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/exams_dialog_description_value"
android:layout_marginTop="25dp"
android:background="?attr/selectableItemBackground"
android:focusable="true"
android:text="@string/all_close"
android:textColor="?android:attr/android:textColorSecondary"
android:textAllCaps="true"
android:textSize="15sp" />
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -1,30 +1,99 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/examContainer"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true">
android:orientation="vertical">
<TextView
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Exam" />
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<!--<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/exams_fragment_tab_layout"
android:layout_width="match_parent"
<ProgressBar
android:id="@+id/examProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:tabMinWidth="125dp"
app:tabMode="scrollable" />
android:layout_gravity="center"
android:indeterminate="true" />
<android.support.v4.view.ViewPager
android:id="@+id/exams_fragment_viewpager"
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/examSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/examRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/examEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/exams_fragment_tab_layout" />
</RelativeLayout>-->
</android.support.design.widget.CoordinatorLayout>
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:visibility="gone">
<android.support.v7.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="100dp"
android:minWidth="100dp"
app:srcCompat="@drawable/ic_menu_main_exam_24dp"
app:tint="?android:attr/textColorPrimary"
tools:ignore="contentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/exam_no_items"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="horizontal">
<Button
android:id="@+id/examPreviousButton"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="4dp"
android:gravity="start|center"
android:text="@string/prev"
android:textAlignment="gravity" />
<TextView
android:id="@+id/examNavDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/app_name" />
<Button
android:id="@+id/examNextButton"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_weight="1"
android:drawablePadding="4dp"
android:gravity="end|center"
android:text="@string/next"
android:textAlignment="gravity" />
</LinearLayout>
</LinearLayout>

View File

@ -1,64 +0,0 @@
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/exams_tab_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.github.wulkanowy.ui.main.grades.GradesFragment">
<RelativeLayout
android:id="@+id/exams_tab_fragment_progress_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/exams_tab_fragment_no_item_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/exams_tab_fragment_no_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/exams_tab_fragment_no_item_text"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:minHeight="100dp"
android:minWidth="100dp"
app:srcCompat="@drawable/ic_menu_main_exam_24dp"
app:tint="?android:attr/textColorPrimary"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/exams_tab_fragment_no_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:gravity="center"
android:text="@string/exam_no_items"
android:textSize="20sp" />
</RelativeLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/exams_tab_fragment_swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/exams_tab_fragment_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@ -10,7 +10,7 @@
android:paddingTop="10dp">
<TextView
android:id="@+id/exams_header_name"
android:id="@+id/examHeaderDay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
@ -18,7 +18,7 @@
android:textSize="18sp" />
<TextView
android:id="@+id/exams_header_date"
android:id="@+id/examHeaderDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
@ -26,8 +26,8 @@
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_toEndOf="@id/exams_header_name"
android:layout_toRightOf="@id/exams_header_name"
android:layout_toEndOf="@id/examHeaderDay"
android:layout_toRightOf="@id/examHeaderDay"
android:gravity="end"
android:text="@string/app_name"
android:textSize="13sp" />

View File

@ -1,56 +1,47 @@
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/exams_subitem_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="60dp"
card_view:cardElevation="0dp">
android:foreground="?attr/selectableItemBackgroundBorderless">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ic_all_divider"
android:foreground="?attr/selectableItemBackgroundBorderless">
<TextView
android:id="@+id/examItemSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:layout_marginTop="10dp"
android:text="@string/app_name"
android:textSize="15sp" />
<TextView
android:id="@+id/exams_subitem_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:layout_marginTop="10dp"
android:text="@string/app_name"
android:textSize="15sp" />
<TextView
android:id="@+id/examItemType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/examItemSubject"
android:layout_alignStart="@id/examItemSubject"
android:layout_below="@id/examItemSubject"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:text="@string/app_name"
android:textSize="13sp" />
<TextView
android:id="@+id/exams_subitems_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/exams_subitem_subject"
android:layout_alignStart="@id/exams_subitem_subject"
android:layout_below="@id/exams_subitem_subject"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:text="@string/app_name"
android:textSize="13sp" />
<TextView
android:id="@+id/exams_subitems_teacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@id/exams_subitem_subject"
android:layout_marginBottom="10dp"
android:layout_marginEnd="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="20dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@id/exams_subitems_type"
android:layout_toRightOf="@id/exams_subitems_type"
android:gravity="end"
android:text="@string/app_name"
android:textSize="13sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
<TextView
android:id="@+id/examItemTeacher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@id/examItemSubject"
android:layout_marginBottom="10dp"
android:layout_marginEnd="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="20dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@id/examItemType"
android:layout_toRightOf="@id/examItemType"
android:gravity="end"
android:text="@string/app_name"
android:textSize="13sp" />
</RelativeLayout>

View File

@ -163,4 +163,7 @@
<string name="all_sync_fail">Podczas synchronizacji wystąpił błąd</string>
<string name="all_timeout">Zbyt długie oczekiwanie na połączenie</string>
<string name="all_login_failed">Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację</string>
<string name="prev">Poprzedni</string>
<string name="next">Następny</string>
</resources>

View File

@ -156,4 +156,6 @@
<string name="all_sync_fail">There was an error during synchronization</string>
<string name="all_timeout">Too long wait for connection</string>
<string name="all_login_failed">Login is failed. Try again or restart the app</string>
<string name="prev">Prev</string>
<string name="next">Next</string>
</resources>

View File

@ -36,4 +36,12 @@
<item name="android:windowBackground">@drawable/layer_splash_background</item>
</style>
<style name="DialogFragmentTheme" parent="Theme.AppCompat.DayNight.Dialog.Alert">
<item name="android:textColorTertiary">@android:color/primary_text_light</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">false</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">false</item>
</style>
</resources>

View File

@ -0,0 +1,56 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.exams.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.doReturn
import org.mockito.MockitoAnnotations
import org.threeten.bp.LocalDate
import java.sql.Date
class ExamRemoteTest {
@Mock
private lateinit var mockApi: Api
@Mock
private lateinit var semesterMock: Semester
@Before
fun initApi() {
MockitoAnnotations.initMocks(this)
}
@Test
fun getExamsTest() {
doReturn(Single.just(listOf(
getExam("2018-09-10"),
getExam("2018-09-17")
))).`when`(mockApi).getExams(any())
doReturn("1").`when`(semesterMock).studentId
doReturn("1").`when`(semesterMock).diaryId
val exams = ExamRemote(mockApi).getExams(semesterMock, LocalDate.of(2018, 9, 10)).blockingGet()
assertEquals(2, exams.size)
}
private fun getExam(dateString: String): Exam {
return Exam().apply {
subject = ""
group = ""
type = ""
description = ""
teacher = ""
teacherSymbol = ""
date = Date.valueOf(dateString)
entryDate = Date.valueOf(dateString)
}
}
}

View File

@ -25,7 +25,7 @@ class StudentRemoteTest {
doReturn(Single.just(listOf(Pupil("", "", "", "test", "", ""))))
.`when`(mockApi).getPupils()
val students = StudentRemote(mockApi).getConnectedStudents("", "", "").blockingGet()
val students = SessionRemote(mockApi).getConnectedStudents("", "", "").blockingGet()
assertEquals(1, students.size)
assertEquals("test", students.first().studentName)
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.ui.login.form
import io.github.wulkanowy.TestSchedulers
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.login.LoginErrorHandler
import io.reactivex.Single
import org.junit.Before
@ -18,7 +18,7 @@ class LoginFormPresenterTest {
lateinit var loginFormView: LoginFormView
@Mock
lateinit var repository: StudentRepository
lateinit var repository: SessionRepository
@Mock
lateinit var errorHandler: LoginErrorHandler

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.login.options
import io.github.wulkanowy.TestSchedulers
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.reactivex.Completable
import io.reactivex.Single
import org.junit.Before
@ -21,7 +21,7 @@ class LoginOptionsPresenterTest {
lateinit var loginOptionsView: LoginOptionsView
@Mock
lateinit var repository: StudentRepository
lateinit var repository: SessionRepository
private lateinit var presenter: LoginOptionsPresenter
@ -62,7 +62,7 @@ class LoginOptionsPresenterTest {
@Test
fun onSelectedStudentTest() {
doReturn(Completable.complete()).`when`(repository).save(testStudent)
doReturn(Completable.complete()).`when`(repository).saveStudent(testStudent)
presenter.onSelectStudent(testStudent)
verify(loginOptionsView).showLoginProgress(true)
verify(loginOptionsView).openMainView()
@ -71,9 +71,9 @@ class LoginOptionsPresenterTest {
@Test
fun onSelectedStudentErrorTest() {
doReturn(Completable.error(testException)).`when`(repository).save(testStudent)
doReturn(Completable.error(testException)).`when`(repository).saveStudent(testStudent)
presenter.onSelectStudent(testStudent)
verify(loginOptionsView).showLoginProgress(true)
verify(errorHandler).proceed(testException)
}
}
}

View File

@ -1,7 +1,7 @@
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 org.junit.Before
import org.junit.Test
import org.mockito.Mock
@ -15,7 +15,7 @@ class SplashPresenterTest {
lateinit var splashView: SplashView
@Mock
lateinit var studentRepository: StudentRepository
lateinit var studentRepository: SessionRepository
@Mock
lateinit var errorHandler: ErrorHandler
@ -30,15 +30,15 @@ class SplashPresenterTest {
@Test
fun testOpenLoginView() {
doReturn(false).`when`(studentRepository).isStudentLoggedIn
doReturn(false).`when`(studentRepository).isSessionSaved
presenter.attachView(splashView)
verify(splashView).openLoginActivity()
verify(splashView).openLoginView()
}
@Test
fun testMainMainView() {
doReturn(true).`when`(studentRepository).isStudentLoggedIn
doReturn(true).`when`(studentRepository).isSessionSaved
presenter.attachView(splashView)
verify(splashView).openMainActivity()
verify(splashView).openMainView()
}
}
}

View File

@ -86,55 +86,55 @@ class TimeUtilsTest {
}
@Test fun isHolidaysInSchoolEndTest() {
assertFalse(isHolidays(LocalDate.of(2017, 6, 23), 2017))
assertFalse(isHolidays(LocalDate.of(2018, 6, 22), 2018))
assertFalse(isHolidays(LocalDate.of(2019, 6, 21), 2019))
assertFalse(isHolidays(LocalDate.of(2020, 6, 26), 2020))
assertFalse(isHolidays(LocalDate.of(2021, 6, 25), 2021))
assertFalse(isHolidays(LocalDate.of(2022, 6, 24), 2022))
assertFalse(isHolidays(LocalDate.of(2023, 6, 23), 2023))
assertFalse(isHolidays(LocalDate.of(2024, 6, 21), 2024))
assertFalse(isHolidays(LocalDate.of(2025, 6, 27), 2025))
assertFalse(isHolidays(LocalDate.of(2017, 6, 23)))
assertFalse(isHolidays(LocalDate.of(2018, 6, 22)))
assertFalse(isHolidays(LocalDate.of(2019, 6, 21)))
assertFalse(isHolidays(LocalDate.of(2020, 6, 26)))
assertFalse(isHolidays(LocalDate.of(2021, 6, 25)))
assertFalse(isHolidays(LocalDate.of(2022, 6, 24)))
assertFalse(isHolidays(LocalDate.of(2023, 6, 23)))
assertFalse(isHolidays(LocalDate.of(2024, 6, 21)))
assertFalse(isHolidays(LocalDate.of(2025, 6, 27)))
}
@Test fun isHolidaysInHolidaysStartTest() {
assertTrue(isHolidays(LocalDate.of(2017, 6, 24), 2017))
assertTrue(isHolidays(LocalDate.of(2018, 6, 23), 2018))
assertTrue(isHolidays(LocalDate.of(2019, 6, 22), 2019))
assertTrue(isHolidays(LocalDate.of(2020, 6, 27), 2020))
assertTrue(isHolidays(LocalDate.of(2021, 6, 26), 2021))
assertTrue(isHolidays(LocalDate.of(2022, 6, 25), 2022))
assertTrue(isHolidays(LocalDate.of(2023, 6, 24), 2023))
assertTrue(isHolidays(LocalDate.of(2024, 6, 22), 2024))
assertTrue(isHolidays(LocalDate.of(2025, 6, 28), 2025))
assertTrue(isHolidays(LocalDate.of(2017, 6, 24)))
assertTrue(isHolidays(LocalDate.of(2018, 6, 23)))
assertTrue(isHolidays(LocalDate.of(2019, 6, 22)))
assertTrue(isHolidays(LocalDate.of(2020, 6, 27)))
assertTrue(isHolidays(LocalDate.of(2021, 6, 26)))
assertTrue(isHolidays(LocalDate.of(2022, 6, 25)))
assertTrue(isHolidays(LocalDate.of(2023, 6, 24)))
assertTrue(isHolidays(LocalDate.of(2024, 6, 22)))
assertTrue(isHolidays(LocalDate.of(2025, 6, 28)))
}
@Test fun isHolidaysInHolidaysEndTest() {
assertTrue(isHolidays(LocalDate.of(2017, 9, 1), 2017)) // friday
assertTrue(isHolidays(LocalDate.of(2017, 9, 2), 2017)) // saturday
assertTrue(isHolidays(LocalDate.of(2017, 9, 3), 2017)) // sunday
assertTrue(isHolidays(LocalDate.of(2018, 9, 1), 2018)) // saturday
assertTrue(isHolidays(LocalDate.of(2018, 9, 2), 2018)) // sunday
assertTrue(isHolidays(LocalDate.of(2019, 9, 1), 2019)) // sunday
assertTrue(isHolidays(LocalDate.of(2020, 8, 31), 2020)) // monday
assertTrue(isHolidays(LocalDate.of(2021, 8, 31), 2021)) // tuesday
assertTrue(isHolidays(LocalDate.of(2022, 8, 31), 2022)) // wednesday
assertTrue(isHolidays(LocalDate.of(2023, 9, 1), 2023)) // friday
assertTrue(isHolidays(LocalDate.of(2023, 9, 2), 2023)) // saturday
assertTrue(isHolidays(LocalDate.of(2023, 9, 3), 2023)) // sunday
assertTrue(isHolidays(LocalDate.of(2024, 9, 1), 2024)) // sunday
assertTrue(isHolidays(LocalDate.of(2025, 8, 31), 2025)) // sunday
assertTrue(isHolidays(LocalDate.of(2017, 9, 1))) // friday
assertTrue(isHolidays(LocalDate.of(2017, 9, 2))) // saturday
assertTrue(isHolidays(LocalDate.of(2017, 9, 3))) // sunday
assertTrue(isHolidays(LocalDate.of(2018, 9, 1))) // saturday
assertTrue(isHolidays(LocalDate.of(2018, 9, 2))) // sunday
assertTrue(isHolidays(LocalDate.of(2019, 9, 1))) // sunday
assertTrue(isHolidays(LocalDate.of(2020, 8, 31))) // monday
assertTrue(isHolidays(LocalDate.of(2021, 8, 31))) // tuesday
assertTrue(isHolidays(LocalDate.of(2022, 8, 31))) // wednesday
assertTrue(isHolidays(LocalDate.of(2023, 9, 1))) // friday
assertTrue(isHolidays(LocalDate.of(2023, 9, 2))) // saturday
assertTrue(isHolidays(LocalDate.of(2023, 9, 3))) // sunday
assertTrue(isHolidays(LocalDate.of(2024, 9, 1))) // sunday
assertTrue(isHolidays(LocalDate.of(2025, 8, 31))) // sunday
}
@Test fun isHolidaysInSchoolStartTest() {
assertFalse(isHolidays(LocalDate.of(2017, 9, 4), 2017)) // monday
assertFalse(isHolidays(LocalDate.of(2018, 9, 3), 2018)) // monday
assertFalse(isHolidays(LocalDate.of(2019, 9, 2), 2019)) // monday
assertFalse(isHolidays(LocalDate.of(2020, 9, 1), 2020)) // tuesday
assertFalse(isHolidays(LocalDate.of(2021, 9, 1), 2021)) // wednesday
assertFalse(isHolidays(LocalDate.of(2022, 9, 1), 2022)) // thursday
assertFalse(isHolidays(LocalDate.of(2023, 9, 4), 2023)) // monday
assertFalse(isHolidays(LocalDate.of(2024, 9, 2), 2024)) // monday
assertFalse(isHolidays(LocalDate.of(2025, 9, 1), 2025)) // monday
assertFalse(isHolidays(LocalDate.of(2017, 9, 4))) // monday
assertFalse(isHolidays(LocalDate.of(2018, 9, 3))) // monday
assertFalse(isHolidays(LocalDate.of(2019, 9, 2))) // monday
assertFalse(isHolidays(LocalDate.of(2020, 9, 1))) // tuesday
assertFalse(isHolidays(LocalDate.of(2021, 9, 1))) // wednesday
assertFalse(isHolidays(LocalDate.of(2022, 9, 1))) // thursday
assertFalse(isHolidays(LocalDate.of(2023, 9, 4))) // monday
assertFalse(isHolidays(LocalDate.of(2024, 9, 2))) // monday
assertFalse(isHolidays(LocalDate.of(2025, 9, 1))) // monday
}
}

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.2.61'
ext.kotlin_version = '1.2.70'
repositories {
mavenCentral()
google()