[APIv2] Simplify endpoint choosing. Optimize imports.

This commit is contained in:
Kuba Szczodrzyński 2019-10-14 10:59:11 +02:00
parent b35df5ef11
commit bdc0ceb11d
6 changed files with 99 additions and 330 deletions

View File

@ -0,0 +1,80 @@
package pl.szczodrzynski.edziennik.api.v2
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.api.v2.models.Feature
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER
fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?) {
val data = this
val possibleLoginMethods = data.loginMethods.toMutableList()
for (loginMethod in loginMethods) {
if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += loginMethod.loginMethodId
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
data.targetLoginMethodIds.clear()
// get all endpoints for every feature, only if possible to login and possible/necessary to sync
for (featureId in featureIds) {
features.filter {
it.featureId == featureId // feature ID matches
&& possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login
&& it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync
}.let {
endpointList.addAll(it)
}
}
val timestamp = System.currentTimeMillis()
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()
// 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 ||
(viewId != null && timer.viewId == viewId) ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
data.targetEndpointIds.add(endpoint.first)
requiredLoginMethods.add(endpoint.second)
}
}
}
}
// check every login method for any dependencies
for (loginMethodId in requiredLoginMethods) {
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
if (requiredLoginMethod != null)
data.targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
}
}
// sort and distinct every login method and endpoint
data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
data.targetLoginMethodIds.sort()
data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
data.targetEndpointIds.sort()
}

View File

