diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 2443679b..c6d023b2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -1247,3 +1247,13 @@ operator fun Iterable>.get(key: K): V? { } fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } + +fun String?.nullIfEmpty() = when { + this.isNullOrEmpty() -> null + else -> this +} + +fun String?.nullIfBlank() = when { + this.isNullOrBlank() -> null + else -> this +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt index d524a6dc..e1d08573 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt @@ -126,3 +126,5 @@ const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia" const val GDYNIA_WEB_URL = "https://nasze.miasto.gdynia.pl/ed_miej" const val GDYNIA_WEB_LOGIN = "login.pl" const val GDYNIA_WEB_LOGIN_CHECK = "login_check.pl" +const val GDYNIA_WEB_START = "zest_start.pl" +const val GDYNIA_WEB_DISPLAY = "display.pl" 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 6df17843..48fd3abf 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 @@ -233,5 +233,6 @@ const val ERROR_ONEDRIVE_DOWNLOAD = 930 const val EXCEPTION_VULCAN_WEB_LOGIN = 931 const val EXCEPTION_VULCAN_WEB_REQUEST = 932 const val EXCEPTION_PODLASIE_API_REQUEST = 940 +const val EXCEPTION_GDYNIA_WEB_REQUEST = 950 const val LOGIN_NO_ARGUMENTS = 1201 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 b2f3cd88..651970ca 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 @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.data.api import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.login.GdyniaLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginApi @@ -154,6 +155,11 @@ val podlasieLoginMethods = listOf( const val LOGIN_TYPE_GDYNIA = 7 const val LOGIN_MODE_GDYNIA_WEB = 0 const val LOGIN_METHOD_GDYNIA_WEB = 100 +val gdyniaLoginMethods = listOf( + LoginMethod(LOGIN_TYPE_GDYNIA, LOGIN_METHOD_GDYNIA_WEB, GdyniaLoginWeb::class.java) + .withIsPossible { _, _ -> true } + .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED } +) val templateLoginMethods = listOf( LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt index 4d3f0aca..0e9aaac7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt @@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.Gdynia import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik @@ -82,6 +83,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_EDUDZIENNIK -> Edudziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_PODLASIE -> Podlasie(app, profile, loginStore, taskCallback) + LOGIN_TYPE_GDYNIA -> Gdynia(app, profile, loginStore, taskCallback) LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback) else -> null } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/DataGdynia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/DataGdynia.kt index 936e914a..86e1ac40 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/DataGdynia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/DataGdynia.kt @@ -34,6 +34,21 @@ class DataGdynia(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mLoginPassword = mLoginPassword ?: loginStore.getLoginData("password", null); return mLoginPassword } set(value) { loginStore.putLoginData("password", value); mLoginPassword = value } + private var mStudentLogin: String? = null + var studentLogin: String? + get() { mStudentLogin = mStudentLogin ?: profile?.getStudentData("studentLogin", null); return mStudentLogin } + set(value) { profile?.putStudentData("studentLogin", value) ?: return; mStudentLogin = value } + + private var mStudentAlias: String? = null + var studentAlias: String? + get() { mStudentAlias = mStudentAlias ?: profile?.getStudentData("studentAlias", null); return mStudentAlias } + set(value) { profile?.putStudentData("studentAlias", value) ?: return; mStudentAlias = value } + + private var mStudentEmail: String? = null + var studentEmail: String? + get() { mStudentEmail = mStudentEmail ?: profile?.getStudentData("studentEmail", null); return mStudentEmail } + set(value) { profile?.putStudentData("studentEmail", value) ?: return; mStudentEmail = value } + /* __ __ _ \ \ / / | | \ \ /\ / /__| |__ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/Gdynia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/Gdynia.kt index 5e3cc3d6..a76bce42 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/Gdynia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/Gdynia.kt @@ -6,9 +6,14 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.gdynia import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.data.GdyniaData +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.firstlogin.GdyniaFirstLogin +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.login.GdyniaLogin +import pl.szczodrzynski.edziennik.data.api.gdyniaLoginMethods 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.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher @@ -19,7 +24,7 @@ import pl.szczodrzynski.edziennik.utils.Utils class Gdynia(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { -private const val TAG = "Gdynia" + private const val TAG = "Gdynia" } val internalErrorList = mutableListOf() @@ -32,8 +37,29 @@ private const val TAG = "Gdynia" } } - override fun sync(featureIds: List, viewId: Int?, onlyEndpoints: List?, arguments: JsonObject?) { + private fun completed() { + data.saveData() + callback.onCompleted() + } + /* _______ _ _ _ _ _ + |__ __| | /\ | | (_) | | | + | | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___ + | | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \ + | | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | | + |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| + __/ | + |__*/ + override fun sync(featureIds: List, viewId: Int?, onlyEndpoints: List?, arguments: JsonObject?) { + data.arguments = arguments + data.prepare(gdyniaLoginMethods, GdyniaFeatures, featureIds, viewId, onlyEndpoints) + Utils.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + Utils.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + GdyniaLogin(data) { + GdyniaData(data) { + completed() + } + } } override fun getMessage(message: MessageFull) { @@ -65,7 +91,9 @@ private const val TAG = "Gdynia" } override fun firstLogin() { - TODO("Not yet implemented") + GdyniaFirstLogin(data) { + completed() + } } override fun cancel() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/GdyniaFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/GdyniaFeatures.kt new file mode 100644 index 00000000..10817d2c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/GdyniaFeatures.kt @@ -0,0 +1,9 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2020-5-17 + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.gdynia + +import pl.szczodrzynski.edziennik.data.api.models.Feature + +val GdyniaFeatures = listOf() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/data/GdyniaData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/data/GdyniaData.kt new file mode 100644 index 00000000..f6b33c26 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/data/GdyniaData.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2020-5-17 + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.data + +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.DataGdynia +import pl.szczodrzynski.edziennik.utils.Utils + +class GdyniaData(val data: DataGdynia, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "GdyniaData" + } + + init { + nextEndpoint(onSuccess) + } + + private fun nextEndpoint(onSuccess: () -> Unit) { + if (data.targetEndpointIds.isEmpty()) { + onSuccess() + return + } + if (data.cancelled) { + onSuccess() + return + } + val id = data.targetEndpointIds.firstKey() + val lastSync = data.targetEndpointIds.remove(id) + useEndpoint(id, lastSync) { + data.progress(data.progressStep) + nextEndpoint(onSuccess) + } + } + + private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) { + Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync") + when (endpointId) { + else -> onSuccess(endpointId) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/data/GdyniaWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/data/GdyniaWeb.kt new file mode 100644 index 00000000..7ed11dd3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/data/GdyniaWeb.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2020-5-17 + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.data + +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import pl.szczodrzynski.edziennik.data.api.* +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.DataGdynia +import pl.szczodrzynski.edziennik.data.api.models.ApiError +import pl.szczodrzynski.edziennik.utils.Utils.d + +open class GdyniaWeb(open val data: DataGdynia, open val lastSync: Long?) { + companion object { + private const val TAG = "GdyniaWeb" + } + + fun webGet(tag: String, endpoint: String, parameters: Map? = null, onSuccess: (text: String) -> Unit) { + val url = "$GDYNIA_WEB_URL/$endpoint" + d(tag, "Request: Gdynia/Web - $url") + + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + if (text == null || response == null) { + data.error(ApiError(tag, ERROR_RESPONSE_EMPTY) + .withResponse(response) + .withApiResponse(text)) + return + } + + try { + onSuccess(text) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_GDYNIA_WEB_REQUEST) + .withApiResponse(text) + .withResponse(response) + .withThrowable(e)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url(url) + .userAgent(SYSTEM_USER_AGENT) + .apply { + parameters?.forEach { (key, value) -> + addParameter(key, value) + } + } + .get() + .callback(callback) + .build() + .enqueue() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/firstlogin/GdyniaFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/firstlogin/GdyniaFirstLogin.kt new file mode 100644 index 00000000..f8789156 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/gdynia/firstlogin/GdyniaFirstLogin.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2020-5-17 + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.firstlogin + +import org.greenrobot.eventbus.EventBus +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.api.GDYNIA_WEB_DISPLAY +import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_GDYNIA +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.DataGdynia +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.data.GdyniaWeb +import pl.szczodrzynski.edziennik.data.api.edziennik.gdynia.login.GdyniaLoginWeb +import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent +import pl.szczodrzynski.edziennik.data.db.entity.Profile + +class GdyniaFirstLogin(val data: DataGdynia, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "GdyniaFirstLogin" + } + + private val web = GdyniaWeb(data, null) + private val profileList = mutableListOf() + + init { + val loginStoreId = data.loginStore.id + val loginStoreType = LOGIN_TYPE_GDYNIA + + GdyniaLoginWeb(data) { + web.webGet(TAG, GDYNIA_WEB_DISPLAY, parameters = mapOf( + "form" to "zmiana_danych" + )) { html -> + run { + val doc = Jsoup.parse(html) + + val firstName = doc.selectFirst("#f_imie")?.`val`() ?: return@run + val lastName = doc.selectFirst("#f_nazwisko")?.`val`() ?: return@run + val studentNameLong = "$firstName $lastName".fixName() + val studentNameShort = studentNameLong.getShortName() + + val login = doc.selectFirst("#f_login").`val`().nullIfBlank() + val alias = doc.selectFirst("#f_kod_logowania").`val`().nullIfBlank() + val email = doc.selectFirst("#f_email")?.`val`().nullIfBlank() + + val subname = alias ?: email ?: data.loginUsername + + val profile = Profile( + loginStoreId, + loginStoreId, + loginStoreType, + studentNameLong, + subname, + studentNameLong, + studentNameShort, + null + ).apply { + studentData["studentLogin"] = login + studentData["studentAlias"] = alias + studentData["studentEmail"] = email + } + + profileList.add(profile) + } + + EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore)) + onSuccess() + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/data/TemplateData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/data/TemplateData.kt index 3489555f..8a47b680 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/data/TemplateData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/data/TemplateData.kt @@ -34,7 +34,7 @@ class TemplateData(val data: DataTemplate, val onSuccess: () -> Unit) { } val id = data.targetEndpointIds.firstKey() val lastSync = data.targetEndpointIds.remove(id) - useEndpoint(id, lastSync) { endpointId -> + useEndpoint(id, lastSync) { data.progress(data.progressStep) nextEndpoint(onSuccess) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt index d7230356..f108d99a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt @@ -361,6 +361,37 @@ object LoginInfo { errorCodes = mapOf() ) ) + ), + Register( + loginType = LOGIN_TYPE_GDYNIA, + internalName = "gdynia", + registerName = R.string.login_type_gdynia, + registerLogo = R.drawable.login_logo_gdynia, + loginModes = listOf( + Mode( + loginMode = LOGIN_MODE_GDYNIA_WEB, + name = R.string.login_mode_gdynia_web, + icon = R.drawable.login_mode_gdynia_web, + guideText = R.string.login_mode_gdynia_web_guide, + credentials = listOf( + Credential( + keyName = "username", + name = R.string.login_hint_username, + icon = CommunityMaterial.Icon.cmd_account_outline, + emptyText = R.string.login_error_no_username, + invalidText = R.string.login_error_incorrect_username, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "^[a-zA-Z0-9_\\-.]+$", + caseMode = Credential.CaseMode.UNCHANGED + ), + getPasswordCredential("password") + ), + errorCodes = mapOf( + ERROR_LOGIN_GDYNIA_WEB_INVALID_CREDENTIALS to R.string.login_error_incorrect_login_or_password + ) + ) + ) ) ) } diff --git a/app/src/main/res/drawable/login_logo_gdynia.png b/app/src/main/res/drawable/login_logo_gdynia.png new file mode 100644 index 00000000..f53cb628 Binary files /dev/null and b/app/src/main/res/drawable/login_logo_gdynia.png differ diff --git a/app/src/main/res/drawable-nodpi/login_logo_podlasie.png b/app/src/main/res/drawable/login_logo_podlasie.png similarity index 100% rename from app/src/main/res/drawable-nodpi/login_logo_podlasie.png rename to app/src/main/res/drawable/login_logo_podlasie.png diff --git a/app/src/main/res/drawable/login_mode_gdynia_web.png b/app/src/main/res/drawable/login_mode_gdynia_web.png new file mode 100644 index 00000000..452ed282 Binary files /dev/null and b/app/src/main/res/drawable/login_mode_gdynia_web.png differ diff --git a/app/src/main/res/drawable-xhdpi/login_mode_podlasie_api.png b/app/src/main/res/drawable/login_mode_podlasie_api.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/login_mode_podlasie_api.png rename to app/src/main/res/drawable/login_mode_podlasie_api.png diff --git a/app/src/main/res/values/errors.xml b/app/src/main/res/values/errors.xml index 7995253c..1f500e72 100644 --- a/app/src/main/res/values/errors.xml +++ b/app/src/main/res/values/errors.xml @@ -193,6 +193,7 @@ EXCEPTION_VULCAN_WEB_LOGIN EXCEPTION_VULCAN_WEB_REQUEST EXCEPTION_PODLASIE_API_REQUEST + EXCEPTION_GDYNIA_WEB_REQUEST LOGIN_NO_ARGUMENTS @@ -385,6 +386,7 @@ EXCEPTION_VULCAN_WEB_LOGIN EXCEPTION_VULCAN_WEB_REQUEST Zgłoś błąd: wyjątek w API PPE + Zgłoś błąd: wyjątek w dzienniku Platformy Miejskiej Gdyni Nie podano parametrów diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 207f12f0..dd61772c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1357,4 +1357,7 @@ Podaj token aplikacji mobilnej. Logowanie do PPE… Logowanie do dziennika gdyńskiego… + Platforma Miejska Gdyni + Zaloguj używając nazwy użytkownika i hasła + Podaj nazwę użytkownika oraz hasło, za pomocą których logujesz się na stronie internetowej dziennika.