[APIv2] Add e-register Template

This commit is contained in:
Kuba Szczodrzyński 2019-10-05 14:26:01 +02:00
parent 18e469af71
commit 535d608829
16 changed files with 720 additions and 16 deletions

View File

@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.Librus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
import pl.szczodrzynski.edziennik.api.v2.template.Template
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import kotlin.math.min
@ -150,6 +151,7 @@ class ApiService : Service() {
edziennikInterface = when (loginStore.type) {
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
else -> null
}
if (edziennikInterface == null) {

View File

@ -48,6 +48,10 @@ const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040
const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040
const val ENDPOINT_TEMPLATE_WEB_SAMPLE = 9991
const val ENDPOINT_TEMPLATE_WEB_SAMPLE_2 = 9992
const val ENDPOINT_TEMPLATE_API_SAMPLE = 9993
val endpoints = listOf(
// LIBRUS: API
@ -141,19 +145,14 @@ val endpoints = listOf(
), listOf(LOGIN_METHOD_LIBRUS_MESSAGES))
)
/*
SYNC:
look up all endpoints for the given API and given features
load "next sync timers" for every endpoint
exclude every endpoint which does not need to sync now
check all needed login methods
create a login method list, using methods' dependencies as well
use all login methods, saving completed logins to data store
instantiate all endpoint classes and sync them (writing to data store, returns onSuccess or error Callback)
*/
val templateEndpoints = listOf(
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_TEMPLATE_WEB_SAMPLE to LOGIN_METHOD_TEMPLATE_WEB
), listOf(LOGIN_METHOD_TEMPLATE_WEB)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
ENDPOINT_TEMPLATE_WEB_SAMPLE_2 to LOGIN_METHOD_TEMPLATE_WEB
), listOf(LOGIN_METHOD_TEMPLATE_WEB)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_TEMPLATE_API_SAMPLE to LOGIN_METHOD_TEMPLATE_API
), listOf(LOGIN_METHOD_TEMPLATE_API))
)

View File

@ -93,6 +93,8 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT = 169
const val ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT = 170
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID = 171
const val ERROR_TEMPLATE_WEB_OTHER = 301
const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901
const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902
const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903

View File

