mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-02-21 20:24:44 +01:00
Modules improvements (#164)
This commit is contained in:
parent
bb69d1b643
commit
27f6fc7e04
@ -7,7 +7,7 @@ references:
|
|||||||
|
|
||||||
container_config: &container_config
|
container_config: &container_config
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/android:api-27-alpha
|
- image: circleci/android:api-28-alpha
|
||||||
working_directory: *workspace_root
|
working_directory: *workspace_root
|
||||||
environment:
|
environment:
|
||||||
environment:
|
environment:
|
||||||
|
@ -73,6 +73,7 @@ ext.supportVersion = "28.0.0"
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation('com.github.wulkanowy:api:a5667f8000') { exclude module: "threetenbp" }
|
implementation('com.github.wulkanowy:api:a5667f8000') { exclude module: "threetenbp" }
|
||||||
|
|
||||||
implementation "com.android.support:support-v4:$supportVersion"
|
implementation "com.android.support:support-v4:$supportVersion"
|
||||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||||
implementation "com.android.support:design:$supportVersion"
|
implementation "com.android.support:design:$supportVersion"
|
||||||
@ -116,6 +117,7 @@ dependencies {
|
|||||||
testImplementation "junit:junit:4.12"
|
testImplementation "junit:junit:4.12"
|
||||||
testImplementation "io.mockk:mockk:1.8.8"
|
testImplementation "io.mockk:mockk:1.8.8"
|
||||||
testImplementation "org.mockito:mockito-inline:2.21.0"
|
testImplementation "org.mockito:mockito-inline:2.21.0"
|
||||||
|
testImplementation 'org.threeten:threetenbp:1.3.7'
|
||||||
|
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
androidTestImplementation "org.mockito:mockito-android:2.21.0"
|
androidTestImplementation "org.mockito:mockito-android:2.21.0"
|
||||||
|
@ -34,9 +34,9 @@ class AttendanceLocalTest {
|
|||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
attendanceLocal.saveAttendance(listOf(
|
attendanceLocal.saveAttendance(listOf(
|
||||||
Attendance(0, "1", "2", LocalDate.of(2018, 9, 10), 0, "", ""),
|
Attendance("1", "2", LocalDate.of(2018, 9, 10), 0, "", ""),
|
||||||
Attendance(0, "1", "2", LocalDate.of(2018, 9, 14), 0, "", ""),
|
Attendance("1", "2", LocalDate.of(2018, 9, 14), 0, "", ""),
|
||||||
Attendance(0, "1", "2", LocalDate.of(2018, 9, 17), 0, "", "")
|
Attendance("1", "2", LocalDate.of(2018, 9, 17), 0, "", "")
|
||||||
))
|
))
|
||||||
|
|
||||||
val attendance = attendanceLocal
|
val attendance = attendanceLocal
|
||||||
|
@ -34,9 +34,9 @@ class ExamLocalTest {
|
|||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
examLocal.saveExams(listOf(
|
examLocal.saveExams(listOf(
|
||||||
Exam(0, "1", "2", LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""),
|
Exam("1", "2", LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""),
|
||||||
Exam(0, "1", "2", LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""),
|
Exam("1", "2", LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""),
|
||||||
Exam(0, "1", "2", LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "")
|
Exam("1", "2", LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "")
|
||||||
))
|
))
|
||||||
|
|
||||||
val exams = examLocal
|
val exams = examLocal
|
||||||
|
@ -11,6 +11,7 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDateTime
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -33,14 +34,17 @@ class TimetableLocalTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
timetableDb.saveLessons(listOf(
|
timetableDb.saveTimetable(listOf(
|
||||||
Timetable(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 10)),
|
Timetable("1", "2", 1, LocalDateTime.now(), LocalDateTime.now(),
|
||||||
Timetable(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 14)),
|
LocalDate.of(2018, 9, 10), "", "", "", "", ""),
|
||||||
Timetable(studentId = "1", diaryId = "2", date = LocalDate.of(2018, 9, 17)) // in next week
|
Timetable("1", "2", 1, LocalDateTime.now(), LocalDateTime.now(),
|
||||||
|
LocalDate.of(2018, 9, 14), "", "", "", "", ""),
|
||||||
|
Timetable("1", "2", 1, LocalDateTime.now(), LocalDateTime.now(),
|
||||||
|
LocalDate.of(2018, 9, 17), "", "", "", "", "")
|
||||||
))
|
))
|
||||||
|
|
||||||
val exams = timetableDb.getLessons(
|
val exams = timetableDb.getTimetable(
|
||||||
Semester(studentId = "1", diaryId = "2", semesterId = "3", diaryName = "", semesterName = 1),
|
Semester(0, "1", "2", "3", "", 1),
|
||||||
LocalDate.of(2018, 9, 10),
|
LocalDate.of(2018, 9, 10),
|
||||||
LocalDate.of(2018, 9, 14)
|
LocalDate.of(2018, 9, 14)
|
||||||
).blockingGet()
|
).blockingGet()
|
||||||
|
@ -17,32 +17,32 @@ class ScramblerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun encryptDecryptTest() {
|
fun encryptDecryptTest() {
|
||||||
assertEquals("TEST", Scrambler.decrypt(Scrambler.encrypt("TEST",
|
assertEquals("TEST", decrypt(encrypt("TEST",
|
||||||
InstrumentationRegistry.getTargetContext())))
|
InstrumentationRegistry.getTargetContext())))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun emptyTextEncryptTest() {
|
fun emptyTextEncryptTest() {
|
||||||
assertFailsWith<ScramblerException> {
|
assertFailsWith<ScramblerException> {
|
||||||
Scrambler.decrypt("")
|
decrypt("")
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith<ScramblerException> {
|
assertFailsWith<ScramblerException> {
|
||||||
Scrambler.encrypt("", InstrumentationRegistry.getTargetContext())
|
encrypt("", InstrumentationRegistry.getTargetContext())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SdkSuppress(minSdkVersion = 18)
|
@SdkSuppress(minSdkVersion = 18)
|
||||||
fun emptyKeyStoreTest() {
|
fun emptyKeyStoreTest() {
|
||||||
val text = Scrambler.encrypt("test", InstrumentationRegistry.getTargetContext())
|
val text = encrypt("test", InstrumentationRegistry.getTargetContext())
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
||||||
keyStore.load(null)
|
keyStore.load(null)
|
||||||
keyStore.deleteEntry("USER_PASSWORD")
|
keyStore.deleteEntry("USER_PASSWORD")
|
||||||
|
|
||||||
assertFailsWith<ScramblerException> {
|
assertFailsWith<ScramblerException> {
|
||||||
Scrambler.decrypt(text)
|
decrypt(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.db.dao
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
import android.arch.persistence.room.Dao
|
import android.arch.persistence.room.Dao
|
||||||
|
import android.arch.persistence.room.Delete
|
||||||
import android.arch.persistence.room.Insert
|
import android.arch.persistence.room.Insert
|
||||||
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
||||||
import android.arch.persistence.room.Query
|
import android.arch.persistence.room.Query
|
||||||
@ -13,6 +14,9 @@ interface GradeSummaryDao {
|
|||||||
@Insert(onConflict = REPLACE)
|
@Insert(onConflict = REPLACE)
|
||||||
fun insertAll(gradesSummary: List<GradeSummary>)
|
fun insertAll(gradesSummary: List<GradeSummary>)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun deleteAll(gradesSummary: List<GradeSummary>)
|
||||||
|
|
||||||
@Query("SELECT * FROM grades_summary WHERE student_id = :studentId AND semester_id = :semesterId")
|
@Query("SELECT * FROM grades_summary WHERE student_id = :studentId AND semester_id = :semesterId")
|
||||||
fun getGradesSummary(semesterId: String, studentId: String): Maybe<List<GradeSummary>>
|
fun getGradesSummary(semesterId: String, studentId: String): Maybe<List<GradeSummary>>
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
|
|
||||||
import android.arch.persistence.room.Dao
|
import android.arch.persistence.room.Dao
|
||||||
import android.arch.persistence.room.Insert
|
import android.arch.persistence.room.Insert
|
||||||
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
|
||||||
import android.arch.persistence.room.Query
|
import android.arch.persistence.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
@ -10,7 +9,7 @@ import io.reactivex.Single
|
|||||||
@Dao
|
@Dao
|
||||||
interface SemesterDao {
|
interface SemesterDao {
|
||||||
|
|
||||||
@Insert(onConflict = REPLACE)
|
@Insert
|
||||||
fun insertAll(semester: List<Semester>)
|
fun insertAll(semester: List<Semester>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
|
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
|
||||||
|
@ -2,7 +2,6 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
|
|
||||||
import android.arch.persistence.room.Dao
|
import android.arch.persistence.room.Dao
|
||||||
import android.arch.persistence.room.Insert
|
import android.arch.persistence.room.Insert
|
||||||
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
|
||||||
import android.arch.persistence.room.Query
|
import android.arch.persistence.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
@ -10,7 +9,7 @@ import io.reactivex.Maybe
|
|||||||
@Dao
|
@Dao
|
||||||
interface StudentDao {
|
interface StudentDao {
|
||||||
|
|
||||||
@Insert(onConflict = REPLACE)
|
@Insert
|
||||||
fun insert(student: Student): Long
|
fun insert(student: Student): Long
|
||||||
|
|
||||||
@Query("SELECT * FROM Students WHERE id = :id")
|
@Query("SELECT * FROM Students WHERE id = :id")
|
||||||
|
@ -9,9 +9,6 @@ import java.io.Serializable
|
|||||||
@Entity(tableName = "Attendance")
|
@Entity(tableName = "Attendance")
|
||||||
data class Attendance(
|
data class Attendance(
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
var id: Long = 0,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "student_id")
|
@ColumnInfo(name = "student_id")
|
||||||
var studentId: String,
|
var studentId: String,
|
||||||
|
|
||||||
@ -37,4 +34,8 @@ data class Attendance(
|
|||||||
var excused: Boolean = false,
|
var excused: Boolean = false,
|
||||||
|
|
||||||
var deleted: Boolean = false
|
var deleted: Boolean = false
|
||||||
) : Serializable
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
||||||
|
@ -9,9 +9,6 @@ import java.io.Serializable
|
|||||||
@Entity(tableName = "Exams")
|
@Entity(tableName = "Exams")
|
||||||
data class Exam(
|
data class Exam(
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
var id: Long = 0,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "student_id")
|
@ColumnInfo(name = "student_id")
|
||||||
var studentId: String,
|
var studentId: String,
|
||||||
|
|
||||||
@ -35,4 +32,8 @@ data class Exam(
|
|||||||
|
|
||||||
@ColumnInfo(name = "teacher_symbol")
|
@ColumnInfo(name = "teacher_symbol")
|
||||||
var teacherSymbol: String
|
var teacherSymbol: String
|
||||||
) : Serializable
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
||||||
|
@ -2,16 +2,11 @@ package io.github.wulkanowy.data.db.entities
|
|||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo
|
import android.arch.persistence.room.ColumnInfo
|
||||||
import android.arch.persistence.room.Entity
|
import android.arch.persistence.room.Entity
|
||||||
import android.arch.persistence.room.Index
|
|
||||||
import android.arch.persistence.room.PrimaryKey
|
import android.arch.persistence.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "Grades_Summary",
|
@Entity(tableName = "Grades_Summary")
|
||||||
indices = [Index(value = ["semester_id", "student_id", "subject"], unique = true)])
|
|
||||||
data class GradeSummary(
|
data class GradeSummary(
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
var id: Long = 0,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "semester_id")
|
@ColumnInfo(name = "semester_id")
|
||||||
var semesterId: String,
|
var semesterId: String,
|
||||||
|
|
||||||
@ -23,4 +18,8 @@ data class GradeSummary(
|
|||||||
var predictedGrade: String,
|
var predictedGrade: String,
|
||||||
|
|
||||||
var finalGrade: String
|
var finalGrade: String
|
||||||
)
|
) {
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -2,11 +2,9 @@ package io.github.wulkanowy.data.db.entities
|
|||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo
|
import android.arch.persistence.room.ColumnInfo
|
||||||
import android.arch.persistence.room.Entity
|
import android.arch.persistence.room.Entity
|
||||||
import android.arch.persistence.room.Index
|
|
||||||
import android.arch.persistence.room.PrimaryKey
|
import android.arch.persistence.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "Semesters",
|
@Entity(tableName = "Semesters")
|
||||||
indices = [Index(value = ["semester_id", "diary_id", "student_id"], unique = true)])
|
|
||||||
data class Semester(
|
data class Semester(
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -2,11 +2,9 @@ package io.github.wulkanowy.data.db.entities
|
|||||||
|
|
||||||
import android.arch.persistence.room.ColumnInfo
|
import android.arch.persistence.room.ColumnInfo
|
||||||
import android.arch.persistence.room.Entity
|
import android.arch.persistence.room.Entity
|
||||||
import android.arch.persistence.room.Index
|
|
||||||
import android.arch.persistence.room.PrimaryKey
|
import android.arch.persistence.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "Students",
|
@Entity(tableName = "Students")
|
||||||
indices = [Index(value = ["student_id", "student_name"], unique = true)])
|
|
||||||
data class Student(
|
data class Student(
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -10,14 +10,11 @@ import java.io.Serializable
|
|||||||
@Entity(tableName = "Timetable")
|
@Entity(tableName = "Timetable")
|
||||||
data class Timetable(
|
data class Timetable(
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
|
||||||
var id: Long = 0,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "student_id")
|
@ColumnInfo(name = "student_id")
|
||||||
var studentId: String = "",
|
var studentId: String,
|
||||||
|
|
||||||
@ColumnInfo(name = "diary_id")
|
@ColumnInfo(name = "diary_id")
|
||||||
var diaryId: String = "",
|
var diaryId: String,
|
||||||
|
|
||||||
val number: Int = 0,
|
val number: Int = 0,
|
||||||
|
|
||||||
@ -27,17 +24,21 @@ data class Timetable(
|
|||||||
|
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
|
|
||||||
val subject: String = "",
|
val subject: String,
|
||||||
|
|
||||||
val group: String = "",
|
val group: String,
|
||||||
|
|
||||||
val room: String = "",
|
val room: String,
|
||||||
|
|
||||||
val teacher: String = "",
|
val teacher: String,
|
||||||
|
|
||||||
val info: String = "",
|
val info: String,
|
||||||
|
|
||||||
val changes: Boolean = false,
|
val changes: Boolean = false,
|
||||||
|
|
||||||
val canceled: Boolean = false
|
val canceled: Boolean = false
|
||||||
) : Serializable
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
||||||
|
@ -6,11 +6,10 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.repositories.local.AttendanceLocal
|
import io.github.wulkanowy.data.repositories.local.AttendanceLocal
|
||||||
import io.github.wulkanowy.data.repositories.remote.AttendanceRemote
|
import io.github.wulkanowy.data.repositories.remote.AttendanceRemote
|
||||||
import io.github.wulkanowy.utils.weekFirstDayAlwaysCurrent
|
import io.github.wulkanowy.utils.friday
|
||||||
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.threeten.bp.DayOfWeek
|
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import org.threeten.bp.temporal.TemporalAdjusters
|
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -22,24 +21,25 @@ class AttendanceRepository @Inject constructor(
|
|||||||
private val remote: AttendanceRemote
|
private val remote: AttendanceRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Attendance>> {
|
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean)
|
||||||
val start = startDate.weekFirstDayAlwaysCurrent
|
: Single<List<Attendance>> {
|
||||||
val end = endDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY))
|
return Single.fromCallable { startDate.monday to endDate.friday }
|
||||||
|
.flatMap { dates ->
|
||||||
return local.getAttendance(semester, start, end).filter { !forceRefresh }
|
local.getAttendance(semester, dates.first, dates.second).filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||||
if (it) remote.getAttendance(semester, start, end)
|
if (it) remote.getAttendance(semester, dates.first, dates.second)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { newLessons ->
|
}.flatMap { newAttendance ->
|
||||||
local.getAttendance(semester, start, end).toSingle(emptyList()).map { grades ->
|
local.getAttendance(semester, dates.first, dates.second)
|
||||||
local.deleteAttendance(grades - newLessons)
|
.toSingle(emptyList())
|
||||||
local.saveAttendance(newLessons - grades)
|
.doOnSuccess { oldAttendance ->
|
||||||
newLessons
|
local.deleteAttendance(oldAttendance - newAttendance)
|
||||||
}
|
local.saveAttendance(newAttendance - oldAttendance)
|
||||||
}).map { list ->
|
}
|
||||||
list.asSequence().filter {
|
}.flatMap {
|
||||||
it.date in startDate..endDate
|
local.getAttendance(semester, dates.first, dates.second)
|
||||||
}.toList()
|
.toSingle(emptyList())
|
||||||
|
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@ import io.github.wulkanowy.data.db.entities.Exam
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.repositories.local.ExamLocal
|
import io.github.wulkanowy.data.repositories.local.ExamLocal
|
||||||
import io.github.wulkanowy.data.repositories.remote.ExamRemote
|
import io.github.wulkanowy.data.repositories.remote.ExamRemote
|
||||||
import io.github.wulkanowy.utils.weekFirstDayAlwaysCurrent
|
import io.github.wulkanowy.utils.friday
|
||||||
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.threeten.bp.DayOfWeek
|
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import org.threeten.bp.temporal.TemporalAdjusters
|
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -23,23 +22,24 @@ class ExamRepository @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Exam>> {
|
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Exam>> {
|
||||||
val start = startDate.weekFirstDayAlwaysCurrent
|
return Single.fromCallable { startDate.monday to endDate.friday }
|
||||||
val end = endDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY))
|
.flatMap { dates ->
|
||||||
|
local.getExams(semester, dates.first, dates.second).filter { !forceRefresh }
|
||||||
return local.getExams(semester, start, end).filter { !forceRefresh }
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
.flatMap {
|
||||||
if (it) remote.getExams(semester, start, end)
|
if (it) remote.getExams(semester, dates.first, dates.second)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { newExams ->
|
}.flatMap { newExams ->
|
||||||
local.getExams(semester, start, end).toSingle(emptyList()).map { grades ->
|
local.getExams(semester, dates.first, dates.second)
|
||||||
local.deleteExams(grades - newExams)
|
.toSingle(emptyList())
|
||||||
local.saveExams(newExams - grades)
|
.doOnSuccess { oldExams ->
|
||||||
newExams
|
local.deleteExams(oldExams - newExams)
|
||||||
}
|
local.saveExams(newExams - oldExams)
|
||||||
}).map { list ->
|
}
|
||||||
list.asSequence().filter {
|
}.flatMap {
|
||||||
it.date in startDate..endDate
|
local.getExams(semester, dates.first, dates.second)
|
||||||
}.toList()
|
.toSingle(emptyList())
|
||||||
|
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,12 @@ class GradeSummaryRepository @Inject constructor(
|
|||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) remote.getGradeSummary(semester)
|
if (it) remote.getGradeSummary(semester)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}
|
}.flatMap { newGradesSummary ->
|
||||||
).doOnSuccess { local.saveGradesSummary(it) }
|
local.getGradesSummary(semester).toSingle(emptyList())
|
||||||
|
.doOnSuccess { oldGradesSummary ->
|
||||||
|
local.deleteGradesSummary(oldGradesSummary - newGradesSummary)
|
||||||
|
local.saveGradesSummary(newGradesSummary - oldGradesSummary)
|
||||||
|
}
|
||||||
|
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,10 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.data.repositories.local.TimetableLocal
|
import io.github.wulkanowy.data.repositories.local.TimetableLocal
|
||||||
import io.github.wulkanowy.data.repositories.remote.TimetableRemote
|
import io.github.wulkanowy.data.repositories.remote.TimetableRemote
|
||||||
import io.github.wulkanowy.utils.weekFirstDayAlwaysCurrent
|
import io.github.wulkanowy.utils.friday
|
||||||
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.threeten.bp.DayOfWeek
|
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import org.threeten.bp.temporal.TemporalAdjusters
|
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -22,24 +21,26 @@ class TimetableRepository @Inject constructor(
|
|||||||
private val remote: TimetableRemote
|
private val remote: TimetableRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Timetable>> {
|
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false)
|
||||||
val start = startDate.weekFirstDayAlwaysCurrent
|
: Single<List<Timetable>> {
|
||||||
val end = endDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY))
|
return Single.fromCallable { startDate.monday to endDate.friday }
|
||||||
|
.flatMap { dates ->
|
||||||
return local.getLessons(semester, start, end).filter { !forceRefresh }
|
local.getTimetable(semester, dates.first, dates.second).filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
if (it) remote.getLessons(semester, start, end)
|
.flatMap {
|
||||||
else Single.error(UnknownHostException())
|
if (it) remote.getTimetable(semester, dates.first, dates.second)
|
||||||
}.flatMap { newLessons ->
|
else Single.error(UnknownHostException())
|
||||||
local.getLessons(semester, start, end).toSingle(emptyList()).map { lessons ->
|
}.flatMap { newTimetable ->
|
||||||
local.deleteLessons(lessons - newLessons)
|
local.getTimetable(semester, dates.first, dates.second)
|
||||||
local.saveLessons(newLessons - lessons)
|
.toSingle(emptyList())
|
||||||
newLessons
|
.doOnSuccess { oldTimetable ->
|
||||||
}
|
local.deleteTimetable(oldTimetable - newTimetable)
|
||||||
}).map { list ->
|
local.saveTimetable(newTimetable - oldTimetable)
|
||||||
list.asSequence().filter {
|
}
|
||||||
it.date in startDate..endDate
|
}.flatMap {
|
||||||
}.toList()
|
local.getTimetable(semester, dates.first, dates.second)
|
||||||
|
.toSingle(emptyList())
|
||||||
|
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,8 @@ class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSum
|
|||||||
fun saveGradesSummary(gradesSummary: List<GradeSummary>) {
|
fun saveGradesSummary(gradesSummary: List<GradeSummary>) {
|
||||||
gradeSummaryDb.insertAll(gradesSummary)
|
gradeSummaryDb.insertAll(gradesSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteGradesSummary(gradesSummary: List<GradeSummary>) {
|
||||||
|
gradeSummaryDb.deleteAll(gradesSummary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import io.github.wulkanowy.data.db.dao.SemesterDao
|
|||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.utils.security.Scrambler.decrypt
|
import io.github.wulkanowy.utils.security.decrypt
|
||||||
import io.github.wulkanowy.utils.security.Scrambler.encrypt
|
import io.github.wulkanowy.utils.security.encrypt
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
@ -9,16 +9,16 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) {
|
class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) {
|
||||||
|
|
||||||
fun getLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Timetable>> {
|
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Timetable>> {
|
||||||
return timetableDb.getTimetable(semester.diaryId, semester.studentId, startDate, endDate)
|
return timetableDb.getTimetable(semester.diaryId, semester.studentId, startDate, endDate)
|
||||||
.filter { !it.isEmpty() }
|
.filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveLessons(lessons: List<Timetable>) {
|
fun saveTimetable(timetables: List<Timetable>) {
|
||||||
timetableDb.insertAll(lessons)
|
timetableDb.insertAll(timetables)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteLessons(exams: List<Timetable>) {
|
fun deleteTimetable(timetables: List<Timetable>) {
|
||||||
timetableDb.deleteAll(exams)
|
timetableDb.deleteAll(timetables)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class GradeRemote @Inject constructor(private val api: Api) {
|
|||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
entry = it.entry,
|
entry = it.entry,
|
||||||
value = it.value,
|
value = it.value,
|
||||||
modifier = it.modifier.toDouble(),
|
modifier = it.modifier,
|
||||||
comment = it.comment,
|
comment = it.comment,
|
||||||
color = it.color,
|
color = it.color,
|
||||||
gradeSymbol = it.symbol,
|
gradeSymbol = it.symbol,
|
||||||
|
@ -11,7 +11,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class TimetableRemote @Inject constructor(private val api: Api) {
|
class TimetableRemote @Inject constructor(private val api: Api) {
|
||||||
|
|
||||||
fun getLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> {
|
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> {
|
||||||
return Single.just(api.run {
|
return Single.just(api.run {
|
||||||
if (diaryId != semester.diaryId) {
|
if (diaryId != semester.diaryId) {
|
||||||
diaryId = semester.diaryId
|
diaryId = semester.diaryId
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
package io.github.wulkanowy.ui.base
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
|
import android.support.design.widget.Snackbar
|
||||||
|
import android.support.design.widget.Snackbar.LENGTH_LONG
|
||||||
|
import android.view.View
|
||||||
import dagger.android.support.DaggerFragment
|
import dagger.android.support.DaggerFragment
|
||||||
|
|
||||||
abstract class BaseFragment : DaggerFragment(), BaseView {
|
abstract class BaseFragment : DaggerFragment(), BaseView {
|
||||||
|
|
||||||
|
protected var messageContainer: View? = null
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
override fun showMessage(text: String) {
|
||||||
(activity as BaseActivity?)?.showMessage(text)
|
if (messageContainer == null) (activity as? BaseActivity)?.showMessage(text)
|
||||||
|
else messageContainer?.also { Snackbar.make(it, text, LENGTH_LONG).show() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,12 @@ open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
|
|||||||
|
|
||||||
var view: T? = null
|
var view: T? = null
|
||||||
|
|
||||||
val isViewAttached: Boolean
|
open fun onAttachView(view: T) {
|
||||||
get() = view != null
|
|
||||||
|
|
||||||
open fun attachView(view: T) {
|
|
||||||
this.view = view
|
this.view = view
|
||||||
errorHandler.showErrorMessage = { view.showMessage(it) }
|
errorHandler.showErrorMessage = { view.showMessage(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun detachView() {
|
open fun onDetachView() {
|
||||||
view = null
|
view = null
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
errorHandler.clear()
|
errorHandler.clear()
|
||||||
|
@ -27,8 +27,9 @@ class LoginActivity : BaseActivity(), LoginView, LoginSwitchListener {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_login)
|
setContentView(R.layout.activity_login)
|
||||||
presenter.attachView(this)
|
|
||||||
messageContainer = loginContainer
|
messageContainer = loginContainer
|
||||||
|
|
||||||
|
presenter.onAttachView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
@ -65,7 +66,7 @@ class LoginActivity : BaseActivity(), LoginView, LoginSwitchListener {
|
|||||||
override fun currentViewPosition() = loginViewpager.currentItem
|
override fun currentViewPosition() = loginViewpager.currentItem
|
||||||
|
|
||||||
public override fun onDestroy() {
|
public override fun onDestroy() {
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ import javax.inject.Inject
|
|||||||
class LoginPresenter @Inject constructor(errorHandler: ErrorHandler)
|
class LoginPresenter @Inject constructor(errorHandler: ErrorHandler)
|
||||||
: BasePresenter<LoginView>(errorHandler) {
|
: BasePresenter<LoginView>(errorHandler) {
|
||||||
|
|
||||||
override fun attachView(view: LoginView) {
|
override fun onAttachView(view: LoginView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.run {
|
view.run {
|
||||||
initAdapter()
|
initAdapter()
|
||||||
hideActionBar()
|
hideActionBar()
|
||||||
|
@ -31,7 +31,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.attachView(this)
|
presenter.onAttachView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initInputs() {
|
override fun initInputs() {
|
||||||
@ -139,6 +139,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
|
|
||||||
private var wasEmpty = false
|
private var wasEmpty = false
|
||||||
|
|
||||||
override fun attachView(view: LoginFormView) {
|
override fun onAttachView(view: LoginFormView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initInputs()
|
view.initInputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.attachView(this)
|
presenter.onAttachView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initRecycler() {
|
override fun initRecycler() {
|
||||||
@ -80,6 +80,6 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView {
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@ class LoginOptionsPresenter @Inject constructor(
|
|||||||
private val schedulers: SchedulersManager)
|
private val schedulers: SchedulersManager)
|
||||||
: BasePresenter<LoginOptionsView>(errorHandler) {
|
: BasePresenter<LoginOptionsView>(errorHandler) {
|
||||||
|
|
||||||
override fun attachView(view: LoginOptionsView) {
|
override fun onAttachView(view: LoginOptionsView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initRecycler()
|
view.initRecycler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class MainActivity : BaseActivity(), MainView {
|
|||||||
setSupportActionBar(mainToolbar)
|
setSupportActionBar(mainToolbar)
|
||||||
messageContainer = mainFragmentContainer
|
messageContainer = mainFragmentContainer
|
||||||
|
|
||||||
presenter.attachView(this)
|
presenter.onAttachView(this)
|
||||||
navController.initialize(DEFAULT_TAB, savedInstanceState)
|
navController.initialize(DEFAULT_TAB, savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,10 +90,6 @@ class MainActivity : BaseActivity(), MainView {
|
|||||||
supportActionBar?.title = title
|
supportActionBar?.title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun expandActionBar(show: Boolean) {
|
|
||||||
mainAppBarContainer.setExpanded(show, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun viewTitle(index: Int): String {
|
override fun viewTitle(index: Int): String {
|
||||||
return getString(listOf(R.string.grade_title,
|
return getString(listOf(R.string.grade_title,
|
||||||
R.string.attendance_title,
|
R.string.attendance_title,
|
||||||
@ -119,6 +115,6 @@ class MainActivity : BaseActivity(), MainView {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ import javax.inject.Inject
|
|||||||
class MainPresenter @Inject constructor(errorHandler: ErrorHandler)
|
class MainPresenter @Inject constructor(errorHandler: ErrorHandler)
|
||||||
: BasePresenter<MainView>(errorHandler) {
|
: BasePresenter<MainView>(errorHandler) {
|
||||||
|
|
||||||
override fun attachView(view: MainView) {
|
override fun onAttachView(view: MainView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +22,6 @@ class MainPresenter @Inject constructor(errorHandler: ErrorHandler)
|
|||||||
|
|
||||||
fun onTabSelected(index: Int, wasSelected: Boolean): Boolean {
|
fun onTabSelected(index: Int, wasSelected: Boolean): Boolean {
|
||||||
return view?.run {
|
return view?.run {
|
||||||
expandActionBar(true)
|
|
||||||
if (wasSelected) {
|
if (wasSelected) {
|
||||||
notifyMenuViewReselected()
|
notifyMenuViewReselected()
|
||||||
false
|
false
|
||||||
|
@ -10,8 +10,6 @@ interface MainView : BaseView {
|
|||||||
|
|
||||||
fun setViewTitle(title: String)
|
fun setViewTitle(title: String)
|
||||||
|
|
||||||
fun expandActionBar(show: Boolean)
|
|
||||||
|
|
||||||
fun viewTitle(index: Int): String
|
fun viewTitle(index: Int): String
|
||||||
|
|
||||||
fun currentMenuIndex(): Int
|
fun currentMenuIndex(): Int
|
||||||
|
@ -26,14 +26,13 @@ class AttendanceDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragmentTheme)
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
arguments?.run {
|
arguments?.run {
|
||||||
attendance = getSerializable(ARGUMENT_KEY) as Attendance
|
attendance = getSerializable(ARGUMENT_KEY) as Attendance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
dialog.setTitle(getString(R.string.all_details))
|
|
||||||
return inflater.inflate(R.layout.dialog_attendance, container, false)
|
return inflater.inflate(R.layout.dialog_attendance, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,11 +10,12 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.main.MainView
|
||||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||||
import kotlinx.android.synthetic.main.fragment_attendance.*
|
import kotlinx.android.synthetic.main.fragment_attendance.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AttendanceFragment : BaseFragment(), AttendanceView {
|
class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MenuFragmentView {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: AttendancePresenter
|
lateinit var presenter: AttendancePresenter
|
||||||
@ -24,6 +25,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||||
|
|
||||||
fun newInstance() = AttendanceFragment()
|
fun newInstance() = AttendanceFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,41 +35,42 @@ class AttendanceFragment : BaseFragment(), AttendanceView {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.run {
|
messageContainer = attendanceRecycler
|
||||||
attachView(this@AttendanceFragment)
|
presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY))
|
||||||
loadData(date = savedInstanceState?.getLong(SAVED_DATE_KEY))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
attendanceAdapter.run {
|
attendanceAdapter.apply {
|
||||||
isAutoCollapseOnExpand = true
|
setOnItemClickListener { presenter.onAttendanceItemSelected(getItem(it)) }
|
||||||
isAutoScrollOnExpand = true
|
|
||||||
setOnItemClickListener { presenter.onAttendanceItemSelected(getItem(it))}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attendanceRecycler.run {
|
attendanceRecycler.run {
|
||||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||||
adapter = attendanceAdapter
|
adapter = attendanceAdapter
|
||||||
}
|
}
|
||||||
attendanceSwipe.setOnRefreshListener { presenter.loadData(date = null, forceRefresh = true) }
|
attendanceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||||
attendancePreviousButton.setOnClickListener { presenter.loadAttendanceForPreviousDay() }
|
attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() }
|
||||||
attendanceNextButton.setOnClickListener { presenter.loadAttendanceForNextDay() }
|
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(data: List<AttendanceItem>) {
|
override fun updateData(data: List<AttendanceItem>) {
|
||||||
attendanceAdapter.updateDataSet(data, true)
|
attendanceAdapter.updateDataSet(data, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearData() {
|
|
||||||
attendanceAdapter.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateNavigationDay(date: String) {
|
override fun updateNavigationDay(date: String) {
|
||||||
attendanceNavDate.text = date
|
attendanceNavDate.text = date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearData() {
|
||||||
|
attendanceAdapter.clear()
|
||||||
|
}
|
||||||
|
|
||||||
override fun isViewEmpty() = attendanceAdapter.isEmpty
|
override fun isViewEmpty() = attendanceAdapter.isEmpty
|
||||||
|
|
||||||
|
override fun onFragmentReselected() {
|
||||||
|
presenter.onViewReselected()
|
||||||
|
}
|
||||||
|
|
||||||
override fun showEmpty(show: Boolean) {
|
override fun showEmpty(show: Boolean) {
|
||||||
attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
@ -80,8 +83,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView {
|
|||||||
attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showRefresh(show: Boolean) {
|
override fun hideRefresh() {
|
||||||
attendanceSwipe.isRefreshing = show
|
attendanceSwipe.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showPreButton(show: Boolean) {
|
override fun showPreButton(show: Boolean) {
|
||||||
@ -102,8 +105,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package io.github.wulkanowy.ui.main.attendance
|
package io.github.wulkanowy.ui.main.attendance
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.INVISIBLE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
@ -10,9 +12,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.item_attendance.*
|
import kotlinx.android.synthetic.main.item_attendance.*
|
||||||
|
|
||||||
class AttendanceItem : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
||||||
|
|
||||||
lateinit var attendance: Attendance
|
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||||
return ViewHolder(view, adapter)
|
return ViewHolder(view, adapter)
|
||||||
@ -20,6 +20,16 @@ class AttendanceItem : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
|||||||
|
|
||||||
override fun getLayoutRes(): Int = R.layout.item_attendance
|
override fun getLayoutRes(): Int = R.layout.item_attendance
|
||||||
|
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
||||||
|
position: Int, payloads: MutableList<Any>?) {
|
||||||
|
holder.apply {
|
||||||
|
attendanceItemNumber.text = attendance.number.toString()
|
||||||
|
attendanceItemSubject.text = attendance.subject
|
||||||
|
attendanceItemDescription.text = attendance.name
|
||||||
|
attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (javaClass != other?.javaClass) return false
|
if (javaClass != other?.javaClass) return false
|
||||||
@ -34,22 +44,10 @@ class AttendanceItem : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
|||||||
return attendance.hashCode()
|
return attendance.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
|
||||||
position: Int, payloads: MutableList<Any>?) {
|
|
||||||
holder.bind(attendance)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||||
LayoutContainer {
|
LayoutContainer {
|
||||||
|
|
||||||
override val containerView: View
|
override val containerView: View
|
||||||
get() = contentView
|
get() = contentView
|
||||||
|
|
||||||
fun bind(lesson: Attendance) {
|
|
||||||
attendanceItemNumber.text = lesson.number.toString()
|
|
||||||
attendanceItemSubject.text = lesson.subject
|
|
||||||
attendanceItemDescription.text = lesson.name
|
|
||||||
attendanceItemAlert.visibility = if (lesson.absence && !lesson.excused) View.VISIBLE else View.INVISIBLE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,15 @@ package io.github.wulkanowy.ui.main.attendance
|
|||||||
|
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import io.github.wulkanowy.data.ErrorHandler
|
import io.github.wulkanowy.data.ErrorHandler
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
|
||||||
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
||||||
import io.github.wulkanowy.data.repositories.SessionRepository
|
import io.github.wulkanowy.data.repositories.SessionRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDate.now
|
||||||
|
import org.threeten.bp.LocalDate.ofEpochDay
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AttendancePresenter @Inject constructor(
|
class AttendancePresenter @Inject constructor(
|
||||||
@ -19,79 +20,79 @@ class AttendancePresenter @Inject constructor(
|
|||||||
private val sessionRepository: SessionRepository
|
private val sessionRepository: SessionRepository
|
||||||
) : BasePresenter<AttendanceView>(errorHandler) {
|
) : BasePresenter<AttendanceView>(errorHandler) {
|
||||||
|
|
||||||
var currentDate: LocalDate = LocalDate.now().nearSchoolDayPrevOnWeekEnd
|
lateinit var currentDate: LocalDate
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun attachView(view: AttendanceView) {
|
fun onAttachView(view: AttendanceView, date: Long?) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
|
loadData(ofEpochDay(date ?: now().previousOrSameSchoolDay.toEpochDay()))
|
||||||
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAttendanceForPreviousDay() = loadData(currentDate.previousWorkDay.toEpochDay())
|
fun onPreviousDay() {
|
||||||
|
loadData(currentDate.previousSchoolDay)
|
||||||
fun loadAttendanceForNextDay() = loadData(currentDate.nextWorkDay.toEpochDay())
|
reloadView()
|
||||||
|
|
||||||
fun loadData(date: Long?, forceRefresh: Boolean = false) {
|
|
||||||
this.currentDate = LocalDate.ofEpochDay(date
|
|
||||||
?: currentDate.nearSchoolDayPrevOnWeekEnd.toEpochDay())
|
|
||||||
if (currentDate.isHolidays) return
|
|
||||||
|
|
||||||
disposable.clear()
|
|
||||||
disposable.add(sessionRepository.getSemesters()
|
|
||||||
.map { selectSemester(it, -1) }
|
|
||||||
.flatMap { attendanceRepository.getAttendance(it, currentDate, currentDate, forceRefresh) }
|
|
||||||
.map { createAttendanceItems(it) }
|
|
||||||
.subscribeOn(schedulers.backgroundThread())
|
|
||||||
.observeOn(schedulers.mainThread())
|
|
||||||
.doOnSubscribe {
|
|
||||||
view?.run {
|
|
||||||
showRefresh(forceRefresh)
|
|
||||||
showProgress(!forceRefresh)
|
|
||||||
if (!forceRefresh) {
|
|
||||||
showEmpty(false)
|
|
||||||
clearData()
|
|
||||||
}
|
|
||||||
showPreButton(!currentDate.minusDays(1).isHolidays)
|
|
||||||
showNextButton(!currentDate.plusDays(1).isHolidays)
|
|
||||||
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.doFinally {
|
|
||||||
view?.run {
|
|
||||||
showRefresh(false)
|
|
||||||
showProgress(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.subscribe({
|
|
||||||
view?.run {
|
|
||||||
showEmpty(it.isEmpty())
|
|
||||||
showContent(it.isNotEmpty())
|
|
||||||
updateData(it)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
view?.run { showEmpty(isViewEmpty()) }
|
|
||||||
errorHandler.proceed(it)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAttendanceItems(items: List<Attendance>): List<AttendanceItem> {
|
fun onNextDay() {
|
||||||
return items.map {
|
loadData(currentDate.nextSchoolDay)
|
||||||
AttendanceItem().apply { attendance = it }
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onSwipeRefresh() {
|
||||||
|
loadData(currentDate, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onViewReselected() {
|
||||||
|
loadData(now().previousOrSameSchoolDay)
|
||||||
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
|
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||||
if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance)
|
if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectSemester(semesters: List<Semester>, index: Int): Semester {
|
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
||||||
return semesters.single { it.current }.let { currentSemester ->
|
currentDate = date
|
||||||
if (index == -1) currentSemester
|
disposable.apply {
|
||||||
else semesters.single { semester ->
|
clear()
|
||||||
semester.run {
|
add(sessionRepository.getSemesters()
|
||||||
semesterName - 1 == index && diaryId == currentSemester.diaryId
|
.delay(200, MILLISECONDS)
|
||||||
}
|
.map { it.single { semester -> semester.current } }
|
||||||
}
|
.flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) }
|
||||||
|
.map { items -> items.map { AttendanceItem(it) } }
|
||||||
|
.subscribeOn(schedulers.backgroundThread())
|
||||||
|
.observeOn(schedulers.mainThread())
|
||||||
|
.doFinally {
|
||||||
|
view?.run {
|
||||||
|
hideRefresh()
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
view?.apply {
|
||||||
|
updateData(it)
|
||||||
|
showEmpty(it.isEmpty())
|
||||||
|
showContent(it.isNotEmpty())
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
view?.run { showEmpty(isViewEmpty()) }
|
||||||
|
errorHandler.proceed(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadView() {
|
||||||
|
view?.apply {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
showEmpty(false)
|
||||||
|
clearData()
|
||||||
|
showNextButton(!currentDate.plusDays(1).isHolidays)
|
||||||
|
showPreButton(!currentDate.minusDays(1).isHolidays)
|
||||||
|
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,20 +9,20 @@ interface AttendanceView : BaseView {
|
|||||||
|
|
||||||
fun updateData(data: List<AttendanceItem>)
|
fun updateData(data: List<AttendanceItem>)
|
||||||
|
|
||||||
fun clearData()
|
|
||||||
|
|
||||||
fun updateNavigationDay(date: String)
|
fun updateNavigationDay(date: String)
|
||||||
|
|
||||||
|
fun clearData()
|
||||||
|
|
||||||
fun isViewEmpty(): Boolean
|
fun isViewEmpty(): Boolean
|
||||||
|
|
||||||
|
fun hideRefresh()
|
||||||
|
|
||||||
fun showEmpty(show: Boolean)
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun showRefresh(show: Boolean)
|
|
||||||
|
|
||||||
fun showPreButton(show: Boolean)
|
fun showPreButton(show: Boolean)
|
||||||
|
|
||||||
fun showNextButton(show: Boolean)
|
fun showNextButton(show: Boolean)
|
||||||
|
@ -26,14 +26,13 @@ class ExamDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragmentTheme)
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
arguments?.run {
|
arguments?.run {
|
||||||
exam = getSerializable(ARGUMENT_KEY) as Exam
|
exam = getSerializable(ARGUMENT_KEY) as Exam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
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)
|
return inflater.inflate(R.layout.dialog_exam, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,12 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.main.MainView
|
||||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||||
import kotlinx.android.synthetic.main.fragment_exam.*
|
import kotlinx.android.synthetic.main.fragment_exam.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ExamFragment : BaseFragment(), ExamView {
|
class ExamFragment : BaseFragment(), ExamView, MainView.MenuFragmentView {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: ExamPresenter
|
lateinit var presenter: ExamPresenter
|
||||||
@ -34,10 +35,8 @@ class ExamFragment : BaseFragment(), ExamView {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.run {
|
messageContainer = examRecycler
|
||||||
attachView(this@ExamFragment)
|
presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY))
|
||||||
loadData(date = savedInstanceState?.getLong(SAVED_DATE_KEY))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
@ -48,9 +47,13 @@ class ExamFragment : BaseFragment(), ExamView {
|
|||||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||||
adapter = examAdapter
|
adapter = examAdapter
|
||||||
}
|
}
|
||||||
examSwipe.setOnRefreshListener { presenter.loadData(date = null, forceRefresh = true) }
|
examSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||||
examPreviousButton.setOnClickListener { presenter.loadExamsForPreviousWeek() }
|
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
|
||||||
examNextButton.setOnClickListener { presenter.loadExamsForNextWeek()}
|
examNextButton.setOnClickListener { presenter.onNextWeek() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideRefresh() {
|
||||||
|
examSwipe.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(data: List<ExamItem>) {
|
override fun updateData(data: List<ExamItem>) {
|
||||||
@ -61,6 +64,16 @@ class ExamFragment : BaseFragment(), ExamView {
|
|||||||
examNavDate.text = date
|
examNavDate.text = date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearData() {
|
||||||
|
examAdapter.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isViewEmpty() = examAdapter.isEmpty
|
||||||
|
|
||||||
|
override fun onFragmentReselected() {
|
||||||
|
presenter.onViewReselected()
|
||||||
|
}
|
||||||
|
|
||||||
override fun showEmpty(show: Boolean) {
|
override fun showEmpty(show: Boolean) {
|
||||||
examEmpty.visibility = if (show) VISIBLE else GONE
|
examEmpty.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
@ -73,10 +86,6 @@ class ExamFragment : BaseFragment(), ExamView {
|
|||||||
examRecycler.visibility = if (show) VISIBLE else GONE
|
examRecycler.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showRefresh(show: Boolean) {
|
|
||||||
examSwipe.isRefreshing = show
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showPreButton(show: Boolean) {
|
override fun showPreButton(show: Boolean) {
|
||||||
examPreviousButton.visibility = if (show) VISIBLE else INVISIBLE
|
examPreviousButton.visibility = if (show) VISIBLE else INVISIBLE
|
||||||
}
|
}
|
||||||
@ -95,7 +104,7 @@ class ExamFragment : BaseFragment(), ExamView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,21 @@ import kotlinx.android.extensions.LayoutContainer
|
|||||||
import kotlinx.android.synthetic.main.header_exam.*
|
import kotlinx.android.synthetic.main.header_exam.*
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
|
||||||
class ExamHeader : AbstractHeaderItem<ExamHeader.ViewHolder>() {
|
class ExamHeader(private val date: LocalDate) : AbstractHeaderItem<ExamHeader.ViewHolder>() {
|
||||||
|
|
||||||
lateinit var date: LocalDate
|
override fun getLayoutRes() = R.layout.header_exam
|
||||||
|
|
||||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||||
return ViewHolder(view, adapter)
|
return ViewHolder(view, adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.header_exam
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder,
|
||||||
|
position: Int, payloads: MutableList<Any>?) {
|
||||||
|
holder.run {
|
||||||
|
examHeaderDay.text = date.weekDayName.capitalize()
|
||||||
|
examHeaderDate.text = date.toFormattedString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@ -37,21 +43,9 @@ class ExamHeader : AbstractHeaderItem<ExamHeader.ViewHolder>() {
|
|||||||
return date.hashCode()
|
return date.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder,
|
|
||||||
position: Int, payloads: MutableList<Any>?) {
|
|
||||||
holder.run {
|
|
||||||
examHeaderDay.text = date.weekDayName.capitalize()
|
|
||||||
examHeaderDate.text = date.toFormattedString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : ExpandableViewHolder(view, adapter),
|
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : ExpandableViewHolder(view, adapter),
|
||||||
LayoutContainer {
|
LayoutContainer {
|
||||||
|
|
||||||
init {
|
|
||||||
contentView.setOnClickListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val containerView: View
|
override val containerView: View
|
||||||
get() = contentView
|
get() = contentView
|
||||||
}
|
}
|
||||||
|
@ -13,21 +13,6 @@ import kotlinx.android.synthetic.main.item_exam.*
|
|||||||
|
|
||||||
class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<ExamItem.ViewHolder, ExamHeader>(header) {
|
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 getLayoutRes() = R.layout.item_exam
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ViewHolder {
|
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ViewHolder {
|
||||||
@ -43,6 +28,21 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||||
LayoutContainer {
|
LayoutContainer {
|
||||||
|
|
||||||
|
@ -3,15 +3,15 @@ package io.github.wulkanowy.ui.main.exam
|
|||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import io.github.wulkanowy.data.ErrorHandler
|
import io.github.wulkanowy.data.ErrorHandler
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
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.ExamRepository
|
||||||
import io.github.wulkanowy.data.repositories.SessionRepository
|
import io.github.wulkanowy.data.repositories.SessionRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.utils.isHolidays
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
|
||||||
import io.github.wulkanowy.utils.weekFirstDayNextOnWeekEnd
|
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDate.now
|
||||||
|
import org.threeten.bp.LocalDate.ofEpochDay
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ExamPresenter @Inject constructor(
|
class ExamPresenter @Inject constructor(
|
||||||
@ -21,79 +21,89 @@ class ExamPresenter @Inject constructor(
|
|||||||
private val sessionRepository: SessionRepository
|
private val sessionRepository: SessionRepository
|
||||||
) : BasePresenter<ExamView>(errorHandler) {
|
) : BasePresenter<ExamView>(errorHandler) {
|
||||||
|
|
||||||
var currentDate: LocalDate = LocalDate.now().weekFirstDayNextOnWeekEnd
|
lateinit var currentDate: LocalDate
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun attachView(view: ExamView) {
|
fun onAttachView(view: ExamView, date: Long?) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
|
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||||
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadExamsForPreviousWeek() = loadData(currentDate.minusDays(7).toEpochDay())
|
fun onPreviousWeek() {
|
||||||
|
loadData(currentDate.minusDays(7))
|
||||||
fun loadExamsForNextWeek() = loadData(currentDate.plusDays(7).toEpochDay())
|
reloadView()
|
||||||
|
|
||||||
fun loadData(date: Long?, forceRefresh: Boolean = false) {
|
|
||||||
this.currentDate = LocalDate.ofEpochDay(date
|
|
||||||
?: currentDate.weekFirstDayNextOnWeekEnd.toEpochDay())
|
|
||||||
if (currentDate.isHolidays) return
|
|
||||||
|
|
||||||
disposable.clear()
|
|
||||||
disposable.add(sessionRepository.getSemesters()
|
|
||||||
.map { selectSemester(it, -1) }
|
|
||||||
.flatMap { examRepository.getExams(it, currentDate, currentDate.plusDays(4), 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.toFormattedString("dd.MM") +
|
|
||||||
"-${currentDate.plusDays(4).toFormattedString("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<LocalDate, List<Exam>>): List<ExamItem> {
|
fun onNextWeek() {
|
||||||
return items.flatMap {
|
loadData(currentDate.plusDays(7))
|
||||||
val header = ExamHeader().apply { date = it.key }
|
reloadView()
|
||||||
it.value.reversed().map { item ->
|
}
|
||||||
ExamItem(header, item)
|
|
||||||
}
|
fun onSwipeRefresh() {
|
||||||
}
|
loadData(currentDate, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onExamItemSelected(item: AbstractFlexibleItem<*>?) {
|
fun onExamItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||||
if (item is ExamItem) view?.showExamDialog(item.exam)
|
if (item is ExamItem) view?.showExamDialog(item.exam)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectSemester(semesters: List<Semester>, index: Int): Semester {
|
fun onViewReselected() {
|
||||||
return semesters.single { it.current }.let { currentSemester ->
|
loadData(now().nextOrSameSchoolDay)
|
||||||
if (index == -1) currentSemester
|
reloadView()
|
||||||
else semesters.single { semester ->
|
}
|
||||||
semester.run {
|
|
||||||
semesterName - 1 == index && diaryId == currentSemester.diaryId
|
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
||||||
}
|
currentDate = date
|
||||||
|
disposable.apply {
|
||||||
|
clear()
|
||||||
|
add(sessionRepository.getSemesters()
|
||||||
|
.delay(200, MILLISECONDS)
|
||||||
|
.map { it.single { semester -> semester.current } }
|
||||||
|
.flatMap {
|
||||||
|
examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh)
|
||||||
|
}.map { it.groupBy { exam -> exam.date }.toSortedMap() }
|
||||||
|
.map { createExamItems(it) }
|
||||||
|
.subscribeOn(schedulers.backgroundThread())
|
||||||
|
.observeOn(schedulers.mainThread())
|
||||||
|
.doFinally {
|
||||||
|
view?.run {
|
||||||
|
hideRefresh()
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
view?.apply {
|
||||||
|
updateData(it)
|
||||||
|
showEmpty(it.isEmpty())
|
||||||
|
showContent(it.isNotEmpty())
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
view?.run { showEmpty(isViewEmpty()) }
|
||||||
|
errorHandler.proceed(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createExamItems(items: Map<LocalDate, List<Exam>>): List<ExamItem> {
|
||||||
|
return items.flatMap {
|
||||||
|
ExamHeader(it.key).let { header ->
|
||||||
|
it.value.reversed().map { item -> ExamItem(header, item) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun reloadView() {
|
||||||
|
view?.apply {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
showEmpty(false)
|
||||||
|
clearData()
|
||||||
|
showPreButton(!currentDate.minusDays(7).isHolidays)
|
||||||
|
showNextButton(!currentDate.plusDays(7).isHolidays)
|
||||||
|
updateNavigationWeek("${currentDate.toFormattedString("dd.MM")} - " +
|
||||||
|
currentDate.plusDays(4).toFormattedString("dd.MM"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,19 +9,23 @@ interface ExamView : BaseView {
|
|||||||
|
|
||||||
fun updateData(data: List<ExamItem>)
|
fun updateData(data: List<ExamItem>)
|
||||||
|
|
||||||
|
fun updateNavigationWeek(date: String)
|
||||||
|
|
||||||
|
fun clearData()
|
||||||
|
|
||||||
|
fun isViewEmpty(): Boolean
|
||||||
|
|
||||||
|
fun hideRefresh()
|
||||||
|
|
||||||
fun showEmpty(show: Boolean)
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun showRefresh(show: Boolean)
|
|
||||||
|
|
||||||
fun showNextButton(show: Boolean)
|
fun showNextButton(show: Boolean)
|
||||||
|
|
||||||
fun showPreButton(show: Boolean)
|
fun showPreButton(show: Boolean)
|
||||||
|
|
||||||
fun showExamDialog(exam: Exam)
|
fun showExamDialog(exam: Exam)
|
||||||
|
|
||||||
fun updateNavigationWeek(date: String)
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MenuFragmentView {
|
|||||||
lateinit var pagerAdapter: BasePagerAdapter
|
lateinit var pagerAdapter: BasePagerAdapter
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER"
|
||||||
|
|
||||||
fun newInstance() = GradeFragment()
|
fun newInstance() = GradeFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MenuFragmentView {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.attachView(this)
|
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||||
@ -113,8 +115,13 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MenuFragmentView {
|
|||||||
(pagerAdapter.registeredFragments[index] as? GradeView.GradeChildView)?.onParentChangeSemester()
|
(pagerAdapter.registeredFragments[index] as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putInt(SAVED_SEMESTER_KEY, presenter.selectedIndex)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.SessionRepository
|
|||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
||||||
import io.reactivex.Completable
|
import io.reactivex.Completable
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GradePresenter @Inject constructor(
|
class GradePresenter @Inject constructor(
|
||||||
@ -14,16 +14,18 @@ class GradePresenter @Inject constructor(
|
|||||||
private val schedulers: SchedulersManager,
|
private val schedulers: SchedulersManager,
|
||||||
private val sessionRepository: SessionRepository) : BasePresenter<GradeView>(errorHandler) {
|
private val sessionRepository: SessionRepository) : BasePresenter<GradeView>(errorHandler) {
|
||||||
|
|
||||||
private var semesters = emptyList<Semester>()
|
var selectedIndex = 0
|
||||||
|
private set
|
||||||
|
|
||||||
private var selectedIndex = 0
|
private var semesters = emptyList<Semester>()
|
||||||
|
|
||||||
private val loadedSemesterId = mutableMapOf<Int, String>()
|
private val loadedSemesterId = mutableMapOf<Int, String>()
|
||||||
|
|
||||||
override fun attachView(view: GradeView) {
|
fun onAttachView(view: GradeView, savedIndex: Int?) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
disposable.add(Completable.timer(150, TimeUnit.MILLISECONDS, schedulers.mainThread())
|
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread())
|
||||||
.subscribe {
|
.subscribe {
|
||||||
|
selectedIndex = savedIndex ?: 0
|
||||||
view.initView()
|
view.initView()
|
||||||
loadData()
|
loadData()
|
||||||
})
|
})
|
||||||
@ -34,13 +36,13 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onSemesterSwitch(): Boolean {
|
fun onSemesterSwitch(): Boolean {
|
||||||
if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex)
|
if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex - 1)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSemesterSelected(index: Int) {
|
fun onSemesterSelected(index: Int) {
|
||||||
if (selectedIndex != index) {
|
if (selectedIndex != index - 1) {
|
||||||
selectedIndex = index
|
selectedIndex = index + 1
|
||||||
loadedSemesterId.clear()
|
loadedSemesterId.clear()
|
||||||
view?.let {
|
view?.let {
|
||||||
notifyChildrenSemesterChange()
|
notifyChildrenSemesterChange()
|
||||||
@ -67,9 +69,9 @@ class GradePresenter @Inject constructor(
|
|||||||
|
|
||||||
private fun loadData() {
|
private fun loadData() {
|
||||||
disposable.add(sessionRepository.getSemesters()
|
disposable.add(sessionRepository.getSemesters()
|
||||||
.map {
|
.doOnSuccess {
|
||||||
it.first { item -> item.current }.also { current ->
|
it.first { item -> item.current }.also { current ->
|
||||||
selectedIndex = current.semesterName - 1
|
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
||||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +83,7 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
||||||
semesters.first { it.semesterName == selectedIndex + 1 }.semesterId.also {
|
semesters.first { it.semesterName == selectedIndex }.semesterId.also {
|
||||||
if (forceRefresh || loadedSemesterId[index] != it) {
|
if (forceRefresh || loadedSemesterId[index] != it) {
|
||||||
view?.notifyChildLoadData(index, it, forceRefresh)
|
view?.notifyChildLoadData(index, it, forceRefresh)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ class GradeDetailsDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
arguments?.run {
|
arguments?.run {
|
||||||
grade = getSerializable(ARGUMENT_KEY) as Grade
|
grade = getSerializable(ARGUMENT_KEY) as Grade
|
||||||
}
|
}
|
||||||
@ -48,7 +49,7 @@ class GradeDetailsDialog : DialogFragment() {
|
|||||||
gradeDialogColorValue.text = getString(grade.colorStringId)
|
gradeDialogColorValue.text = getString(grade.colorStringId)
|
||||||
|
|
||||||
gradeDialogCommentValue.apply {
|
gradeDialogCommentValue.apply {
|
||||||
if (grade.comment.isEmpty()) {
|
if (grade.comment.isBlank()) {
|
||||||
visibility = GONE
|
visibility = GONE
|
||||||
gradeDialogComment.visibility = GONE
|
gradeDialogComment.visibility = GONE
|
||||||
} else text = grade.comment
|
} else text = grade.comment
|
||||||
@ -59,15 +60,15 @@ class GradeDetailsDialog : DialogFragment() {
|
|||||||
setBackgroundResource(grade.valueColor)
|
setBackgroundResource(grade.valueColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
gradeDialogTeacherValue.text = if (grade.teacher.isEmpty()) {
|
gradeDialogTeacherValue.text = if (grade.teacher.isBlank()) {
|
||||||
getString(R.string.all_no_data)
|
getString(R.string.all_no_data)
|
||||||
} else grade.teacher
|
} else grade.teacher
|
||||||
|
|
||||||
gradeDialogDescriptionValue.text = grade.run {
|
gradeDialogDescriptionValue.text = grade.run {
|
||||||
when {
|
when {
|
||||||
description.isEmpty() && gradeSymbol.isNotEmpty() -> gradeSymbol
|
description.isBlank() && gradeSymbol.isNotBlank() -> gradeSymbol
|
||||||
description.isEmpty() && gradeSymbol.isEmpty() -> getString(R.string.all_no_description)
|
description.isBlank() && gradeSymbol.isBlank() -> getString(R.string.all_no_description)
|
||||||
gradeSymbol.isNotEmpty() && description.isNotEmpty() -> "$gradeSymbol - $description"
|
gradeSymbol.isNotBlank() && description.isNotBlank() -> "$gradeSymbol - $description"
|
||||||
else -> description
|
else -> description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,8 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.attachView(this)
|
messageContainer = gradeDetailsRecycler
|
||||||
|
presenter.onAttachView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
@ -47,6 +48,8 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
|||||||
setOnItemClickListener { presenter.onGradeItemSelected(getItem(it)) }
|
setOnItemClickListener { presenter.onGradeItemSelected(getItem(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gradeDetailsAdapter.getItemCountOfTypes()
|
||||||
|
|
||||||
gradeDetailsRecycler.run {
|
gradeDetailsRecycler.run {
|
||||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||||
adapter = gradeDetailsAdapter
|
adapter = gradeDetailsAdapter
|
||||||
@ -100,7 +103,7 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentLoadData(semesterId: String, forceRefresh: Boolean) {
|
override fun onParentLoadData(semesterId: String, forceRefresh: Boolean) {
|
||||||
presenter.loadData(semesterId, forceRefresh)
|
presenter.onParentViewLoadData(semesterId, forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentReselected() {
|
override fun onParentReselected() {
|
||||||
@ -108,7 +111,7 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentChangeSemester() {
|
override fun onParentChangeSemester() {
|
||||||
presenter.onParentChangeSemester()
|
presenter.onParentViewChangeSemester()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyParentDataLoaded(semesterId: String) {
|
override fun notifyParentDataLoaded(semesterId: String) {
|
||||||
@ -129,6 +132,6 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class GradeDetailsItem(val grade: Grade, private val weightString: String, priva
|
|||||||
text = grade.entry
|
text = grade.entry
|
||||||
setBackgroundResource(valueColor)
|
setBackgroundResource(valueColor)
|
||||||
}
|
}
|
||||||
gradeItemDescription.text = if (grade.description.isNotEmpty()) grade.description else grade.gradeSymbol
|
gradeItemDescription.text = if (grade.description.isNotBlank()) grade.description else grade.gradeSymbol
|
||||||
gradeItemDate.text = grade.date.toFormattedString()
|
gradeItemDate.text = grade.date.toFormattedString()
|
||||||
gradeItemWeight.text = "$weightString: ${grade.weight}"
|
gradeItemWeight.text = "$weightString: ${grade.weight}"
|
||||||
gradeItemNote.visibility = if (grade.isNew) VISIBLE else GONE
|
gradeItemNote.visibility = if (grade.isNew) VISIBLE else GONE
|
||||||
|
@ -17,12 +17,12 @@ class GradeDetailsPresenter @Inject constructor(
|
|||||||
private val gradeRepository: GradeRepository,
|
private val gradeRepository: GradeRepository,
|
||||||
private val sessionRepository: SessionRepository) : BasePresenter<GradeDetailsView>(errorHandler) {
|
private val sessionRepository: SessionRepository) : BasePresenter<GradeDetailsView>(errorHandler) {
|
||||||
|
|
||||||
override fun attachView(view: GradeDetailsView) {
|
override fun onAttachView(view: GradeDetailsView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadData(semesterId: String, forceRefresh: Boolean) {
|
fun onParentViewLoadData(semesterId: String, forceRefresh: Boolean) {
|
||||||
disposable.add(sessionRepository.getSemesters()
|
disposable.add(sessionRepository.getSemesters()
|
||||||
.flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) }
|
.flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) }
|
||||||
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
|
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
|
||||||
@ -76,7 +76,7 @@ class GradeDetailsPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentChangeSemester() {
|
fun onParentViewChangeSemester() {
|
||||||
view?.run {
|
view?.run {
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
showRefresh(false)
|
showRefresh(false)
|
||||||
|
@ -33,7 +33,8 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.attachView(this)
|
messageContainer = gradeSummaryRecycler
|
||||||
|
presenter.onAttachView(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
@ -81,7 +82,7 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentLoadData(semesterId: String, forceRefresh: Boolean) {
|
override fun onParentLoadData(semesterId: String, forceRefresh: Boolean) {
|
||||||
presenter.loadData(semesterId, forceRefresh)
|
presenter.onParentViewLoadData(semesterId, forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentReselected() {
|
override fun onParentReselected() {
|
||||||
@ -89,7 +90,7 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onParentChangeSemester() {
|
override fun onParentChangeSemester() {
|
||||||
presenter.onParentChangeSemester()
|
presenter.onParentViewChangeSemester()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyParentDataLoaded(semesterId: String) {
|
override fun notifyParentDataLoaded(semesterId: String) {
|
||||||
@ -106,6 +107,6 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
presenter.onDetachView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ class GradeSummaryPresenter @Inject constructor(
|
|||||||
private val schedulers: SchedulersManager)
|
private val schedulers: SchedulersManager)
|
||||||
: BasePresenter<GradeSummaryView>(errorHandler) {
|
: BasePresenter<GradeSummaryView>(errorHandler) {
|
||||||
|
|
||||||
override fun attachView(view: GradeSummaryView) {
|
override fun onAttachView(view: GradeSummaryView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadData(semesterId: String, forceRefresh: Boolean) {
|
fun onParentViewLoadData(semesterId: String, forceRefresh: Boolean) {
|
||||||
disposable.add(sessionRepository.getSemesters()
|
disposable.add(sessionRepository.getSemesters()
|
||||||
.map { semester -> semester.first { it.semesterId == semesterId } }
|
.map { semester -> semester.first { it.semesterId == semesterId } }
|
||||||
.flatMap {
|
.flatMap {
|
||||||
@ -76,7 +76,7 @@ class GradeSummaryPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentChangeSemester() {
|
fun onParentViewChangeSemester() {
|
||||||
view?.run {
|
view?.run {
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
showRefresh(false)
|
showRefresh(false)
|
||||||
@ -110,12 +110,12 @@ class GradeSummaryPresenter @Inject constructor(
|
|||||||
|
|
||||||
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {
|
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {
|
||||||
return gradeSummary.run {
|
return gradeSummary.run {
|
||||||
finalGrade.isEmpty() && predictedGrade.isEmpty() && averages[subject] == null
|
finalGrade.isBlank() && predictedGrade.isBlank() && averages[subject] == null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatAverage(average: Double, defaultValue: String = "-- --"): String {
|
private fun formatAverage(average: Double, defaultValue: String = "-- --"): String {
|
||||||
return if (average == 0.0 || average.isNaN()) defaultValue
|
return if (average == 0.0) defaultValue
|
||||||
else format(FRANCE, "%.2f", average)
|
else format(FRANCE, "%.2f", average)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,13 @@ class TimetableDialog : DialogFragment() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragmentTheme)
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
arguments?.run {
|
arguments?.run {
|
||||||
lesson = getSerializable(ARGUMENT_KEY) as Timetable
|
lesson = getSerializable(ARGUMENT_KEY) as Timetable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
dialog.setTitle(getString(R.string.all_details))
|
|
||||||
return inflater.inflate(R.layout.dialog_timetable, container, false)
|
return inflater.inflate(R.layout.dialog_timetable, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,11 +10,12 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.main.MainView
|
||||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||||
import kotlinx.android.synthetic.main.fragment_timetable.*
|
import kotlinx.android.synthetic.main.fragment_timetable.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimetableFragment : BaseFragment(), TimetableView {
|
class TimetableFragment : BaseFragment(), TimetableView, MainView.MenuFragmentView {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: TimetablePresenter
|
lateinit var presenter: TimetablePresenter
|
||||||
@ -24,6 +25,7 @@ class TimetableFragment : BaseFragment(), TimetableView {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||||
|
|
||||||
fun newInstance() = TimetableFragment()
|
fun newInstance() = TimetableFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,25 +35,22 @@ class TimetableFragment : BaseFragment(), TimetableView {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
presenter.run {
|
messageContainer = timetableRecycler
|
||||||
attachView(this@TimetableFragment)
|
presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY))
|
||||||
loadData(date = savedInstanceState?.getLong(SAVED_DATE_KEY))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
timetableAdapter.run {
|
timetableAdapter.run {
|
||||||
isAutoCollapseOnExpand = true
|
setOnItemClickListener { presenter.onTimetableItemSelected(getItem(it)) }
|
||||||
isAutoScrollOnExpand = true
|
|
||||||
setOnItemClickListener { presenter.onTimetableItemSelected(getItem(it))}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timetableRecycler.run {
|
timetableRecycler.run {
|
||||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||||
adapter = timetableAdapter
|
adapter = timetableAdapter
|
||||||
}
|
}
|
||||||
timetableSwipe.setOnRefreshListener { presenter.loadData(date = null, forceRefresh = true) }
|
timetableSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||||
timetablePreviousButton.setOnClickListener { presenter.loadTimetableForPreviousDay() }
|
timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() }
|
||||||
timetableNextButton.setOnClickListener { presenter.loadTimetableForNextDay() }
|
timetableNextButton.setOnClickListener { presenter.onNextDay() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(data: List<TimetableItem>) {
|
override fun updateData(data: List<TimetableItem>) {
|
||||||
@ -68,6 +67,14 @@ class TimetableFragment : BaseFragment(), TimetableView {
|
|||||||
|
|
||||||
override fun isViewEmpty() = timetableAdapter.isEmpty
|
override fun isViewEmpty() = timetableAdapter.isEmpty
|
||||||
|
|
||||||
|
override fun hideRefresh() {
|
||||||
|
timetableSwipe.isRefreshing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentReselected() {
|
||||||
|
presenter.onViewReselected()
|
||||||
|
}
|
||||||
|
|
||||||
override fun showEmpty(show: Boolean) {
|
override fun showEmpty(show: Boolean) {
|
||||||
timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
@ -80,10 +87,6 @@ class TimetableFragment : BaseFragment(), TimetableView {
|
|||||||
timetableRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
timetableRecycler.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showRefresh(show: Boolean) {
|
|
||||||
timetableSwipe.isRefreshing = show
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showPreButton(show: Boolean) {
|
override fun showPreButton(show: Boolean) {
|
||||||
timetablePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
timetablePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||||
}
|
}
|
||||||
@ -96,13 +99,15 @@ class TimetableFragment : BaseFragment(), TimetableView {
|
|||||||
TimetableDialog.newInstance(lesson).show(fragmentManager, lesson.toString())
|
TimetableDialog.newInstance(lesson).show(fragmentManager, lesson.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun roomString() = getString(R.string.timetable_room)
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
presenter.detachView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,29 @@ import io.github.wulkanowy.utils.toFormattedString
|
|||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.item_timetable.*
|
import kotlinx.android.synthetic.main.item_timetable.*
|
||||||
|
|
||||||
class TimetableItem : AbstractFlexibleItem<TimetableItem.ViewHolder>() {
|
class TimetableItem(val lesson: Timetable, private val roomText: String)
|
||||||
|
: AbstractFlexibleItem<TimetableItem.ViewHolder>() {
|
||||||
|
|
||||||
lateinit var lesson: Timetable
|
override fun getLayoutRes(): Int = R.layout.item_timetable
|
||||||
|
|
||||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||||
return ViewHolder(view, adapter)
|
return ViewHolder(view, adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLayoutRes(): Int = R.layout.item_timetable
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
||||||
|
position: Int, payloads: MutableList<Any>?) {
|
||||||
|
holder.apply {
|
||||||
|
timetableItemNumber.text = lesson.number.toString()
|
||||||
|
timetableItemSubject.text = lesson.subject
|
||||||
|
timetableItemRoom.text = if (lesson.room.isNotBlank()) "$roomText ${lesson.room}" else ""
|
||||||
|
timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}"
|
||||||
|
timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE
|
||||||
|
timetableItemSubject.paintFlags =
|
||||||
|
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||||
|
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
@ -39,27 +53,10 @@ class TimetableItem : AbstractFlexibleItem<TimetableItem.ViewHolder>() {
|
|||||||
return lesson.hashCode()
|
return lesson.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
|
|
||||||
position: Int, payloads: MutableList<Any>?) {
|
|
||||||
holder.bind(lesson)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
|
||||||
LayoutContainer {
|
LayoutContainer {
|
||||||
|
|
||||||
override val containerView: View
|
override val containerView: View
|
||||||
get() = contentView
|
get() = contentView
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
fun bind(lesson: Timetable) {
|
|
||||||
timetableItemNumber.text = lesson.number.toString()
|
|
||||||
timetableItemSubject.text = lesson.subject
|
|
||||||
timetableItemRoom.text = if (lesson.room.isNotBlank()) "${view.context.getString(R.string.timetable_room)} ${lesson.room}" else ""
|
|
||||||
timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}"
|
|
||||||
timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE
|
|
||||||
timetableItemSubject.paintFlags =
|
|
||||||
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
|
||||||
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,15 @@ package io.github.wulkanowy.ui.main.timetable
|
|||||||
|
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import io.github.wulkanowy.data.ErrorHandler
|
import io.github.wulkanowy.data.ErrorHandler
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
|
||||||
import io.github.wulkanowy.data.repositories.SessionRepository
|
import io.github.wulkanowy.data.repositories.SessionRepository
|
||||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
import io.github.wulkanowy.utils.schedulers.SchedulersManager
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDate.now
|
||||||
|
import org.threeten.bp.LocalDate.ofEpochDay
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimetablePresenter @Inject constructor(
|
class TimetablePresenter @Inject constructor(
|
||||||
@ -19,75 +20,78 @@ class TimetablePresenter @Inject constructor(
|
|||||||
private val sessionRepository: SessionRepository
|
private val sessionRepository: SessionRepository
|
||||||
) : BasePresenter<TimetableView>(errorHandler) {
|
) : BasePresenter<TimetableView>(errorHandler) {
|
||||||
|
|
||||||
var currentDate: LocalDate = LocalDate.now().nearSchoolDayNextOnWeekEnd
|
lateinit var currentDate: LocalDate
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun attachView(view: TimetableView) {
|
fun onAttachView(view: TimetableView, date: Long?) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
|
loadData(ofEpochDay(date ?: now().nextOrSameSchoolDay.toEpochDay()))
|
||||||
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadTimetableForPreviousDay() = loadData(currentDate.previousWorkDay.toEpochDay())
|
fun onPreviousDay() {
|
||||||
|
loadData(currentDate.previousSchoolDay)
|
||||||
fun loadTimetableForNextDay() = loadData(currentDate.nextWorkDay.toEpochDay())
|
reloadView()
|
||||||
|
|
||||||
fun loadData(date: Long?, forceRefresh: Boolean = false) {
|
|
||||||
this.currentDate = LocalDate.ofEpochDay(date ?: currentDate.nearSchoolDayNextOnWeekEnd.toEpochDay())
|
|
||||||
if (currentDate.isHolidays) return
|
|
||||||
|
|
||||||
disposable.clear()
|
|
||||||
disposable.add(sessionRepository.getSemesters()
|
|
||||||
.map { selectSemester(it, -1) }
|
|
||||||
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
|
|
||||||
.map { createTimetableItems(it) }
|
|
||||||
.subscribeOn(schedulers.backgroundThread())
|
|
||||||
.observeOn(schedulers.mainThread())
|
|
||||||
.doOnSubscribe {
|
|
||||||
view?.run {
|
|
||||||
showRefresh(forceRefresh)
|
|
||||||
showProgress(!forceRefresh)
|
|
||||||
if (!forceRefresh) clearData()
|
|
||||||
showPreButton(!currentDate.minusDays(1).isHolidays)
|
|
||||||
showNextButton(!currentDate.plusDays(1).isHolidays)
|
|
||||||
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.doFinally {
|
|
||||||
view?.run {
|
|
||||||
showRefresh(false)
|
|
||||||
showProgress(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.subscribe({
|
|
||||||
view?.run {
|
|
||||||
showEmpty(it.isEmpty())
|
|
||||||
showContent(it.isNotEmpty())
|
|
||||||
updateData(it)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
view?.run { showEmpty(isViewEmpty()) }
|
|
||||||
errorHandler.proceed(it)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTimetableItems(items: List<Timetable>): List<TimetableItem> {
|
fun onNextDay() {
|
||||||
return items.map {
|
loadData(currentDate.nextSchoolDay)
|
||||||
TimetableItem().apply { lesson = it }
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onSwipeRefresh() {
|
||||||
|
loadData(currentDate, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onViewReselected() {
|
||||||
|
loadData(now().nextOrSameSchoolDay)
|
||||||
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) {
|
fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||||
if (item is TimetableItem) view?.showTimetableDialog(item.lesson)
|
if (item is TimetableItem) view?.showTimetableDialog(item.lesson)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectSemester(semesters: List<Semester>, index: Int): Semester {
|
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
||||||
return semesters.single { it.current }.let { currentSemester ->
|
currentDate = date
|
||||||
if (index == -1) currentSemester
|
disposable.apply {
|
||||||
else semesters.single { semester ->
|
clear()
|
||||||
semester.run {
|
add(sessionRepository.getSemesters()
|
||||||
semesterName - 1 == index && diaryId == currentSemester.diaryId
|
.delay(200, MILLISECONDS)
|
||||||
}
|
.map { it.single { semester -> semester.current } }
|
||||||
}
|
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
|
||||||
|
.map { items -> items.map { TimetableItem(it, view?.roomString().orEmpty()) } }
|
||||||
|
.subscribeOn(schedulers.backgroundThread())
|
||||||
|
.observeOn(schedulers.mainThread())
|
||||||
|
.doFinally {
|
||||||
|
view?.run {
|
||||||
|
hideRefresh()
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
view?.apply {
|
||||||
|
updateData(it)
|
||||||
|
showEmpty(it.isEmpty())
|
||||||
|
showContent(it.isNotEmpty())
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
view?.run { showEmpty(isViewEmpty()) }
|
||||||
|
errorHandler.proceed(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadView() {
|
||||||
|
view?.apply {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
showEmpty(false)
|
||||||
|
clearData()
|
||||||
|
showNextButton(!currentDate.plusDays(1).isHolidays)
|
||||||
|
showPreButton(!currentDate.minusDays(1).isHolidays)
|
||||||
|
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,23 @@ interface TimetableView : BaseView {
|
|||||||
|
|
||||||
fun updateNavigationDay(date: String)
|
fun updateNavigationDay(date: String)
|
||||||
|
|
||||||
|
fun isViewEmpty(): Boolean
|
||||||
|
|
||||||
|
fun clearData()
|
||||||
|
|
||||||
|
fun hideRefresh()
|
||||||
|
|
||||||
fun showEmpty(show: Boolean)
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun showRefresh(show: Boolean)
|
|
||||||
|
|
||||||
fun showPreButton(show: Boolean)
|
fun showPreButton(show: Boolean)
|
||||||
|
|
||||||
fun showNextButton(show: Boolean)
|
fun showNextButton(show: Boolean)
|
||||||
|
|
||||||
fun showTimetableDialog(lesson: Timetable)
|
fun showTimetableDialog(lesson: Timetable)
|
||||||
|
|
||||||
fun isViewEmpty(): Boolean
|
fun roomString(): String
|
||||||
fun clearData()
|
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,7 @@ class SplashActivity : BaseActivity(), SplashView {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
presenter.attachView(this)
|
presenter.onAttachView(this)
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
presenter.detachView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openLoginView() {
|
override fun openLoginView() {
|
||||||
@ -30,4 +25,9 @@ class SplashActivity : BaseActivity(), SplashView {
|
|||||||
startActivity(MainActivity.getStartIntent(this))
|
startActivity(MainActivity.getStartIntent(this))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
presenter.onDetachView()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ class SplashPresenter @Inject constructor(private val sessionRepository: Session
|
|||||||
errorHandler: ErrorHandler)
|
errorHandler: ErrorHandler)
|
||||||
: BasePresenter<SplashView>(errorHandler) {
|
: BasePresenter<SplashView>(errorHandler) {
|
||||||
|
|
||||||
override fun attachView(view: SplashView) {
|
override fun onAttachView(view: SplashView) {
|
||||||
super.attachView(view)
|
super.onAttachView(view)
|
||||||
view.run { if (sessionRepository.isSessionSaved) openMainView() else openLoginView() }
|
view.run { if (sessionRepository.isSessionSaved) openMainView() else openLoginView() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ fun List<Grade>.calcAverage(): Double {
|
|||||||
fun List<GradeSummary>.calcAverage(): Double {
|
fun List<GradeSummary>.calcAverage(): Double {
|
||||||
return asSequence().mapNotNull {
|
return asSequence().mapNotNull {
|
||||||
if (it.finalGrade.matches("[0-6]".toRegex())) it.finalGrade.toDouble() else null
|
if (it.finalGrade.matches("[0-6]".toRegex())) it.finalGrade.toDouble() else null
|
||||||
}.average()
|
}.average().let { if (it.isNaN()) 0.0 else it }
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val Grade.valueColor: Int
|
inline val Grade.valueColor: Int
|
||||||
@ -43,6 +43,7 @@ inline val Grade.colorStringId: Int
|
|||||||
"F04C4C" -> R.string.all_red
|
"F04C4C" -> R.string.all_red
|
||||||
"20A4F7" -> R.string.all_blue
|
"20A4F7" -> R.string.all_blue
|
||||||
"6ECD07" -> R.string.all_green
|
"6ECD07" -> R.string.all_green
|
||||||
|
"B16CF1" -> R.string.all_purple
|
||||||
else -> R.string.all_empty_color
|
else -> R.string.all_empty_color
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,23 +1,24 @@
|
|||||||
package io.github.wulkanowy.utils
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
import org.threeten.bp.DayOfWeek.*
|
import org.threeten.bp.DayOfWeek.*
|
||||||
|
import org.threeten.bp.Instant
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import org.threeten.bp.LocalDateTime
|
import org.threeten.bp.LocalDateTime
|
||||||
|
import org.threeten.bp.ZoneId
|
||||||
import org.threeten.bp.format.DateTimeFormatter
|
import org.threeten.bp.format.DateTimeFormatter
|
||||||
import org.threeten.bp.format.DateTimeFormatter.ofPattern
|
import org.threeten.bp.format.DateTimeFormatter.ofPattern
|
||||||
import org.threeten.bp.temporal.TemporalAdjusters
|
|
||||||
import org.threeten.bp.temporal.TemporalAdjusters.*
|
import org.threeten.bp.temporal.TemporalAdjusters.*
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private const val DATE_PATTERN = "yyyy-MM-dd"
|
private const val DATE_PATTERN = "dd.MM.yyyy"
|
||||||
|
|
||||||
fun Date.toLocalDate(): LocalDate {
|
fun Date.toLocalDate(): LocalDate {
|
||||||
return LocalDate.parse(SimpleDateFormat(DATE_PATTERN, Locale.getDefault()).format(this))
|
return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Date.toLocalDateTime(): LocalDateTime = LocalDateTime.parse(SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
|
fun Date.toLocalDateTime(): LocalDateTime {
|
||||||
Locale.getDefault()).format(this), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
return Instant.ofEpochMilli(this.time).atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||||
|
}
|
||||||
|
|
||||||
fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate {
|
fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate {
|
||||||
return LocalDate.parse(this, DateTimeFormatter.ofPattern(format))
|
return LocalDate.parse(this, DateTimeFormatter.ofPattern(format))
|
||||||
@ -25,9 +26,9 @@ fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate {
|
|||||||
|
|
||||||
fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format))
|
fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format))
|
||||||
|
|
||||||
fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(DateTimeFormatter.ofPattern(format))
|
fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format))
|
||||||
|
|
||||||
inline val LocalDate.nextWorkDay: LocalDate
|
inline val LocalDate.nextSchoolDay: LocalDate
|
||||||
get() {
|
get() {
|
||||||
return when (this.dayOfWeek) {
|
return when (this.dayOfWeek) {
|
||||||
FRIDAY, SATURDAY, SUNDAY -> this.with(next(MONDAY))
|
FRIDAY, SATURDAY, SUNDAY -> this.with(next(MONDAY))
|
||||||
@ -35,7 +36,7 @@ inline val LocalDate.nextWorkDay: LocalDate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val LocalDate.previousWorkDay: LocalDate
|
inline val LocalDate.previousSchoolDay: LocalDate
|
||||||
get() {
|
get() {
|
||||||
return when (this.dayOfWeek) {
|
return when (this.dayOfWeek) {
|
||||||
SATURDAY, SUNDAY, MONDAY -> this.with(previous(FRIDAY))
|
SATURDAY, SUNDAY, MONDAY -> this.with(previous(FRIDAY))
|
||||||
@ -43,7 +44,15 @@ inline val LocalDate.previousWorkDay: LocalDate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val LocalDate.nearSchoolDayPrevOnWeekEnd: LocalDate
|
inline val LocalDate.nextOrSameSchoolDay: LocalDate
|
||||||
|
get() {
|
||||||
|
return when (this.dayOfWeek) {
|
||||||
|
SATURDAY, SUNDAY -> this.with(next(MONDAY))
|
||||||
|
else -> this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline val LocalDate.previousOrSameSchoolDay: LocalDate
|
||||||
get() {
|
get() {
|
||||||
return when (this.dayOfWeek) {
|
return when (this.dayOfWeek) {
|
||||||
SATURDAY, SUNDAY -> this.with(previous(FRIDAY))
|
SATURDAY, SUNDAY -> this.with(previous(FRIDAY))
|
||||||
@ -51,27 +60,14 @@ inline val LocalDate.nearSchoolDayPrevOnWeekEnd: LocalDate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline val LocalDate.nearSchoolDayNextOnWeekEnd: LocalDate
|
|
||||||
get() {
|
|
||||||
return when (this.dayOfWeek) {
|
|
||||||
SATURDAY, SUNDAY -> this.with(next(MONDAY))
|
|
||||||
else -> this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline val LocalDate.weekDayName: String
|
inline val LocalDate.weekDayName: String
|
||||||
get() = this.format(ofPattern("EEEE", Locale.getDefault()))
|
get() = this.format(ofPattern("EEEE", Locale.getDefault()))
|
||||||
|
|
||||||
inline val LocalDate.weekFirstDayAlwaysCurrent: LocalDate
|
inline val LocalDate.monday: LocalDate
|
||||||
get() = this.with(TemporalAdjusters.previousOrSame(MONDAY))
|
get() = this.with(MONDAY)
|
||||||
|
|
||||||
inline val LocalDate.weekFirstDayNextOnWeekEnd: LocalDate
|
inline val LocalDate.friday: LocalDate
|
||||||
get() {
|
get() = this.with(FRIDAY)
|
||||||
return when (this.dayOfWeek) {
|
|
||||||
SATURDAY, SUNDAY -> this.with(next(MONDAY))
|
|
||||||
else -> this.with(previousOrSame(MONDAY))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335)
|
* [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335)
|
||||||
|
@ -11,7 +11,7 @@ import android.security.KeyPairGeneratorSpec
|
|||||||
import android.security.keystore.KeyGenParameterSpec
|
import android.security.keystore.KeyGenParameterSpec
|
||||||
import android.security.keystore.KeyProperties.*
|
import android.security.keystore.KeyProperties.*
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Base64.DEFAULT
|
import android.util.Base64.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
@ -20,8 +20,8 @@ import java.nio.charset.Charset
|
|||||||
import java.security.KeyPairGenerator
|
import java.security.KeyPairGenerator
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.Calendar.YEAR
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.Cipher.DECRYPT_MODE
|
import javax.crypto.Cipher.DECRYPT_MODE
|
||||||
import javax.crypto.Cipher.ENCRYPT_MODE
|
import javax.crypto.Cipher.ENCRYPT_MODE
|
||||||
@ -30,131 +30,114 @@ import javax.crypto.CipherOutputStream
|
|||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
object Scrambler {
|
private const val KEY_ALIAS = "USER_PASSWORD"
|
||||||
|
|
||||||
private const val KEY_ALIAS = "USER_PASSWORD"
|
private const val ALGORITHM_RSA = "RSA"
|
||||||
|
|
||||||
private const val ALGORITHM_RSA = "RSA"
|
private const val KEYSTORE_NAME = "AndroidKeyStore"
|
||||||
|
|
||||||
private const val KEYSTORE_NAME = "AndroidKeyStore"
|
private const val KEY_TRANSFORMATION_ALGORITHM = "RSA/ECB/PKCS1Padding"
|
||||||
|
|
||||||
private const val KEY_TRANSFORMATION_ALGORITHM = "RSA/ECB/PKCS1Padding"
|
private const val KEY_CIPHER_JELLY_PROVIDER = "AndroidOpenSSL"
|
||||||
|
|
||||||
private const val KEY_CIPHER_JELLY_PROVIDER = "AndroidOpenSSL"
|
private const val KEY_CIPHER_M_PROVIDER = "AndroidKeyStoreBCWorkaround"
|
||||||
|
|
||||||
private const val KEY_CIPHER_M_PROVIDER = "AndroidKeyStoreBCWorkaround"
|
private val KEY_CHARSET = Charset.forName("UTF-8")
|
||||||
|
|
||||||
private val KEY_CHARSET = Charset.forName("UTF-8")
|
private val isKeyPairExists: Boolean
|
||||||
|
get() = keyStore.getKey(KEY_ALIAS, null) != null
|
||||||
|
|
||||||
@JvmStatic
|
private val cipher: Cipher
|
||||||
fun encrypt(plainText: String, context: Context): String {
|
get() {
|
||||||
if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty")
|
|
||||||
|
|
||||||
if (SDK_INT < JELLY_BEAN_MR2) {
|
|
||||||
return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
|
||||||
if (!isKeyPairExist()) generateKeyPair(context)
|
|
||||||
|
|
||||||
val cipher = getCipher()
|
|
||||||
cipher.init(ENCRYPT_MODE, getPublicKey())
|
|
||||||
|
|
||||||
val outputStream = ByteArrayOutputStream()
|
|
||||||
val cipherOutputStream = CipherOutputStream(outputStream, cipher)
|
|
||||||
cipherOutputStream.write(plainText.toByteArray(KEY_CHARSET))
|
|
||||||
cipherOutputStream.close()
|
|
||||||
|
|
||||||
Base64.encodeToString(outputStream.toByteArray(), DEFAULT)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Timber.e(exception, "An error occurred while encrypting text")
|
|
||||||
String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun decrypt(cipherText: String): String {
|
|
||||||
if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty")
|
|
||||||
|
|
||||||
if (SDK_INT < JELLY_BEAN_MR2 || cipherText.length < 250) {
|
|
||||||
return String(Base64.decode(cipherText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isKeyPairExist()) throw ScramblerException("KeyPair doesn't exist")
|
|
||||||
|
|
||||||
try {
|
|
||||||
val cipher = getCipher()
|
|
||||||
cipher.init(DECRYPT_MODE, getPrivateKey())
|
|
||||||
|
|
||||||
val input = CipherInputStream(ByteArrayInputStream(Base64.decode(cipherText, DEFAULT)), cipher)
|
|
||||||
val values = ArrayList<Byte>()
|
|
||||||
|
|
||||||
var nextByte = 0
|
|
||||||
while ({ nextByte = input.read(); nextByte }() != -1) {
|
|
||||||
values.add(nextByte.toByte())
|
|
||||||
}
|
|
||||||
|
|
||||||
val bytes = ByteArray(values.size)
|
|
||||||
for (i in bytes.indices) {
|
|
||||||
bytes[i] = values[i]
|
|
||||||
}
|
|
||||||
return String(bytes, 0, bytes.size, KEY_CHARSET)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw ScramblerException("An error occurred while decrypting text", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getKeyStoreInstance(): KeyStore {
|
|
||||||
val keyStore = KeyStore.getInstance(KEYSTORE_NAME)
|
|
||||||
keyStore.load(null)
|
|
||||||
return keyStore
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPublicKey(): PublicKey =
|
|
||||||
(getKeyStoreInstance().getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry)
|
|
||||||
.certificate.publicKey
|
|
||||||
|
|
||||||
|
|
||||||
private fun getPrivateKey(): PrivateKey =
|
|
||||||
(getKeyStoreInstance().getEntry(KEY_ALIAS, null) as KeyStore.PrivateKeyEntry).privateKey
|
|
||||||
|
|
||||||
|
|
||||||
private fun getCipher(): Cipher {
|
|
||||||
return if (SDK_INT >= M) Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM, KEY_CIPHER_M_PROVIDER)
|
return if (SDK_INT >= M) Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM, KEY_CIPHER_M_PROVIDER)
|
||||||
else Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM, KEY_CIPHER_JELLY_PROVIDER)
|
else Cipher.getInstance(KEY_TRANSFORMATION_ALGORITHM, KEY_CIPHER_JELLY_PROVIDER)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(JELLY_BEAN_MR2)
|
private val keyStore: KeyStore
|
||||||
private fun generateKeyPair(context: Context) {
|
get() = KeyStore.getInstance(KEYSTORE_NAME).apply { load(null) }
|
||||||
val spec = if (SDK_INT >= M) {
|
|
||||||
KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT)
|
|
||||||
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
|
|
||||||
.setCertificateSubject(X500Principal("CN=Wulkanowy"))
|
|
||||||
.setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
|
|
||||||
.setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
|
|
||||||
.setCertificateSerialNumber(BigInteger.TEN)
|
|
||||||
.build()
|
|
||||||
} else {
|
|
||||||
val start = Calendar.getInstance()
|
|
||||||
val end = Calendar.getInstance()
|
|
||||||
end.add(Calendar.YEAR, 99)
|
|
||||||
|
|
||||||
KeyPairGeneratorSpec.Builder(context)
|
fun encrypt(plainText: String, context: Context): String {
|
||||||
.setAlias(KEY_ALIAS)
|
if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty")
|
||||||
.setSubject(X500Principal("CN=Wulkanowy"))
|
|
||||||
.setSerialNumber(BigInteger.TEN)
|
|
||||||
.setStartDate(start.time)
|
|
||||||
.setEndDate(end.time)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
val generator = KeyPairGenerator.getInstance(ALGORITHM_RSA, KEYSTORE_NAME)
|
if (SDK_INT < JELLY_BEAN_MR2) {
|
||||||
generator.initialize(spec)
|
return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
|
||||||
generator.generateKeyPair()
|
}
|
||||||
|
|
||||||
Timber.i("A new KeyPair has been generated")
|
return try {
|
||||||
|
if (!isKeyPairExists) generateKeyPair(context)
|
||||||
|
cipher.let {
|
||||||
|
it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey)
|
||||||
|
|
||||||
|
ByteArrayOutputStream().let { output ->
|
||||||
|
CipherOutputStream(output, it).apply {
|
||||||
|
write(plainText.toByteArray(KEY_CHARSET))
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
encodeToString(output.toByteArray(), DEFAULT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Timber.e(exception, "An error occurred while encrypting text")
|
||||||
|
String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isKeyPairExist(): Boolean = getKeyStoreInstance().getKey(KEY_ALIAS, null) != null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun decrypt(cipherText: String): String {
|
||||||
|
if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty")
|
||||||
|
|
||||||
|
if (SDK_INT < JELLY_BEAN_MR2 || cipherText.length < 250) {
|
||||||
|
return String(decode(cipherText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isKeyPairExists) throw ScramblerException("KeyPair doesn't exist")
|
||||||
|
|
||||||
|
return try {
|
||||||
|
cipher.let {
|
||||||
|
it.init(DECRYPT_MODE, (keyStore.getKey(KEY_ALIAS, null) as PrivateKey))
|
||||||
|
|
||||||
|
CipherInputStream(ByteArrayInputStream(decode(cipherText, DEFAULT)), it).let { input ->
|
||||||
|
val values = ArrayList<Byte>()
|
||||||
|
var nextByte = 0
|
||||||
|
while ({ nextByte = input.read(); nextByte }() != -1) {
|
||||||
|
values.add(nextByte.toByte())
|
||||||
|
}
|
||||||
|
val bytes = ByteArray(values.size)
|
||||||
|
for (i in bytes.indices) {
|
||||||
|
bytes[i] = values[i]
|
||||||
|
}
|
||||||
|
String(bytes, 0, bytes.size, KEY_CHARSET)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw ScramblerException("An error occurred while decrypting text", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(JELLY_BEAN_MR2)
|
||||||
|
private fun generateKeyPair(context: Context) {
|
||||||
|
(if (SDK_INT >= M) {
|
||||||
|
KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT)
|
||||||
|
.setDigests(DIGEST_SHA256, DIGEST_SHA512)
|
||||||
|
.setCertificateSubject(X500Principal("CN=Wulkanowy"))
|
||||||
|
.setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
|
||||||
|
.setSignaturePaddings(SIGNATURE_PADDING_RSA_PKCS1)
|
||||||
|
.setCertificateSerialNumber(BigInteger.TEN)
|
||||||
|
.build()
|
||||||
|
} else {
|
||||||
|
KeyPairGeneratorSpec.Builder(context)
|
||||||
|
.setAlias(KEY_ALIAS)
|
||||||
|
.setSubject(X500Principal("CN=Wulkanowy"))
|
||||||
|
.setSerialNumber(BigInteger.TEN)
|
||||||
|
.setStartDate(Calendar.getInstance().time)
|
||||||
|
.setEndDate(Calendar.getInstance().apply { add(YEAR, 99) }.time)
|
||||||
|
.build()
|
||||||
|
}).let {
|
||||||
|
KeyPairGenerator.getInstance(ALGORITHM_RSA, KEYSTORE_NAME).apply {
|
||||||
|
initialize(it)
|
||||||
|
genKeyPair()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timber.i("A new KeyPair has been generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -4,17 +4,24 @@
|
|||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="300dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minWidth="300dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendance_dialog_details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:text="@string/all_details"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/all_subject"
|
android:text="@string/all_subject"
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textSize="17sp" />
|
android:textSize="17sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -81,10 +88,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
android:padding="0dp"
|
|
||||||
android:text="@string/all_close"
|
android:text="@string/all_close"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textSize="15sp" />
|
android:textSize="15sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@ -4,18 +4,25 @@
|
|||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="300dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minWidth="300dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/exam_dialog_details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:text="@string/all_details"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/examDialogSubject"
|
android:id="@+id/examDialogSubject"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/all_subject"
|
android:text="@string/all_subject"
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textSize="17sp" />
|
android:textSize="17sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -33,7 +40,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:text="@string/exam_type"
|
android:text="@string/exam_type"
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textSize="17sp" />
|
android:textSize="17sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -102,8 +108,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="20dp"
|
||||||
android:padding="0dp"
|
|
||||||
android:text="@string/all_close"
|
android:text="@string/all_close"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textSize="15sp" />
|
android:textSize="15sp" />
|
||||||
|
@ -4,12 +4,20 @@
|
|||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="300dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minWidth="300dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/exam_dialog_details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:text="@string/all_details"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/timetableDialogChangesTitle"
|
android:id="@+id/timetableDialogChangesTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -33,7 +41,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:text="@string/timetable_lesson"
|
android:text="@string/timetable_lesson"
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textSize="17sp" />
|
android:textSize="17sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -119,7 +126,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
android:padding="0dp"
|
|
||||||
android:text="@string/all_close"
|
android:text="@string/all_close"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textSize="15sp" />
|
android:textSize="15sp" />
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<FrameLayout
|
<android.support.design.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_marginBottom="50dp">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/attendanceProgress"
|
android:id="@+id/attendanceProgress"
|
||||||
@ -42,8 +40,8 @@
|
|||||||
<android.support.v7.widget.AppCompatImageView
|
<android.support.v7.widget.AppCompatImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="100dp"
|
|
||||||
android:minWidth="100dp"
|
android:minWidth="100dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
app:srcCompat="@drawable/ic_menu_main_attendance_24dp"
|
app:srcCompat="@drawable/ic_menu_main_attendance_24dp"
|
||||||
app:tint="?android:attr/textColorPrimary"
|
app:tint="?android:attr/textColorPrimary"
|
||||||
tools:ignore="contentDescription" />
|
tools:ignore="contentDescription" />
|
||||||
@ -56,12 +54,14 @@
|
|||||||
android:text="@string/attendance_no_items"
|
android:text="@string/attendance_no_items"
|
||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</FrameLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="50dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:elevation="10dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -72,7 +72,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:gravity="start|center"
|
android:gravity="start|center"
|
||||||
android:text="@string/prev"
|
android:text="@string/all_prev"
|
||||||
android:textAlignment="gravity" />
|
android:textAlignment="gravity" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -91,9 +91,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:gravity="end|center"
|
android:gravity="end|center"
|
||||||
android:text="@string/next"
|
android:text="@string/all_next"
|
||||||
android:textAlignment="gravity" />
|
android:textAlignment="gravity" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
</LinearLayout>
|
|
||||||
|
@ -1,63 +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/attendance_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/attendance_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/attendance_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/attendance_tab_fragment_no_item_image"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@+id/attendance_tab_fragment_no_item_text"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:layout_marginTop="40dp"
|
|
||||||
android:minHeight="100dp"
|
|
||||||
android:minWidth="100dp"
|
|
||||||
app:tint="?android:attr/textColorPrimary"
|
|
||||||
app:srcCompat="@drawable/ic_menu_main_attendance_24dp"
|
|
||||||
tools:ignore="contentDescription" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/attendance_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/attendance_no_items"
|
|
||||||
android:textSize="20sp" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<android.support.v4.widget.SwipeRefreshLayout
|
|
||||||
android:id="@+id/attendance_tab_fragment_swipe_refresh"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
|
||||||
android:id="@+id/attendance_tab_fragment_recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
</android.support.v4.widget.SwipeRefreshLayout>
|
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
|
@ -1,15 +1,13 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<FrameLayout
|
<android.support.design.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_marginBottom="50dp">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/examProgress"
|
android:id="@+id/examProgress"
|
||||||
@ -42,8 +40,8 @@
|
|||||||
<android.support.v7.widget.AppCompatImageView
|
<android.support.v7.widget.AppCompatImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="100dp"
|
|
||||||
android:minWidth="100dp"
|
android:minWidth="100dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
app:srcCompat="@drawable/ic_menu_main_exam_24dp"
|
app:srcCompat="@drawable/ic_menu_main_exam_24dp"
|
||||||
app:tint="?android:attr/textColorPrimary"
|
app:tint="?android:attr/textColorPrimary"
|
||||||
tools:ignore="contentDescription" />
|
tools:ignore="contentDescription" />
|
||||||
@ -56,12 +54,14 @@
|
|||||||
android:text="@string/exam_no_items"
|
android:text="@string/exam_no_items"
|
||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</FrameLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="50dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:elevation="10dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -72,7 +72,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:gravity="start|center"
|
android:gravity="start|center"
|
||||||
android:text="@string/prev"
|
android:text="@string/all_prev"
|
||||||
android:textAlignment="gravity" />
|
android:textAlignment="gravity" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -91,9 +91,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:gravity="end|center"
|
android:gravity="end|center"
|
||||||
android:text="@string/next"
|
android:text="@string/all_next"
|
||||||
android:textAlignment="gravity" />
|
android:textAlignment="gravity" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
</LinearLayout>
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -7,7 +7,7 @@
|
|||||||
<android.support.design.widget.TabLayout
|
<android.support.design.widget.TabLayout
|
||||||
android:id="@+id/gradeTabLayout"
|
android:id="@+id/gradeTabLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="48dp"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
@ -21,13 +21,13 @@
|
|||||||
android:id="@+id/gradeViewPager"
|
android:id="@+id/gradeViewPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@id/gradeTabLayout"
|
android:layout_marginTop="48dp"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/gradeProgress"
|
android:id="@+id/gradeProgress"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_gravity="center"
|
||||||
android:indeterminate="true" />
|
android:indeterminate="true" />
|
||||||
</RelativeLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
@ -1,15 +1,13 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<FrameLayout
|
<android.support.design.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_marginBottom="50dp">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/timetableProgress"
|
android:id="@+id/timetableProgress"
|
||||||
@ -42,8 +40,8 @@
|
|||||||
<android.support.v7.widget.AppCompatImageView
|
<android.support.v7.widget.AppCompatImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="100dp"
|
|
||||||
android:minWidth="100dp"
|
android:minWidth="100dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
app:srcCompat="@drawable/ic_menu_main_timetable_24dp"
|
app:srcCompat="@drawable/ic_menu_main_timetable_24dp"
|
||||||
app:tint="?android:attr/textColorPrimary"
|
app:tint="?android:attr/textColorPrimary"
|
||||||
tools:ignore="contentDescription" />
|
tools:ignore="contentDescription" />
|
||||||
@ -56,12 +54,14 @@
|
|||||||
android:text="@string/timetable_no_items"
|
android:text="@string/timetable_no_items"
|
||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</FrameLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="50dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:elevation="10dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -72,7 +72,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:gravity="start|center"
|
android:gravity="start|center"
|
||||||
android:text="@string/prev"
|
android:text="@string/all_prev"
|
||||||
android:textAlignment="gravity" />
|
android:textAlignment="gravity" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -91,9 +91,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:gravity="end|center"
|
android:gravity="end|center"
|
||||||
android:text="@string/next"
|
android:text="@string/all_next"
|
||||||
android:textAlignment="gravity" />
|
android:textAlignment="gravity" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
</LinearLayout>
|
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/ic_all_divider"
|
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless">
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:paddingTop="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/attendance_header_day"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_marginEnd="15dp"
|
|
||||||
android:layout_marginRight="15dp"
|
|
||||||
android:layout_toLeftOf="@id/attendance_header_alert_image"
|
|
||||||
android:layout_toStartOf="@+id/attendance_header_alert_image"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textSize="19sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/attendance_header_date"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_below="@+id/attendance_header_day"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textColor="?android:attr/android:textColorSecondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/attendance_header_description"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_toEndOf="@+id/attendance_header_date"
|
|
||||||
android:layout_toRightOf="@+id/attendance_header_date"
|
|
||||||
android:layout_below="@+id/attendance_header_day"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_marginStart="10dp"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textColor="?android:attr/android:textColorSecondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/attendance_header_free_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginLeft="180dp"
|
|
||||||
android:layout_marginStart="180dp"
|
|
||||||
android:gravity="end"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:text="@string/attendance_no_items"
|
|
||||||
android:textColor="?android:attr/android:textColorSecondary"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/attendance_header_alert_image"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
app:srcCompat="@drawable/ic_all_note_24dp"
|
|
||||||
tools:ignore="contentDescription" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
</FrameLayout>
|
|
@ -1,19 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorControlHighlight"
|
android:background="?attr/colorControlHighlight"
|
||||||
android:minHeight="40dp"
|
android:minHeight="40dp"
|
||||||
android:paddingBottom="10dp"
|
android:orientation="horizontal"
|
||||||
android:paddingLeft="20dp"
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
android:paddingRight="20dp"
|
android:paddingRight="20dp"
|
||||||
android:paddingTop="10dp">
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/examHeaderDay"
|
android:id="@+id/examHeaderDay"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerVertical="true"
|
android:layout_weight="1"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp" />
|
||||||
|
|
||||||
@ -21,15 +22,10 @@
|
|||||||
android:id="@+id/examHeaderDate"
|
android:id="@+id/examHeaderDate"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:layout_toEndOf="@id/examHeaderDay"
|
android:layout_marginLeft="10dp"
|
||||||
android:layout_toRightOf="@id/examHeaderDay"
|
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorControlHighlight"
|
android:background="?attr/colorControlHighlight"
|
||||||
android:paddingBottom="7dp"
|
android:orientation="horizontal"
|
||||||
android:paddingLeft="20dp"
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
android:paddingRight="20dp"
|
android:paddingRight="20dp"
|
||||||
android:paddingTop="7dp">
|
android:paddingBottom="7dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/gradeSummaryHeaderName"
|
android:id="@+id/gradeSummaryHeaderName"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginEnd="40dp"
|
android:layout_marginEnd="40dp"
|
||||||
android:layout_marginRight="40dp"
|
android:layout_marginRight="40dp"
|
||||||
android:layout_toLeftOf="@id/gradeSummaryHeaderAverage"
|
android:layout_weight="1"
|
||||||
android:layout_toStartOf="@id/gradeSummaryHeaderAverage"
|
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="17sp" />
|
android:textSize="17sp" />
|
||||||
|
|
||||||
@ -24,11 +23,8 @@
|
|||||||
android:id="@+id/gradeSummaryHeaderAverage"
|
android:id="@+id/gradeSummaryHeaderAverage"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="12sp" />
|
android:textSize="12sp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/ic_all_divider"
|
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless">
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:paddingLeft="16dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:paddingTop="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/timetable_header_day"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_marginEnd="15dp"
|
|
||||||
android:layout_marginRight="15dp"
|
|
||||||
android:layout_toLeftOf="@id/timetable_header_alert_image"
|
|
||||||
android:layout_toStartOf="@+id/timetable_header_alert_image"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textSize="19sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/timetable_header_date"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_below="@+id/timetable_header_day"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textColor="?android:attr/android:textColorSecondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/timetable_header_free_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginLeft="180dp"
|
|
||||||
android:layout_marginStart="180dp"
|
|
||||||
android:gravity="end"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:text="@string/app_name"
|
|
||||||
android:textColor="?android:attr/android:textColorSecondary"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/timetable_header_alert_image"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
app:srcCompat="@drawable/ic_all_note_24dp"
|
|
||||||
tools:ignore="contentDescription" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
</FrameLayout>
|
|
@ -4,6 +4,7 @@
|
|||||||
android:id="@+id/attendanceItemContainer"
|
android:id="@+id/attendanceItemContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/ic_all_divider"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
android:foreground="?attr/selectableItemBackgroundBorderless"
|
||||||
android:paddingStart="12dp"
|
android:paddingStart="12dp"
|
||||||
android:paddingLeft="12dp"
|
android:paddingLeft="12dp"
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
android:id="@+id/exams_subitem_container"
|
android:id="@+id/exams_subitem_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/ic_all_divider"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless">
|
android:foreground="?attr/selectableItemBackgroundBorderless">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/examItemSubject"
|
android:id="@+id/examItemSubject"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="20dp"
|
|
||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="15sp" />
|
android:textSize="15sp" />
|
||||||
@ -18,11 +19,11 @@
|
|||||||
android:id="@+id/examItemType"
|
android:id="@+id/examItemType"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignLeft="@id/examItemSubject"
|
|
||||||
android:layout_alignStart="@id/examItemSubject"
|
|
||||||
android:layout_below="@id/examItemSubject"
|
android:layout_below="@id/examItemSubject"
|
||||||
android:layout_marginBottom="5dp"
|
android:layout_alignStart="@id/examItemSubject"
|
||||||
|
android:layout_alignLeft="@id/examItemSubject"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
@ -30,15 +31,15 @@
|
|||||||
android:id="@+id/examItemTeacher"
|
android:id="@+id/examItemTeacher"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/examItemSubject"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="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_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
android:layout_toEndOf="@id/examItemType"
|
android:layout_toEndOf="@id/examItemType"
|
||||||
android:layout_toRightOf="@id/examItemType"
|
android:layout_toRightOf="@id/examItemType"
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/ic_all_divider"
|
android:background="@drawable/ic_all_divider"
|
||||||
android:minHeight="35dp">
|
android:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/gradeSummaryItemTitle"
|
android:id="@+id/gradeSummaryItemTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerVertical="true"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginLeft="20dp"
|
|
||||||
android:layout_marginStart="20dp"
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
android:text="@string/grade_summary_predicted_grade"
|
android:text="@string/grade_summary_predicted_grade"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
@ -18,16 +20,12 @@
|
|||||||
android:id="@+id/gradeSummaryItemGrade"
|
android:id="@+id/gradeSummaryItemGrade"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginEnd="25dp"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_marginRight="25dp"
|
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:layout_toEndOf="@id/gradeSummaryItemTitle"
|
android:layout_marginLeft="10dp"
|
||||||
android:layout_toRightOf="@id/gradeSummaryItemTitle"
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textSize="12sp" />
|
android:textSize="12sp" />
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
android:id="@+id/timetable_subitem_container"
|
android:id="@+id/timetable_subitem_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/ic_all_divider"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
android:foreground="?attr/selectableItemBackgroundBorderless"
|
||||||
android:paddingBottom="7dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
android:paddingLeft="12dp"
|
|
||||||
android:paddingRight="12dp"
|
|
||||||
android:paddingStart="12dp"
|
android:paddingStart="12dp"
|
||||||
android:paddingTop="7dp">
|
android:paddingLeft="12dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingRight="12dp"
|
||||||
|
android:paddingBottom="7dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/timetableItemNumber"
|
android:id="@+id/timetableItemNumber"
|
||||||
@ -28,10 +29,10 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginEnd="40dp"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_marginRight="40dp"
|
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:layout_marginRight="40dp"
|
||||||
android:layout_toEndOf="@+id/timetableItemNumber"
|
android:layout_toEndOf="@+id/timetableItemNumber"
|
||||||
android:layout_toRightOf="@+id/timetableItemNumber"
|
android:layout_toRightOf="@+id/timetableItemNumber"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
@ -44,9 +45,9 @@
|
|||||||
android:id="@+id/timetableItemTime"
|
android:id="@+id/timetableItemTime"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBottom="@+id/timetableItemNumber"
|
|
||||||
android:layout_alignLeft="@id/timetableItemSubject"
|
|
||||||
android:layout_alignStart="@id/timetableItemSubject"
|
android:layout_alignStart="@id/timetableItemSubject"
|
||||||
|
android:layout_alignLeft="@id/timetableItemSubject"
|
||||||
|
android:layout_alignBottom="@+id/timetableItemNumber"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textColor="?android:attr/android:textColorSecondary"
|
android:textColor="?android:attr/android:textColorSecondary"
|
||||||
@ -57,10 +58,10 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBottom="@+id/timetableItemNumber"
|
android:layout_alignBottom="@+id/timetableItemNumber"
|
||||||
android:layout_marginEnd="40dp"
|
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
android:layout_marginRight="40dp"
|
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:layout_marginRight="40dp"
|
||||||
android:layout_toEndOf="@+id/timetableItemTime"
|
android:layout_toEndOf="@+id/timetableItemTime"
|
||||||
android:layout_toRightOf="@+id/timetableItemTime"
|
android:layout_toRightOf="@+id/timetableItemTime"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
|
@ -104,6 +104,8 @@
|
|||||||
<string name="all_cancel">Anuluj</string>
|
<string name="all_cancel">Anuluj</string>
|
||||||
<string name="all_no_data">Brak danych</string>
|
<string name="all_no_data">Brak danych</string>
|
||||||
<string name="all_subject">Przedmiot</string>
|
<string name="all_subject">Przedmiot</string>
|
||||||
|
<string name="all_prev">Poprzedni</string>
|
||||||
|
<string name="all_next">Następny</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Timetable Widget-->
|
<!--Timetable Widget-->
|
||||||
@ -157,6 +159,7 @@
|
|||||||
<string name="all_red">Czerwony</string>
|
<string name="all_red">Czerwony</string>
|
||||||
<string name="all_blue">Niebieski</string>
|
<string name="all_blue">Niebieski</string>
|
||||||
<string name="all_green">Zielony</string>
|
<string name="all_green">Zielony</string>
|
||||||
|
<string name="all_purple">Fioletowy</string>
|
||||||
<string name="all_empty_color">Brak koloru</string>
|
<string name="all_empty_color">Brak koloru</string>
|
||||||
|
|
||||||
|
|
||||||
@ -166,7 +169,4 @@
|
|||||||
<string name="all_sync_fail">Podczas synchronizacji wystąpił błąd</string>
|
<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_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="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>
|
</resources>
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
<!--Timetable-->
|
<!--Timetable-->
|
||||||
<string name="timetable_lesson">Lesson</string>
|
<string name="timetable_lesson">Lesson</string>
|
||||||
<string name="timetable_room">Room %s</string>
|
<string name="timetable_room">Room</string>
|
||||||
<string name="timetable_group">Group</string>
|
<string name="timetable_group">Group</string>
|
||||||
<string name="timetable_time">Hours</string>
|
<string name="timetable_time">Hours</string>
|
||||||
<string name="timetable_changes">Changes</string>
|
<string name="timetable_changes">Changes</string>
|
||||||
@ -99,6 +99,8 @@
|
|||||||
<string name="all_cancel">Cancel</string>
|
<string name="all_cancel">Cancel</string>
|
||||||
<string name="all_no_data">No data</string>
|
<string name="all_no_data">No data</string>
|
||||||
<string name="all_subject">Subject</string>
|
<string name="all_subject">Subject</string>
|
||||||
|
<string name="all_prev">Prev</string>
|
||||||
|
<string name="all_next">Next</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Timetable Widget-->
|
<!--Timetable Widget-->
|
||||||
@ -150,6 +152,7 @@
|
|||||||
<string name="all_red">Red</string>
|
<string name="all_red">Red</string>
|
||||||
<string name="all_blue">Blue</string>
|
<string name="all_blue">Blue</string>
|
||||||
<string name="all_green">Green</string>
|
<string name="all_green">Green</string>
|
||||||
|
<string name="all_purple">Purple</string>
|
||||||
<string name="all_empty_color">No color</string>
|
<string name="all_empty_color">No color</string>
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +162,4 @@
|
|||||||
<string name="all_sync_fail">There was an error during synchronization</string>
|
<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_timeout">Too long wait for connection</string>
|
||||||
<string name="all_login_failed">Login is failed. Try again or restart the app</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>
|
</resources>
|
||||||
|
@ -11,7 +11,6 @@ import org.junit.Assert.assertEquals
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import org.threeten.bp.LocalDateTime
|
|
||||||
import java.sql.Date
|
import java.sql.Date
|
||||||
|
|
||||||
class TimetableRemoteTest {
|
class TimetableRemoteTest {
|
||||||
@ -41,7 +40,7 @@ class TimetableRemoteTest {
|
|||||||
every { semesterMock.studentId } returns "1"
|
every { semesterMock.studentId } returns "1"
|
||||||
every { semesterMock.diaryId } returns "1"
|
every { semesterMock.diaryId } returns "1"
|
||||||
|
|
||||||
val timetable = TimetableRemote(mockApi).getLessons(semesterMock,
|
val timetable = TimetableRemote(mockApi).getTimetable(semesterMock,
|
||||||
LocalDate.of(2018, 9, 10),
|
LocalDate.of(2018, 9, 10),
|
||||||
LocalDate.of(2018, 9, 15)
|
LocalDate.of(2018, 9, 15)
|
||||||
).blockingGet()
|
).blockingGet()
|
||||||
|
@ -22,7 +22,7 @@ class LoginPresenterTest {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
clearInvocations(loginView)
|
clearInvocations(loginView)
|
||||||
presenter = LoginPresenter(errorHandler)
|
presenter = LoginPresenter(errorHandler)
|
||||||
presenter.attachView(loginView)
|
presenter.onAttachView(loginView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -31,7 +31,7 @@ class LoginFormPresenterTest {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
clearInvocations(repository, loginFormView)
|
clearInvocations(repository, loginFormView)
|
||||||
presenter = LoginFormPresenter(TestSchedulers(), errorHandler, repository)
|
presenter = LoginFormPresenter(TestSchedulers(), errorHandler, repository)
|
||||||
presenter.attachView(loginFormView)
|
presenter.onAttachView(loginFormView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -35,7 +35,7 @@ class LoginOptionsPresenterTest {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
clearInvocations(repository, loginOptionsView)
|
clearInvocations(repository, loginOptionsView)
|
||||||
presenter = LoginOptionsPresenter(errorHandler, repository, TestSchedulers())
|
presenter = LoginOptionsPresenter(errorHandler, repository, TestSchedulers())
|
||||||
presenter.attachView(loginOptionsView)
|
presenter.onAttachView(loginOptionsView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -23,7 +23,7 @@ class MainPresenterTest {
|
|||||||
clearInvocations(mainView)
|
clearInvocations(mainView)
|
||||||
|
|
||||||
presenter = MainPresenter(errorHandler)
|
presenter = MainPresenter(errorHandler)
|
||||||
presenter.attachView(mainView)
|
presenter.onAttachView(mainView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -31,14 +31,14 @@ class SplashPresenterTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testOpenLoginView() {
|
fun testOpenLoginView() {
|
||||||
doReturn(false).`when`(sessionRepository).isSessionSaved
|
doReturn(false).`when`(sessionRepository).isSessionSaved
|
||||||
presenter.attachView(splashView)
|
presenter.onAttachView(splashView)
|
||||||
verify(splashView).openLoginView()
|
verify(splashView).openLoginView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMainMainView() {
|
fun testMainMainView() {
|
||||||
doReturn(true).`when`(sessionRepository).isSessionSaved
|
doReturn(true).`when`(sessionRepository).isSessionSaved
|
||||||
presenter.attachView(splashView)
|
presenter.onAttachView(splashView)
|
||||||
verify(splashView).openMainView()
|
verify(splashView).openMainView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,10 @@ class GradeExtensionTest {
|
|||||||
@Test
|
@Test
|
||||||
fun calcSummaryAverage() {
|
fun calcSummaryAverage() {
|
||||||
assertEquals(2.5, listOf(
|
assertEquals(2.5, listOf(
|
||||||
GradeSummary(0, "", "", "", "",
|
GradeSummary("", "", "", "", "5"),
|
||||||
"5"),
|
GradeSummary("", "", "", "", "-5"),
|
||||||
GradeSummary(0, "", "", "", "",
|
GradeSummary("", "", "", "", "test"),
|
||||||
"-5"),
|
GradeSummary("", "", "", "", "0")
|
||||||
GradeSummary(0, "", "", "", "",
|
|
||||||
"test"),
|
|
||||||
GradeSummary(0, "", "", "", "",
|
|
||||||
"0")
|
|
||||||
).calcAverage(), 0.005)
|
).calcAverage(), 0.005)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,33 +14,33 @@ class TimeExtensionTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun toFormattedStringTest() {
|
fun toFormattedStringLocalDateTest() {
|
||||||
assertEquals("2018-10-01", LocalDate.of(2018, 10, 1).toFormattedString())
|
assertEquals("01.10.2018", LocalDate.of(2018, 10, 1).toFormattedString())
|
||||||
assertEquals("2018-10.01", LocalDate.of(2018, 10, 1).toFormattedString("yyyy-MM.dd"))
|
assertEquals("2018-10.01", LocalDate.of(2018, 10, 1).toFormattedString("yyyy-MM.dd"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun toFormat_LocalDateTime() {
|
fun toFormattedStringLocalDateTimeTest() {
|
||||||
assertEquals("2018-10-01", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString())
|
assertEquals("01.10.2018", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString())
|
||||||
assertEquals("2018-10-01 10:00:00", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString("uuuu-MM-dd HH:mm:ss"))
|
assertEquals("2018-10-01 10:00:00", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString("uuuu-MM-dd HH:mm:ss"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun weekFirstDayAlwaysCurrentTest() {
|
fun mondayTest() {
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 2).weekFirstDayAlwaysCurrent)
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 2).monday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 5).weekFirstDayAlwaysCurrent)
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 5).monday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 6).weekFirstDayAlwaysCurrent)
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 6).monday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 7).weekFirstDayAlwaysCurrent)
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 7).monday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 8).weekFirstDayAlwaysCurrent)
|
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 8).monday)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun weekFirstDayNextOnWeekEndTest() {
|
fun fridayTest() {
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 2).weekFirstDayNextOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 2).friday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 5).weekFirstDayNextOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 5).friday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 6).weekFirstDayNextOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 6).friday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 7).weekFirstDayNextOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 7).friday)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 8).weekFirstDayNextOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 12), LocalDate.of(2018, 10, 8).friday)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -53,43 +53,44 @@ class TimeExtensionTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun nextSchoolDayTest() {
|
fun nextSchoolDayTest() {
|
||||||
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 1).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 1).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 2).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 2).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 4), LocalDate.of(2018, 10, 3).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 4), LocalDate.of(2018, 10, 3).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 4).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 4).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 5).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 5).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 6).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 6).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 7).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 7).nextSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 9), LocalDate.of(2018, 10, 8).nextWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 9), LocalDate.of(2018, 10, 8).nextSchoolDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun previousSchoolDayTest() {
|
fun previousSchoolDayTest() {
|
||||||
assertEquals(LocalDate.of(2018, 10, 9), LocalDate.of(2018, 10, 10).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 9), LocalDate.of(2018, 10, 10).previousSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 9).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 8), LocalDate.of(2018, 10, 9).previousSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 8).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 8).previousSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 7).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 7).previousSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 6).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 5), LocalDate.of(2018, 10, 6).previousSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 4), LocalDate.of(2018, 10, 5).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 4), LocalDate.of(2018, 10, 5).previousSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 4).previousWorkDay)
|
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 4).previousSchoolDay)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nextOrSameSchoolDayTest() {
|
||||||
|
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).nextOrSameSchoolDay)
|
||||||
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 9, 29).nextOrSameSchoolDay)
|
||||||
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 9, 30).nextOrSameSchoolDay)
|
||||||
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 1).nextOrSameSchoolDay)
|
||||||
|
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 2).nextOrSameSchoolDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun nearSchoolDayPrevOnWeekEndTest() {
|
fun previousOrSameSchoolDayTest() {
|
||||||
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).nearSchoolDayPrevOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).previousOrSameSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 29).nearSchoolDayPrevOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 29).previousOrSameSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 30).nearSchoolDayPrevOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 30).previousOrSameSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 1).nearSchoolDayPrevOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 1).previousOrSameSchoolDay)
|
||||||
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 2).nearSchoolDayPrevOnWeekEnd)
|
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 2).previousOrSameSchoolDay)
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun nearSchoolDayNextOnWeekEndTest() {
|
|
||||||
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).nearSchoolDayNextOnWeekEnd)
|
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 9, 29).nearSchoolDayNextOnWeekEnd)
|
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 9, 30).nearSchoolDayNextOnWeekEnd)
|
|
||||||
assertEquals(LocalDate.of(2018, 10, 1), LocalDate.of(2018, 10, 1).nearSchoolDayNextOnWeekEnd)
|
|
||||||
assertEquals(LocalDate.of(2018, 10, 2), LocalDate.of(2018, 10, 2).nearSchoolDayNextOnWeekEnd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user