@ -7,18 +7,16 @@ package pl.szczodrzynski.edziennik.api.v2.librus
import android.util.Log import android.util.Log
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.MobidziennikFeatures
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.prepare
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
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
companion object { companion object {
@ -27,13 +25,12 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
val internalErrorList = mutableListOf<Int>() val internalErrorList = mutableListOf<Int>()
val data: DataLibrus val data: DataLibrus
private var cancelled = false
init { init {
data = DataLibrus(app, profile, loginStore).apply { data = DataLibrus(app, profile, loginStore).apply {
callback = wrapCallback(this@Librus.callback) callback = wrapCallback(this@Librus.callback)
satisfyLoginMethods()
} }
data.satisfyLoginMethods()
} }
private fun completed() { private fun completed() {
@ -50,77 +47,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?) { override fun sync(featureIds: List<Int>, viewId: Int?) {
val possibleLoginMethods = data.loginMethods.toMutableList() data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId)
for (loginMethod in librusLoginMethods) {
if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += loginMethod.loginMethodId
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
data.targetLoginMethodIds.clear()
// get all endpoints for every feature, only if possible to login and possible/necessary to sync
for (featureId in featureIds) {
LibrusFeatures.filter {
it.featureId == featureId // feature ID matches
&& possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login
&& it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync
}.let {
endpointList.addAll(it)
}
}
val timestamp = System.currentTimeMillis()
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()
// 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 ||
(viewId != null && timer.viewId == viewId) ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
data.targetEndpointIds.add(endpoint.first)
requiredLoginMethods.add(endpoint.second)
}
}
}
}
// check every login method for any dependencies
for (loginMethodId in requiredLoginMethods) {
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
if (requiredLoginMethod != null)
data.targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
}
}
// sort and distinct every login method and endpoint
data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
data.targetLoginMethodIds.sort()
data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
data.targetEndpointIds.sort()
Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
LibrusLogin(data) { LibrusLogin(data) {
LibrusData(data) { LibrusData(data) {
completed() completed()
@ -137,6 +66,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun cancel() { override fun cancel() {
Utils.d(TAG, "Cancelled")
data.cancel() data.cancel()
} }

View File

@ -6,17 +6,14 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik
import android.util.Log import android.util.Log
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin
import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER
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 import pl.szczodrzynski.edziennik.utils.Utils
@ -28,13 +25,12 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
val internalErrorList = mutableListOf<Int>() val internalErrorList = mutableListOf<Int>()
val data: DataMobidziennik val data: DataMobidziennik
private var cancelled = false
init { init {
data = DataMobidziennik(app, profile, loginStore).apply { data = DataMobidziennik(app, profile, loginStore).apply {
callback = wrapCallback(this@Mobidziennik.callback) callback = wrapCallback(this@Mobidziennik.callback)
satisfyLoginMethods()
} }
data.satisfyLoginMethods()
} }
private fun completed() { private fun completed() {
@ -51,77 +47,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?) { override fun sync(featureIds: List<Int>, viewId: Int?) {
val possibleLoginMethods = data.loginMethods.toMutableList() data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
for (loginMethod in mobidziennikLoginMethods) {
if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += loginMethod.loginMethodId
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
data.targetLoginMethodIds.clear()
// get all endpoints for every feature, only if possible to login and possible/necessary to sync
for (featureId in featureIds) {
MobidziennikFeatures.filter {
it.featureId == featureId // feature ID matches
&& possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login
&& it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync
}.let {
endpointList.addAll(it)
}
}
val timestamp = System.currentTimeMillis()
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()
// 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 ||
(viewId != null && timer.viewId == viewId) ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
data.targetEndpointIds.add(endpoint.first)
requiredLoginMethods.add(endpoint.second)
}
}
}
}
// check every login method for any dependencies
for (loginMethodId in requiredLoginMethods) {
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
mobidziennikLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
if (requiredLoginMethod != null)
data.targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
}
}
// sort and distinct every login method and endpoint
data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
data.targetLoginMethodIds.sort()
data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
data.targetEndpointIds.sort()
Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
MobidziennikLogin(data) { MobidziennikLogin(data) {
MobidziennikData(data) { MobidziennikData(data) {
completed() completed()
@ -139,7 +67,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
override fun cancel() { override fun cancel() {
Utils.d(TAG, "Cancelled") Utils.d(TAG, "Cancelled")
cancelled = true data.cancel()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -2,7 +2,6 @@ package pl.szczodrzynski.edziennik.api.v2.models
import android.util.LongSparseArray import android.util.LongSparseArray
import android.util.SparseArray import android.util.SparseArray
import androidx.core.util.isNotEmpty
import com.google.gson.JsonObject import com.google.gson.JsonObject
import im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
@ -84,18 +83,6 @@ 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>() var endpointTimers = mutableListOf<EndpointTimer>()
val teacherList = LongSparseArray<Teacher>() val teacherList = LongSparseArray<Teacher>()
@ -148,9 +135,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
val db by lazy { app.db } val db by lazy { app.db }
init { init {
clear() clear()
if (profile != null) { if (profile != null) {
endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList() endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList()
db.teacherDao().getAllNow(profileId).toSparseArray(teacherList) { it.id } db.teacherDao().getAllNow(profileId).toSparseArray(teacherList) { it.id }
@ -159,11 +144,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
db.lessonRangeDao().getAllNow(profileId).toSparseArray(lessonRanges) { it.lessonNumber } db.lessonRangeDao().getAllNow(profileId).toSparseArray(lessonRanges) { it.lessonNumber }
db.gradeCategoryDao().getAllNow(profileId).toSparseArray(gradeCategories) { it.categoryId } db.gradeCategoryDao().getAllNow(profileId).toSparseArray(gradeCategories) { it.categoryId }
} }
/*val teacher = teachers.byNameFirstLast("Jan Kowalski") ?: Teacher(1, 1, "", "").let {
teachers.add(it)
}*/
} }
fun clear() { fun clear() {

View File

@ -7,19 +7,13 @@ package pl.szczodrzynski.edziennik.api.v2.template
import android.util.Log import android.util.Log
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin
import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods
import pl.szczodrzynski.edziennik.api.v2.vulcan.VulcanFeatures
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER
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 import pl.szczodrzynski.edziennik.utils.Utils
@ -31,13 +25,12 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
val internalErrorList = mutableListOf<Int>() val internalErrorList = mutableListOf<Int>()
val data: DataTemplate val data: DataTemplate
private var cancelled = false
init { init {
data = DataTemplate(app, profile, loginStore).apply { data = DataTemplate(app, profile, loginStore).apply {
callback = wrapCallback(this@Template.callback) callback = wrapCallback(this@Template.callback)
satisfyLoginMethods()
} }
data.satisfyLoginMethods()
} }
private fun completed() { private fun completed() {
@ -54,77 +47,9 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?) { override fun sync(featureIds: List<Int>, viewId: Int?) {
val possibleLoginMethods = data.loginMethods.toMutableList() data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId)
for (loginMethod in templateLoginMethods) {
if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += loginMethod.loginMethodId
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
data.targetLoginMethodIds.clear()
// get all endpoints for every feature, only if possible to login and possible/necessary to sync
for (featureId in featureIds) {
VulcanFeatures.filter {
it.featureId == featureId // feature ID matches
&& possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login
&& it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync
}.let {
endpointList.addAll(it)
}
}
val timestamp = System.currentTimeMillis()
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()
// 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 ||
(viewId != null && timer.viewId == viewId) ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
data.targetEndpointIds.add(endpoint.first)
requiredLoginMethods.add(endpoint.second)
}
}
}
}
// check every login method for any dependencies
for (loginMethodId in requiredLoginMethods) {
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
templateLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
if (requiredLoginMethod != null)
data.targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
}
}
// sort and distinct every login method and endpoint
data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
data.targetLoginMethodIds.sort()
data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
data.targetEndpointIds.sort()
Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
TemplateLogin(data) { TemplateLogin(data) {
TemplateData(data) { TemplateData(data) {
completed() completed()
@ -142,7 +67,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
override fun cancel() { override fun cancel() {
Utils.d(TAG, "Cancelled") Utils.d(TAG, "Cancelled")
cancelled = true data.cancel()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {

View File

@ -7,18 +7,13 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan
import android.util.Log import android.util.Log
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanData import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanData
import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin
import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER
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 import pl.szczodrzynski.edziennik.utils.Utils
@ -30,13 +25,12 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
val internalErrorList = mutableListOf<Int>() val internalErrorList = mutableListOf<Int>()
val data: DataVulcan val data: DataVulcan
private var cancelled = false
init { init {
data = DataVulcan(app, profile, loginStore).apply { data = DataVulcan(app, profile, loginStore).apply {
callback = wrapCallback(this@Vulcan.callback) callback = wrapCallback(this@Vulcan.callback)
satisfyLoginMethods()
} }
data.satisfyLoginMethods()
} }
private fun completed() { private fun completed() {
@ -53,77 +47,9 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?) { override fun sync(featureIds: List<Int>, viewId: Int?) {
val possibleLoginMethods = data.loginMethods.toMutableList() data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
for (loginMethod in vulcanLoginMethods) {
if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += loginMethod.loginMethodId
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
data.targetLoginMethodIds.clear()
// get all endpoints for every feature, only if possible to login and possible/necessary to sync
for (featureId in featureIds) {
VulcanFeatures.filter {
it.featureId == featureId // feature ID matches
&& possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login
&& it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync
}.let {
endpointList.addAll(it)
}
}
val timestamp = System.currentTimeMillis()
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()
// 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 ||
(viewId != null && timer.viewId == viewId) ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
data.targetEndpointIds.add(endpoint.first)
requiredLoginMethods.add(endpoint.second)
}
}
}
}
// check every login method for any dependencies
for (loginMethodId in requiredLoginMethods) {
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
vulcanLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
if (requiredLoginMethod != null)
data.targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
}
}
// sort and distinct every login method and endpoint
data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
data.targetLoginMethodIds.sort()
data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
data.targetEndpointIds.sort()
Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
VulcanLogin(data) { VulcanLogin(data) {
VulcanData(data) { VulcanData(data) {
completed() completed()
@ -141,7 +67,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
override fun cancel() { override fun cancel() {
Utils.d(TAG, "Cancelled") Utils.d(TAG, "Cancelled")
cancelled = true data.cancel()
} }
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {