diff --git a/.idea/dictionaries/Kuba.xml b/.idea/dictionaries/Kuba.xml index 592a5d5e..aba38775 100644 --- a/.idea/dictionaries/Kuba.xml +++ b/.idea/dictionaries/Kuba.xml @@ -13,6 +13,7 @@ synergia szczodrzyński szkolny + usos \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt index 227561e6..617ef5ed 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Errors.kt @@ -204,6 +204,8 @@ const val ERROR_PODLASIE_API_NO_TOKEN = 630 const val ERROR_PODLASIE_API_OTHER = 631 const val ERROR_PODLASIE_API_DATA_MISSING = 632 +const val ERROR_USOS_OAUTH_LOGIN_REQUEST = 701 + const val ERROR_TEMPLATE_WEB_OTHER = 801 const val EXCEPTION_API_TASK = 900 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/LoginMethods.kt index 0f0c07ae..9825ea3f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/LoginMethods.kt @@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.Mobidzie import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb +import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginHebe import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginWebMain import pl.szczodrzynski.edziennik.data.api.models.LoginMethod @@ -127,6 +128,15 @@ val podlasieLoginMethods = listOf( .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED } ) +const val LOGIN_TYPE_USOS = 7 +const val LOGIN_MODE_USOS_OAUTH = 0 +const val LOGIN_METHOD_USOS_API = 100 +val usosLoginMethods = listOf( + LoginMethod(LOGIN_TYPE_USOS, LOGIN_METHOD_USOS_API, UsosLoginApi::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 } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/DataUsos.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/DataUsos.kt new file mode 100644 index 00000000..b4a925bf --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/DataUsos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-10-11. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.usos + +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_USOS_API +import pl.szczodrzynski.edziennik.data.api.models.Data +import pl.szczodrzynski.edziennik.data.db.entity.LoginStore +import pl.szczodrzynski.edziennik.data.db.entity.Profile + +class DataUsos( + app: App, + profile: Profile?, + loginStore: LoginStore, +) : Data(app, profile, loginStore) { + + fun isApiLoginValid() = oauthTokenKey != null && oauthTokenSecret != null + + override fun satisfyLoginMethods() { + loginMethods.clear() + if (isApiLoginValid()) { + loginMethods += LOGIN_METHOD_USOS_API + } + } + + override fun generateUserCode() = "USOS:TEST" + + var oauthTokenKey: String? + get() { mOauthTokenKey = mOauthTokenKey ?: loginStore.getLoginData("oauthTokenKey", null); return mOauthTokenKey } + set(value) { loginStore.putLoginData("oauthTokenKey", value); mOauthTokenKey = value } + private var mOauthTokenKey: String? = null + + var oauthTokenSecret: String? + get() { mOauthTokenSecret = mOauthTokenSecret ?: loginStore.getLoginData("oauthTokenSecret", null); return mOauthTokenSecret } + set(value) { loginStore.putLoginData("oauthTokenSecret", value); mOauthTokenSecret = value } + private var mOauthTokenSecret: String? = null +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/Usos.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/Usos.kt new file mode 100644 index 00000000..c97e3695 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/Usos.kt @@ -0,0 +1,109 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-10-11. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.usos + +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLogin +import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback +import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface +import pl.szczodrzynski.edziennik.data.api.models.ApiError +import pl.szczodrzynski.edziennik.data.api.prepare +import pl.szczodrzynski.edziennik.data.api.usosLoginMethods +import pl.szczodrzynski.edziennik.data.db.entity.LoginStore +import pl.szczodrzynski.edziennik.data.db.entity.Profile +import pl.szczodrzynski.edziennik.data.db.entity.Teacher +import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull +import pl.szczodrzynski.edziennik.data.db.full.EventFull +import pl.szczodrzynski.edziennik.data.db.full.MessageFull +import pl.szczodrzynski.edziennik.utils.Utils.d + +class Usos( + val app: App, + val profile: Profile?, + val loginStore: LoginStore, + val callback: EdziennikCallback, +) : EdziennikInterface { + companion object { + private const val TAG = "Usos" + } + + val internalErrorList = mutableListOf() + val data: DataUsos + + init { + data = DataUsos(app, profile, loginStore).apply { + callback = wrapCallback(this@Usos.callback) + satisfyLoginMethods() + } + } + + private fun completed() { + data.saveData() + callback.onCompleted() + } + + override fun sync( + featureIds: List, + viewId: Int?, + onlyEndpoints: List?, + arguments: JsonObject?, + ) { + data.arguments = arguments + data.prepare(usosLoginMethods, UsosFeatures, featureIds, viewId, onlyEndpoints) + d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + UsosLogin(data) { + /*UsosData(data) { + completed() + }*/ + } + } + + override fun getMessage(message: MessageFull) {} + override fun sendMessage(recipients: List, subject: String, text: String) {} + override fun markAllAnnouncementsAsRead() {} + override fun getAnnouncement(announcement: AnnouncementFull) {} + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {} + override fun getRecipientList() {} + override fun getEvent(eventFull: EventFull) {} + + override fun firstLogin() { + /*UsosFirstLogin(data) { + completed() + }*/ + } + + override fun cancel() { + d(TAG, "Cancelled") + data.cancel() + } + + private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { + return object : EdziennikCallback { + override fun onCompleted() { + callback.onCompleted() + } + + override fun onProgress(step: Float) { + 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) + } + else -> callback.onError(apiError) + } + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/UsosFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/UsosFeatures.kt new file mode 100644 index 00000000..a4ee1389 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/UsosFeatures.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-10-11. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.usos + +import pl.szczodrzynski.edziennik.data.api.FEATURE_STUDENT_INFO +import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_USOS_API +import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_USOS +import pl.szczodrzynski.edziennik.data.api.models.Feature + +const val ENDPOINT_USOS_API_USER = 7000 + +val UsosFeatures = listOf( + Feature(LOGIN_TYPE_USOS, FEATURE_STUDENT_INFO, listOf( + ENDPOINT_USOS_API_USER to LOGIN_METHOD_USOS_API, + ), listOf(LOGIN_METHOD_USOS_API)), +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/login/UsosLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/login/UsosLogin.kt new file mode 100644 index 00000000..599b8f16 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/login/UsosLogin.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-10-11. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.usos.login + +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_USOS_API +import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos +import pl.szczodrzynski.edziennik.utils.Utils.d + +class UsosLogin(val data: DataUsos, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "UsosLogin" + } + + private var cancelled = false + + init { + nextLoginMethod(onSuccess) + } + + private fun nextLoginMethod(onSuccess: () -> Unit) { + if (data.targetLoginMethodIds.isEmpty()) { + onSuccess() + return + } + if (cancelled) { + onSuccess() + return + } + useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + data.progress(data.progressStep) + if (usedMethodId != -1) + data.loginMethods.add(usedMethodId) + nextLoginMethod(onSuccess) + } + } + + private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) { + // this should never be true + if (data.loginMethods.contains(loginMethodId)) { + onSuccess(-1) + return + } + d(TAG, "Using login method $loginMethodId") + when (loginMethodId) { + LOGIN_METHOD_USOS_API -> { + data.startProgress(R.string.edziennik_progress_login_usos_api) + UsosLoginApi(data) { onSuccess(loginMethodId) } + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/login/UsosLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/login/UsosLoginApi.kt new file mode 100644 index 00000000..bdf771c3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/usos/login/UsosLoginApi.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-10-11. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.usos.login + +import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_MISSING +import pl.szczodrzynski.edziennik.data.api.ERROR_USOS_OAUTH_LOGIN_REQUEST +import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos +import pl.szczodrzynski.edziennik.data.api.models.ApiError + +class UsosLoginApi(val data: DataUsos, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "UsosLoginApi" + } + + init { run { + if (data.profile == null) { + data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) + return@run + } + + if (data.isApiLoginValid()) { + onSuccess() + } else { + data.error(ApiError(TAG, ERROR_USOS_OAUTH_LOGIN_REQUEST)) + } + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/login/LoginInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/login/LoginInfo.kt index 0448b514..be6d62d8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/login/LoginInfo.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/login/LoginInfo.kt @@ -313,7 +313,24 @@ object LoginInfo { errorCodes = mapOf() ) ) - ) + ), + Register( + loginType = LOGIN_TYPE_USOS, + internalName = "usos", + registerName = R.string.login_type_usos, + registerLogo = R.drawable.login_logo_usos, + loginModes = listOf( + Mode( + loginMode = LOGIN_MODE_USOS_OAUTH, + name = R.string.login_mode_usos_oauth, + icon = R.drawable.login_logo_usos, + guideText = R.string.login_mode_usos_oauth_guide, + isPlatformSelection = true, + credentials = listOf(), + errorCodes = mapOf(), + ), + ), + ), ) } diff --git a/app/src/main/res/drawable/login_logo_usos.png b/app/src/main/res/drawable/login_logo_usos.png new file mode 100644 index 00000000..7c376bdd Binary files /dev/null and b/app/src/main/res/drawable/login_logo_usos.png differ diff --git a/app/src/main/res/values/errors.xml b/app/src/main/res/values/errors.xml index f0916a8b..4e978f7b 100644 --- a/app/src/main/res/values/errors.xml +++ b/app/src/main/res/values/errors.xml @@ -174,6 +174,8 @@ ERROR_PODLASIE_API_OTHER ERROR_PODLASIE_API_DATA_MISSING + ERROR_USOS_OAUTH_LOGIN_REQUEST + ERROR_TEMPLATE_WEB_OTHER EXCEPTION_API_TASK @@ -364,6 +366,8 @@ ERROR_PODLASIE_API_OTHER Brak danych. Zgłoś błąd programiście. + Wymagane logowanie w przeglądarce + ERROR_TEMPLATE_WEB_OTHER Błąd synchronizacji. Upewnij się, że masz połączenie z internetem, a następnie zgłoś błąd. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8f81073..66ac0299 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1537,4 +1537,8 @@ (rodzic) - nieznany przedmiot - {cmd-information-outline} Oceny, których przedmiot nie został podany w dzienniku. Może to być na przykład taki, który nie jest prowadzony w tym roku szkolnym. + Logowanie do USOS API... + USOS + Logowanie z użyciem przeglądarki + TODO