[APIv2] Add Mobidziennik login & API prototype. Refactor some classes

This commit is contained in:
Kuba Szczodrzyński 2019-10-05 23:42:37 +02:00
parent 535d608829
commit ddf4fb0b46
32 changed files with 830 additions and 83 deletions

View File

@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.api.v2.events.requests.*
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.Librus
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.Mobidziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
import pl.szczodrzynski.edziennik.api.v2.template.Template
@ -151,6 +152,7 @@ class ApiService : Service() {
edziennikInterface = when (loginStore.type) {
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
else -> null
}
@ -159,8 +161,7 @@ class ApiService : Service() {
}
when (task) {
is SyncProfileRequest -> edziennikInterface?.sync(task.featureIds ?: Features.getAllIds())
is SyncViewRequest -> edziennikInterface?.sync(Features.getIdsByView(task.targetId), task.targetId)
is SyncProfileRequest -> edziennikInterface?.sync(task.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds())
is MessageGetRequest -> edziennikInterface?.getMessage(task.messageId)
}
}

View File

@ -7,7 +7,9 @@ package pl.szczodrzynski.edziennik.api.v2
const val GET = 0
const val POST = 1
const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp"
val SYSTEM_USER_AGENT = System.getProperty("http.agent") ?: "Dalvik/2.1.0 Android"
val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT LibrusMobileApp"
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv"
const val LIBRUS_REDIRECT_URL = "http://localhost/bar"
@ -36,4 +38,8 @@ const val LIBRUS_JST_DEMO_PIN = "1290"
const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies"
const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module/"
const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT

View File

