[APIv2] Implement endpoint timers. Fix compilation issues after merge.

This commit is contained in:
Kuba Szczodrzyński 2019-10-04 17:14:56 +02:00
parent 870a429f3d
commit c8c933fb20
17 changed files with 298 additions and 63 deletions

5
.idea/misc.xml generated
View File

@ -5,6 +5,11 @@
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" /> <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations> </configurations>
</component> </component>
<component name="EntryPointsManager">
<list size="1">
<item index="0" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
</list>
</component>
<component name="NullableNotNullManager"> <component name="NullableNotNullManager">
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" /> <option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
<option name="myDefaultNotNull" value="androidx.annotation.RecentlyNonNull" /> <option name="myDefaultNotNull" value="androidx.annotation.RecentlyNonNull" />

View File

@ -129,3 +129,10 @@ fun Response?.getUnixDate(): Long {
val format = SimpleDateFormat(pattern, Locale.ENGLISH) val format = SimpleDateFormat(pattern, Locale.ENGLISH)
return format.parse(rfcDate).time / 1000 return format.parse(rfcDate).time / 1000
} }
const val MINUTE = 60L
const val HOUR = 60L*MINUTE
const val DAY = 24L*HOUR
const val WEEK = 7L*DAY
const val MONTH = 30L*DAY
const val YEAR = 365L*DAY

View File

