[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

View File

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

View File

@ -129,3 +129,10 @@ fun Response?.getUnixDate(): Long {
val format = SimpleDateFormat(pattern, Locale.ENGLISH)
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
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf(
ENDPOINT_LIBRUS_API_TIMETABLES,
ENDPOINT_LIBRUS_API_SUBSTITUTIONS
ENDPOINT_LIBRUS_API_TIMETABLES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_SUBSTITUTIONS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf(
ENDPOINT_LIBRUS_API_EVENTS,
ENDPOINT_LIBRUS_API_EVENT_TYPES,
ENDPOINT_LIBRUS_API_PT_MEETINGS,
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS,
ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS,
ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS
ENDPOINT_LIBRUS_API_EVENTS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_EVENT_TYPES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_PT_MEETINGS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC,
ENDPOINT_LIBRUS_API_POINT_GC,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC,
ENDPOINT_LIBRUS_API_TEXT_GC,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC,
ENDPOINT_LIBRUS_API_NORMAL_GRADES,
ENDPOINT_LIBRUS_API_POINT_GRADES,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES,
ENDPOINT_LIBRUS_API_TEXT_GRADES,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES
ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
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)),
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)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf(
ENDPOINT_LIBRUS_API_ATTENDANCE,
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES
ENDPOINT_LIBRUS_API_ATTENDANCE to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
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)),
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)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
ENDPOINT_LIBRUS_API_SCHOOLS,
ENDPOINT_LIBRUS_API_UNITS
ENDPOINT_LIBRUS_API_SCHOOLS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_UNITS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
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)),
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)),
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)),
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)),
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)),
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)),
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)),
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)),
/*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)),*/
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC,
ENDPOINT_LIBRUS_API_NORMAL_GRADES,
ENDPOINT_LIBRUS_SYNERGIA_GRADES
ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_SYNERGIA_GRADES to 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_OUTBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_SENT), listOf(LOGIN_METHOD_LIBRUS_MESSAGES))
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(
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.models.ApiError
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.profiles.Profile
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
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 {
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
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Endpoint::featureId, Endpoint::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()
// add all endpoint IDs and required login methods
.onEach { endpoint ->
data.targetEndpointIds.addAll(endpoint.endpointIds)
requiredLoginMethods.addAll(endpoint.requiredLoginMethods)
// add all endpoint IDs and required login methods, filtering using timers
.onEach { feature ->
feature.endpointIds.forEach { endpoint ->
(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

View File

@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus
import pl.szczodrzynski.edziennik.R
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.LibrusApiMe
import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiMe
import pl.szczodrzynski.edziennik.utils.Utils
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()
override fun satisfyLoginMethods() {
loginMethods.clear()
if (isPortalLoginValid())
loginMethods += LOGIN_METHOD_LIBRUS_PORTAL
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.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.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,
val onSuccess: () -> Unit) : LibrusApi(data) {
@ -29,6 +32,7 @@ class LibrusApiMe(override val data: DataLibrus,
data.profile?.studentNameLong =
buildFullName(user?.getString("FirstName"), user?.getString("LastName"))
data.setSyncNext(ENDPOINT_LIBRUS_API_ME, 2*DAY)
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.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.data.api.AppError.*
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.events.Event
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>()
/**
* 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 subjectList = LongSparseArray<Subject>()
val teamList = mutableListOf<Team>()
var lessonsToRemove: DataRemoveModel? = null
val lessonList = mutableListOf<Lesson>()
val lessonChangeList = mutableListOf<LessonChange>()
var gradesToRemove: DataRemoveModel? = null
val gradeCategoryList = mutableListOf<GradeCategory>()
val gradeList = mutableListOf<Grade>()
var eventsToRemove: DataRemoveModel? = null
val eventList = mutableListOf<Event>()
val eventTypeList = mutableListOf<EventType>()
var noticesToRemove: DataRemoveModel? = null
val noticeList = mutableListOf<Notice>()
var attendanceToRemove: DataRemoveModel? = null
val attendanceList = mutableListOf<Attendance>()
var announcementsToRemove: DataRemoveModel? = null
val announcementList = mutableListOf<Announcement>()
val messageList = mutableListOf<Message>()
val messageRecipientList = mutableListOf<MessageRecipient>()
val messageRecipientIgnoreList = mutableListOf<MessageRecipient>()
val metadataList = mutableListOf<Metadata>()
val messageMetadataList = mutableListOf<Metadata>()
@ -97,6 +126,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
clear()
if (profile != null) {
db.endpointTimerDao().getAllNow(profile.id).forEach { endpointTimer ->
endpointTimers.add(endpointTimer)
}
db.teacherDao().getAllNow(profile.id).forEach { teacher ->
teacherList.put(teacher.id, teacher)
}
@ -114,6 +146,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
fun clear() {
loginMethods.clear()
endpointTimers.clear()
teacherList.clear()
subjectList.clear()
teamList.clear()
@ -139,6 +173,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
db.profileDao().add(profile)
db.loginStoreDao().add(loginStore)
db.endpointTimerDao().addAll(endpointTimers)
if (teacherList.isNotEmpty()) {
val tempList: ArrayList<Teacher> = ArrayList()
teacherList.forEach { _, teacher ->
@ -193,6 +229,21 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
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) {
var code = when (throwable) {
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(
val loginType: Int,
val featureId: Int,
val endpointIds: List<Int>,
val endpointIds: List<Pair<Int, Int>>,
val requiredLoginMethods: List<Int>
) {
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.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.AttendanceDao;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate;
@ -81,7 +83,8 @@ import android.content.Context;
Message.class,
MessageRecipient.class,
DebugLog.class,
Metadata.class}, version = 54)
EndpointTimer.class,
Metadata.class}, version = 55)
@TypeConverters({
ConverterTime.class,
ConverterDate.class,
@ -111,6 +114,7 @@ public abstract class AppDb extends RoomDatabase {
public abstract MessageDao messageDao();
public abstract MessageRecipientDao messageRecipientDao();
public abstract DebugLogDao debugLogDao();
public abstract EndpointTimerDao endpointTimerDao();
public abstract MetadataDao metadataDao();
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");
}
};
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) {
@ -609,7 +626,8 @@ public abstract class AppDb extends RoomDatabase {
MIGRATION_50_51,
MIGRATION_51_52,
MIGRATION_52_53,
MIGRATION_53_54
MIGRATION_53_54,
MIGRATION_54_55
)
.allowMainThreadQueries()
//.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 metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE +
" 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, " +
"metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " +
@ -34,4 +34,7 @@ interface TeacherAbsenceDao {
" AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " +
"AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
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)) {
List<TeacherAbsenceFull> teacherAbsenceList = app.db.teacherAbsenceDao().getAllFull(App.profileId);
List<TeacherAbsenceFull> teacherAbsenceList = app.db.teacherAbsenceDao().getAllFullNow(App.profileId);
List<TeacherAbsenceCounter> teacherAbsenceCounters = new ArrayList<>();
for (TeacherAbsenceFull absence : teacherAbsenceList) {