forked from github/wulkanowy-mirror
Add a summary of attendance (#132)
This commit is contained in:
parent
900065d758
commit
f96d0ebed9
@ -81,7 +81,7 @@ configurations.all {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
implementation('com.github.wulkanowy:api:ba17abc') { exclude module: "threetenbp" }
|
implementation('com.github.wulkanowy:api:6a73b0e') { exclude module: "threetenbp" }
|
||||||
|
|
||||||
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
||||||
implementation "androidx.appcompat:appcompat:1.0.2"
|
implementation "androidx.appcompat:appcompat:1.0.2"
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
-keep class android.support.test.internal** { *; }
|
-keep class android.support.test.internal** { *; }
|
||||||
-keep class org.junit.** { *; }
|
-keep class org.junit.** { *; }
|
||||||
-keep public class io.github.wulkanowy** { *; }
|
-keep public class io.github.wulkanowy** { *; }
|
||||||
|
@ -77,6 +77,10 @@ internal class RepositoryModule {
|
|||||||
@Provides
|
@Provides
|
||||||
fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao
|
fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideAttendanceSummaryDao(database: AppDatabase) = database.attendanceSummaryDao
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideTimetableDao(database: AppDatabase) = database.timetableDao
|
fun provideTimetableDao(database: AppDatabase) = database.timetableDao
|
||||||
@ -88,4 +92,8 @@ internal class RepositoryModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao
|
fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideSubjectDao(database: AppDatabase) = database.subjectDao
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import androidx.room.RoomDatabase
|
|||||||
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||||
@ -15,8 +16,10 @@ import io.github.wulkanowy.data.db.dao.HomeworkDao
|
|||||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
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.dao.SubjectDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
@ -25,6 +28,7 @@ import io.github.wulkanowy.data.db.entities.Homework
|
|||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
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.data.db.entities.Subject
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -36,11 +40,13 @@ import javax.inject.Singleton
|
|||||||
Exam::class,
|
Exam::class,
|
||||||
Timetable::class,
|
Timetable::class,
|
||||||
Attendance::class,
|
Attendance::class,
|
||||||
|
AttendanceSummary::class,
|
||||||
Grade::class,
|
Grade::class,
|
||||||
GradeSummary::class,
|
GradeSummary::class,
|
||||||
Message::class,
|
Message::class,
|
||||||
Note::class,
|
Note::class,
|
||||||
Homework::class
|
Homework::class,
|
||||||
|
Subject::class
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 1,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
@ -66,6 +72,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
|
|
||||||
abstract val attendanceDao: AttendanceDao
|
abstract val attendanceDao: AttendanceDao
|
||||||
|
|
||||||
|
abstract val attendanceSummaryDao: AttendanceSummaryDao
|
||||||
|
|
||||||
abstract val gradeDao: GradeDao
|
abstract val gradeDao: GradeDao
|
||||||
|
|
||||||
abstract val gradeSummaryDao: GradeSummaryDao
|
abstract val gradeSummaryDao: GradeSummaryDao
|
||||||
@ -75,4 +83,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
abstract val noteDao: NoteDao
|
abstract val noteDao: NoteDao
|
||||||
|
|
||||||
abstract val homeworkDao: HomeworkDao
|
abstract val homeworkDao: HomeworkDao
|
||||||
|
|
||||||
|
abstract val subjectDao: SubjectDao
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package io.github.wulkanowy.data.db
|
package io.github.wulkanowy.data.db
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import org.threeten.bp.*
|
import org.threeten.bp.DateTimeUtils
|
||||||
import java.util.*
|
import org.threeten.bp.Instant
|
||||||
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDateTime
|
||||||
|
import org.threeten.bp.Month
|
||||||
|
import org.threeten.bp.ZoneOffset
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
|
||||||
@ -25,4 +30,10 @@ class Converters {
|
|||||||
fun timeToTimestamp(date: LocalDateTime?): Long? {
|
fun timeToTimestamp(date: LocalDateTime?): Long? {
|
||||||
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
|
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun monthToInt(month: Month?) = month?.value
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun intToMonth(value: Int?) = value?.let { Month.of(it) }
|
||||||
}
|
}
|
||||||
|
@ -20,5 +20,5 @@ interface AttendanceDao {
|
|||||||
fun deleteAll(exams: List<Attendance>)
|
fun deleteAll(exams: List<Attendance>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Attendance>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Attendance>>
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
|
import io.reactivex.Maybe
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface AttendanceSummaryDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertAll(exams: List<AttendanceSummary>): List<Long>
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun deleteAll(exams: List<AttendanceSummary>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId")
|
||||||
|
fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe<List<AttendanceSummary>>
|
||||||
|
}
|
@ -20,5 +20,5 @@ interface ExamDao {
|
|||||||
fun deleteAll(exams: List<Exam>)
|
fun deleteAll(exams: List<Exam>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Exam>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Exam>>
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ interface GradeDao {
|
|||||||
fun deleteAll(grades: List<Grade>)
|
fun deleteAll(grades: List<Grade>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
|
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
|
||||||
fun load(semesterId: Int, studentId: Int): Maybe<List<Grade>>
|
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<Grade>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Grades WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
|
@Query("SELECT * FROM Grades WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
|
||||||
fun loadNew(semesterId: Int, studentId: Int): Maybe<List<Grade>>
|
fun loadAllNew(semesterId: Int, studentId: Int): Maybe<List<Grade>>
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,5 @@ interface GradeSummaryDao {
|
|||||||
fun deleteAll(gradesSummary: List<GradeSummary>)
|
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 load(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>>
|
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>>
|
||||||
}
|
}
|
||||||
|
@ -20,5 +20,5 @@ interface HomeworkDao {
|
|||||||
fun deleteAll(homework: List<Homework>)
|
fun deleteAll(homework: List<Homework>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date = :date")
|
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date = :date")
|
||||||
fun load(semesterId: Int, studentId: Int, date: LocalDate): Maybe<List<Homework>>
|
fun loadAll(semesterId: Int, studentId: Int, date: LocalDate): Maybe<List<Homework>>
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ interface NoteDao {
|
|||||||
fun deleteAll(notes: List<Note>)
|
fun deleteAll(notes: List<Note>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Notes WHERE semester_id = :semesterId AND student_id = :studentId")
|
@Query("SELECT * FROM Notes WHERE semester_id = :semesterId AND student_id = :studentId")
|
||||||
fun load(semesterId: Int, studentId: Int): Maybe<List<Note>>
|
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<Note>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Notes WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
|
@Query("SELECT * FROM Notes WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
|
||||||
fun loadNew(semesterId: Int, studentId: Int): Maybe<List<Note>>
|
fun loadNew(semesterId: Int, studentId: Int): Maybe<List<Note>>
|
||||||
|
@ -16,7 +16,7 @@ interface SemesterDao {
|
|||||||
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")
|
||||||
fun load(studentId: Int): Maybe<List<Semester>>
|
fun loadAll(studentId: Int): Maybe<List<Semester>>
|
||||||
|
|
||||||
@Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId")
|
@Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId")
|
||||||
fun updateCurrent(semesterId: Int, diaryId: Int)
|
fun updateCurrent(semesterId: Int, diaryId: Int)
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.Subject
|
||||||
|
import io.reactivex.Maybe
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface SubjectDao {
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insertAll(subjects: List<Subject>): List<Long>
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun deleteAll(subjects: List<Subject>)
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId")
|
||||||
|
fun loadAll(diaryId: Int, studentId: Int): Maybe<List<Subject>>
|
||||||
|
}
|
@ -20,5 +20,5 @@ interface TimetableDao {
|
|||||||
fun deleteAll(exams: List<Timetable>)
|
fun deleteAll(exams: List<Timetable>)
|
||||||
|
|
||||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Timetable>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Timetable>>
|
||||||
}
|
}
|
||||||
|
@ -9,31 +9,31 @@ import java.io.Serializable
|
|||||||
@Entity(tableName = "Attendance")
|
@Entity(tableName = "Attendance")
|
||||||
data class Attendance(
|
data class Attendance(
|
||||||
|
|
||||||
@ColumnInfo(name = "student_id")
|
@ColumnInfo(name = "student_id")
|
||||||
var studentId: Int,
|
var studentId: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "diary_id")
|
@ColumnInfo(name = "diary_id")
|
||||||
var diaryId: Int,
|
var diaryId: Int,
|
||||||
|
|
||||||
var date: LocalDate,
|
var date: LocalDate,
|
||||||
|
|
||||||
var number: Int,
|
var number: Int,
|
||||||
|
|
||||||
var subject: String,
|
var subject: String,
|
||||||
|
|
||||||
var name: String,
|
var name: String,
|
||||||
|
|
||||||
var presence: Boolean = false,
|
var presence: Boolean = false,
|
||||||
|
|
||||||
var absence: Boolean = false,
|
var absence: Boolean = false,
|
||||||
|
|
||||||
var exemption: Boolean = false,
|
var exemption: Boolean = false,
|
||||||
|
|
||||||
var lateness: Boolean = false,
|
var lateness: Boolean = false,
|
||||||
|
|
||||||
var excused: Boolean = false,
|
var excused: Boolean = false,
|
||||||
|
|
||||||
var deleted: Boolean = false
|
var deleted: Boolean = false
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import org.threeten.bp.Month
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Entity(tableName = "AttendanceSummary")
|
||||||
|
data class AttendanceSummary(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "student_id")
|
||||||
|
var studentId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "diary_id")
|
||||||
|
var diaryId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "subject_id")
|
||||||
|
var subjectId: Int = 0,
|
||||||
|
|
||||||
|
val month: Month,
|
||||||
|
|
||||||
|
val presence: Int,
|
||||||
|
|
||||||
|
val absence: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "absence_excused")
|
||||||
|
val absenceExcused: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "absence_for_school_reasons")
|
||||||
|
val absenceForSchoolReasons: Int,
|
||||||
|
|
||||||
|
val lateness: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "lateness_excused")
|
||||||
|
val latenessExcused: Int,
|
||||||
|
|
||||||
|
val exemption: Int
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Entity(tableName = "Subjects")
|
||||||
|
data class Subject(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "student_id")
|
||||||
|
var studentId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "diary_id")
|
||||||
|
var diaryId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "real_id")
|
||||||
|
var realId: Int,
|
||||||
|
|
||||||
|
var name: String
|
||||||
|
) : Serializable {
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -16,30 +16,30 @@ import javax.inject.Singleton
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class AttendanceRepository @Inject constructor(
|
class AttendanceRepository @Inject constructor(
|
||||||
private val settings: InternetObservingSettings,
|
private val settings: InternetObservingSettings,
|
||||||
private val local: AttendanceLocal,
|
private val local: AttendanceLocal,
|
||||||
private val remote: AttendanceRemote
|
private val remote: AttendanceRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean)
|
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean)
|
||||||
: Single<List<Attendance>> {
|
: Single<List<Attendance>> {
|
||||||
return Single.fromCallable { startDate.monday to endDate.friday }
|
return Single.fromCallable { startDate.monday to endDate.friday }
|
||||||
.flatMap { dates ->
|
.flatMap { dates ->
|
||||||
local.getAttendance(semester, dates.first, dates.second).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, dates.first, dates.second)
|
if (it) remote.getAttendance(semester, dates.first, dates.second)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { newAttendance ->
|
}.flatMap { newAttendance ->
|
||||||
local.getAttendance(semester, dates.first, dates.second)
|
local.getAttendance(semester, dates.first, dates.second)
|
||||||
.toSingle(emptyList())
|
.toSingle(emptyList())
|
||||||
.doOnSuccess { oldAttendance ->
|
.doOnSuccess { oldAttendance ->
|
||||||
local.deleteAttendance(oldAttendance - newAttendance)
|
local.deleteAttendance(oldAttendance - newAttendance)
|
||||||
local.saveAttendance(newAttendance - oldAttendance)
|
local.saveAttendance(newAttendance - oldAttendance)
|
||||||
}
|
}
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
local.getAttendance(semester, dates.first, dates.second)
|
local.getAttendance(semester, dates.first, dates.second)
|
||||||
.toSingle(emptyList())
|
.toSingle(emptyList())
|
||||||
}).map { list -> list.filter { it.date in startDate..endDate } }
|
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.repositories.local.AttendanceSummaryLocal
|
||||||
|
import io.github.wulkanowy.data.repositories.remote.AttendanceSummaryRemote
|
||||||
|
import io.reactivex.Single
|
||||||
|
import java.net.UnknownHostException
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AttendanceSummaryRepository @Inject constructor(
|
||||||
|
private val settings: InternetObservingSettings,
|
||||||
|
private val local: AttendanceSummaryLocal,
|
||||||
|
private val remote: AttendanceSummaryRemote
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun getAttendanceSummary(semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single<List<AttendanceSummary>>? {
|
||||||
|
return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh }
|
||||||
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
|
.flatMap {
|
||||||
|
if (it) remote.getAttendanceSummary(semester, subjectId)
|
||||||
|
else Single.error(UnknownHostException())
|
||||||
|
}.flatMap { new ->
|
||||||
|
local.getAttendanceSummary(semester, subjectId).toSingle(emptyList())
|
||||||
|
.doOnSuccess { old ->
|
||||||
|
local.deleteAttendanceSummary(old - new)
|
||||||
|
local.saveAttendanceSummary(new - old)
|
||||||
|
}
|
||||||
|
}.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) })
|
||||||
|
}
|
||||||
|
}
|
@ -13,23 +13,23 @@ import javax.inject.Singleton
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class GradeSummaryRepository @Inject constructor(
|
class GradeSummaryRepository @Inject constructor(
|
||||||
private val settings: InternetObservingSettings,
|
private val settings: InternetObservingSettings,
|
||||||
private val local: GradeSummaryLocal,
|
private val local: GradeSummaryLocal,
|
||||||
private val remote: GradeSummaryRemote
|
private val remote: GradeSummaryRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getGradesSummary(semester: Semester, forceRefresh: Boolean = false): Single<List<GradeSummary>> {
|
fun getGradesSummary(semester: Semester, forceRefresh: Boolean = false): Single<List<GradeSummary>> {
|
||||||
return local.getGradesSummary(semester).filter { !forceRefresh }
|
return local.getGradesSummary(semester).filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) remote.getGradeSummary(semester)
|
if (it) remote.getGradeSummary(semester)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { newGradesSummary ->
|
}.flatMap { newGradesSummary ->
|
||||||
local.getGradesSummary(semester).toSingle(emptyList())
|
local.getGradesSummary(semester).toSingle(emptyList())
|
||||||
.doOnSuccess { oldGradesSummary ->
|
.doOnSuccess { oldGradesSummary ->
|
||||||
local.deleteGradesSummary(oldGradesSummary - newGradesSummary)
|
local.deleteGradesSummary(oldGradesSummary - newGradesSummary)
|
||||||
local.saveGradesSummary(newGradesSummary - oldGradesSummary)
|
local.saveGradesSummary(newGradesSummary - oldGradesSummary)
|
||||||
}
|
}
|
||||||
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
|
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Subject
|
||||||
|
import io.github.wulkanowy.data.repositories.local.SubjectLocal
|
||||||
|
import io.github.wulkanowy.data.repositories.remote.SubjectRemote
|
||||||
|
import io.reactivex.Single
|
||||||
|
import java.net.UnknownHostException
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class SubjectRepostory @Inject constructor(
|
||||||
|
private val settings: InternetObservingSettings,
|
||||||
|
private val local: SubjectLocal,
|
||||||
|
private val remote: SubjectRemote
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun getSubjects(semester: Semester, forceRefresh: Boolean = false): Single<List<Subject>> {
|
||||||
|
return local.getSubjects(semester).filter { !forceRefresh }
|
||||||
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
|
.flatMap {
|
||||||
|
if (it) remote.getSubjects(semester)
|
||||||
|
else Single.error(UnknownHostException())
|
||||||
|
}.flatMap { new ->
|
||||||
|
local.getSubjects(semester)
|
||||||
|
.toSingle(emptyList())
|
||||||
|
.doOnSuccess { old ->
|
||||||
|
local.deleteSubjects(old - new)
|
||||||
|
local.saveSubjects(new - old)
|
||||||
|
}
|
||||||
|
}.flatMap {
|
||||||
|
local.getSubjects(semester).toSingle(emptyList())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -16,31 +16,31 @@ import javax.inject.Singleton
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class TimetableRepository @Inject constructor(
|
class TimetableRepository @Inject constructor(
|
||||||
private val settings: InternetObservingSettings,
|
private val settings: InternetObservingSettings,
|
||||||
private val local: TimetableLocal,
|
private val local: TimetableLocal,
|
||||||
private val remote: TimetableRemote
|
private val remote: TimetableRemote
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false)
|
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false)
|
||||||
: Single<List<Timetable>> {
|
: Single<List<Timetable>> {
|
||||||
return Single.fromCallable { startDate.monday to endDate.friday }
|
return Single.fromCallable { startDate.monday to endDate.friday }
|
||||||
.flatMap { dates ->
|
.flatMap { dates ->
|
||||||
local.getTimetable(semester, dates.first, dates.second).filter { !forceRefresh }
|
local.getTimetable(semester, dates.first, dates.second).filter { !forceRefresh }
|
||||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
if (it) remote.getTimetable(semester, dates.first, dates.second)
|
if (it) remote.getTimetable(semester, dates.first, dates.second)
|
||||||
else Single.error(UnknownHostException())
|
else Single.error(UnknownHostException())
|
||||||
}.flatMap { newTimetable ->
|
}.flatMap { newTimetable ->
|
||||||
local.getTimetable(semester, dates.first, dates.second)
|
local.getTimetable(semester, dates.first, dates.second)
|
||||||
.toSingle(emptyList())
|
.toSingle(emptyList())
|
||||||
.doOnSuccess { oldTimetable ->
|
.doOnSuccess { oldTimetable ->
|
||||||
local.deleteTimetable(oldTimetable - newTimetable)
|
local.deleteTimetable(oldTimetable - newTimetable)
|
||||||
local.saveTimetable(newTimetable - oldTimetable)
|
local.saveTimetable(newTimetable - oldTimetable)
|
||||||
}
|
}
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
local.getTimetable(semester, dates.first, dates.second)
|
local.getTimetable(semester, dates.first, dates.second)
|
||||||
.toSingle(emptyList())
|
.toSingle(emptyList())
|
||||||
}).map { list -> list.filter { it.date in startDate..endDate } }
|
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import javax.inject.Inject
|
|||||||
class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) {
|
class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) {
|
||||||
|
|
||||||
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Attendance>> {
|
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Attendance>> {
|
||||||
return attendanceDb.load(semester.diaryId, semester.studentId, startDate, endDate)
|
return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
|
||||||
.filter { !it.isEmpty() }
|
.filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.local
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.reactivex.Maybe
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) {
|
||||||
|
|
||||||
|
fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe<List<AttendanceSummary>> {
|
||||||
|
return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { !it.isEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveAttendanceSummary(attendance: List<AttendanceSummary>) {
|
||||||
|
attendanceDb.insertAll(attendance)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteAttendanceSummary(attendance: List<AttendanceSummary>) {
|
||||||
|
attendanceDb.deleteAll(attendance)
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import javax.inject.Inject
|
|||||||
class ExamLocal @Inject constructor(private val examDb: ExamDao) {
|
class ExamLocal @Inject constructor(private val examDb: ExamDao) {
|
||||||
|
|
||||||
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> {
|
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> {
|
||||||
return examDb.load(semester.diaryId, semester.studentId, startDate, endDate)
|
return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
|
||||||
.filter { !it.isEmpty() }
|
.filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ import javax.inject.Singleton
|
|||||||
class GradeLocal @Inject constructor(private val gradeDb: GradeDao) {
|
class GradeLocal @Inject constructor(private val gradeDb: GradeDao) {
|
||||||
|
|
||||||
fun getGrades(semester: Semester): Maybe<List<Grade>> {
|
fun getGrades(semester: Semester): Maybe<List<Grade>> {
|
||||||
return gradeDb.load(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
|
return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNewGrades(semester: Semester): Maybe<List<Grade>> {
|
fun getNewGrades(semester: Semester): Maybe<List<Grade>> {
|
||||||
return gradeDb.loadNew(semester.semesterId, semester.studentId)
|
return gradeDb.loadAllNew(semester.semesterId, semester.studentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveGrades(grades: List<Grade>) {
|
fun saveGrades(grades: List<Grade>) {
|
||||||
|
@ -11,7 +11,7 @@ import javax.inject.Singleton
|
|||||||
class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) {
|
class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) {
|
||||||
|
|
||||||
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
|
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
|
||||||
return gradeSummaryDb.load(semester.semesterId, semester.studentId)
|
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
|
||||||
.filter { !it.isEmpty() }
|
.filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import javax.inject.Singleton
|
|||||||
class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
|
class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
|
||||||
|
|
||||||
fun getHomework(semester: Semester, date: LocalDate): Maybe<List<Homework>> {
|
fun getHomework(semester: Semester, date: LocalDate): Maybe<List<Homework>> {
|
||||||
return homeworkDb.load(semester.semesterId, semester.studentId, date).filter { !it.isEmpty() }
|
return homeworkDb.loadAll(semester.semesterId, semester.studentId, date).filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveHomework(homework: List<Homework>) {
|
fun saveHomework(homework: List<Homework>) {
|
||||||
|
@ -12,7 +12,7 @@ import javax.inject.Singleton
|
|||||||
class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
|
class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
|
||||||
|
|
||||||
fun getNotes(semester: Semester): Maybe<List<Note>> {
|
fun getNotes(semester: Semester): Maybe<List<Note>> {
|
||||||
return noteDb.load(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
|
return noteDb.loadAll(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNewNotes(semester: Semester): Maybe<List<Note>> {
|
fun getNewNotes(semester: Semester): Maybe<List<Note>> {
|
||||||
|
@ -15,7 +15,7 @@ class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSemesters(student: Student): Maybe<List<Semester>> {
|
fun getSemesters(student: Student): Maybe<List<Semester>> {
|
||||||
return semesterDb.load(student.studentId).filter { !it.isEmpty() }
|
return semesterDb.loadAll(student.studentId).filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCurrentSemester(semester: Semester) {
|
fun setCurrentSemester(semester: Semester) {
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.local
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Subject
|
||||||
|
import io.reactivex.Maybe
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) {
|
||||||
|
|
||||||
|
fun getSubjects(semester: Semester): Maybe<List<Subject>> {
|
||||||
|
return subjectDao.loadAll(semester.diaryId, semester.studentId)
|
||||||
|
.filter { !it.isEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveSubjects(subjects: List<Subject>) {
|
||||||
|
subjectDao.insertAll(subjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteSubjects(subjects: List<Subject>) {
|
||||||
|
subjectDao.deleteAll(subjects)
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import javax.inject.Inject
|
|||||||
class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) {
|
class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) {
|
||||||
|
|
||||||
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Timetable>> {
|
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Timetable>> {
|
||||||
return timetableDb.load(semester.diaryId, semester.studentId, startDate, endDate)
|
return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
|
||||||
.filter { !it.isEmpty() }
|
.filter { !it.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,9 @@ import io.github.wulkanowy.utils.toLocalDate
|
|||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
class AttendanceRemote @Inject constructor(private val api: Api) {
|
class AttendanceRemote @Inject constructor(private val api: Api) {
|
||||||
|
|
||||||
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
|
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.remote
|
||||||
|
|
||||||
|
import io.github.wulkanowy.api.Api
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.reactivex.Single
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AttendanceSummaryRemote @Inject constructor(private val api: Api) {
|
||||||
|
|
||||||
|
fun getAttendanceSummary(semester: Semester, subjectId: Int): Single<List<AttendanceSummary>> {
|
||||||
|
return Single.just(api.apply { diaryId = semester.diaryId })
|
||||||
|
.flatMap { api.getAttendanceSummary(subjectId) }.map { attendance ->
|
||||||
|
attendance.map {
|
||||||
|
AttendanceSummary(
|
||||||
|
studentId = semester.studentId,
|
||||||
|
diaryId = semester.diaryId,
|
||||||
|
subjectId = subjectId,
|
||||||
|
month = it.month,
|
||||||
|
presence = it.presence,
|
||||||
|
absence = it.absence,
|
||||||
|
absenceExcused = it.absenceExcused,
|
||||||
|
absenceForSchoolReasons = it.absenceForSchoolReasons,
|
||||||
|
lateness = it.lateness,
|
||||||
|
latenessExcused = it.latenessExcused,
|
||||||
|
exemption = it.exemption
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.remote
|
||||||
|
|
||||||
|
import io.github.wulkanowy.api.Api
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Subject
|
||||||
|
import io.reactivex.Single
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class SubjectRemote @Inject constructor(private val api: Api) {
|
||||||
|
|
||||||
|
fun getSubjects(semester: Semester): Single<List<Subject>> {
|
||||||
|
return Single.just(api.apply { diaryId = semester.diaryId })
|
||||||
|
.flatMap { api.getSubjects() }
|
||||||
|
.map { subjects ->
|
||||||
|
subjects.map {
|
||||||
|
Subject(
|
||||||
|
studentId = semester.studentId,
|
||||||
|
diaryId = semester.diaryId,
|
||||||
|
name = it.name,
|
||||||
|
realId = it.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,9 @@ package io.github.wulkanowy.ui.modules.attendance
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
@ -10,6 +13,8 @@ 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.modules.attendance.summary.AttendanceSummaryFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.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.*
|
||||||
@ -35,6 +40,14 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
override val isViewEmpty: Boolean
|
override val isViewEmpty: Boolean
|
||||||
get() = attendanceAdapter.isEmpty
|
get() = attendanceAdapter.isEmpty
|
||||||
|
|
||||||
|
override val currentStackSize: Int?
|
||||||
|
get() = (activity as? MainActivity)?.currentStackSize
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_attendance, container, false)
|
return inflater.inflate(R.layout.fragment_attendance, container, false)
|
||||||
}
|
}
|
||||||
@ -59,6 +72,15 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
|
||||||
|
inflater?.inflate(R.menu.action_menu_attendance, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||||
|
return if (item?.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
||||||
|
else false
|
||||||
|
}
|
||||||
|
|
||||||
override fun updateData(data: List<AttendanceItem>) {
|
override fun updateData(data: List<AttendanceItem>) {
|
||||||
attendanceAdapter.updateDataSet(data, true)
|
attendanceAdapter.updateDataSet(data, true)
|
||||||
}
|
}
|
||||||
@ -72,13 +94,17 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun resetView() {
|
override fun resetView() {
|
||||||
attendanceAdapter.smoothScrollToPosition(0)
|
attendanceRecycler.smoothScrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentReselected() {
|
override fun onFragmentReselected() {
|
||||||
presenter.onViewReselected()
|
presenter.onViewReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun popView() {
|
||||||
|
(activity as? MainActivity)?.popView()
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
@ -107,6 +133,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
AttendanceDialog.newInstance(lesson).show(fragmentManager, lesson.toString())
|
AttendanceDialog.newInstance(lesson).show(fragmentManager, lesson.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openSummaryView() {
|
||||||
|
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
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())
|
||||||
|
@ -56,11 +56,15 @@ class AttendancePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onViewReselected() {
|
fun onViewReselected() {
|
||||||
now().previousOrSameSchoolDay.also {
|
view?.also { view ->
|
||||||
if (currentDate != it) {
|
if (view.currentStackSize == 1) {
|
||||||
loadData(it)
|
now().previousOrSameSchoolDay.also {
|
||||||
reloadView()
|
if (currentDate != it) {
|
||||||
} else view?.resetView()
|
loadData(it)
|
||||||
|
reloadView()
|
||||||
|
} else if (!view.isViewEmpty) view.resetView()
|
||||||
|
}
|
||||||
|
} else view.popView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +72,11 @@ class AttendancePresenter @Inject constructor(
|
|||||||
if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance)
|
if (item is AttendanceItem) view?.showAttendanceDialog(item.attendance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onSummarySwitchSelected(): Boolean {
|
||||||
|
view?.openSummaryView()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
|
||||||
currentDate = date
|
currentDate = date
|
||||||
disposable.apply {
|
disposable.apply {
|
||||||
|
@ -7,6 +7,8 @@ interface AttendanceView : BaseView {
|
|||||||
|
|
||||||
val isViewEmpty: Boolean
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
|
val currentStackSize: Int?
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateData(data: List<AttendanceItem>)
|
fun updateData(data: List<AttendanceItem>)
|
||||||
@ -30,4 +32,8 @@ interface AttendanceView : BaseView {
|
|||||||
fun showNextButton(show: Boolean)
|
fun showNextButton(show: Boolean)
|
||||||
|
|
||||||
fun showAttendanceDialog(lesson: Attendance)
|
fun showAttendanceDialog(lesson: Attendance)
|
||||||
|
|
||||||
|
fun openSummaryView()
|
||||||
|
|
||||||
|
fun popView()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.TextView
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||||
|
import kotlinx.android.synthetic.main.fragment_attendance_summary.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainView.TitledView {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var presenter: AttendanceSummaryPresenter
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var attendanceSummaryAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||||
|
|
||||||
|
private lateinit var subjectsAdapter: ArrayAdapter<String>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SAVED_SUBJECT_KEY = "CURRENT_SUBJECT"
|
||||||
|
|
||||||
|
fun newInstance() = AttendanceSummaryFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val titleStringId: Int
|
||||||
|
get() = R.string.attendance_title
|
||||||
|
|
||||||
|
override val isViewEmpty
|
||||||
|
get() = attendanceSummaryAdapter.isEmpty
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_attendance_summary, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
messageContainer = attendanceSummaryRecycler
|
||||||
|
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SUBJECT_KEY))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initView() {
|
||||||
|
attendanceSummaryRecycler.run {
|
||||||
|
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||||
|
adapter = attendanceSummaryAdapter
|
||||||
|
}
|
||||||
|
attendanceSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||||
|
|
||||||
|
context?.let {
|
||||||
|
subjectsAdapter = ArrayAdapter(it, android.R.layout.simple_spinner_item, ArrayList<String>())
|
||||||
|
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
attendanceSummarySubjects.run {
|
||||||
|
adapter = subjectsAdapter
|
||||||
|
setOnItemSelectedListener { presenter.onSubjectSelected((it as TextView).text.toString()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateSubjects(data: ArrayList<String>) {
|
||||||
|
subjectsAdapter.run {
|
||||||
|
clear()
|
||||||
|
addAll(data)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateDataSet(data: List<AttendanceSummaryItem>, header: AttendanceSummaryScrollableHeader) {
|
||||||
|
attendanceSummaryAdapter.apply {
|
||||||
|
updateDataSet(data, true)
|
||||||
|
removeAllScrollableHeaders()
|
||||||
|
addScrollableHeader(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearView() {
|
||||||
|
attendanceSummaryAdapter.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showEmpty(show: Boolean) {
|
||||||
|
attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(show: Boolean) {
|
||||||
|
attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showContent(show: Boolean) {
|
||||||
|
attendanceSummaryRecycler.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showSubjects(show: Boolean) {
|
||||||
|
attendanceSummarySubjects.visibility = if (show) VISIBLE else VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideRefresh() {
|
||||||
|
attendanceSummarySwipe.isRefreshing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putInt(SAVED_SUBJECT_KEY, presenter.currentSubjectId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
presenter.onDetachView()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import kotlinx.android.synthetic.main.item_attendance_summary.*
|
||||||
|
|
||||||
|
class AttendanceSummaryItem(
|
||||||
|
private val month: String,
|
||||||
|
private val percentage: String,
|
||||||
|
private val present: String,
|
||||||
|
private val absence: String,
|
||||||
|
private val excusedAbsence: String,
|
||||||
|
private val schoolAbsence: String,
|
||||||
|
private val exemption: String,
|
||||||
|
private val lateness: String,
|
||||||
|
private val excusedLateness: String
|
||||||
|
) : AbstractFlexibleItem<AttendanceSummaryItem.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun getLayoutRes() = R.layout.item_attendance_summary
|
||||||
|
|
||||||
|
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||||
|
return ViewHolder(view, adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
|
||||||
|
holder?.apply {
|
||||||
|
attendanceSummaryMonth.text = month
|
||||||
|
attendanceSummaryPercentage.text = percentage
|
||||||
|
attendanceSummaryPresent.text = present
|
||||||
|
attendanceSummaryAbsenceUnexcused.text = absence
|
||||||
|
attendanceSummaryAbsenceExcused.text = excusedAbsence
|
||||||
|
attendanceSummaryAbsenceSchool.text = schoolAbsence
|
||||||
|
attendanceSummaryExemption.text = exemption
|
||||||
|
attendanceSummaryLatenessUnexcused.text = lateness
|
||||||
|
attendanceSummaryLatenessExcused.text = excusedLateness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as AttendanceSummaryItem
|
||||||
|
|
||||||
|
if (month != other.month) return false
|
||||||
|
if (percentage != other.percentage) return false
|
||||||
|
if (present != other.present) return false
|
||||||
|
if (absence != other.absence) return false
|
||||||
|
if (excusedAbsence != other.excusedAbsence) return false
|
||||||
|
if (schoolAbsence != other.schoolAbsence) return false
|
||||||
|
if (exemption != other.exemption) return false
|
||||||
|
if (lateness != other.lateness) return false
|
||||||
|
if (excusedLateness != other.excusedLateness) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = month.hashCode()
|
||||||
|
result = 31 * result + percentage.hashCode()
|
||||||
|
result = 31 * result + present.hashCode()
|
||||||
|
result = 31 * result + absence.hashCode()
|
||||||
|
result = 31 * result + excusedAbsence.hashCode()
|
||||||
|
result = 31 * result + schoolAbsence.hashCode()
|
||||||
|
result = 31 * result + exemption.hashCode()
|
||||||
|
result = 31 * result + lateness.hashCode()
|
||||||
|
result = 31 * result + excusedLateness.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||||
|
LayoutContainer {
|
||||||
|
|
||||||
|
override val containerView: View?
|
||||||
|
get() = contentView
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.ErrorHandler
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
|
import io.github.wulkanowy.data.db.entities.Subject
|
||||||
|
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.data.repositories.SubjectRepostory
|
||||||
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
|
import io.github.wulkanowy.utils.SchedulersProvider
|
||||||
|
import io.github.wulkanowy.utils.calculatePercentage
|
||||||
|
import io.github.wulkanowy.utils.getFormattedName
|
||||||
|
import io.github.wulkanowy.utils.logEvent
|
||||||
|
import java.lang.String.format
|
||||||
|
import java.util.Locale.FRANCE
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AttendanceSummaryPresenter @Inject constructor(
|
||||||
|
private val errorHandler: ErrorHandler,
|
||||||
|
private val attendanceSummaryRepository: AttendanceSummaryRepository,
|
||||||
|
private val subjectRepository: SubjectRepostory,
|
||||||
|
private val studentRepository: StudentRepository,
|
||||||
|
private val semesterRepository: SemesterRepository,
|
||||||
|
private val schedulers: SchedulersProvider
|
||||||
|
) : BasePresenter<AttendanceSummaryView>(errorHandler) {
|
||||||
|
|
||||||
|
private var subjects = emptyList<Subject>()
|
||||||
|
|
||||||
|
var currentSubjectId = -1
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
|
||||||
|
super.onAttachView(view)
|
||||||
|
view.initView()
|
||||||
|
loadData(subjectId ?: -1)
|
||||||
|
loadSubjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSwipeRefresh() {
|
||||||
|
loadData(currentSubjectId, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSubjectSelected(name: String) {
|
||||||
|
view?.run {
|
||||||
|
showContent(false)
|
||||||
|
showProgress(true)
|
||||||
|
clearView()
|
||||||
|
}
|
||||||
|
loadData(subjects.singleOrNull { it.name == name }?.realId ?: -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData(subjectId: Int, forceRefresh: Boolean = false) {
|
||||||
|
currentSubjectId = subjectId
|
||||||
|
disposable.apply {
|
||||||
|
clear()
|
||||||
|
add(studentRepository.getCurrentStudent()
|
||||||
|
.delay(200, MILLISECONDS)
|
||||||
|
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||||
|
.flatMap { attendanceSummaryRepository.getAttendanceSummary(it, subjectId, forceRefresh) }
|
||||||
|
.map { createAttendanceSummaryItems(it) to AttendanceSummaryScrollableHeader(formatPercentage(it.calculatePercentage())) }
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.doFinally {
|
||||||
|
view?.run {
|
||||||
|
hideRefresh()
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
view?.apply {
|
||||||
|
showEmpty(it.first.isEmpty())
|
||||||
|
showContent(it.first.isNotEmpty())
|
||||||
|
updateDataSet(it.first, it.second)
|
||||||
|
}
|
||||||
|
logEvent("Attendance load", mapOf("forceRefresh" to forceRefresh))
|
||||||
|
}) {
|
||||||
|
view?.run { showEmpty(isViewEmpty) }
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadSubjects() {
|
||||||
|
disposable.add(studentRepository.getCurrentStudent()
|
||||||
|
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||||
|
.flatMap { subjectRepository.getSubjects(it) }
|
||||||
|
.doOnSuccess { subjects = it }
|
||||||
|
.map { ArrayList(it.map { subject -> subject.name }) }
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.subscribe({
|
||||||
|
view?.run {
|
||||||
|
view?.updateSubjects(it)
|
||||||
|
showSubjects(true)
|
||||||
|
}
|
||||||
|
}, { errorHandler.dispatch(it) })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAttendanceSummaryItems(attendanceSummary: List<AttendanceSummary>): List<AttendanceSummaryItem> {
|
||||||
|
return attendanceSummary.sortedByDescending { it.id }.map {
|
||||||
|
AttendanceSummaryItem(
|
||||||
|
month = it.month.getFormattedName(),
|
||||||
|
percentage = formatPercentage(it.calculatePercentage()),
|
||||||
|
present = it.presence.toString(),
|
||||||
|
absence = it.absence.toString(),
|
||||||
|
excusedAbsence = it.absenceExcused.toString(),
|
||||||
|
schoolAbsence = it.absenceForSchoolReasons.toString(),
|
||||||
|
exemption = it.exemption.toString(),
|
||||||
|
lateness = it.lateness.toString(),
|
||||||
|
excusedLateness = it.latenessExcused.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatPercentage(percentage: Double): String {
|
||||||
|
return if (percentage == 0.0) "0%"
|
||||||
|
else "${format(FRANCE, "%.2f", percentage)}%"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import kotlinx.android.synthetic.main.scrollable_header_attendance_summary.*
|
||||||
|
|
||||||
|
class AttendanceSummaryScrollableHeader(private val percentage: String) :
|
||||||
|
AbstractFlexibleItem<AttendanceSummaryScrollableHeader.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun getLayoutRes() = R.layout.scrollable_header_attendance_summary
|
||||||
|
|
||||||
|
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
|
||||||
|
return ViewHolder(view, adapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
|
||||||
|
holder?.apply { attendanceSummaryScrollableHeaderPercentage.text = percentage }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as AttendanceSummaryScrollableHeader
|
||||||
|
|
||||||
|
if (percentage != other.percentage) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return percentage.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||||
|
LayoutContainer {
|
||||||
|
|
||||||
|
override val containerView: View?
|
||||||
|
get() = contentView
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance.summary
|
||||||
|
|
||||||
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
|
|
||||||
|
interface AttendanceSummaryView : BaseView {
|
||||||
|
|
||||||
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
|
fun initView()
|
||||||
|
|
||||||
|
fun hideRefresh()
|
||||||
|
|
||||||
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
fun showEmpty(show: Boolean)
|
||||||
|
|
||||||
|
fun updateDataSet(data: List<AttendanceSummaryItem>, header: AttendanceSummaryScrollableHeader)
|
||||||
|
|
||||||
|
fun updateSubjects(data: ArrayList<String>)
|
||||||
|
|
||||||
|
fun showSubjects(show: Boolean)
|
||||||
|
|
||||||
|
fun clearView()
|
||||||
|
}
|
@ -78,7 +78,7 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun resetView() {
|
override fun resetView() {
|
||||||
examAdapter.smoothScrollToPosition(0)
|
examRecycler.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentReselected() {
|
override fun onFragmentReselected() {
|
||||||
|
@ -63,8 +63,7 @@ class ExamPresenter @Inject constructor(
|
|||||||
if (currentDate != it) {
|
if (currentDate != it) {
|
||||||
loadData(it)
|
loadData(it)
|
||||||
reloadView()
|
reloadView()
|
||||||
view?.resetView()
|
} else if (view?.isViewEmpty == false) view?.resetView()
|
||||||
} else view?.resetView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,12 @@ import eu.davidea.flexibleadapter.items.AbstractSectionableItem
|
|||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.davidea.viewholders.FlexibleViewHolder
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.item_grade_summary.*
|
import kotlinx.android.synthetic.main.item_grade_summary.*
|
||||||
|
|
||||||
class GradeSummaryItem(header: GradeSummaryHeader, private val grade: String, private val title: String)
|
class GradeSummaryItem(header: GradeSummaryHeader, private val grade: String, private val title: String) :
|
||||||
: AbstractSectionableItem<GradeSummaryItem.ViewHolder, GradeSummaryHeader>(header) {
|
AbstractSectionableItem<GradeSummaryItem.ViewHolder, GradeSummaryHeader>(header) {
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.item_grade_summary
|
override fun getLayoutRes() = R.layout.item_grade_summary
|
||||||
|
|
||||||
@ -18,8 +19,10 @@ class GradeSummaryItem(header: GradeSummaryHeader, private val grade: String, pr
|
|||||||
return ViewHolder(view, adapter)
|
return ViewHolder(view, adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?,
|
override fun bindViewHolder(
|
||||||
position: Int, payloads: MutableList<Any>?) {
|
adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?,
|
||||||
|
position: Int, payloads: MutableList<Any>?
|
||||||
|
) {
|
||||||
holder?.run {
|
holder?.run {
|
||||||
gradeSummaryItemGrade.text = grade
|
gradeSummaryItemGrade.text = grade
|
||||||
gradeSummaryItemTitle.text = title
|
gradeSummaryItemTitle.text = title
|
||||||
@ -46,9 +49,8 @@ class GradeSummaryItem(header: GradeSummaryHeader, private val grade: String, pr
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
|
||||||
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?)
|
LayoutContainer {
|
||||||
: FlexibleViewHolder(view, adapter), LayoutContainer {
|
|
||||||
|
|
||||||
override val containerView: View?
|
override val containerView: View?
|
||||||
get() = contentView
|
get() = contentView
|
||||||
|
@ -11,6 +11,7 @@ import io.github.wulkanowy.ui.modules.about.AboutFragment
|
|||||||
import io.github.wulkanowy.ui.modules.about.AboutModule
|
import io.github.wulkanowy.ui.modules.about.AboutModule
|
||||||
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeModule
|
import io.github.wulkanowy.ui.modules.grade.GradeModule
|
||||||
@ -41,6 +42,10 @@ abstract class MainModule {
|
|||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract fun bindAttendanceFragment(): AttendanceFragment
|
abstract fun bindAttendanceFragment(): AttendanceFragment
|
||||||
|
|
||||||
|
@PerFragment
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun bindAttendanceSummaryFragment(): AttendanceSummaryFragment
|
||||||
|
|
||||||
@PerFragment
|
@PerFragment
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract fun bindExamFragment(): ExamFragment
|
abstract fun bindExamFragment(): ExamFragment
|
||||||
|
@ -72,7 +72,7 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
|
|||||||
get() {
|
get() {
|
||||||
return context?.run {
|
return context?.run {
|
||||||
getString(R.string.about_title) to
|
getString(R.string.about_title) to
|
||||||
ContextCompat.getDrawable(this, R.drawable.ic_more_about_24dp)
|
ContextCompat.getDrawable(this, R.drawable.ic_all_about_24dp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun resetView() {
|
override fun resetView() {
|
||||||
timetableAdapter.smoothScrollToPosition(0)
|
timetableRecycler.smoothScrollToPosition(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFragmentReselected() {
|
override fun onFragmentReselected() {
|
||||||
|
@ -58,7 +58,7 @@ class TimetablePresenter @Inject constructor(
|
|||||||
if (currentDate != it) {
|
if (currentDate != it) {
|
||||||
loadData(it)
|
loadData(it)
|
||||||
reloadView()
|
reloadView()
|
||||||
} else view?.resetView()
|
} else if (view?.isViewEmpty == false) view?.resetView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji]
|
||||||
|
* (https://www.vulcan.edu.pl/vulcang_files/user/AABW/AABW-PDF/uonetplus/uonetplus_Frekwencja-liczby-obecnych-nieobecnych.pdf)
|
||||||
|
*/
|
||||||
|
|
||||||
|
private inline val AttendanceSummary.allPresences: Double
|
||||||
|
get() = presence.toDouble() + absenceForSchoolReasons + lateness + latenessExcused
|
||||||
|
|
||||||
|
private inline val AttendanceSummary.allAbsences: Double
|
||||||
|
get() = absence.toDouble() + absenceExcused
|
||||||
|
|
||||||
|
fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences)
|
||||||
|
|
||||||
|
fun List<AttendanceSummary>.calculatePercentage(): Double {
|
||||||
|
return calculatePercentage(sumByDouble { it.allPresences }, sumByDouble { it.allAbsences })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculatePercentage(presence: Double, absence: Double): Double {
|
||||||
|
return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -5,6 +5,7 @@ import com.crashlytics.android.answers.CustomEvent
|
|||||||
import com.crashlytics.android.answers.LoginEvent
|
import com.crashlytics.android.answers.LoginEvent
|
||||||
import com.crashlytics.android.answers.SignUpEvent
|
import com.crashlytics.android.answers.SignUpEvent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
fun logLogin(method: String) {
|
fun logLogin(method: String) {
|
||||||
try {
|
try {
|
||||||
@ -20,7 +21,7 @@ fun logRegister(message: String, result: Boolean, symbol: String, endpoint: Stri
|
|||||||
.putMethod("Login activity")
|
.putMethod("Login activity")
|
||||||
.putSuccess(result)
|
.putSuccess(result)
|
||||||
.putCustomAttribute("symbol", symbol)
|
.putCustomAttribute("symbol", symbol)
|
||||||
.putCustomAttribute("message", message)
|
.putCustomAttribute("message", message.substring(0, min(message.length, 100)))
|
||||||
.putCustomAttribute("endpoint", endpoint)
|
.putCustomAttribute("endpoint", endpoint)
|
||||||
)
|
)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import android.widget.Spinner
|
||||||
|
|
||||||
|
inline fun Spinner.setOnItemSelectedListener(crossinline listener: (view: View?) -> Unit) {
|
||||||
|
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
|
listener(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,22 @@
|
|||||||
package io.github.wulkanowy.utils
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
import org.threeten.bp.DateTimeUtils
|
import org.threeten.bp.DayOfWeek.FRIDAY
|
||||||
import org.threeten.bp.DayOfWeek.*
|
import org.threeten.bp.DayOfWeek.MONDAY
|
||||||
|
import org.threeten.bp.DayOfWeek.SATURDAY
|
||||||
|
import org.threeten.bp.DayOfWeek.SUNDAY
|
||||||
import org.threeten.bp.Instant
|
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.Month
|
||||||
import org.threeten.bp.ZoneId
|
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.format.TextStyle.FULL_STANDALONE
|
||||||
import java.util.*
|
import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth
|
||||||
|
import org.threeten.bp.temporal.TemporalAdjusters.next
|
||||||
|
import org.threeten.bp.temporal.TemporalAdjusters.previous
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
private const val DATE_PATTERN = "dd.MM.yyyy"
|
private const val DATE_PATTERN = "dd.MM.yyyy"
|
||||||
|
|
||||||
@ -29,6 +36,31 @@ fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = this.fo
|
|||||||
|
|
||||||
fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format))
|
fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = this.format(ofPattern(format))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://github.com/ThreeTen/threetenbp/issues/55
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Month.getFormattedName(): String {
|
||||||
|
return getDisplayName(FULL_STANDALONE, Locale.getDefault())
|
||||||
|
.let {
|
||||||
|
when (it) {
|
||||||
|
"stycznia" -> "Styczeń"
|
||||||
|
"lutego" -> "Luty"
|
||||||
|
"marca" -> "Marzec"
|
||||||
|
"kwietnia" -> "Kwiecień"
|
||||||
|
"maja" -> "Maj"
|
||||||
|
"czerwca" -> "Czerwiec"
|
||||||
|
"lipca" -> "Lipiec"
|
||||||
|
"sierpnia" -> "Sierpień"
|
||||||
|
"września" -> "Wrzesień"
|
||||||
|
"października" -> "Październik"
|
||||||
|
"listopada" -> "Listopad"
|
||||||
|
"grudnia" -> "Grudzień"
|
||||||
|
else -> it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline val LocalDate.nextSchoolDay: LocalDate
|
inline val LocalDate.nextSchoolDay: LocalDate
|
||||||
get() {
|
get() {
|
||||||
return when (this.dayOfWeek) {
|
return when (this.dayOfWeek) {
|
||||||
@ -73,6 +105,7 @@ inline val LocalDate.friday: LocalDate
|
|||||||
/**
|
/**
|
||||||
* [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)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline val LocalDate.isHolidays: Boolean
|
inline val LocalDate.isHolidays: Boolean
|
||||||
get() {
|
get() {
|
||||||
return LocalDate.of(this.year, 9, 1).run {
|
return LocalDate.of(this.year, 9, 1).run {
|
||||||
@ -82,7 +115,7 @@ inline val LocalDate.isHolidays: Boolean
|
|||||||
}
|
}
|
||||||
}.let { firstSchoolDay ->
|
}.let { firstSchoolDay ->
|
||||||
LocalDate.of(this.year, 6, 20)
|
LocalDate.of(this.year, 6, 20)
|
||||||
.with(next(FRIDAY))
|
.with(next(FRIDAY))
|
||||||
.let { lastSchoolDay -> this.isBefore(firstSchoolDay) && this.isAfter(lastSchoolDay) }
|
.let { lastSchoolDay -> this.isBefore(firstSchoolDay) && this.isAfter(lastSchoolDay) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
android:viewportHeight="24"
|
android:viewportHeight="24"
|
||||||
android:viewportWidth="24">
|
android:viewportWidth="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#000"
|
android:fillColor="#FFFFFFFF"
|
||||||
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2z" />
|
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2z" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="#000"
|
android:fillColor="#FFFFFFFF"
|
||||||
android:pathData="M12,2a10,10 0,1 0,0 20,10 10,0 0,0 0,-20zM12,20a8,8 0,1 1,0 -16,8 8,0 0,1 0,16z" />
|
android:pathData="M12,2a10,10 0,1 0,0 20,10 10,0 0,0 0,-20zM12,20a8,8 0,1 1,0 -16,8 8,0 0,1 0,16z" />
|
||||||
</vector>
|
</vector>
|
66
app/src/main/res/layout/fragment_attendance_summary.xml
Normal file
66
app/src/main/res/layout/fragment_attendance_summary.xml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<FrameLayout 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">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
|
android:id="@+id/attendanceSummarySubjects"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:background="?android:attr/windowBackground"
|
||||||
|
android:elevation="5dp"
|
||||||
|
android:padding="15dp"
|
||||||
|
android:spinnerMode="dialog" />
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/attendanceSummarySwipe"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/attendanceSummaryRecycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/attendanceSummaryProgress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/attendanceSummaryEmpty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="invisible">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="100dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
app:srcCompat="@drawable/ic_menu_main_attendance_24dp"
|
||||||
|
app:tint="?android:attr/textColorPrimary"
|
||||||
|
tools:ignore="contentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/attendance_no_items"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
252
app/src/main/res/layout/item_attendance_summary.xml
Normal file
252
app/src/main/res/layout/item_attendance_summary.xml
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:foreground="?attr/colorControlHighlight"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
|
android:paddingRight="20dp"
|
||||||
|
android:paddingBottom="7dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryMonth"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:layout_marginRight="40dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="17sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryPercentage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/ic_all_divider"
|
||||||
|
android:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_present"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryPresent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout 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:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_absence_unexcused"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryAbsenceUnexcused"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout 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:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_absence_excused"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryAbsenceExcused"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout 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:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_absence_school"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryAbsenceSchool"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout 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:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_exemption"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryExemption"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout 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:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_unexcused_lateness"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryLatenessUnexcused"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout 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:minHeight="35dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="20dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/attendance_excused_lateness"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryLatenessExcused"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginRight="25dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
14
app/src/main/res/layout/item_attendance_summary_subject.xml
Normal file
14
app/src/main/res/layout/item_attendance_summary_subject.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/attendanceSummaryItemSubject"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:gravity="start"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:textAlignment="textStart"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="16sp" />
|
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minLines="2"
|
||||||
|
android:text="@string/attendance_title"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceSummaryScrollableHeaderPercentage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:text="100%"
|
||||||
|
android:textSize="21sp" />
|
||||||
|
</LinearLayout>
|
10
app/src/main/res/menu/action_menu_attendance.xml
Normal file
10
app/src/main/res/menu/action_menu_attendance.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/attendanceMenuSummary"
|
||||||
|
android:icon="@drawable/ic_all_about_24dp"
|
||||||
|
android:orderInCategory="1"
|
||||||
|
android:title="@string/grade_switch_semester"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
</menu>
|
@ -1,6 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu 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">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/gradeMenuSemester"
|
android:id="@+id/gradeMenuSemester"
|
||||||
android:icon="@drawable/ic_menu_grade_semester_24dp"
|
android:icon="@drawable/ic_menu_grade_semester_24dp"
|
||||||
|
@ -118,6 +118,8 @@
|
|||||||
<item quantity="many">%1$d nieobecności</item>
|
<item quantity="many">%1$d nieobecności</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
|
<!--Attendance summary-->
|
||||||
|
<string name="attendance_summary_final">Frekwencja</string>
|
||||||
|
|
||||||
<!--Exam-->
|
<!--Exam-->
|
||||||
<string name="exam_no_items">Brak sprawdzianów w tym tygodniu</string>
|
<string name="exam_no_items">Brak sprawdzianów w tym tygodniu</string>
|
||||||
|
@ -108,6 +108,9 @@
|
|||||||
<item quantity="other">%1$d absences</item>
|
<item quantity="other">%1$d absences</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
|
<!--Attendance summary-->
|
||||||
|
<string name="attendance_summary_final">Attendance</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Exam-->
|
<!--Exam-->
|
||||||
<string name="exam_no_items">No exams in this week</string>
|
<string name="exam_no_items">No exams in this week</string>
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package io.github.wulkanowy.utils
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import org.threeten.bp.LocalDateTime
|
import org.threeten.bp.LocalDateTime
|
||||||
import java.util.*
|
import org.threeten.bp.Month.JANUARY
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class TimeExtensionTest {
|
class TimeExtensionTest {
|
||||||
|
|
||||||
@ -43,6 +46,14 @@ class TimeExtensionTest {
|
|||||||
assertEquals(LocalDate.of(2018, 10, 12), LocalDate.of(2018, 10, 8).friday)
|
assertEquals(LocalDate.of(2018, 10, 12), LocalDate.of(2018, 10, 8).friday)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun monthNameTest() {
|
||||||
|
Locale.setDefault(Locale.forLanguageTag("PL"))
|
||||||
|
assertEquals("Styczeń", JANUARY.getFormattedName())
|
||||||
|
Locale.setDefault(Locale.forLanguageTag("US"))
|
||||||
|
assertEquals("January", JANUARY.getFormattedName())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun weekDayNameTest() {
|
fun weekDayNameTest() {
|
||||||
Locale.setDefault(Locale.forLanguageTag("PL"))
|
Locale.setDefault(Locale.forLanguageTag("PL"))
|
||||||
@ -74,7 +85,6 @@ class TimeExtensionTest {
|
|||||||
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 4).previousSchoolDay)
|
assertEquals(LocalDate.of(2018, 10, 3), LocalDate.of(2018, 10, 4).previousSchoolDay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun nextOrSameSchoolDayTest() {
|
fun nextOrSameSchoolDayTest() {
|
||||||
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).nextOrSameSchoolDay)
|
assertEquals(LocalDate.of(2018, 9, 28), LocalDate.of(2018, 9, 28).nextOrSameSchoolDay)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user