@ -52,89 +52,93 @@ val endpoints = listOf(
// LIBRUS: API // LIBRUS: API
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf(
ENDPOINT_LIBRUS_API_TIMETABLES, ENDPOINT_LIBRUS_API_TIMETABLES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_SUBSTITUTIONS ENDPOINT_LIBRUS_API_SUBSTITUTIONS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf(
ENDPOINT_LIBRUS_API_EVENTS, ENDPOINT_LIBRUS_API_EVENTS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_EVENT_TYPES, ENDPOINT_LIBRUS_API_EVENT_TYPES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_PT_MEETINGS, ENDPOINT_LIBRUS_API_PT_MEETINGS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS, ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC, ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GC, ENDPOINT_LIBRUS_API_POINT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC, ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GC, ENDPOINT_LIBRUS_API_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC, ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC, ENDPOINT_LIBRUS_API_BEHAVIOUR_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES, ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GRADES, ENDPOINT_LIBRUS_API_POINT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES, ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GRADES, ENDPOINT_LIBRUS_API_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES, ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_API_HOMEWORK ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_NOTICES, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_NOTICES, listOf(
ENDPOINT_LIBRUS_API_NOTICES ENDPOINT_LIBRUS_API_NOTICES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf(
ENDPOINT_LIBRUS_API_ATTENDANCE, ENDPOINT_LIBRUS_API_ATTENDANCE to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf(
ENDPOINT_LIBRUS_API_ANNOUNCEMENTS ENDPOINT_LIBRUS_API_ANNOUNCEMENTS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_API_ME ENDPOINT_LIBRUS_API_ME to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
ENDPOINT_LIBRUS_API_SCHOOLS, ENDPOINT_LIBRUS_API_SCHOOLS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_UNITS ENDPOINT_LIBRUS_API_UNITS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf(
ENDPOINT_LIBRUS_API_CLASSES ENDPOINT_LIBRUS_API_CLASSES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf(
ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_LIBRUS_API_LUCKY_NUMBER ENDPOINT_LIBRUS_API_LUCKY_NUMBER to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf(
ENDPOINT_LIBRUS_API_USERS ENDPOINT_LIBRUS_API_USERS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf(
ENDPOINT_LIBRUS_API_SUBJECTS ENDPOINT_LIBRUS_API_SUBJECTS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf(
ENDPOINT_LIBRUS_API_CLASSROOMS ENDPOINT_LIBRUS_API_CLASSROOMS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)), ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)), ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),
/*Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( /*Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_SYNERGIA_GRADES ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/ ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC, ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES, ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_SYNERGIA_GRADES ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_API, LOGIN_METHOD_LIBRUS_SYNERGIA)), ), listOf(LOGIN_METHOD_LIBRUS_API, LOGIN_METHOD_LIBRUS_SYNERGIA)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_RECEIVED), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_OUTBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_SENT), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)) ENDPOINT_LIBRUS_MESSAGES_RECEIVED to LOGIN_METHOD_LIBRUS_MESSAGES
), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_OUTBOX, listOf(
ENDPOINT_LIBRUS_MESSAGES_SENT to LOGIN_METHOD_LIBRUS_MESSAGES
), listOf(LOGIN_METHOD_LIBRUS_MESSAGES))
) )
/* /*

View File

@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.data.db.modules.api.*
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -65,12 +66,6 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
// get all endpoints for every feature, only if possible to login // get all endpoints for every feature, only if possible to login
for (featureId in featureIds) { for (featureId in featureIds) {
/*endpoints.filter { it.featureId == featureId }.forEach { endpoint ->
if (possibleLoginMethods.containsAll(endpoint.requiredLoginMethods)) {
endpointList.add(endpoint)
//highestLoginMethod = max(highestLoginMethod, endpoint.requiredLoginMethods.max() ?: 0)
}
}*/
endpoints.filter { endpoints.filter {
it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods)
} }
@ -79,16 +74,30 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
} }
val timestamp = System.currentTimeMillis()
val viewId = 0
endpointList = endpointList endpointList = endpointList
// sort the endpoint list by feature ID and priority // sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Endpoint::featureId, Endpoint::priority)) .sortedWith(compareBy(Endpoint::featureId, Endpoint::priority))
// select only the most important endpoint for each feature // select only the most important endpoint for each feature
.distinctBy { it.featureId } .distinctBy { it.featureId }
.toMutableList() .toMutableList()
// add all endpoint IDs and required login methods // add all endpoint IDs and required login methods, filtering using timers
.onEach { endpoint -> .onEach { feature ->
data.targetEndpointIds.addAll(endpoint.endpointIds) feature.endpointIds.forEach { endpoint ->
requiredLoginMethods.addAll(endpoint.requiredLoginMethods) (data.endpointTimers
.singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first))
.let { timer ->
if (timer.nextSync == SYNC_ALWAYS ||
(timer.nextSync == SYNC_IF_EXPLICIT && timer.viewId == viewId) ||
(timer.nextSync == SYNC_IF_EXPLICIT_OR_ALL && viewId == null) ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
data.targetEndpointIds.add(endpoint.first)
requiredLoginMethods.add(endpoint.second)
}
}
}
} }
// check every login method for any dependencies // check every login method for any dependencies

View File

@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiMe import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiMe
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
class LibrusEndpoints(val data: DataLibrus, val onSuccess: () -> Unit) { class LibrusEndpoints(val data: DataLibrus, val onSuccess: () -> Unit) {

View File

@ -25,6 +25,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
fun isMessagesLoginValid() = messagesSessionIdExpiryTime-30 > currentTimeUnix() && messagesSessionId.isNotNullNorEmpty() fun isMessagesLoginValid() = messagesSessionIdExpiryTime-30 > currentTimeUnix() && messagesSessionId.isNotNullNorEmpty()
override fun satisfyLoginMethods() { override fun satisfyLoginMethods() {
loginMethods.clear()
if (isPortalLoginValid()) if (isPortalLoginValid())
loginMethods += LOGIN_METHOD_LIBRUS_PORTAL loginMethods += LOGIN_METHOD_LIBRUS_PORTAL
if (isApiLoginValid()) if (isApiLoginValid())

View File

@ -1,4 +1,4 @@
package pl.szczodrzynski.edziennik.api.v2.librus.data package pl.szczodrzynski.edziennik.api.v2.librus.data.api
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.api.v2.models.Data

View File

@ -1,10 +1,13 @@
/* /*
* Copyright (c) Kuba Szczodrzyński 2019-9-21. * Copyright (c) Kuba Szczodrzyński 2019-10-3.
*/ */
package pl.szczodrzynski.edziennik.api.v2.librus.data package pl.szczodrzynski.edziennik.api.v2.librus.data.api
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
class LibrusApiMe(override val data: DataLibrus, class LibrusApiMe(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { val onSuccess: () -> Unit) : LibrusApi(data) {
@ -29,6 +32,7 @@ class LibrusApiMe(override val data: DataLibrus,
data.profile?.studentNameLong = data.profile?.studentNameLong =
buildFullName(user?.getString("FirstName"), user?.getString("LastName")) buildFullName(user?.getString("FirstName"), user?.getString("LastName"))
data.setSyncNext(ENDPOINT_LIBRUS_API_ME, 2*DAY)
onSuccess() onSuccess()
} }
} }

View File

@ -1,4 +1,4 @@
package pl.szczodrzynski.edziennik.api.v2.librus.data package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.api.v2.models.Data

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback
import pl.szczodrzynski.edziennik.data.api.AppError.* import pl.szczodrzynski.edziennik.data.api.AppError.*
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
@ -72,21 +73,49 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
*/ */
var endpointArgs = mutableMapOf<Int, JsonObject>() var endpointArgs = mutableMapOf<Int, JsonObject>()
/**
* A list of per-endpoint next sync time descriptors.
*
* [EndpointTimer.nextSync] may be:
* - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER] to never sync the endpoint (pretty useless)
* - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS] to sync the endpoint during every sync
* - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_IF_EXPLICIT] to sync the endpoint only if the matching
* feature ID is in the input set
* - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_IF_EXPLICIT_OR_ALL] to sync if the matching feature ID
* is in the input set OR the sync covers all feature IDs
* - a Unix-epoch timestamp (in millis) to sync the endpoint if [System.currentTimeMillis] is greater or equal to this value
*/
var endpointTimers = mutableListOf<EndpointTimer>()
val teacherList = LongSparseArray<Teacher>() val teacherList = LongSparseArray<Teacher>()
val subjectList = LongSparseArray<Subject>() val subjectList = LongSparseArray<Subject>()
val teamList = mutableListOf<Team>() val teamList = mutableListOf<Team>()
var lessonsToRemove: DataRemoveModel? = null
val lessonList = mutableListOf<Lesson>() val lessonList = mutableListOf<Lesson>()
val lessonChangeList = mutableListOf<LessonChange>() val lessonChangeList = mutableListOf<LessonChange>()
var gradesToRemove: DataRemoveModel? = null
val gradeCategoryList = mutableListOf<GradeCategory>() val gradeCategoryList = mutableListOf<GradeCategory>()
val gradeList = mutableListOf<Grade>() val gradeList = mutableListOf<Grade>()
var eventsToRemove: DataRemoveModel? = null
val eventList = mutableListOf<Event>() val eventList = mutableListOf<Event>()
val eventTypeList = mutableListOf<EventType>() val eventTypeList = mutableListOf<EventType>()
var noticesToRemove: DataRemoveModel? = null
val noticeList = mutableListOf<Notice>() val noticeList = mutableListOf<Notice>()
var attendanceToRemove: DataRemoveModel? = null
val attendanceList = mutableListOf<Attendance>() val attendanceList = mutableListOf<Attendance>()
var announcementsToRemove: DataRemoveModel? = null
val announcementList = mutableListOf<Announcement>() val announcementList = mutableListOf<Announcement>()
val messageList = mutableListOf<Message>() val messageList = mutableListOf<Message>()
val messageRecipientList = mutableListOf<MessageRecipient>() val messageRecipientList = mutableListOf<MessageRecipient>()
val messageRecipientIgnoreList = mutableListOf<MessageRecipient>() val messageRecipientIgnoreList = mutableListOf<MessageRecipient>()
val metadataList = mutableListOf<Metadata>() val metadataList = mutableListOf<Metadata>()
val messageMetadataList = mutableListOf<Metadata>() val messageMetadataList = mutableListOf<Metadata>()
@ -97,6 +126,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
clear() clear()
if (profile != null) { if (profile != null) {
db.endpointTimerDao().getAllNow(profile.id).forEach { endpointTimer ->
endpointTimers.add(endpointTimer)
}
db.teacherDao().getAllNow(profile.id).forEach { teacher -> db.teacherDao().getAllNow(profile.id).forEach { teacher ->
teacherList.put(teacher.id, teacher) teacherList.put(teacher.id, teacher)
} }
@ -114,6 +146,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
fun clear() { fun clear() {
loginMethods.clear() loginMethods.clear()
endpointTimers.clear()
teacherList.clear() teacherList.clear()
subjectList.clear() subjectList.clear()
teamList.clear() teamList.clear()
@ -139,6 +173,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
db.profileDao().add(profile) db.profileDao().add(profile)
db.loginStoreDao().add(loginStore) db.loginStoreDao().add(loginStore)
db.endpointTimerDao().addAll(endpointTimers)
if (teacherList.isNotEmpty()) { if (teacherList.isNotEmpty()) {
val tempList: ArrayList<Teacher> = ArrayList() val tempList: ArrayList<Teacher> = ArrayList()
teacherList.forEach { _, teacher -> teacherList.forEach { _, teacher ->
@ -193,6 +229,21 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
db.metadataDao().setSeen(messageMetadataList) db.metadataDao().setSeen(messageMetadataList)
} }
fun setSyncNext(endpointId: Int, syncIn: Long, viewId: Int? = null, syncIfAll: Boolean = false) {
EndpointTimer(profile?.id ?: -1, endpointId).apply {
syncedNow()
if (syncIn < 10) {
nextSync = syncIn
}
else {
syncIn(syncIn)
if (viewId != null)
syncWhenView(viewId, syncIfAll)
}
endpointTimers.add(this)
}
}
fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {
var code = when (throwable) { var code = when (throwable) {
is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-2.
*/
package pl.szczodrzynski.edziennik.api.v2.models
import pl.szczodrzynski.edziennik.utils.models.Date
class DataRemoveModel {
var removeSemester: Int? = null
var removeDateFrom: Date? = null
var removeDateTo: Date? = null
constructor(semester: Int) {
this.removeSemester = semester
}
constructor(dateFrom: Date?, dateTo: Date) {
this.removeDateFrom = dateFrom
this.removeDateTo = dateTo
}
constructor(dateFrom: Date) {
this.removeDateFrom = dateFrom
}
}

View File

@ -16,7 +16,7 @@ package pl.szczodrzynski.edziennik.api.v2.models
data class Endpoint( data class Endpoint(
val loginType: Int, val loginType: Int,
val featureId: Int, val featureId: Int,
val endpointIds: List<Int>, val endpointIds: List<Pair<Int, Int>>,
val requiredLoginMethods: List<Int> val requiredLoginMethods: List<Int>
) { ) {
val priority val priority

View File

@ -10,6 +10,8 @@ import androidx.room.migration.Migration;
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement; import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement;
import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementDao; import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementDao;
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer;
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimerDao;
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance;
import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceDao; import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceDao;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate; import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate;
@ -81,7 +83,8 @@ import android.content.Context;
Message.class, Message.class,
MessageRecipient.class, MessageRecipient.class,
DebugLog.class, DebugLog.class,
Metadata.class}, version = 54) EndpointTimer.class,
Metadata.class}, version = 55)
@TypeConverters({ @TypeConverters({
ConverterTime.class, ConverterTime.class,
ConverterDate.class, ConverterDate.class,
@ -111,6 +114,7 @@ public abstract class AppDb extends RoomDatabase {
public abstract MessageDao messageDao(); public abstract MessageDao messageDao();
public abstract MessageRecipientDao messageRecipientDao(); public abstract MessageRecipientDao messageRecipientDao();
public abstract DebugLogDao debugLogDao(); public abstract DebugLogDao debugLogDao();
public abstract EndpointTimerDao endpointTimerDao();
public abstract MetadataDao metadataDao(); public abstract MetadataDao metadataDao();
private static volatile AppDb INSTANCE; private static volatile AppDb INSTANCE;
@ -558,6 +562,19 @@ public abstract class AppDb extends RoomDatabase {
database.execSQL("ALTER TABLE teacherAbsence ADD teacherAbsenceTimeTo TEXT DEFAULT NULL"); database.execSQL("ALTER TABLE teacherAbsence ADD teacherAbsenceTimeTo TEXT DEFAULT NULL");
} }
}; };
private static final Migration MIGRATION_54_55 = new Migration(54, 55) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS endpointTimers (" +
"profileId INTEGER NOT NULL," +
"endpointId INTEGER NOT NULL," +
"endpointLastSync INTEGER DEFAULT NULL," +
"endpointNextSync INTEGER NOT NULL DEFAULT 1," +
"endpointViewId INTEGER DEFAULT NULL," +
"PRIMARY KEY(profileId, endpointId)" +
")");
}
};
public static AppDb getDatabase(final Context context) { public static AppDb getDatabase(final Context context) {
@ -609,7 +626,8 @@ public abstract class AppDb extends RoomDatabase {
MIGRATION_50_51, MIGRATION_50_51,
MIGRATION_51_52, MIGRATION_51_52,
MIGRATION_52_53, MIGRATION_52_53,
MIGRATION_53_54 MIGRATION_53_54,
MIGRATION_54_55
) )
.allowMainThreadQueries() .allowMainThreadQueries()
//.fallbackToDestructiveMigration() //.fallbackToDestructiveMigration()

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-3.
*/
package pl.szczodrzynski.edziennik.data.db.modules.api
import androidx.room.ColumnInfo
import androidx.room.Entity
const val SYNC_NEVER = 0L
const val SYNC_ALWAYS = 1L
const val SYNC_IF_EXPLICIT = 2L
const val SYNC_IF_EXPLICIT_OR_ALL = 3L
@Entity(tableName = "endpointTimers",
primaryKeys = ["profileId", "endpointId"])
data class EndpointTimer (
val profileId: Int,
@ColumnInfo(name = "endpointId")
val endpointId: Int,
@ColumnInfo(name = "endpointLastSync")
var lastSync: Long? = null,
@ColumnInfo(name = "endpointNextSync")
var nextSync: Long = SYNC_ALWAYS,
@ColumnInfo(name = "endpointViewId")
var viewId: Int? = null
) {
/**
* Tell this timer that an endpoint has just been synced.
*/
fun syncedNow(): EndpointTimer {
lastSync = System.currentTimeMillis()
return this
}
/**
* This will "schedule" the next sync.
*
* @param nextSyncIn value in seconds
*/
fun syncIn(nextSyncIn: Long): EndpointTimer {
nextSync = System.currentTimeMillis() + nextSyncIn*1000
viewId = null
return this
}
/**
* Set this timer to sync only if [viewId] is the only
* selected feature during the current process.
*/
fun syncWhenView(viewId: Int, syncIfAll: Boolean = false): EndpointTimer {
nextSync = if (syncIfAll) SYNC_IF_EXPLICIT_OR_ALL else SYNC_IF_EXPLICIT
this.viewId = viewId
return this
}
/**
* Set this endpoint to always sync.
*/
fun syncAlways(): EndpointTimer {
nextSync = SYNC_ALWAYS
viewId = null
return this
}
/**
* This is a suicide as this endpoint will never be synced again.
*/
fun syncNever(): EndpointTimer {
nextSync = SYNC_NEVER
viewId = null
return this
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-3.
*/
package pl.szczodrzynski.edziennik.data.db.modules.api
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface EndpointTimerDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun add(endpointTimer: EndpointTimer)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun addAll(endpointTimerList: List<EndpointTimer>)
@Query("SELECT * FROM endpointTimers WHERE profileId = :profileId")
fun getAllNow(profileId: Int): List<EndpointTimer>
@Query("DELETE FROM endpointTimers WHERE profileId = :profileId")
fun clear(profileId: Int)
}

View File

@ -25,7 +25,7 @@ interface TeacherAbsenceDao {
"LEFT JOIN teachers USING (profileId, teacherId) " + "LEFT JOIN teachers USING (profileId, teacherId) " +
"LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE + "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE +
" AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId") " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId")
fun getAllFull(profileId: Int): List<TeacherAbsenceFull> fun getAllFullNow(profileId: Int): List<TeacherAbsenceFull>
@Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " + @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " +
"metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " + "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " +
@ -34,4 +34,7 @@ interface TeacherAbsenceDao {
" AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " + " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " +
"AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo") "AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
fun getAllByDateFull(profileId: Int, date: Date): LiveData<List<TeacherAbsenceFull>> fun getAllByDateFull(profileId: Int, date: Date): LiveData<List<TeacherAbsenceFull>>
@Query("DELETE FROM teacherAbsence WHERE profileId = :profileId")
fun clear(profileId: Int)
} }

View File

@ -182,7 +182,7 @@ public class AgendaFragment extends Fragment {
} }
if (app.profile.getStudentData("showTeacherAbsences", true)) { if (app.profile.getStudentData("showTeacherAbsences", true)) {
List<TeacherAbsenceFull> teacherAbsenceList = app.db.teacherAbsenceDao().getAllFull(App.profileId); List<TeacherAbsenceFull> teacherAbsenceList = app.db.teacherAbsenceDao().getAllFullNow(App.profileId);
List<TeacherAbsenceCounter> teacherAbsenceCounters = new ArrayList<>(); List<TeacherAbsenceCounter> teacherAbsenceCounters = new ArrayList<>();
for (TeacherAbsenceFull absence : teacherAbsenceList) { for (TeacherAbsenceFull absence : teacherAbsenceList) {