@ -9,6 +9,8 @@ import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLoginApi
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLoginWeb
const val SYNERGIA_API_ENABLED = true
@ -17,6 +19,7 @@ const val LOGIN_TYPE_LIBRUS = 2
const val LOGIN_TYPE_IUCZNIOWIE = 3
const val LOGIN_TYPE_VULCAN = 4
const val LOGIN_TYPE_DEMO = 20
const val LOGIN_TYPE_TEMPLATE = 21
// LOGIN MODES
const val LOGIN_MODE_LIBRUS_EMAIL = 0
@ -25,6 +28,7 @@ const val LOGIN_MODE_LIBRUS_JST = 2
const val LOGIN_MODE_MOBIDZIENNIK_WEB = 0
const val LOGIN_MODE_IDZIENNIK_WEB = 0
const val LOGIN_MODE_VULCAN_WEB = 0
const val LOGIN_MODE_TEMPLATE_WEB = 0
// LOGIN METHODS
const val LOGIN_METHOD_NOT_NEEDED = -1
@ -37,6 +41,8 @@ const val LOGIN_METHOD_IDZIENNIK_WEB = 100
const val LOGIN_METHOD_IDZIENNIK_API = 200
const val LOGIN_METHOD_VULCAN_WEB = 100
const val LOGIN_METHOD_VULCAN_API = 200
const val LOGIN_METHOD_TEMPLATE_WEB = 100
const val LOGIN_METHOD_TEMPLATE_API = 200
val librusLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, LibrusLoginPortal::class.java)
@ -64,4 +70,14 @@ val librusLoginMethods = listOf(
.withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
}
)
val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_API, TemplateLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_TEMPLATE_WEB }
)

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template
import android.util.Log
import pl.szczodrzynski.edziennik.App
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.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin
import pl.szczodrzynski.edziennik.api.v2.templateEndpoints
import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods
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.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils
class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
companion object {
private const val TAG = "Template"
}
val internalErrorList = mutableListOf<Int>()
val data: DataTemplate
private var cancelled = false
init {
data = DataTemplate(app, profile, loginStore).apply {
callback = wrapCallback(this@Template.callback)
}
data.satisfyLoginMethods()
}
private fun completed() {
data.saveData()
callback.onCompleted()
}
/* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | |
| | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
| | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
| | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
__/ |
|__*/
override fun sync(featureIds: List<Int>, viewId: Int?) {
val possibleLoginMethods = data.loginMethods.toMutableList()
for (loginMethod in templateLoginMethods) {
if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += loginMethod.loginMethodId
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Endpoint>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
data.targetLoginMethodIds.clear()
// get all endpoints for every feature, only if possible to login
for (featureId in featureIds) {
templateEndpoints.filter {
it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods)
}
.let {
endpointList.addAll(it)
}
}
val timestamp = System.currentTimeMillis()
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, 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.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, "Endpoint IDs: ${data.targetEndpointIds}")
TemplateLogin(data) {
TemplateData(data) {
completed()
}
}
}
override fun getMessage(messageId: Int) {
}
override fun cancel() {
Utils.d(TAG, "Cancelled")
cancelled = true
}
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback {
override fun onCompleted() {
callback.onCompleted()
}
override fun onProgress(step: Int) {
callback.onProgress(step)
}
override fun onStartProgress(stringRes: Int) {
callback.onStartProgress(stringRes)
}
override fun onError(apiError: ApiError) {
when (apiError.errorCode) {
in internalErrorList -> {
// finish immediately if the same error occurs twice during the same sync
callback.onError(apiError)
}
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
internalErrorList.add(apiError.errorCode)
loginStore.removeLoginData("refreshToken") // force a clean login
//loginLibrus()
}
else -> callback.onError(apiError)
}
}
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_TEMPLATE_API
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_TEMPLATE_WEB
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
/**
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
*
* Use https://codepen.io/kubasz/pen/RwwwbGN to easily generate the student data getters/setters
*/
class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
fun isWebLoginValid() = webExpiryTime-30 > currentTimeUnix() && webCookie.isNotNullNorEmpty()
fun isApiLoginValid() = apiExpiryTime-30 > currentTimeUnix() && apiToken.isNotNullNorEmpty()
override fun satisfyLoginMethods() {
loginMethods.clear()
if (isWebLoginValid()) {
loginMethods += LOGIN_METHOD_TEMPLATE_WEB
app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name("AuthCookie")
.value(webCookie!!)
.domain("eregister.example.com")
.secure().httpOnly().build()
))
}
if (isApiLoginValid())
loginMethods += LOGIN_METHOD_TEMPLATE_API
}
/* __ __ _
\ \ / / | |
\ \ /\ / /__| |__
\ \/ \/ / _ \ '_ \
\ /\ / __/ |_) |
\/ \/ \___|_._*/
private var mWebCookie: String? = null
var webCookie: String?
get() { mWebCookie = mWebCookie ?: profile?.getStudentData("webCookie", null); return mWebCookie }
set(value) { profile?.putStudentData("webCookie", value) ?: return; mWebCookie = value }
private var mWebExpiryTime: Long? = null
var webExpiryTime: Long
get() { mWebExpiryTime = mWebExpiryTime ?: profile?.getStudentData("webExpiryTime", 0L); return mWebExpiryTime ?: 0L }
set(value) { profile?.putStudentData("webExpiryTime", value) ?: return; mWebExpiryTime = value }
/* _
/\ (_)
/ \ _ __ _
/ /\ \ | '_ \| |
/ ____ \| |_) | |
/_/ \_\ .__/|_|
| |
|*/
private var mApiToken: String? = null
var apiToken: String?
get() { mApiToken = mApiToken ?: profile?.getStudentData("apiToken", null); return mApiToken }
set(value) { profile?.putStudentData("apiToken", value) ?: return; mApiToken = value }
private var mApiExpiryTime: Long? = null
var apiExpiryTime: Long
get() { mApiExpiryTime = mApiExpiryTime ?: profile?.getStudentData("apiExpiryTime", 0L); return mApiExpiryTime ?: 0L }
set(value) { profile?.putStudentData("apiExpiryTime", value) ?: return; mApiExpiryTime = value }
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.api.v2.ERROR_TEMPLATE_WEB_OTHER
import pl.szczodrzynski.edziennik.api.v2.GET
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.currentTimeUnix
open class TemplateApi(open val data: DataTemplate) {
companion object {
private const val TAG = "TemplateApi"
}
val profileId
get() = data.profile?.id ?: -1
val profile
get() = data.profile
/**
* This will be used by all TemplateApi* endpoints.
*
* You can customize this method's parameters to best fit the implemented e-register.
* Just make sure that [tag] and [onSuccess] is present.
*/
fun apiGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject?) -> Unit) {
val json = JsonObject()
json.addProperty("foo", "bar")
json.addProperty("sample", "text")
if (currentTimeUnix() % 4L == 0L) {
// let's set a 20% chance of error, just as a test
data.error(ApiError(tag, ERROR_TEMPLATE_WEB_OTHER)
.withApiResponse("404 Not Found - this is the text returned by the API"))
return
}
onSuccess(json)
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_API_SAMPLE
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE_2
import pl.szczodrzynski.edziennik.api.v2.template.data.api.TemplateApiSample
import pl.szczodrzynski.edziennik.api.v2.template.data.web.TemplateWebSample
import pl.szczodrzynski.edziennik.api.v2.template.data.web.TemplateWebSample2
import pl.szczodrzynski.edziennik.utils.Utils
class TemplateData(val data: DataTemplate, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "TemplateData"
}
private var cancelled = false
init {
nextEndpoint(onSuccess)
}
private fun nextEndpoint(onSuccess: () -> Unit) {
if (data.targetEndpointIds.isEmpty()) {
onSuccess()
return
}
useEndpoint(data.targetEndpointIds.removeAt(0)) {
if (cancelled) {
onSuccess()
return@useEndpoint
}
nextEndpoint(onSuccess)
}
}
private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) {
Utils.d(TAG, "Using endpoint $endpointId")
when (endpointId) {
ENDPOINT_TEMPLATE_WEB_SAMPLE -> {
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
TemplateWebSample(data) { onSuccess() }
}
ENDPOINT_TEMPLATE_WEB_SAMPLE_2 -> {
data.startProgress(R.string.edziennik_progress_endpoint_school_info)
TemplateWebSample2(data) { onSuccess() }
}
ENDPOINT_TEMPLATE_API_SAMPLE -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades)
TemplateApiSample(data) { onSuccess() }
}
else -> onSuccess()
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.api.v2.ERROR_REQUEST_FAILURE
import pl.szczodrzynski.edziennik.api.v2.ERROR_RESPONSE_EMPTY
import pl.szczodrzynski.edziennik.api.v2.ERROR_TEMPLATE_WEB_OTHER
import pl.szczodrzynski.edziennik.api.v2.GET
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.currentTimeUnix
open class TemplateWeb(open val data: DataTemplate) {
companion object {
private const val TAG = "TemplateWeb"
}
val profileId
get() = data.profile?.id ?: -1
val profile
get() = data.profile
/**
* This will be used by all TemplateWeb* endpoints.
*
* You can customize this method's parameters to best fit the implemented e-register.
* Just make sure that [tag] and [onSuccess] is present.
*/
fun webGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject?) -> Unit) {
val json = JsonObject()
json.addProperty("foo", "bar")
json.addProperty("sample", "text")
if (currentTimeUnix() % 4L == 0L) {
// let's set a 20% chance of error, just as a test
data.error(ApiError(tag, ERROR_TEMPLATE_WEB_OTHER)
.withApiResponse("404 Not Found - this is the text returned by the API"))
return
}
onSuccess(json)
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data.api
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_API_SAMPLE
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateApi
import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateWeb
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
class TemplateApiSample(override val data: DataTemplate,
val onSuccess: () -> Unit) : TemplateApi(data) {
companion object {
private const val TAG = "TemplateApiSample"
}
init {
apiGet(TAG, "/api/v3/getData.php") { json ->
// here you can access and update any fields of the `data` object
// ================
// schedule a sync:
// not sooner than two days later
data.setSyncNext(ENDPOINT_TEMPLATE_API_SAMPLE, 2 * DAY)
// in two days OR on explicit "grades" sync
data.setSyncNext(ENDPOINT_TEMPLATE_API_SAMPLE, 2 * DAY, MainActivity.DRAWER_ITEM_GRADES)
// only if sync is executed on Home view
data.setSyncNext(ENDPOINT_TEMPLATE_API_SAMPLE, syncIn = null, viewId = MainActivity.DRAWER_ITEM_HOME)
// always, in every sync
data.setSyncNext(ENDPOINT_TEMPLATE_API_SAMPLE, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data.web
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateWeb
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
class TemplateWebSample(override val data: DataTemplate,
val onSuccess: () -> Unit) : TemplateWeb(data) {
companion object {
private const val TAG = "TemplateWebSample"
}
init {
webGet(TAG, "/api/v3/getData.php") { json ->
// here you can access and update any fields of the `data` object
// ================
// schedule a sync:
// not sooner than two days later
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE, 2 * DAY)
// in two days OR on explicit "grades" sync
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE, 2 * DAY, DRAWER_ITEM_GRADES)
// only if sync is executed on Home view
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE, syncIn = null, viewId = DRAWER_ITEM_HOME)
// always, in every sync
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.data.web
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE
import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE_2
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateWeb
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
class TemplateWebSample2(override val data: DataTemplate,
val onSuccess: () -> Unit) : TemplateWeb(data) {
companion object {
private const val TAG = "TemplateWebSample2"
}
init {
webGet(TAG, "/api/v3/getData.php") { json ->
// here you can access and update any fields of the `data` object
// ================
// schedule a sync:
// not sooner than two days later
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE_2, 2 * DAY)
// in two days OR on explicit "grades" sync
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE_2, 2 * DAY, MainActivity.DRAWER_ITEM_GRADES)
// only if sync is executed on Home view
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE_2, syncIn = null, viewId = MainActivity.DRAWER_ITEM_HOME)
// always, in every sync
data.setSyncNext(ENDPOINT_TEMPLATE_WEB_SAMPLE_2, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.login
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.utils.Utils
class TemplateLogin(val data: DataTemplate, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "TemplateLogin"
}
private var cancelled = false
init {
nextLoginMethod(onSuccess)
}
private fun nextLoginMethod(onSuccess: () -> Unit) {
if (data.targetLoginMethodIds.isEmpty()) {
onSuccess()
return
}
useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId ->
if (usedMethodId != -1)
data.loginMethods.add(usedMethodId)
if (cancelled) {
onSuccess()
return@useLoginMethod
}
nextLoginMethod(onSuccess)
}
}
private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) {
// this should never be true
if (data.loginMethods.contains(loginMethodId)) {
onSuccess(-1)
return
}
Utils.d(TAG, "Using login method $loginMethodId")
when (loginMethodId) {
LOGIN_METHOD_TEMPLATE_WEB -> {
data.startProgress(R.string.edziennik_progress_login_template_web)
TemplateLoginWeb(data) { onSuccess(loginMethodId) }
}
LOGIN_METHOD_TEMPLATE_API -> {
data.startProgress(R.string.edziennik_progress_login_template_api)
TemplateLoginApi(data) { onSuccess(loginMethodId) }
}
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.login
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.api.v2.ERROR_LOGIN_DATA_MISSING
import pl.szczodrzynski.edziennik.api.v2.ERROR_PROFILE_MISSING
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.currentTimeUnix
class TemplateLoginApi(val data: DataTemplate, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "TemplateLoginApi"
}
init { run {
if (data.profile == null) {
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
return@run
}
if (data.isApiLoginValid()) {
onSuccess()
}
else {
if (/*data.webLogin != null && data.webPassword != null && */true) {
loginWithCredentials()
}
else {
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
}
}
}}
fun loginWithCredentials() {
// succeed immediately
data.apiToken = "ThisIsAVeryLongToken"
data.apiExpiryTime = currentTimeUnix() + 24 * HOUR
onSuccess()
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.template.login
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.api.v2.ERROR_LOGIN_DATA_MISSING
import pl.szczodrzynski.edziennik.api.v2.ERROR_PROFILE_MISSING
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.getUnixDate
class TemplateLoginWeb(val data: DataTemplate, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "TemplateLoginWeb"
}
init { run {
if (data.profile == null) {
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
return@run
}
if (data.isWebLoginValid()) {
data.app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name("AuthCookie")
.value(data.webCookie!!)
.domain("eregister.example.com")
.secure().httpOnly().build()
))
onSuccess()
}
else {
data.app.cookieJar.clearForDomain("eregister.example.com")
if (/*data.webLogin != null && data.webPassword != null && */true) {
loginWithCredentials()
}
else {
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
}
}
}}
fun loginWithCredentials() {
// succeed immediately
data.webCookie = "ThisIsACookie"
data.webExpiryTime = currentTimeUnix() + 45 * 60 /* 45min */
onSuccess()
}
}

View File

@ -926,4 +926,6 @@
<string name="settings_register_show_teacher_absences_text">Pokazuj nieobecności nauczycieli w Terminarzu</string>
<string name="edziennik_progress_endpoint_school_info">Pobieram informacje o szkole...</string>
<string name="edziennik_progress_endpoint_grades">Pobieranie ocen ucznia...</string>
<string name="edziennik_progress_login_template_web">Logowanie do Template WEB...</string>
<string name="edziennik_progress_login_template_api">Logowanie do Template API...</string>
</resources>