@ -4,7 +4,7 @@
package pl.szczodrzynski.edziennik.api.v2
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.api.v2.models.Feature
const val ENDPOINT_LIBRUS_API_ME = 1001
const val ENDPOINT_LIBRUS_API_SCHOOLS = 1002
@ -55,11 +55,11 @@ const val ENDPOINT_TEMPLATE_API_SAMPLE = 9993
val endpoints = listOf(
// LIBRUS: API
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf(
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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf(
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,
@ -67,7 +67,7 @@ val endpoints = listOf(
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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
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,
@ -81,49 +81,49 @@ val endpoints = listOf(
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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_NOTICES, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_BEHAVIOUR, listOf(
ENDPOINT_LIBRUS_API_NOTICES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCE, listOf(
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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf(
ENDPOINT_LIBRUS_API_ANNOUNCEMENTS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_API_ME to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf(
ENDPOINT_LIBRUS_API_CLASSES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf(
ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_LIBRUS_API_LUCKY_NUMBER to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf(
ENDPOINT_LIBRUS_API_USERS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf(
ENDPOINT_LIBRUS_API_SUBJECTS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf(
ENDPOINT_LIBRUS_API_CLASSROOMS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),
/*Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
@ -131,28 +131,28 @@ val endpoints = listOf(
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
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(
Feature(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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_SENT, listOf(
ENDPOINT_LIBRUS_MESSAGES_SENT to LOGIN_METHOD_LIBRUS_MESSAGES
), listOf(LOGIN_METHOD_LIBRUS_MESSAGES))
)
val templateEndpoints = listOf(
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
Feature(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(
Feature(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(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_TEMPLATE_API_SAMPLE to LOGIN_METHOD_TEMPLATE_API
), listOf(LOGIN_METHOD_TEMPLATE_API))
)

View File

@ -54,7 +54,7 @@ const val ERROR_LOGIN_LIBRUS_API_INVALID_CLIENT = 126
const val ERROR_LOGIN_LIBRUS_API_REG_ACCEPT_NEEDED = 127
const val ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR = 128
const val ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED = 129
const val ERROR_LOGIN_LIBRUS_API_INVALID_GRANT = 130
const val ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN = 130
const val ERROR_LOGIN_LIBRUS_API_OTHER = 131
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING = 132
const val ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED = 133
@ -63,7 +63,7 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING = 139
const val ERROR_LIBRUS_API_TOKEN_EXPIRED = 140
const val ERROR_LIBRUS_API_INSUFFICIENT_SCOPES = 141
const val ERROR_LIBRUS_API_OTHER = 142
const val ERROR_LIBRUS_API_REQUEST_DENIED = 143
const val ERROR_LIBRUS_API_ACCESS_DENIED = 143
const val ERROR_LIBRUS_API_RESOURCE_NOT_FOUND = 144
const val ERROR_LIBRUS_API_DATA_NOT_FOUND = 145
const val ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC = 146
@ -93,6 +93,18 @@ 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_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_DEVICE = 203
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED = 204
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_MAINTENANCE = 205
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS = 206
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OTHER = 210
const val ERROR_MOBIDZIENNIK_WEB_ACCESS_DENIED = 211
const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY = 212
const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 212
const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213
const val ERROR_TEMPLATE_WEB_OTHER = 301
const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901

View File

@ -15,19 +15,19 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
const val FEATURE_ALL = 0
const val FEATURE_TIMETABLE = 1
const val FEATURE_AGENDA = 2
const val FEATURE_GRADES = 3
const val FEATURE_HOMEWORK = 4
const val FEATURE_NOTICES = 5
const val FEATURE_ATTENDANCES = 6
const val FEATURE_BEHAVIOUR = 5
const val FEATURE_ATTENDANCE = 6
const val FEATURE_MESSAGES_INBOX = 7
const val FEATURE_MESSAGES_OUTBOX = 8
const val FEATURE_MESSAGES_SENT = 8
const val FEATURE_ANNOUNCEMENTS = 9
const val FEATURE_ALWAYS_NEEDED = 100
const val FEATURE_STUDENT_INFO = 101
const val FEATURE_STUDENT_NUMBER = 109
const val FEATURE_SCHOOL_INFO = 102
@ -42,6 +42,7 @@ const val FEATURE_MESSAGE_GET = 201
object Features {
private fun getAllNecessary(): List<Int> = listOf(
FEATURE_ALWAYS_NEEDED,
FEATURE_STUDENT_INFO,
FEATURE_STUDENT_NUMBER,
FEATURE_SCHOOL_INFO,
@ -57,30 +58,30 @@ object Features {
FEATURE_AGENDA,
FEATURE_GRADES,
FEATURE_HOMEWORK,
FEATURE_NOTICES,
FEATURE_ATTENDANCES,
FEATURE_BEHAVIOUR,
FEATURE_ATTENDANCE,
FEATURE_MESSAGES_INBOX,
FEATURE_MESSAGES_OUTBOX,
FEATURE_MESSAGES_SENT,
FEATURE_ANNOUNCEMENTS)
fun getAllIds(): List<Int> = getAllFeatures() + getAllNecessary()
fun getIdsByView(targetId: Int): List<Int> {
return when (targetId) {
fun getIdsByView(targetId: Int, targetType: Int): List<Int> {
return (when (targetId) {
DRAWER_ITEM_HOME -> getAllFeatures()
DRAWER_ITEM_TIMETABLE -> listOf(FEATURE_TIMETABLE)
DRAWER_ITEM_AGENDA -> listOf(FEATURE_AGENDA)
DRAWER_ITEM_GRADES -> listOf(FEATURE_GRADES)
DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) {
DRAWER_ITEM_MESSAGES -> when (targetType) {
TYPE_RECEIVED -> listOf(FEATURE_MESSAGES_INBOX)
TYPE_SENT -> listOf(FEATURE_MESSAGES_OUTBOX)
else -> listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX)
TYPE_SENT -> listOf(FEATURE_MESSAGES_SENT)
else -> listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_SENT)
}
DRAWER_ITEM_HOMEWORK -> listOf(FEATURE_HOMEWORK)
DRAWER_ITEM_BEHAVIOUR -> listOf(FEATURE_NOTICES)
DRAWER_ITEM_ATTENDANCE -> listOf(FEATURE_ATTENDANCES)
DRAWER_ITEM_BEHAVIOUR -> listOf(FEATURE_BEHAVIOUR)
DRAWER_ITEM_ATTENDANCE -> listOf(FEATURE_ATTENDANCE)
DRAWER_ITEM_ANNOUNCEMENTS -> listOf(FEATURE_ANNOUNCEMENTS)
else -> getAllFeatures()
} + getAllNecessary()
} + getAllNecessary()).sorted()
}
}

View File

@ -8,17 +8,23 @@ import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
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.mobidziennik.login.MobidziennikLoginWeb
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
// librus
// mobidziennik
// idziennik
// vulcan
// mobireg
const val SYNERGIA_API_ENABLED = true
const val LOGIN_TYPE_MOBIDZIENNIK = 1
const val LOGIN_TYPE_LIBRUS = 2
const val LOGIN_TYPE_IUCZNIOWIE = 3
const val LOGIN_TYPE_MOBIDZIENNIK = 1
const val LOGIN_TYPE_IDZIENNIK = 3
const val LOGIN_TYPE_VULCAN = 4
const val LOGIN_TYPE_DEMO = 20
const val LOGIN_TYPE_TEMPLATE = 21
// LOGIN MODES
@ -36,7 +42,8 @@ const val LOGIN_METHOD_LIBRUS_PORTAL = 100
const val LOGIN_METHOD_LIBRUS_API = 200
const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300
const val LOGIN_METHOD_LIBRUS_MESSAGES = 400
const val LOGIN_METHOD_MOBIDZIENNIK_API = 100
const val LOGIN_METHOD_MOBIDZIENNIK_WEB = 100
const val LOGIN_METHOD_MOBIDZIENNIK_API2 = 300
const val LOGIN_METHOD_IDZIENNIK_WEB = 100
const val LOGIN_METHOD_IDZIENNIK_API = 200
const val LOGIN_METHOD_VULCAN_WEB = 100
@ -72,6 +79,12 @@ val librusLoginMethods = listOf(
}
)
val mobidziennikLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_WEB, MobidziennikLoginWeb::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true }

View File

@ -6,8 +6,8 @@ package pl.szczodrzynski.edziennik.api.v2.events.requests
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
data class SyncProfileRequest(override val profileId: Int, val featureIds: List<Int>? = null) : ApiTask(profileId) {
data class SyncProfileRequest(override val profileId: Int, val viewIds: List<Pair<Int, Int>>? = null) : ApiTask(profileId) {
override fun toString(): String {
return "SyncProfileRequest(profileId=$profileId, featureIds=$featureIds)"
return "SyncProfileRequest(profileId=$profileId, viewIds=$viewIds)"
}
}

View File

@ -6,8 +6,8 @@ package pl.szczodrzynski.edziennik.api.v2.events.requests
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
class SyncViewRequest(override val profileId: Int, val targetId: Int) : ApiTask(profileId) {
class SyncViewRequest(override val profileId: Int, val targetId: Int, val targetType: Int) : ApiTask(profileId) {
override fun toString(): String {
return "SyncViewRequest(profileId=$profileId, targetId=$targetId)"
return "SyncViewRequest(profileId=$profileId, targetId=$targetId, targetType=$targetType)"
}
}

View File

@ -4,12 +4,12 @@
package pl.szczodrzynski.edziennik.api.v2.interfaces
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.api.v2.models.Feature
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
/**
* A callback passed only to an e-register class.
* All [Endpoint]s and [LoginMethod]s receive this callback,
* All [Feature]s and [LoginMethod]s receive this callback,
* but may only use [EndpointCallback]'s methods.
*/
interface EdziennikCallback : EndpointCallback {

View File

@ -5,11 +5,11 @@
package pl.szczodrzynski.edziennik.api.v2.interfaces
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.api.v2.models.Feature
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
/**
* A callback passed to all [Endpoint]s and [LoginMethod]s
* A callback passed to all [Feature]s and [LoginMethod]s
*/
interface EndpointCallback {
fun onError(apiError: ApiError)

View File

@ -16,7 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
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.api.v2.models.Feature
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
@ -60,7 +60,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Endpoint>()
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
@ -80,7 +80,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Endpoint::featureId, Endpoint::priority))
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()

View File

@ -5,7 +5,6 @@
package pl.szczodrzynski.edziennik.api.v2.librus.data
import okhttp3.Cookie
import okhttp3.HttpUrl
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_MESSAGES
@ -90,7 +89,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
* A Synergia login, like 1234567u.
* Used: for login (API Login Method) in Synergia mode.
* Used: for login (Synergia Login Method) in Synergia mode.
* And also in various places in [pl.szczodrzynski.edziennik.api.v2.models.Endpoint]s
* And also in various places in [pl.szczodrzynski.edziennik.api.v2.models.Feature]s
*/
private var mApiLogin: String? = null
var apiLogin: String?

View File

@ -41,7 +41,7 @@ open class LibrusApi(open val data: DataLibrus) {
when (code) {
"TokenIsExpired" -> ERROR_LIBRUS_API_TOKEN_EXPIRED
"Insufficient scopes" -> ERROR_LIBRUS_API_INSUFFICIENT_SCOPES
"Request is denied" -> ERROR_LIBRUS_API_REQUEST_DENIED
"Request is denied" -> ERROR_LIBRUS_API_ACCESS_DENIED
"Resource not found" -> ERROR_LIBRUS_API_RESOURCE_NOT_FOUND
"NotFound" -> ERROR_LIBRUS_API_DATA_NOT_FOUND
"AccessDeny" -> when (json.getString("Message")) {

View File

@ -126,7 +126,7 @@ class LibrusLoginApi {
"librus_reg_accept_needed" -> ERROR_LOGIN_LIBRUS_API_REG_ACCEPT_NEEDED
"librus_change_password_error" -> ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR
"librus_password_change_required" -> ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED
"invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_GRANT
"invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN
else -> ERROR_LOGIN_LIBRUS_API_OTHER
}.let { errorCode ->
data.error(ApiError(TAG, errorCode)

View File

@ -82,7 +82,7 @@ class LibrusLoginSynergia(val data: DataLibrus, val onSuccess: () -> Unit) {
when (code) {
"TokenIsExpired" -> ERROR_LIBRUS_API_TOKEN_EXPIRED
"Insufficient scopes" -> ERROR_LIBRUS_API_INSUFFICIENT_SCOPES
"Request is denied" -> ERROR_LIBRUS_API_REQUEST_DENIED
"Request is denied" -> ERROR_LIBRUS_API_ACCESS_DENIED
"Resource not found" -> ERROR_LIBRUS_API_RESOURCE_NOT_FOUND
"NotFound" -> ERROR_LIBRUS_API_DATA_NOT_FOUND
"AccessDeny" -> when (json.getString("Message")) {

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik
import android.util.Log
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Feature
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.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 Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
companion object {
private const val TAG = "Mobidziennik"
}
val internalErrorList = mutableListOf<Int>()
val data: DataMobidziennik
private var cancelled = false
init {
data = DataMobidziennik(app, profile, loginStore).apply {
callback = wrapCallback(this@Mobidziennik.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 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
for (featureId in featureIds) {
MobidziennikFeatures.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(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 ||
(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, "Endpoint IDs: ${data.targetEndpointIds}")
MobidziennikLogin(data) {
MobidziennikData(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,107 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.models.Feature
const val ENDPOINT_MOBIDZIENNIK_API = 1000
const val ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX = 2011
const val ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_SENT = 2012
const val ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL = 2019
const val ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR = 2020
const val ENDPOINT_MOBIDZIENNIK_WEB_GRADES = 2030
const val ENDPOINT_MOBIDZIENNIK_WEB_NOTICES = 2040
const val ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE = 2050
const val ENDPOINT_MOBIDZIENNIK_WEB_MANUALS = 2100
const val ENDPOINT_MOBIDZIENNIK_API2 = 3000
val MobidziennikFeatures = listOf(
// timetable
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TIMETABLE, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
// agenda
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_AGENDA, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB,
ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)),
// grades
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_GRADES, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB,
ENDPOINT_MOBIDZIENNIK_WEB_GRADES to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)),
// homework
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_HOMEWORK, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
// behaviour
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_BEHAVIOUR, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB,
ENDPOINT_MOBIDZIENNIK_WEB_NOTICES to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)),
// attendance
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ATTENDANCE, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
// messages
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_INBOX, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_SENT, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
/*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_STUDENT_NUMBER, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_SCHOOL_INFO, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_CLASS_INFO, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TEAM_INFO, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TEACHERS, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_SUBJECTS, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_CLASSROOMS, listOf(
ENDPOINT_MOBIDZIENNIK_API to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),*/
// lucky number possibilities
// all endpoints that may supply the lucky number
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_MANUALS to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 10 },
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 3 },
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_GRADES to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 2 },
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_NOTICES to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 1 },
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_MOBIDZIENNIK_WEB
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 4 }
)

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_MOBIDZIENNIK_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
class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
fun isWebLoginValid() = webSessionIdExpiryTime-30 > currentTimeUnix()
&& webSessionValue.isNotNullNorEmpty()
&& webSessionKey.isNotNullNorEmpty()
&& webServerId.isNotNullNorEmpty()
override fun satisfyLoginMethods() {
loginMethods.clear()
if (isWebLoginValid()) {
loginMethods += LOGIN_METHOD_MOBIDZIENNIK_WEB
}
}
private var mLoginServerName: String? = null
var loginServerName: String?
get() { mLoginServerName = mLoginServerName ?: loginStore.getLoginData("serverName", null); return mLoginServerName }
set(value) { loginStore.putLoginData("serverName", value); mLoginServerName = value }
private var mLoginUsername: String? = null
var loginUsername: String?
get() { mLoginUsername = mLoginUsername ?: loginStore.getLoginData("username", null); return mLoginUsername }
set(value) { loginStore.putLoginData("username", value); mLoginUsername = value }
private var mLoginPassword: String? = null
var loginPassword: String?
get() { mLoginPassword = mLoginPassword ?: loginStore.getLoginData("password", null); return mLoginPassword }
set(value) { loginStore.putLoginData("password", value); mLoginPassword = value }
/* __ __ _
\ \ / / | |
\ \ /\ / /__| |__
\ \/ \/ / _ \ '_ \
\ /\ / __/ |_) |
\/ \/ \___|_._*/
private var mWebSessionKey: String? = null
var webSessionKey: String?
get() { mWebSessionKey = mWebSessionKey ?: loginStore.getLoginData("sessionCookie", null); return mWebSessionKey }
set(value) { loginStore.putLoginData("sessionCookie", value); mWebSessionKey = value }
private var mWebSessionValue: String? = null
var webSessionValue: String?
get() { mWebSessionValue = mWebSessionValue ?: loginStore.getLoginData("sessionID", null); return mWebSessionValue }
set(value) { loginStore.putLoginData("sessionID", value); mWebSessionValue = value }
private var mWebServerId: String? = null
var webServerId: String?
get() { mWebServerId = mWebServerId ?: loginStore.getLoginData("sessionServer", null); return mWebServerId }
set(value) { loginStore.putLoginData("sessionServer", value); mWebServerId = value }
private var mWebSessionIdExpiryTime: Long? = null
var webSessionIdExpiryTime: Long
get() { mWebSessionIdExpiryTime = mWebSessionIdExpiryTime ?: loginStore.getLoginData("sessionIDTime", 0L); return mWebSessionIdExpiryTime ?: 0L }
set(value) { loginStore.putLoginData("sessionIDTime", value); mWebSessionIdExpiryTime = value }
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.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.mobidziennik.*
import pl.szczodrzynski.edziennik.api.v2.template.data.DataTemplate
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 MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "MobidziennikData"
}
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_MOBIDZIENNIK_API -> {
data.startProgress(R.string.edziennik_progress_endpoint_data)
MobidziennikApi(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
MobidziennikWebMessagesInbox(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages)
MobidziennikWebMessagesAll(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR -> {
data.startProgress(R.string.edziennik_progress_endpoint_calendar)
MobidziennikWebCalendar(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades)
MobidziennikWebGrades(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_NOTICES -> {
data.startProgress(R.string.edziennik_progress_endpoint_behaviour)
MobidziennikWebNotices(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
MobidziennikWebAttendance(data) { onSuccess() }
}
ENDPOINT_MOBIDZIENNIK_WEB_MANUALS -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
MobidziennikWebManuals(data) { onSuccess() }
}*/
else -> onSuccess()
}
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
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.data.api.AppError
import pl.szczodrzynski.edziennik.data.api.AppError.*
import pl.szczodrzynski.edziennik.utils.Utils.d
open class MobidziennikWeb(open val data: DataMobidziennik) {
companion object {
private const val TAG = "MobidziennikWeb"
}
val profileId
get() = data.profile?.id ?: -1
val profile
get() = data.profile
fun webGet(tag: String, endpoint: String, method: Int = GET, payload: List<Pair<String, String>>? = null, onSuccess: (text: String) -> Unit) {
val url = "https://${data.loginServerName}.mobidziennik.pl$endpoint"
d(tag, "Requesting $url")
if (data.webSessionKey == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY)
return
}
if (data.webSessionValue == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE)
return
}
if (data.webServerId == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID)
return
}
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
if (text.isNullOrEmpty()) {
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
if (text == "Nie jestes zalogowany") {
data.error(ApiError(TAG, ERROR_MOBIDZIENNIK_WEB_ACCESS_DENIED)
.withResponse(response))
return
}
onSuccess(text)
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
data.app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name(data.webSessionKey!!)
.value(data.webSessionValue!!)
.domain("${data.loginServerName}.mobidziennik.pl")
.secure().httpOnly().build(),
Cookie.Builder()
.name("SERVERID")
.value(data.webServerId!!)
.domain("${data.loginServerName}.mobidziennik.pl")
.secure().httpOnly().build()
))
Request.builder()
.url(url)
.userAgent(MOBIDZIENNIK_USER_AGENT)
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.login
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_MOBIDZIENNIK_WEB
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.DataMobidziennik
import pl.szczodrzynski.edziennik.utils.Utils
class MobidziennikLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "MobidziennikLogin"
}
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_MOBIDZIENNIK_WEB -> {
data.startProgress(R.string.edziennik_progress_login_mobidziennik_web)
MobidziennikLoginWeb(data) { onSuccess(loginMethodId) }
}
}
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.login
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "MobidziennikLoginWeb"
}
init { run {
if (data.profile == null) {
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
return@run
}
if (data.isWebLoginValid()) {
onSuccess()
}
else {
if (data.loginServerName.isNotNullNorEmpty() && data.loginUsername.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
data.app.cookieJar.clearForDomain(data.loginServerName + ".mobidziennik.pl")
loginWithCredentials()
}
else {
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
}
}
}}
private fun loginWithCredentials() {
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
if (text != "ok") {
when {
text == "Nie jestes zalogowany" -> ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN
text == "ifun" -> ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_DEVICE
text == "stare haslo" -> ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD
text == "Archiwum" -> ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED
text == "Trwają prace techniczne lub pojawił się jakiś problem" -> ERROR_LOGIN_MOBIDZIENNIK_WEB_MAINTENANCE
text?.contains("Uuuups... nieprawidłowy adres") == true -> ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS
text?.contains("przerwa techniczna") == true -> ERROR_LOGIN_MOBIDZIENNIK_WEB_MAINTENANCE
else -> ERROR_LOGIN_MOBIDZIENNIK_WEB_OTHER
}.let { errorCode ->
data.error(ApiError(TAG, errorCode)
.withApiResponse(text)
.withResponse(response))
return
}
}
val cookies = data.app.cookieJar.getForDomain("${data.loginServerName}.mobidziennik.pl")
val cookie = cookies.singleOrNull { it.name().length > 32 }
val sessionKey = cookie?.name()
val sessionId = cookie?.value()
if (sessionId == null) {
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID)
.withResponse(response)
.withApiResponse(text))
return
}
data.webSessionKey = sessionKey
data.webSessionValue = sessionId
data.webServerId = data.app.cookieJar.getCookie("${data.loginServerName}.mobidziennik.pl", "SERVERID")
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
onSuccess()
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url("https://${data.loginServerName}.mobidziennik.pl/api/")
.userAgent(System.getProperty("http.agent"))
.contentType("application/x-www-form-urlencoded; charset=UTF-8")
.addParameter("wersja", "20")
.addParameter("ip", data.app.deviceId)
.addParameter("login", data.loginUsername)
.addParameter("haslo", data.loginPassword)
.addParameter("token", data.app.appConfig.fcmTokens[LOGIN_TYPE_MOBIDZIENNIK]?.first)
.post()
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -38,7 +38,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
var fakeLogin = false
/**
* A callback passed to all [Endpoint]s and [LoginMethod]s
* A callback passed to all [Feature]s and [LoginMethod]s
*/
lateinit var callback: EndpointCallback

View File

@ -10,15 +10,14 @@ package pl.szczodrzynski.edziennik.api.v2.models
*
* @param loginType type of the e-register this endpoint handles
* @param featureId a feature ID
* @param endpointIds a [List] of [Endpoint]s that satisfy this feature ID
* @param endpointIds a [List] of [Feature]s that satisfy this feature ID
* @param requiredLoginMethod a required login method, which will have to be executed before this endpoint.
*/
data class Endpoint(
data class Feature(
val loginType: Int,
val featureId: Int,
val endpointIds: List<Pair<Int, Int>>,
val requiredLoginMethods: List<Int>
) {
val priority
get() = endpointIds.size
var priority = endpointIds.size
}

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
/**
* A Login Method descriptor class.
*
* This is used by the API to satisfy all [Endpoint]s' dependencies.
* This is used by the API to satisfy all [Feature]s' dependencies.
* A login method may have its own dependencies which need to be
* satisfied before the [loginMethodClass]'s constructor is invoked.
*

View File

@ -11,7 +11,7 @@ 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.models.Feature
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
@ -62,7 +62,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
}
//var highestLoginMethod = 0
var endpointList = mutableListOf<Endpoint>()
var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<Int>()
data.targetEndpointIds.clear()
@ -82,7 +82,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
endpointList = endpointList
// sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Endpoint::featureId, Endpoint::priority))
.sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature
.distinctBy { it.featureId }
.toMutableList()

View File

@ -157,7 +157,7 @@ public class LoginStore {
case LOGIN_TYPE_LIBRUS:
return "LOGIN_TYPE_LIBRUS";
case LOGIN_TYPE_IUCZNIOWIE:
return "LOGIN_TYPE_IUCZNIOWIE";
return "LOGIN_TYPE_IDZIENNIK";
case LOGIN_TYPE_VULCAN:
return "LOGIN_TYPE_VULCAN";
case LOGIN_TYPE_DEMO:

View File

@ -223,7 +223,7 @@ class ProfileFull : Profile {
when (loginStoreType) {
LOGIN_TYPE_MOBIDZIENNIK -> return "LOGIN_TYPE_MOBIDZIENNIK"
LOGIN_TYPE_LIBRUS -> return "LOGIN_TYPE_LIBRUS"
LOGIN_TYPE_IUCZNIOWIE -> return "LOGIN_TYPE_IUCZNIOWIE"
LOGIN_TYPE_IUCZNIOWIE -> return "LOGIN_TYPE_IDZIENNIK"
LOGIN_TYPE_VULCAN -> return "LOGIN_TYPE_VULCAN"
LOGIN_TYPE_DEMO -> return "LOGIN_TYPE_DEMO"
else -> return "LOGIN_TYPE_UNKNOWN"

View File

@ -39,6 +39,7 @@ import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.List;
import kotlin.Pair;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
@ -66,6 +67,8 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE;
import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_GRADES;
import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_HOME;
import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_MESSAGES;
import static pl.szczodrzynski.edziennik.api.v2.FeaturesKt.FEATURE_STUDENT_INFO;
import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_FINAL;
import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED;
@ -74,6 +77,7 @@ import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMES
import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_FINAL;
import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_PROPOSED;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
import static pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT;
public class HomeFragment extends Fragment {
private static final String TAG = "HomeFragment";
@ -129,19 +133,23 @@ public class HomeFragment extends Fragment {
b.test2.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
b.test2.setOnClickListener((v -> {
List<Integer> list = new ArrayList<>();
list.add(FEATURE_STUDENT_INFO);
EventBus.getDefault().post(new SyncProfileRequest(16, list));
List<Pair<Integer, Integer>> list = new ArrayList<>();
list.add(new Pair<>(DRAWER_ITEM_HOME, 0));
EventBus.getDefault().post(new SyncProfileRequest(10, list));
}));
b.test3.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
b.test3.setOnClickListener((v -> {
EventBus.getDefault().post(new SyncProfileRequest(16, null));
List<Pair<Integer, Integer>> list = new ArrayList<>();
list.add(new Pair<>(DRAWER_ITEM_MESSAGES, TYPE_SENT));
EventBus.getDefault().post(new SyncProfileRequest(10, list));
}));
b.test4.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
b.test4.setOnClickListener((v -> {
EventBus.getDefault().post(new SyncViewRequest(16, DRAWER_ITEM_GRADES));
List<Pair<Integer, Integer>> list = new ArrayList<>();
list.add(new Pair<>(DRAWER_ITEM_GRADES, 0));
EventBus.getDefault().post(new SyncProfileRequest(10, list));
}));
//((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS()));

View File

@ -64,7 +64,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="Sync profile 16 - feature me"
android:text="Sync profile 16 - view home"
android:visibility="gone"/>
<com.google.android.material.button.MaterialButton
@ -73,7 +73,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="Sync profile 16 - feature all"
android:text="Sync profile 16 - view messages sent"
android:visibility="gone"/>
<com.google.android.material.button.MaterialButton

View File

@ -928,4 +928,13 @@
<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>
<string name="edziennik_progress_login_mobidziennik_web">Logowanie do MobiDziennika...</string>
<string name="edziennik_progress_login_mobidziennik_api">Logowanie do API MobiDziennika...</string>
<string name="edziennik_progress_endpoint_data">Pobieram dane...</string>
<string name="edziennik_progress_endpoint_messages_inbox">Pobieram wiadomości odebrane...</string>
<string name="edziennik_progress_endpoint_messages">Pobieram wiadomości...</string>
<string name="edziennik_progress_endpoint_lucky_number">Pobieram szczęśliwy numerek...</string>
<string name="edziennik_progress_endpoint_calendar">Pobieram kalendarz...</string>
<string name="edziennik_progress_endpoint_behaviour">Pobieram uwagi ucznia...</string>
<string name="edziennik_progress_endpoint_attendance">Pobieram frekwencję ucznia...</string>
</resources>

View File

@ -74,6 +74,27 @@ public class PersistentCookieJar implements ClearableCookieJar {
return validCookies;
}
@NonNull
synchronized public List<Cookie> getForDomain(String domain) {
List<Cookie> removedCookies = new ArrayList<>();
List<Cookie> validCookies = new ArrayList<>();
for (Iterator<Cookie> it = cache.iterator(); it.hasNext(); ) {
Cookie currentCookie = it.next();
if (isCookieExpired(currentCookie)) {
removedCookies.add(currentCookie);
it.remove();
} else if (domain.equals(currentCookie.domain())) {
validCookies.add(currentCookie);
}
}
persistor.removeAll(removedCookies);
return validCookies;
}
@Nullable
synchronized public String getCookie(String domain, String name) {
String cookieValue = null;