From 76d39ac623c9848cb6ac8078390d7d882b063d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 21 Sep 2019 23:01:31 +0200 Subject: [PATCH] [APIv2/Librus] Create Login methods, update API structure --- .../szczodrzynski/edziennik/api/AppError.java | 3 +- .../szczodrzynski/edziennik/api/Librus.java | 3 - .../pl/szczodrzynski/edziennik/api/v2/Api.kt | 21 ++ .../edziennik/api/v2/Constants.kt | 43 ++- .../edziennik/api/v2/Endpoints.kt | 4 +- .../szczodrzynski/edziennik/api/v2/Errors.kt | 41 +++ .../edziennik/api/v2/LoginMethods.kt | 44 ++++ .../edziennik/api/v2/librus/Librus.kt | 170 ++---------- .../edziennik/api/v2/librus/LibrusHelpers.kt | 6 + .../edziennik/api/v2/librus/LibrusOld.kt | 172 ++++++++++++ .../edziennik/api/v2/librus/LibrusTest.kt | 63 +++++ .../api/v2/librus/data/DataLibrus.kt | 116 ++++++++- .../edziennik/api/v2/librus/data/LibrusApi.kt | 115 ++++++++ .../api/v2/librus/data/LibrusApiGrades.kt | 9 +- .../api/v2/librus/data/LibrusApiMe.kt | 35 +++ .../v2/librus/data/LibrusSynergiaGrades.kt | 12 +- .../edziennik/api/v2/librus/login/LoginJst.kt | 20 -- .../api/v2/librus/login/LoginLibrus.kt | 228 +++++----------- .../api/v2/librus/login/LoginLibrusApi.kt | 246 ++++++++++++++++++ .../v2/librus/login/LoginLibrusMessages.kt | 8 + .../api/v2/librus/login/LoginLibrusPortal.kt | 179 +++++++++++++ .../v2/librus/login/LoginLibrusSynergia.kt | 8 + .../api/v2/librus/login/LoginSynergia.kt | 12 - .../v2/librus/login/SynergiaTokenExtractor.kt | 81 +++--- .../api/v2/models/{DataStore.kt => Data.kt} | 73 +++--- .../edziennik/api/v2/models/Endpoint.kt | 3 +- .../edziennik/api/v2/models/Feature.kt | 1 - .../edziennik/api/v2/models/LoginMethod.kt | 3 +- .../edziennik/fragments/HomeFragment.java | 44 +--- 29 files changed, 1258 insertions(+), 505 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/{DataStore.kt => Data.kt} (61%) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java index d5f6c2a7..a22c8df8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java @@ -47,11 +47,10 @@ public class AppError { public static final int CODE_LIBRUS_DISCONNECTED = 31; public static final int CODE_PROFILE_ARCHIVED = 30; + public static final int CODE_INTERNAL_MISSING_DATA = 100; // internal errors - not for user's information. // these error codes are processed in API main classes - public static final int CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120; - public static final int CODE_INTERNAL_LIBRUS_ACCOUNT_410_ = 120; public String TAG; public int line; diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java index c3d01bb3..4a552dd8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java @@ -51,7 +51,6 @@ import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.api.v2.models.DataStore; import pl.szczodrzynski.edziennik.datamodels.Announcement; import pl.szczodrzynski.edziennik.datamodels.Attendance; import pl.szczodrzynski.edziennik.datamodels.Event; @@ -213,8 +212,6 @@ public class Librus implements EdziennikInterface { this.fullSync = profile == null || profile.getEmpty() || profile.shouldFullSync(activityContext); this.today = Date.getToday(); - DataStore ds = new DataStore(app.db, profileId); - this.librusEmail = loginStore.getLoginData("email", ""); this.librusPassword = loginStore.getLoginData("password", ""); if (profile == null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt new file mode 100644 index 00000000..97060c70 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +import com.crashlytics.android.Crashlytics +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.v2.models.Data + +open class Api(open val data: Data) { + fun finishWithError(error: AppError) { + try { + data.saveData() + } catch (e: Exception) { + Crashlytics.logException(e) + } + + data.callback.onError(null, error) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 0fa7e319..047dd38f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -16,33 +16,8 @@ const val FEATURE_MESSAGES_INBOX = 7 const val FEATURE_MESSAGES_OUTBOX = 8 const val FEATURE_ANNOUNCEMENTS = 9 -const val LOGIN_TYPE_MOBIDZIENNIK = 1 -const val LOGIN_TYPE_LIBRUS = 2 -const val LOGIN_TYPE_IUCZNIOWIE = 3 -const val LOGIN_TYPE_VULCAN = 4 -const val LOGIN_TYPE_DEMO = 20 - -// LOGIN MODES -const val LOGIN_MODE_LIBRUS_EMAIL = 0 -const val LOGIN_MODE_LIBRUS_SYNERGIA = 1 -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 - -// LOGIN METHODS -const val LOGIN_METHOD_NOT_NEEDED = -1 -const val LOGIN_METHOD_LIBRUS_PORTAL = 0 -const val LOGIN_METHOD_LIBRUS_API = 1 -const val LOGIN_METHOD_LIBRUS_SYNERGIA = 2 -const val LOGIN_METHOD_LIBRUS_MESSAGES = 3 -const val LOGIN_METHOD_MOBIDZIENNIK_API = 0 -const val LOGIN_METHOD_IDZIENNIK_WEB = 0 -const val LOGIN_METHOD_IDZIENNIK_API = 1 -const val LOGIN_METHOD_VULCAN_WEB = 0 -const val LOGIN_METHOD_VULCAN_API = 1 - const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android 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" const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?client_id=$LIBRUS_CLIENT_ID&redirect_uri=$LIBRUS_REDIRECT_URL&response_type=code" @@ -51,3 +26,19 @@ const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token" const val LIBRUS_ACCOUNT_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts/fresh/" // + login const val LIBRUS_ACCOUNTS_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts" + +const val LIBRUS_API_URL = "https://api.librus.pl/2.0/" +const val LIBRUS_API_TOKEN_URL = "https://api.librus.pl/OAuth/Token" +const val LIBRUS_API_TOKEN_JST_URL = "https://api.librus.pl/OAuth/TokenJST" +const val LIBRUS_API_AUTHORIZATION = "Mjg6ODRmZGQzYTg3YjAzZDNlYTZmZmU3NzdiNThiMzMyYjE=" +const val LIBRUS_API_SECRET_JST = "18b7c1ee08216f636a1b1a2440e68398" +const val LIBRUS_API_CLIENT_ID_JST = "49" +//const val LIBRUS_API_CLIENT_ID_JST_REFRESH = "42" + +const val LIBRUS_JST_DEMO_CODE = "68656A21" +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=" \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt index bcbc0cc9..8def0345 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt @@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2 import android.util.Log import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiGrades +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiMe import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergiaGrades import pl.szczodrzynski.edziennik.api.v2.models.Endpoint @@ -13,7 +14,8 @@ const val ENDPOINT_LIBRUS_API_ME = 0 const val ENDPOINT_LIBRUS_API_GRADES = 0 const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 0 -val librusEndpoints = listOf( +val endpoints = listOf( + Endpoint(LOGIN_TYPE_LIBRUS, ENDPOINT_LIBRUS_API_ME, null, LibrusApiMe::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API}, Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusSynergiaGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_SYNERGIA }, Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusApiGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API } ) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt new file mode 100644 index 00000000..4f944965 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +/*const val CODE_OTHER = 0 +const val CODE_OK = 1 +const val CODE_NO_INTERNET = 10 +const val CODE_SSL_ERROR = 13 +const val CODE_ARCHIVED = 5 +const val CODE_MAINTENANCE = 6 +const val CODE_LOGIN_ERROR = 7 +const val CODE_ACCOUNT_MISMATCH = 8 +const val CODE_APP_SERVER_ERROR = 9 +const val CODE_MULTIACCOUNT_SETUP = 12 +const val CODE_TIMEOUT = 11 +const val CODE_PROFILE_NOT_FOUND = 14 +const val CODE_ATTACHMENT_NOT_AVAILABLE = 28 +const val CODE_INVALID_LOGIN = 2 +const val CODE_INVALID_SERVER_ADDRESS = 21 +const val CODE_INVALID_SCHOOL_NAME = 22 +const val CODE_INVALID_DEVICE = 23 +const val CODE_OLD_PASSWORD = 4 +const val CODE_INVALID_TOKEN = 24 +const val CODE_EXPIRED_TOKEN = 27 +const val CODE_INVALID_SYMBOL = 25 +const val CODE_INVALID_PIN = 26 +const val CODE_LIBRUS_NOT_ACTIVATED = 29 +const val CODE_SYNERGIA_NOT_ACTIVATED = 32 +const val CODE_LIBRUS_DISCONNECTED = 31 +const val CODE_PROFILE_ARCHIVED = 30*/ +const val CODE_INVALID_LOGIN_MODE = 130 +const val CODE_INTERNAL_MISSING_DATA = 100 +const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120 +const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121 +const val CODE_LOGIN_METHOD_NOT_SATISFIED = 122 +const val CODE_LIBRUS_PROFILE_NULL = 123 +const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124 + +const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index aed463b5..26a39c7c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -4,3 +4,47 @@ package pl.szczodrzynski.edziennik.api.v2 +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusApi +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusMessages +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod + +const val LOGIN_TYPE_MOBIDZIENNIK = 1 +const val LOGIN_TYPE_LIBRUS = 2 +const val LOGIN_TYPE_IUCZNIOWIE = 3 +const val LOGIN_TYPE_VULCAN = 4 +const val LOGIN_TYPE_DEMO = 20 + +// LOGIN MODES +const val LOGIN_MODE_LIBRUS_EMAIL = 0 +const val LOGIN_MODE_LIBRUS_SYNERGIA = 1 +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 + +// LOGIN METHODS +const val LOGIN_METHOD_NOT_NEEDED = -1 +const val LOGIN_METHOD_LIBRUS_PORTAL = 100// 0 +const val LOGIN_METHOD_LIBRUS_API = 200// 1 +const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300 // 2 +const val LOGIN_METHOD_LIBRUS_MESSAGES = 400 +const val LOGIN_METHOD_MOBIDZIENNIK_API = 100 +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 + +val librusLoginMethods = listOf( + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, null, LoginLibrusPortal::class.java) { _, _ -> LOGIN_METHOD_NOT_NEEDED }, + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, null, LoginLibrusApi::class.java) { _, loginStore -> + if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED + }, + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, listOf(FEATURE_GRADES, FEATURE_HOMEWORKS, FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusSynergia::class.java) { profile, _ -> + if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED + }, + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusMessages::class.java) { profile, _ -> + if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED + } +) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 82544fa6..b00d7901 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -1,111 +1,31 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + package pl.szczodrzynski.edziennik.api.v2.librus import android.content.Context import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.AppError.* -import pl.szczodrzynski.edziennik.api.interfaces.* -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_JST -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_SYNERGIA -import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus -import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia -import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginJst -import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrus -import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginSynergia -import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor -import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback +import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.MessageFull import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.ProfileFull -import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo -import pl.szczodrzynski.edziennik.models.Endpoint -import pl.szczodrzynski.edziennik.utils.Utils.d -import java.lang.Exception -class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface { - private val TAG = "librus.Librus" +class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: SyncCallback) { - lateinit var syncCallback: SyncCallback - lateinit var featureList: ArrayList - lateinit var dataStore: DataStore - var onLogin: (() -> Unit)? = null - val internalErrorList = ArrayList() + val internalErrorList = mutableListOf() + lateinit var data: DataLibrus + + init { + data = DataLibrus(app, profile, loginStore).apply { + callback = wrapCallback(this@Librus.callback) + } - fun isError(error: AppError?): Boolean { - if (error == null) - return false - syncCallback.onError(null, error) - return true - } - /* _ _ _ - | | (_) | - | | _| |__ _ __ _ _ ___ - | | | | '_ \| '__| | | / __| - | |____| | |_) | | | |_| \__ \ - |______|_|_.__/|_| \__,_|__*/ - private fun loginLibrus() { - LoginLibrus(app, loginStore, syncCallback) { - if (profile == null) { - firstLoginLibrus() - return@LoginLibrus - } - synergiaTokenExtractor() - } - } - private fun firstLoginLibrus() { - FirstLoginLibrus(app, loginStore, syncCallback) { profileList -> - syncCallback.onLoginFirst(profileList, loginStore) - } - } - private fun synergiaTokenExtractor() { - if (profile == null) { - throw Exception("Profile may not be null") - } - SynergiaTokenExtractor(app, profile, loginStore, syncCallback) { - d(TAG, "Profile $profile") - d(TAG, "LoginStore $loginStore") - onLogin?.invoke() - } - } - /* _____ _ - / ____| (_) - | (___ _ _ _ __ ___ _ __ __ _ _ __ _ - \___ \| | | | '_ \ / _ \ '__/ _` | |/ _` | - ____) | |_| | | | | __/ | | (_| | | (_| | - |_____/ \__, |_| |_|\___|_| \__, |_|\__,_| - __/ | __/ | - |___/ |__*/ - private fun loginSynergia() { - LoginSynergia(app, loginStore, syncCallback) { - if (profile == null) { - firstLoginSynergia() - return@LoginSynergia - } - onLogin?.invoke() - } - } - private fun firstLoginSynergia() { - FirstLoginSynergia(app, loginStore, syncCallback) { profileList -> - syncCallback.onLoginFirst(profileList, loginStore) - } - } - /* _ _____ _______ - | |/ ____|__ __| - | | (___ | | - _ | |\___ \ | | - | |__| |____) | | | - \____/|_____/ |*/ - private fun loginJst() { - LoginJst(app, null, loginStore, syncCallback) { - if (profile == null) { - firstLoginSynergia() - return@LoginJst - } - onLogin?.invoke() - } } private fun wrapCallback(callback: SyncCallback): SyncCallback { @@ -135,67 +55,11 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore) : CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { internalErrorList.add(error.errorCode) loginStore.removeLoginData("refreshToken") // force a clean login - loginLibrus() + //loginLibrus() } else -> callback.onError(activityContext, error) } } } } - - fun login(callback: SyncCallback) { - this.internalErrorList.clear() - this.syncCallback = wrapCallback(callback) - when (loginStore.mode) { - LOGIN_MODE_LIBRUS_EMAIL -> { - loginLibrus() - } - LOGIN_MODE_LIBRUS_SYNERGIA -> { - - } - LOGIN_MODE_LIBRUS_JST -> { - - } - } - } - - fun getData() { - - } - - override fun sync(activityContext: Context, callback: SyncCallback, profileId: Int, profile: Profile?, loginStore: LoginStore) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun syncMessages(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun syncFeature(activityContext: Context, callback: SyncCallback, profile: ProfileFull, vararg featureList: Int) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getMessage(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, messageCallback: MessageGetCallback) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getAttachment(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, attachmentId: Long, attachmentCallback: AttachmentGetCallback) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getRecipientList(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, recipientListGetCallback: RecipientListGetCallback) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getComposeInfo(profile: ProfileFull): MessagesComposeInfo { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getConfigurableEndpoints(profile: Profile?): MutableMap { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun isEndpointEnabled(profile: Profile?, defaultActive: Boolean, name: String?): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt new file mode 100644 index 00000000..7096c1d0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt @@ -0,0 +1,6 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt new file mode 100644 index 00000000..e04437bf --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt @@ -0,0 +1,172 @@ +package pl.szczodrzynski.edziennik.api.v2.librus + +import android.content.Context +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.interfaces.* +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia +import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.MessageFull +import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.datamodels.ProfileFull +import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo +import pl.szczodrzynski.edziennik.models.Endpoint +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.lang.Exception + +class LibrusOld(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface { + private val TAG = "librus.Librus" + + lateinit var syncCallback: SyncCallback + lateinit var featureList: ArrayList + lateinit var data: Data + var onLogin: (() -> Unit)? = null + val internalErrorList = ArrayList() + + fun isError(error: AppError?): Boolean { + if (error == null) + return false + syncCallback.onError(null, error) + return true + } + + + /* _ _ _ + | | (_) | + | | _| |__ _ __ _ _ ___ + | | | | '_ \| '__| | | / __| + | |____| | |_) | | | |_| \__ \ + |______|_|_.__/|_| \__,_|__*/ + + private fun firstLoginLibrus() { + FirstLoginLibrus(app, loginStore, syncCallback) { profileList -> + syncCallback.onLoginFirst(profileList, loginStore) + } + } + private fun synergiaTokenExtractor() { + if (profile == null) { + throw Exception("Profile may not be null") + } + + } + /* _____ _ + / ____| (_) + | (___ _ _ _ __ ___ _ __ __ _ _ __ _ + \___ \| | | | '_ \ / _ \ '__/ _` | |/ _` | + ____) | |_| | | | | __/ | | (_| | | (_| | + |_____/ \__, |_| |_|\___|_| \__, |_|\__,_| + __/ | __/ | + |___/ |__*/ + private fun loginSynergia() { + + } + private fun firstLoginSynergia() { + FirstLoginSynergia(app, loginStore, syncCallback) { profileList -> + syncCallback.onLoginFirst(profileList, loginStore) + } + } + /* _ _____ _______ + | |/ ____|__ __| + | | (___ | | + _ | |\___ \ | | + | |__| |____) | | | + \____/|_____/ |*/ + private fun loginJst() { + + } + + private fun wrapCallback(callback: SyncCallback): SyncCallback { + return object : SyncCallback { + override fun onSuccess(activityContext: Context?, profileFull: ProfileFull?) { + callback.onSuccess(activityContext, profileFull) + } + + override fun onProgress(progressStep: Int) { + callback.onProgress(progressStep) + } + + override fun onActionStarted(stringResId: Int) { + callback.onActionStarted(stringResId) + } + + override fun onLoginFirst(profileList: MutableList?, loginStore: LoginStore?) { + callback.onLoginFirst(profileList, loginStore) + } + + override fun onError(activityContext: Context?, error: AppError) { + when (error.errorCode) { + in internalErrorList -> { + // finish immediately if the same error occurs twice during the same sync + callback.onError(activityContext, error) + } + /* CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { + internalErrorList.add(error.errorCode) + loginStore.removeLoginData("refreshToken") // force a clean login + //loginLibrus() + }*/ + else -> callback.onError(activityContext, error) + } + } + } + } + + fun login(callback: SyncCallback) { + this.internalErrorList.clear() + this.syncCallback = wrapCallback(callback) + when (loginStore.mode) { + LOGIN_MODE_LIBRUS_EMAIL -> { + //loginLibrus() + } + LOGIN_MODE_LIBRUS_SYNERGIA -> { + + } + LOGIN_MODE_LIBRUS_JST -> { + + } + } + } + + fun getData() { + + } + + override fun sync(activityContext: Context, callback: SyncCallback, profileId: Int, profile: Profile?, loginStore: LoginStore) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun syncMessages(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun syncFeature(activityContext: Context, callback: SyncCallback, profile: ProfileFull, vararg featureList: Int) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getMessage(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, messageCallback: MessageGetCallback) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getAttachment(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, attachmentId: Long, attachmentCallback: AttachmentGetCallback) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getRecipientList(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, recipientListGetCallback: RecipientListGetCallback) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getComposeInfo(profile: ProfileFull): MessagesComposeInfo { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getConfigurableEndpoints(profile: Profile?): MutableMap { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun isEndpointEnabled(profile: Profile?, defaultActive: Boolean, name: String?): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt new file mode 100644 index 00000000..592423b3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus + +import android.content.Context +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.utils.Utils.d + +class LibrusTest(val app: App) { + companion object { + private const val TAG = "LibrusTest" + } + + val profile = Profile(1, "Profil", "xd", 1).apply { + //putStudentData("accountLogin", "1234567") + //putStudentData("accountPassword", "zaq1@WSX") + + putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) + putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) + } + val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { + addProperty("email", "test@example.com") + addProperty("password", "zaq1@WSX") + }).also { + it.mode = LOGIN_MODE_LIBRUS_JST + } + + fun go() { + val data = DataLibrus(app, profile, loginStore).apply { + callback = object : ProgressCallback { + override fun onProgress(progressStep: Int) { + + } + + override fun onActionStarted(stringResId: Int) { + d(TAG, app.getString(stringResId)) + } + + override fun onError(activityContext: Context?, error: AppError) { + error.changeIfCodeOther() + d(TAG, "Error "+error.getDetails(app)) + } + } + } + + LoginLibrus(data, LOGIN_METHOD_LIBRUS_API) { + d(TAG, "Login succeeded.") + d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") + d(TAG, "LoginStore data: ${data.loginStore.data}") + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index 66fc9c39..61ede774 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -1,10 +1,122 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile -class DataLibrus(val app: App, val profile: Profile, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { -} \ No newline at end of file + /* _____ _ _ + | __ \ | | | | + | |__) |__ _ __| |_ __ _| | + | ___/ _ \| '__| __/ _` | | + | | | (_) | | | || (_| | | + |_| \___/|_| \__\__,_|*/ + private var mPortalEmail: String? = null + var portalEmail: String? + get() { mPortalEmail = mPortalEmail ?: loginStore.getLoginData("email", null); return mPortalEmail } + set(value) { loginStore.putLoginData("email", value); mPortalEmail = value } + private var mPortalPassword: String? = null + var portalPassword: String? + get() { mPortalPassword = mPortalPassword ?: loginStore.getLoginData("password", null); return mPortalPassword } + set(value) { loginStore.putLoginData("password", value); mPortalPassword = value } + + private var mPortalAccessToken: String? = null + var portalAccessToken: String? + get() { mPortalAccessToken = mPortalAccessToken ?: loginStore.getLoginData("accessToken", null); return mPortalAccessToken } + set(value) { loginStore.putLoginData("accessToken", value); mPortalAccessToken = value } + private var mPortalRefreshToken: String? = null + var portalRefreshToken: String? + get() { mPortalRefreshToken = mPortalRefreshToken ?: loginStore.getLoginData("refreshToken", null); return mPortalRefreshToken } + set(value) { loginStore.putLoginData("refreshToken", value); mPortalRefreshToken = value } + private var mPortalTokenExpiryTime: Long? = null + var portalTokenExpiryTime: Long + get() { mPortalTokenExpiryTime = mPortalTokenExpiryTime ?: loginStore.getLoginData("tokenExpiryTime", 0L); return mPortalTokenExpiryTime ?: 0L } + set(value) { loginStore.putLoginData("tokenExpiryTime", value); mPortalTokenExpiryTime = value } + + /* _____ _____ + /\ | __ \_ _| + / \ | |__) || | + / /\ \ | ___/ | | + / ____ \| | _| |_ + /_/ \_\_| |____*/ + /** + * A Synergia login, like 1234567u. + * Used: for login (API Login Method) in Synergia mode. + * And also in various places in [pl.szczodrzynski.edziennik.api.v2.models.Endpoint]s + */ + private var mApiLogin: String? = null + var apiLogin: String? + get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin } + set(value) { profile?.putStudentData("accountLogin", value) ?: return; mApiLogin = value } + /** + * A Synergia password. + * Used: for login (API Login Method) in Synergia mode. + */ + private var mApiPassword: String? = null + var apiPassword: String? + get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword } + set(value) { profile?.putStudentData("accountPassword", value) ?: return; mApiPassword = value } + + /** + * A JST login Code. + * Used only during first login in JST mode. + */ + private var mApiCode: String? = null + var apiCode: String? + get() { mApiCode = mApiCode ?: profile?.getStudentData("accountCode", null); return mApiCode } + set(value) { profile?.putStudentData("accountCode", value) ?: return; mApiCode = value } + /** + * A JST login PIN. + * Used only during first login in JST mode. + */ + private var mApiPin: String? = null + var apiPin: String? + get() { mApiPin = mApiPin ?: profile?.getStudentData("accountPin", null); return mApiPin } + set(value) { profile?.putStudentData("accountPin", value) ?: return; mApiPin = value } + + /** + * A Synergia API access token. + * Used in all Api Endpoints. + * Created in Login Method Api. + * Applicable for all login modes. + */ + private var mApiAccessToken: String? = null + var apiAccessToken: String? + get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken } + set(value) { profile?.putStudentData("accountToken", value) ?: return; mApiAccessToken = value } + /** + * A Synergia API refresh token. + * Used when refreshing the [apiAccessToken] in JST, Synergia modes. + */ + private var mApiRefreshToken: String? = null + var apiRefreshToken: String? + get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken } + set(value) { profile?.putStudentData("accountRefreshToken", value) ?: return; mApiRefreshToken = value } + /** + * The expiry time for [apiAccessToken], as a UNIX timestamp. + * Used when refreshing the [apiAccessToken] in JST, Synergia modes. + * Used when refreshing the [apiAccessToken] in Portal mode ([pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor]) + */ + private var mApiTokenExpiryTime: Long? = null + var apiTokenExpiryTime: Long + get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L } + set(value) { profile?.putStudentData("accountTokenTime", value) ?: return; mApiTokenExpiryTime = value } + + /* ____ _ _ + / __ \| | | | + | | | | |_| |__ ___ _ __ + | | | | __| '_ \ / _ \ '__| + | |__| | |_| | | | __/ | + \____/ \__|_| |_|\___|*/ + var isPremium + get() = profile?.getStudentData("isPremium", false) ?: false + set(value) { profile?.putStudentData("isPremium", value) } + +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt new file mode 100644 index 00000000..3720b212 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.JsonCallbackHandler +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.CODE_MAINTENANCE +import pl.szczodrzynski.edziennik.api.AppError.CODE_OTHER +import pl.szczodrzynski.edziennik.api.v2.Api +import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED +import pl.szczodrzynski.edziennik.api.v2.LIBRUS_API_URL +import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.net.HttpURLConnection.* + +open class LibrusApi(override val data: DataLibrus) : Api(data) { + companion object { + const val TAG = "LibrusApi" + } + fun apiRequest(endpoint: String, callback: (json: JsonObject?) -> Unit) { + d(TAG, "Requesting $LIBRUS_API_URL$endpoint") + Request.builder() + .url(if (data.fakeLogin) "http://szkolny.eu/librus/api/$endpoint" else LIBRUS_API_URL + endpoint) + .userAgent(LIBRUS_USER_AGENT) + .addHeader("Authorization", "Bearer ${data.apiAccessToken}") + .get() + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .allowErrorCode(HTTP_BAD_REQUEST) + .callback(object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + if (response.parserErrorBody != null && response.parserErrorBody == "Nieprawidłowy węzeł.") { + callback(null) + return + } + finishWithError(AppError(TAG, 453, CODE_MAINTENANCE, response)) + return + } + if (json.get("Status") != null) { + val message = json.get("Message") + val code = json.get("Code") + d(TAG, "apiRequest Error " + json.get("Status").asString + " " + (if (message == null) "" else message.asString) + " " + (if (code == null) "" else code.asString) + "\n\n" + response.request().url().toString()) + if (message != null && message !is JsonNull && message.asString == "Student timetable is not public") { + try { + callback(null) + } catch (e: NullPointerException) { + e.printStackTrace() + d(TAG, "apiRequest exception " + e.message) + finishWithError(AppError(TAG, 503, CODE_OTHER, response, e, json)) + } + + return + } + if (code != null + && code !is JsonNull + && (code.asString == "LuckyNumberIsNotActive" + || code.asString == "NotesIsNotActive" + || code.asString == "AccessDeny")) { + try { + callback(null) + } catch (e: NullPointerException) { + e.printStackTrace() + d(TAG, "apiRequest exception " + e.message) + finishWithError(AppError(TAG, 504, CODE_OTHER, response, e, json)) + } + + return + } + val errorText = json.get("Status").asString + " " + (if (message == null) "" else message.asString) + " " + if (code == null) "" else code.asString + if (code != null && code !is JsonNull && code.asString == "TokenIsExpired") { + finishWithError(AppError(TAG, 74, CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED, errorText, response, json)) + return + } + finishWithError(AppError(TAG, 497, CODE_OTHER, errorText, response, json)) + return + } + try { + callback(json) + } catch (e: NullPointerException) { + e.printStackTrace() + d(TAG, "apiRequest exception " + e.message) + finishWithError(AppError(TAG, 505, CODE_OTHER, response, e, json)) + } + + } + + override fun onFailure(response: Response, throwable: Throwable) { + if (response.code() == 405) { + // method not allowed + finishWithError(AppError(TAG, 511, CODE_OTHER, response, throwable)) + return + } + if (response.code() == 500) { + // TODO: 2019-09-10 dirty hotfix + if ("Classrooms" == endpoint) { + callback(null) + return + } + finishWithError(AppError(TAG, 516, CODE_MAINTENANCE, response, throwable)) + return + } + finishWithError(AppError(TAG, 520, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt index eae53a75..b60fefef 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt @@ -2,19 +2,16 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile class LibrusApiGrades(val app: App, val profile: Profile, val loginStore: LoginStore, - val dataStore: DataStore, + val data: Data, val callback: ProgressCallback, - val onSuccess: () -> Unit) : EndpointInterface { - override fun sync() { - - } + val onSuccess: () -> Unit) { init { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt new file mode 100644 index 00000000..a852de2b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +class LibrusApiMe(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + init { + apiRequest("Me") { json -> + val me = json?.getJsonObject("Me") + val account = me?.getJsonObject("Account") + val user = me?.getJsonObject("User") + + data.isPremium = account?.getBoolean("isPremium") == true || account?.getBoolean("isPremiumDemo") == true + + val isParent = account?.getInt("GroupId") == 5 + data.profile?.accountNameLong = + if (isParent) + buildFullName(account?.getString("FirstName"), account?.getString("LastName")) + else null + + data.profile?.studentNameLong = + buildFullName(user?.getString("FirstName"), user?.getString("LastName")) + + onSuccess() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt index ecf10682..693715a2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt @@ -2,16 +2,16 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile class LibrusSynergiaGrades(val app: App, - val profile: Profile, - val loginStore: LoginStore, - val dataStore: DataStore, - val callback: ProgressCallback, - val onSuccess: () -> Unit) { + val profile: Profile, + val loginStore: LoginStore, + val data: Data, + val callback: ProgressCallback, + val onSuccess: () -> Unit) { init { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt deleted file mode 100644 index f21290eb..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.librus.login - -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.interfaces.ILoginMethod -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.Profile - -class LoginJst( - app: App, - profile: Profile?, - loginStore: LoginStore, - callback: ProgressCallback, - onSuccess: () -> Unit -): ILoginMethod(app, profile, loginStore, callback, onSuccess) { - companion object { - private const val TAG = "librus.LoginJst" - } - -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index 959fc74c..e16ada31 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -1,183 +1,79 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + package pl.szczodrzynski.edziennik.api.v2.librus.login -import android.util.Pair -import com.google.gson.JsonObject -import im.wangchao.mhttp.Request -import im.wangchao.mhttp.Response -import im.wangchao.mhttp.body.MediaTypeUtils -import im.wangchao.mhttp.callback.JsonCallbackHandler -import im.wangchao.mhttp.callback.TextCallbackHandler -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.AppError.* -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback import pl.szczodrzynski.edziennik.api.v2.* -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.getInt -import pl.szczodrzynski.edziennik.getString -import pl.szczodrzynski.edziennik.utils.Utils.c -import java.net.HttpURLConnection.HTTP_UNAUTHORIZED -import java.util.ArrayList -import java.util.regex.Pattern +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod +import pl.szczodrzynski.edziennik.utils.Utils.d +import kotlin.math.log -class LoginLibrus(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSuccess: () -> Unit) { companion object { - private const val TAG = "librus.LoginLibrus" + private const val TAG = "LoginLibrus" } + private var loginMethodList = mutableListOf() + init { - // ustawiamy tokeny, generujemy itp - // nic nie robimy z dostępem do api.librus.pl - // to będzie później - val accessToken = loginStore.getLoginData("accessToken", null) - val refreshToken = loginStore.getLoginData("refreshToken", null) - val tokenExpiryTime = loginStore.getLoginData("tokenExpiryTime", 0L) + for (loginMethodId in loginMethodIds) { + var requiredLoginMethod = loginMethodId + while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { + librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> + loginMethodList.add(requiredLoginMethod) + requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) + } + } + } + loginMethodList = loginMethodList.toHashSet().toMutableList() + loginMethodList.sort() - // succeed having a non-expired access token and a refresh token - if (tokenExpiryTime-30 > System.currentTimeMillis() / 1000 && refreshToken != null && accessToken != null) { + nextLoginMethod() + } + + private fun nextLoginMethod() { + if (loginMethodList.isEmpty()) { onSuccess() + return } - else if (refreshToken != null) { - app.cookieJar.clearForDomain("portal.librus.pl") - accessToken(null, refreshToken) - } - else { - app.cookieJar.clearForDomain("portal.librus.pl") - authorize(LIBRUS_AUTHORIZE_URL) + useLoginMethod(loginMethodList.removeAt(0)) { + nextLoginMethod() } } - private fun authorize(url: String?) { - callback.onActionStarted(R.string.sync_action_authorizing) - Request.builder() - .url(url) - .userAgent(LIBRUS_USER_AGENT) - .withClient(app.httpLazy) - .callback(object : TextCallbackHandler() { - override fun onSuccess(data: String, response: Response) { - //d("headers "+response.headers().toString()); - val location = response.headers().get("Location") - if (location != null) { - val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location) - if (authMatcher.find()) { - accessToken(authMatcher.group(1), null) - } else { - //callback.onError(activityContext, Edziennik.CODE_OTHER, "Auth code not found: "+location); - authorize(location) - } - } else { - val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(data) - if (csrfMatcher.find()) { - login(csrfMatcher.group(1)) - } else { - callback.onError(null, AppError(TAG, 463, CODE_OTHER, "CSRF token not found.", response, data)) - } - } - } - - override fun onFailure(response: Response, throwable: Throwable) { - callback.onError(null, AppError(TAG, 207, CODE_OTHER, response, throwable)) - } - }) - .build() - .enqueue() - } - - private fun login(csrfToken: String) { - callback.onActionStarted(R.string.sync_action_logging_in) - val email = loginStore.getLoginData("email", "") - val password = loginStore.getLoginData("password", "") - Request.builder() - .url(LIBRUS_LOGIN_URL) - .userAgent(LIBRUS_USER_AGENT) - .addParameter("email", email) - .addParameter("password", password) - .addHeader("X-CSRF-TOKEN", csrfToken) - .contentType(MediaTypeUtils.APPLICATION_JSON) - .post() - .callback(object : JsonCallbackHandler() { - override fun onSuccess(data: JsonObject?, response: Response) { - if (data == null) { - if (response.parserErrorBody != null && response.parserErrorBody.contains("wciąż nieaktywne")) { - callback.onError(null, AppError(TAG, 487, CODE_LIBRUS_NOT_ACTIVATED, response)) - } - callback.onError(null, AppError(TAG, 489, CODE_MAINTENANCE, response)) - return - } - if (data.get("errors") != null) { - callback.onError(null, AppError(TAG, 490, CODE_OTHER, data.get("errors").asJsonArray.get(0).asString, response, data)) - return - } - authorize(data.getString("redirect") ?: LIBRUS_AUTHORIZE_URL) - } - - override fun onFailure(response: Response, throwable: Throwable) { - if (response.code() == 403 || response.code() == 401) { - callback.onError(null, AppError(TAG, 248, CODE_INVALID_LOGIN, response, throwable)) - return - } - callback.onError(null, AppError(TAG, 251, CODE_OTHER, response, throwable)) - } - }) - .build() - .enqueue() - } - - private var refreshTokenFailed = false - private fun accessToken(code: String?, refreshToken: String?) { - callback.onActionStarted(R.string.sync_action_getting_token) - val params = ArrayList>() - params.add(Pair("client_id", LIBRUS_CLIENT_ID)) - if (code != null) { - params.add(Pair("grant_type", "authorization_code")) - params.add(Pair("code", code)) - params.add(Pair("redirect_uri", LIBRUS_REDIRECT_URL)) - } else if (refreshToken != null) { - params.add(Pair("grant_type", "refresh_token")) - params.add(Pair("refresh_token", refreshToken)) + private fun useLoginMethod(loginMethodId: Int, onSuccess: () -> Unit) { + if (data.loginMethods.contains(loginMethodId)) { + onSuccess() + return + } + d(TAG, "Using login method $loginMethodId") + when (loginMethodId) { + LOGIN_METHOD_LIBRUS_PORTAL -> { + LoginLibrusPortal(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } + LOGIN_METHOD_LIBRUS_API -> { + LoginLibrusApi(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } + LOGIN_METHOD_LIBRUS_SYNERGIA -> { + LoginLibrusApi(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } + LOGIN_METHOD_LIBRUS_MESSAGES -> { + LoginLibrusApi(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } } - Request.builder() - .url(LIBRUS_TOKEN_URL) - .userAgent(LIBRUS_USER_AGENT) - .addParams(params) - .allowErrorCode(HTTP_UNAUTHORIZED) - .post() - .callback(object : JsonCallbackHandler() { - override fun onSuccess(data: JsonObject?, response: Response) { - if (data == null) { - callback.onError(null, AppError(TAG, 539, CODE_MAINTENANCE, response)) - return - } - if (data.get("error") != null) { - val hint = data.getString("hint") - if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) { - c(TAG, "refreshing the token failed. Trying to log in again.") - refreshTokenFailed = true - authorize(LIBRUS_AUTHORIZE_URL) - return - } - val errorText = data.getString("error") + " " + (data.getString("message") ?: "") + " " + (hint ?: "") - callback.onError(null, AppError(TAG, 552, CODE_OTHER, errorText, response, data)) - return - } - try { - loginStore.putLoginData("tokenType", data.getString("token_type")) - loginStore.putLoginData("accessToken", data.getString("access_token")) - loginStore.putLoginData("refreshToken", data.getString("refresh_token")) - loginStore.putLoginData("tokenExpiryTime", System.currentTimeMillis() / 1000 + (data.getInt("expires_in") ?: 86400)) - onSuccess() - } catch (e: NullPointerException) { - callback.onError(null, AppError(TAG, 311, CODE_OTHER, response, e, data)) - } - - } - - override fun onFailure(response: Response, throwable: Throwable) { - callback.onError(null, AppError(TAG, 317, CODE_OTHER, response, throwable)) - } - }) - .build() - .enqueue() } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt new file mode 100644 index 00000000..ef6da7d5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt @@ -0,0 +1,246 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.login + +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.body.MediaTypeUtils +import im.wangchao.mhttp.callback.JsonCallbackHandler +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.* +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import java.net.HttpURLConnection +import java.net.HttpURLConnection.* + +class LoginLibrusApi { + companion object { + private const val TAG = "LoginLibrusApi" + } + + private lateinit var data: DataLibrus + private lateinit var onSuccess: () -> Unit + + constructor(data: DataLibrus, onSuccess: () -> Unit) { + this.data = data + this.onSuccess = onSuccess + + if (data.profile == null) { + data.callback.onError(null, AppError(TAG, 19, CODE_LIBRUS_PROFILE_NULL)) + return + } + + if (data.apiTokenExpiryTime-30 > currentTimeUnix() && data.apiAccessToken.isNotNullNorEmpty()) { + onSuccess() + } + else { + when (data.loginStore.mode) { + LOGIN_MODE_LIBRUS_EMAIL -> loginWithPortal() + LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithSynergia() + LOGIN_MODE_LIBRUS_JST -> loginWithJst() + else -> { + data.callback.onError(null, AppError(TAG, 25, CODE_INVALID_LOGIN_MODE)) + } + } + } + } + + private fun loginWithPortal() { + if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_PORTAL)) { + data.callback.onError(null, AppError(TAG, 26, CODE_LOGIN_METHOD_NOT_SATISFIED)) + return + } + SynergiaTokenExtractor(data) { + onSuccess() + } + } + + private fun copyFromLoginStore() { + data.loginStore.data?.apply { + if (has("accountLogin")) { + data.apiLogin = getString("accountLogin") + remove("accountLogin") + } + if (has("accountPassword")) { + data.apiPassword = getString("accountPassword") + remove("accountPassword") + } + if (has("accountCode")) { + data.apiCode = getString("accountCode") + remove("accountCode") + } + if (has("accountPin")) { + data.apiPin = getString("accountPin") + remove("accountPin") + } + } + } + + private fun loginWithSynergia() { + copyFromLoginStore() + if (data.apiRefreshToken != null) { + // refresh a Synergia token + synergiaRefreshToken() + } + else if (data.apiLogin != null && data.apiPassword != null) { + synergiaGetToken() + } + else { + // cannot log in: token expired, no login data present + data.callback.onError(null, AppError(TAG, 91, CODE_INVALID_LOGIN)) + } + } + + private fun loginWithJst() { + copyFromLoginStore() + + if (data.apiRefreshToken != null) { + // refresh a JST token + jstRefreshToken() + } + else if (data.apiCode != null && data.apiPin != null) { + // get a JST token from Code and PIN + jstGetToken() + } + else { + // cannot log in: token expired, no login data present + data.callback.onError(null, AppError(TAG, 110, CODE_INVALID_LOGIN)) + } + } + + private val tokenCallback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null) { + data.callback.onError(null, AppError(TAG, 117, CODE_MAINTENANCE, response)) + return + } + json.getString("error")?.let { error -> + when (error) { + "librus_captcha_needed" -> { + + } + "connection_problems" -> { + + } + "invalid_client" -> { + + } + "librus_reg_accept_needed" -> { + + } + "librus_change_password_error" -> { + + } + "librus_password_change_required" -> { + + } + "invalid_grant" -> { + + } + else -> { + + } + } + return + } + + try { + data.apiAccessToken = json.getString("access_token") + data.apiRefreshToken = json.getString("refresh_token") + data.apiTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) + onSuccess() + } catch (e: NullPointerException) { + data.callback.onError(null, AppError(TAG, 154, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.callback.onError(null, AppError(TAG, 159, CODE_OTHER, response, throwable)) + } + } + + private fun synergiaGetToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "password") + .addParameter("username", data.apiLogin) + .addParameter("password", data.apiPassword) + .addParameter("librus_long_term_token", "1") + .addParameter("librus_rules_accepted", "1") + .addHeader("Authorization", "Basic $LIBRUS_API_AUTHORIZATION") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } + private fun synergiaRefreshToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "refresh_token") + .addParameter("refresh_token", data.apiRefreshToken) + .addParameter("librus_long_term_token", "1") + .addParameter("librus_rules_accepted", "1") + .addHeader("Authorization", "Basic $LIBRUS_API_AUTHORIZATION") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } + private fun jstGetToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_JST_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "implicit_grant") + .addParameter("client_id", LIBRUS_API_CLIENT_ID_JST) + .addParameter("secret", LIBRUS_API_SECRET_JST) + .addParameter("code", data.apiCode) + .addParameter("pin", data.apiPin) + .addParameter("librus_rules_accepted", "1") + .addParameter("librus_mobile_rules_accepted", "1") + .addParameter("librus_long_term_token", "1") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } + private fun jstRefreshToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_JST_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "refresh_token") + .addParameter("client_id", LIBRUS_API_CLIENT_ID_JST) + .addParameter("refresh_token", data.apiRefreshToken) + .addParameter("librus_long_term_token", "1") + .addParameter("mobile_app_accept_rules", "1") + .addParameter("synergy_accept_rules", "1") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt new file mode 100644 index 00000000..26e3db39 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -0,0 +1,8 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.login + +class LoginLibrusMessages { +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt new file mode 100644 index 00000000..e0d5c309 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt @@ -0,0 +1,179 @@ +package pl.szczodrzynski.edziennik.api.v2.librus.login + +import android.util.Pair +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.body.MediaTypeUtils +import im.wangchao.mhttp.callback.JsonCallbackHandler +import im.wangchao.mhttp.callback.TextCallbackHandler +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.* +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.utils.Utils.c +import java.net.HttpURLConnection.HTTP_UNAUTHORIZED +import java.util.ArrayList +import java.util.regex.Pattern + +class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LoginLibrusPortal" + } + + init { run { + if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { + data.callback.onError(null, AppError(TAG, 27, CODE_INVALID_LOGIN_MODE)) + return@run + } + if (data.portalEmail == null || data.portalPassword == null) { + data.callback.onError(null, AppError(TAG, 31, CODE_INVALID_LOGIN)) + return@run + } + + // succeed having a non-expired access token and a refresh token + if (data.portalTokenExpiryTime-30 > currentTimeUnix() && data.portalRefreshToken.isNotNullNorEmpty() && data.portalAccessToken.isNotNullNorEmpty()) { + onSuccess() + } + else if (data.portalRefreshToken != null) { + data.app.cookieJar.clearForDomain("portal.librus.pl") + accessToken(null, data.portalRefreshToken) + } + else { + data.app.cookieJar.clearForDomain("portal.librus.pl") + authorize(LIBRUS_AUTHORIZE_URL) + } + }} + + private fun authorize(url: String?) { + data.callback.onActionStarted(R.string.sync_action_authorizing) + Request.builder() + .url(url) + .userAgent(LIBRUS_USER_AGENT) + .withClient(data.app.httpLazy) + .callback(object : TextCallbackHandler() { + override fun onSuccess(json: String, response: Response) { + val location = response.headers().get("Location") + if (location != null) { + val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location) + if (authMatcher.find()) { + accessToken(authMatcher.group(1), null) + } else { + authorize(location) + } + } else { + val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(json) + if (csrfMatcher.find()) { + login(csrfMatcher.group(1)) + } else { + data.callback.onError(null, AppError(TAG, 463, CODE_OTHER, "CSRF token not found.", response, json)) + } + } + } + + override fun onFailure(response: Response, throwable: Throwable) { + data.callback.onError(null, AppError(TAG, 207, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } + + private fun login(csrfToken: String) { + data.callback.onActionStarted(R.string.sync_action_logging_in) + Request.builder() + .url(LIBRUS_LOGIN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("email", data.portalEmail) + .addParameter("password", data.portalPassword) + .addHeader("X-CSRF-TOKEN", csrfToken) + .contentType(MediaTypeUtils.APPLICATION_JSON) + .post() + .callback(object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) { + data.callback.onError(null, AppError(TAG, 487, CODE_LIBRUS_NOT_ACTIVATED, response)) + return + } + data.callback.onError(null, AppError(TAG, 489, CODE_MAINTENANCE, response)) + return + } + if (json.get("errors") != null) { + data.callback.onError(null, AppError(TAG, 490, CODE_OTHER, json.getJsonArray("errors")?.get(0)?.asString, response, json)) + return + } + authorize(json.getString("redirect", LIBRUS_AUTHORIZE_URL)) + } + + override fun onFailure(response: Response, throwable: Throwable) { + if (response.code() == 403 || response.code() == 401) { + data.callback.onError(null, AppError(TAG, 248, CODE_INVALID_LOGIN, response, throwable)) + return + } + data.callback.onError(null, AppError(TAG, 251, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } + + private var refreshTokenFailed = false + private fun accessToken(code: String?, refreshToken: String?) { + data.callback.onActionStarted(R.string.sync_action_getting_token) + val params = ArrayList>() + params.add(Pair("client_id", LIBRUS_CLIENT_ID)) + if (code != null) { + params.add(Pair("grant_type", "authorization_code")) + params.add(Pair("code", code)) + params.add(Pair("redirect_uri", LIBRUS_REDIRECT_URL)) + } else if (refreshToken != null) { + params.add(Pair("grant_type", "refresh_token")) + params.add(Pair("refresh_token", refreshToken)) + } + Request.builder() + .url(LIBRUS_TOKEN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParams(params) + .allowErrorCode(HTTP_UNAUTHORIZED) + .post() + .callback(object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + data.callback.onError(null, AppError(TAG, 539, CODE_MAINTENANCE, response)) + return + } + json.getString("error")?.let { error -> + val hint = json.getString("hint", "") + val message = json.getString("message", "") + if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) { + c(TAG, "refreshing the token failed. Trying to log in again.") + refreshTokenFailed = true + authorize(LIBRUS_AUTHORIZE_URL) + return + } + val errorText = "$error $message $hint" + data.callback.onError(null, AppError(TAG, 552, CODE_OTHER, errorText, response, json)) + return + } + + try { + data.portalAccessToken = json.getString("access_token") + data.portalRefreshToken = json.getString("refresh_token") + data.portalTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) + onSuccess() + } catch (e: NullPointerException) { + data.callback.onError(null, AppError(TAG, 311, CODE_OTHER, response, e, json)) + } + + } + + override fun onFailure(response: Response, throwable: Throwable) { + data.callback.onError(null, AppError(TAG, 317, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt new file mode 100644 index 00000000..2bad6904 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -0,0 +1,8 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.login + +class LoginLibrusSynergia { +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt deleted file mode 100644 index 026f3e50..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt +++ /dev/null @@ -1,12 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.librus.login - -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.datamodels.LoginStore - -class LoginSynergia(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { - companion object { - private const val TAG = "librus.LoginSynergia" - } - -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index c7f3f69e..80f26158 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -8,41 +8,43 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.AppError.* -import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNT_URL -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.* -class SynergiaTokenExtractor(val app: App, val profile: Profile, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { private const val TAG = "librus.SynergiaToken" } - init { - val accountToken = profile.getStudentData("accountToken", null) - val accountTokenTime = profile.getStudentData("accountTokenTime", 0L) - if (accountToken.isNotNullNorEmpty() && currentTimeUnix() - accountTokenTime < 3 * 60 * 60) { + init { run { + if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { + data.callback.onError(null, AppError(TAG, 23, CODE_INVALID_LOGIN_MODE)) + return@run + } + if (data.profile == null) { + data.callback.onError(null, AppError(TAG, 28, CODE_LIBRUS_PROFILE_NULL)) + return@run + } + + if (data.apiTokenExpiryTime-30 > currentTimeUnix() && data.apiAccessToken.isNotNullNorEmpty()) { onSuccess() } else { - if (!synergiaAccount()) - callback.onError(null, AppError(TAG, 33, CODE_INTERNAL_MISSING_DATA)) + synergiaAccount() } - } + }} private fun synergiaAccount(): Boolean { - val accountLogin = profile.getStudentData("accountLogin", null) ?: return false - val tokenType = loginStore.getLoginData("tokenType", null) ?: return false - val accessToken = loginStore.getLoginData("accessToken", null) ?: return false - callback.onActionStarted(R.string.sync_action_getting_account) + val accountLogin = data.apiLogin ?: return false + val accessToken = data.portalAccessToken ?: return false + data.callback.onActionStarted(R.string.sync_action_getting_account) d(TAG, "Requesting " + (LIBRUS_ACCOUNT_URL + accountLogin)) Request.builder() .url(LIBRUS_ACCOUNT_URL + accountLogin) .userAgent(LIBRUS_USER_AGENT) - .addHeader("Authorization", "$tokenType $accessToken") + .addHeader("Authorization", "Bearer $accessToken") .get() .allowErrorCode(HTTP_NOT_FOUND) .allowErrorCode(HTTP_FORBIDDEN) @@ -50,57 +52,56 @@ class SynergiaTokenExtractor(val app: App, val profile: Profile, val loginStore: .allowErrorCode(HTTP_BAD_REQUEST) .allowErrorCode(HTTP_GONE) .callback(object : JsonCallbackHandler() { - override fun onSuccess(data: JsonObject?, response: Response) { - if (data == null) { - callback.onError(null, AppError(TAG, 641, CODE_MAINTENANCE, response)) + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + data.callback.onError(null, AppError(TAG, 641, CODE_MAINTENANCE, response)) return } if (response.code() == 410) { - val reason = data.get("reason") + val reason = json.get("reason") if (reason != null && reason !is JsonNull && reason.asString == "requires_an_action") { - callback.onError(null, AppError(TAG, 1078, CODE_LIBRUS_DISCONNECTED, response, data)) + data.callback.onError(null, AppError(TAG, 1078, CODE_LIBRUS_DISCONNECTED, response, json)) return } - callback.onError(null, AppError(TAG, 70, CODE_INTERNAL_LIBRUS_ACCOUNT_410)) + data.callback.onError(null, AppError(TAG, 70, CODE_INTERNAL_LIBRUS_ACCOUNT_410)) return } - if (data.get("message") != null) { - val message = data.get("message").asString + if (json.get("message") != null) { + val message = json.get("message").asString if (message == "Account not found") { - callback.onError(null, AppError(TAG, 651, CODE_OTHER, app.getString(R.string.sync_error_register_student_not_associated_format, profile.studentNameLong, accountLogin), response, data)) + data.callback.onError(null, AppError(TAG, 651, CODE_OTHER, data.app.getString(R.string.sync_error_register_student_not_associated_format, data.profile?.studentNameLong ?: "", accountLogin), response, json)) return } - callback.onError(null, AppError(TAG, 654, CODE_OTHER, message + "\n\n" + accountLogin, response, data)) + data.callback.onError(null, AppError(TAG, 654, CODE_OTHER, message + "\n\n" + accountLogin, response, json)) return } if (response.code() == HTTP_OK) { try { // synergiaAccount is executed when a synergia token needs a refresh - val accountId = data.getInt("id") - val accountToken = data.getString("accessToken") + val accountId = json.getInt("id") + val accountToken = json.getString("accessToken") if (accountId == null || accountToken == null) { - callback.onError(null, AppError(TAG, 1284, CODE_OTHER, data)) + data.callback.onError(null, AppError(TAG, 1284, CODE_OTHER, json)) return } - profile.putStudentData("accountId", accountId) - profile.putStudentData("accountToken", accountToken) - profile.putStudentData("accountTokenTime", System.currentTimeMillis() / 1000) - profile.studentNameLong = data.getString("studentName") - val nameParts = data.getString("studentName")?.split(" ")?.toTypedArray() - profile.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) + data.apiAccessToken = accountToken + data.apiTokenExpiryTime = currentTimeUnix() + 6*60*60 + data.profile?.studentNameLong = json.getString("studentName") + val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray() + data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) onSuccess() } catch (e: NullPointerException) { e.printStackTrace() - callback.onError(null, AppError(TAG, 662, CODE_OTHER, response, e, data)) + data.callback.onError(null, AppError(TAG, 662, CODE_OTHER, response, e, json)) } } else { - callback.onError(null, AppError(TAG, 425, CODE_OTHER, response, data)) + data.callback.onError(null, AppError(TAG, 425, CODE_OTHER, response, json)) } } override fun onFailure(response: Response, throwable: Throwable) { - callback.onError(null, AppError(TAG, 432, CODE_OTHER, response, throwable)) + data.callback.onError(null, AppError(TAG, 432, CODE_OTHER, response, throwable)) } }) .build() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt similarity index 61% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 23d500a8..7578c64d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -3,15 +3,21 @@ package pl.szczodrzynski.edziennik.api.v2.models import android.util.LongSparseArray import androidx.core.util.forEach import androidx.core.util.isNotEmpty +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback import pl.szczodrzynski.edziennik.datamodels.* import pl.szczodrzynski.edziennik.models.Date -data class DataStore(private val appDb: AppDb, val profileId: Int) { +open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) { + + var fakeLogin = false + + lateinit var callback: ProgressCallback val loginMethods = mutableListOf() - val teacherList: LongSparseArray = LongSparseArray() - val subjectList: LongSparseArray = LongSparseArray() + val teacherList = LongSparseArray() + val subjectList = LongSparseArray() val teamList = mutableListOf() val lessonList = mutableListOf() val lessonChangeList = mutableListOf() @@ -28,15 +34,19 @@ data class DataStore(private val appDb: AppDb, val profileId: Int) { val metadataList = mutableListOf() val messageMetadataList = mutableListOf() + private val db by lazy { app.db } + init { clear() - - appDb.teacherDao().getAllNow(profileId).forEach { teacher -> - teacherList.put(teacher.id, teacher) - } - appDb.subjectDao().getAllNow(profileId).forEach { subject -> - subjectList.put(subject.id, subject) + + if (profile != null) { + db.teacherDao().getAllNow(profile.id).forEach { teacher -> + teacherList.put(teacher.id, teacher) + } + db.subjectDao().getAllNow(profile.id).forEach { subject -> + subjectList.put(subject.id, subject) + } } /*val teacher = teachers.byNameFirstLast("Jan Kowalski") ?: Teacher(1, 1, "", "").let { @@ -67,57 +77,60 @@ data class DataStore(private val appDb: AppDb, val profileId: Int) { } fun saveData() { + if (profile == null) + return + if (teacherList.isNotEmpty()) { val tempList: ArrayList = ArrayList() teacherList.forEach { _, teacher -> tempList.add(teacher) } - appDb.teacherDao().addAll(tempList) + db.teacherDao().addAll(tempList) } if (subjectList.isNotEmpty()) { val tempList: ArrayList = ArrayList() subjectList.forEach { _, subject -> tempList.add(subject) } - appDb.subjectDao().addAll(tempList) + db.subjectDao().addAll(tempList) } if (teamList.isNotEmpty()) - appDb.teamDao().addAll(teamList) + db.teamDao().addAll(teamList) if (lessonList.isNotEmpty()) { - appDb.lessonDao().clear(profileId) - appDb.lessonDao().addAll(lessonList) + db.lessonDao().clear(profile.id) + db.lessonDao().addAll(lessonList) } if (lessonChangeList.isNotEmpty()) - appDb.lessonChangeDao().addAll(lessonChangeList) + db.lessonChangeDao().addAll(lessonChangeList) if (gradeCategoryList.isNotEmpty()) - appDb.gradeCategoryDao().addAll(gradeCategoryList) + db.gradeCategoryDao().addAll(gradeCategoryList) if (gradeList.isNotEmpty()) { - appDb.gradeDao().clear(profileId) - appDb.gradeDao().addAll(gradeList) + db.gradeDao().clear(profile.id) + db.gradeDao().addAll(gradeList) } if (eventList.isNotEmpty()) { - appDb.eventDao().removeFuture(profileId, Date.getToday()) - appDb.eventDao().addAll(eventList) + db.eventDao().removeFuture(profile.id, Date.getToday()) + db.eventDao().addAll(eventList) } if (eventTypeList.isNotEmpty()) - appDb.eventTypeDao().addAll(eventTypeList) + db.eventTypeDao().addAll(eventTypeList) if (noticeList.isNotEmpty()) { - appDb.noticeDao().clear(profileId) - appDb.noticeDao().addAll(noticeList) + db.noticeDao().clear(profile.id) + db.noticeDao().addAll(noticeList) } if (attendanceList.isNotEmpty()) - appDb.attendanceDao().addAll(attendanceList) + db.attendanceDao().addAll(attendanceList) if (announcementList.isNotEmpty()) - appDb.announcementDao().addAll(announcementList) + db.announcementDao().addAll(announcementList) if (messageList.isNotEmpty()) - appDb.messageDao().addAllIgnore(messageList) + db.messageDao().addAllIgnore(messageList) if (messageRecipientList.isNotEmpty()) - appDb.messageRecipientDao().addAll(messageRecipientList) + db.messageRecipientDao().addAll(messageRecipientList) if (messageRecipientIgnoreList.isNotEmpty()) - appDb.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList) + db.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList) if (metadataList.isNotEmpty()) - appDb.metadataDao().addAllIgnore(metadataList) + db.metadataDao().addAllIgnore(metadataList) if (messageMetadataList.isNotEmpty()) - appDb.metadataDao().setSeen(messageMetadataList) + db.metadataDao().setSeen(messageMetadataList) } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt index d760b3ab..e2c6281b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt @@ -14,13 +14,14 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * @param loginType type of the e-register this endpoint handles * @param endpointId a unique ID of this endpoint * @param featureIds a [List] of [Feature]s (their IDs) this endpoint can download + * May be null if no strict feature set is associated with this method. * @param endpointClass a [Class] which constructor will be invoked when a data download is needed * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. */ class Endpoint( val loginType: Int, val endpointId: Int, - val featureIds: List, + val featureIds: List?, val endpointClass: Class<*>, val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt index f83c4801..3ffda1af 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt @@ -1,6 +1,5 @@ package pl.szczodrzynski.edziennik.api.v2.models -import pl.szczodrzynski.edziennik.api.v2.endpoint data class Feature(val featureId: Int, val loginOptions: Map>) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt index 9b00bf0b..3fda0eb1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt @@ -17,13 +17,14 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * @param loginType type of the e-register this login method handles * @param loginMethodId a unique ID of this login method * @param featureIds a [List] of [Feature]s (their IDs) this login method can provide access to + * May be null if no strict feature set is associated with this method. * @param loginMethodClass a [Class] which constructor will be invoked when a log in is needed * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. */ class LoginMethod( val loginType: Int, val loginMethodId: Int, - val featureIds: List, + val featureIds: List?, val loginMethodClass: Class<*>, val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java index 47c9e837..b4689a40 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java @@ -44,7 +44,8 @@ import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.api.AppError; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.api.v2.librus.Librus; +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusOld; +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusTest; import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; @@ -115,45 +116,18 @@ public class HomeFragment extends Fragment { return true; });*/ - b.testButton.setOnClickListener((v -> { - LoginStore loginStore = new LoginStore(1, LOGIN_TYPE_LIBRUS, new JsonObject()); - loginStore.putLoginData("email", "example@example.com"); - loginStore.putLoginData("password", "zaq1@WSX"); - Profile profile = new Profile(1, "test", "testsubname", 1); - profile.putStudentData("accountLogin", "1234567"); - new Librus(app, profile, loginStore).login(new SyncCallback() { - @Override - public void onLoginFirst(List profileList, LoginStore loginStore) { - - } - - @Override - public void onSuccess(Context activityContext, ProfileFull profileFull) { - - } - - @Override - public void onProgress(int progressStep) { - - } - - @Override - public void onActionStarted(int stringResId) { - - } - - @Override - public void onError(Context activityContext, @NonNull AppError error) { - - } - }); - })); - b.composeButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.composeButton.setOnClickListener((v -> { startActivity(new Intent(activity, MessagesComposeActivity.class)); })); + LibrusTest test = new LibrusTest(app); + + b.testButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.testButton.setOnClickListener((v -> { + test.go(); + })); + //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS()));