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 af006d5a..5bc08159 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 @@ -119,6 +119,7 @@ const val VULCAN_WEB_ENDPOINT_REGISTER_DEVICE = "RejestracjaUrzadzeniaToken.mvc/ const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}" -const val PODLASIE_API_VERSION = "1.0.31" +const val PODLASIE_API_VERSION = "1.0.62" const val PODLASIE_API_URL = "https://cpdklaser.zeto.bialystok.pl/api" const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia" +const val PODLASIE_API_LOGOUT_DEVICES_ENDPOINT = "/wyczyscUrzadzenia" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt index 9b92d541..a1e9d0eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/podlasie/firstlogin/PodlasieFirstLogin.kt @@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.firstlogin import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_PODLASIE +import pl.szczodrzynski.edziennik.data.api.PODLASIE_API_LOGOUT_DEVICES_ENDPOINT import pl.szczodrzynski.edziennik.data.api.PODLASIE_API_USER_ENDPOINT import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.DataPodlasie import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.data.PodlasieApi @@ -22,50 +23,62 @@ class PodlasieFirstLogin(val data: DataPodlasie, val onSuccess: () -> Unit) { private val api = PodlasieApi(data, null) init { + PodlasieLoginApi(data) { + doLogin() + } + } + + private fun doLogin() { val loginStoreId = data.loginStore.id val loginStoreType = LOGIN_TYPE_PODLASIE - PodlasieLoginApi(data) { - api.apiGet(TAG, PODLASIE_API_USER_ENDPOINT) { json -> - val uuid = json.getString("Uuid") - val login = json.getString("Login") - val firstName = json.getString("FirstName") - val lastName = json.getString("LastName") - val studentNameLong = "$firstName $lastName".fixName() - val studentNameShort = studentNameLong.getShortName() - val schoolName = json.getString("SchoolName") - val className = json.getString("SchoolClass") - val schoolYear = json.getString("ActualSchoolYear")?.replace(' ', '/') - val semester = json.getString("ActualTermShortcut")?.length - val apiUrl = json.getString("URL") - - val profile = Profile( - loginStoreId, - loginStoreId, - loginStoreType, - studentNameLong, - login, - studentNameLong, - studentNameShort, - null - ).apply { - studentData["studentId"] = uuid - studentData["studentLogin"] = login - studentData["schoolName"] = schoolName - studentData["className"] = className - studentData["schoolYear"] = schoolYear - studentData["currentSemester"] = semester ?: 1 - studentData["apiUrl"] = apiUrl - - schoolYear?.split('/')?.get(0)?.toInt()?.let { - studentSchoolYearStart = it - } - studentClassName = className - } - - EventBus.getDefault().postSticky(FirstLoginFinishedEvent(listOf(profile), data.loginStore)) - onSuccess() + if (data.loginStore.getLoginData("logoutDevices", false)) { + data.loginStore.removeLoginData("logoutDevices") + api.apiGet(TAG, PODLASIE_API_LOGOUT_DEVICES_ENDPOINT) { + doLogin() } + return + } + + api.apiGet(TAG, PODLASIE_API_USER_ENDPOINT) { json -> + val uuid = json.getString("Uuid") + val login = json.getString("Login") + val firstName = json.getString("FirstName") + val lastName = json.getString("LastName") + val studentNameLong = "$firstName $lastName".fixName() + val studentNameShort = studentNameLong.getShortName() + val schoolName = json.getString("SchoolName") + val className = json.getString("SchoolClass") + val schoolYear = json.getString("ActualSchoolYear")?.replace(' ', '/') + val semester = json.getString("ActualTermShortcut")?.length + val apiUrl = json.getString("URL") + + val profile = Profile( + loginStoreId, + loginStoreId, + loginStoreType, + studentNameLong, + login, + studentNameLong, + studentNameShort, + null + ).apply { + studentData["studentId"] = uuid + studentData["studentLogin"] = login + studentData["schoolName"] = schoolName + studentData["className"] = className + studentData["schoolYear"] = schoolYear + studentData["currentSemester"] = semester ?: 1 + studentData["apiUrl"] = apiUrl + + schoolYear?.split('/')?.get(0)?.toInt()?.let { + studentSchoolYearStart = it + } + studentClassName = className + } + + EventBus.getDefault().postSticky(FirstLoginFinishedEvent(listOf(profile), data.loginStore)) + onSuccess() } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt index 7a5410fa..82e4f895 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt @@ -22,8 +22,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.databinding.LoginFormCheckboxItemBinding +import pl.szczodrzynski.edziennik.databinding.LoginFormFieldItemBinding import pl.szczodrzynski.edziennik.databinding.LoginFormFragmentBinding -import pl.szczodrzynski.edziennik.databinding.LoginFormItemBinding import pl.szczodrzynski.navlib.colorAttr import java.util.* import kotlin.coroutines.CoroutineContext @@ -75,33 +76,48 @@ class LoginFormFragment : Fragment(), CoroutineScope { b.subTitle.text = platformName ?: app.getString(mode.name) b.text.text = platformGuideText ?: app.getString(mode.guideText) - val credentials = mutableMapOf() + val credentials = mutableMapOf() for (credential in mode.credentials) { if (platformFormFields?.contains(credential.keyName) == false) continue - val b = LoginFormItemBinding.inflate(layoutInflater) - b.textLayout.hint = app.getString(credential.name) - if (credential.hideText) { - b.textEdit.inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD - b.textLayout.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE + if (credential is LoginInfo.FormField) { + val b = LoginFormFieldItemBinding.inflate(layoutInflater) + b.textLayout.hint = app.getString(credential.name) + if (credential.hideText) { + b.textEdit.inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD + b.textLayout.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE + } + b.textEdit.addTextChangedListener { + b.textLayout.error = null + } + + b.textEdit.id = credential.name + + b.textEdit.setText(arguments?.getString(credential.keyName) ?: "") + b.textLayout.startIconDrawable = IconicsDrawable(activity) + .icon(credential.icon) + .sizeDp(24) + .paddingDp(2) + .colorAttr(activity, R.attr.colorOnBackground) + + this.b.formContainer.addView(b.root) + credentials[credential] = b } - b.textEdit.addTextChangedListener { - b.textLayout.error = null + if (credential is LoginInfo.FormCheckbox) { + val b = LoginFormCheckboxItemBinding.inflate(layoutInflater) + b.checkbox.text = app.getString(credential.name) + b.checkbox.onChange { _, _ -> + b.errorText.text = null + } + if (arguments?.containsKey(credential.keyName) == true) { + b.checkbox.isChecked = arguments?.getBoolean(credential.keyName) == true + } + + this.b.formContainer.addView(b.root) + credentials[credential] = b } - - b.textEdit.id = credential.name - - b.textEdit.setText(arguments?.getString(credential.keyName) ?: "") - b.textLayout.startIconDrawable = IconicsDrawable(activity) - .icon(credential.icon) - .sizeDp(24) - .paddingDp(2) - .colorAttr(activity, R.attr.colorOnBackground) - - this.b.formContainer.addView(b.root) - credentials[credential] = b } activity.lastError?.let { error -> @@ -109,7 +125,12 @@ class LoginFormFragment : Fragment(), CoroutineScope { startCoroutineTimer(delayMillis = 200L) { for (credential in credentials) { credential.key.errorCodes[error.errorCode]?.let { - credential.value.textLayout.error = app.getString(it) + (credential.value as? LoginFormFieldItemBinding)?.let { b -> + b.textLayout.error = app.getString(it) + } + (credential.value as? LoginFormCheckboxItemBinding)?.let { b -> + b.errorText.text = app.getString(it) + } return@startCoroutineTimer } } @@ -137,35 +158,42 @@ class LoginFormFragment : Fragment(), CoroutineScope { var hasErrors = false credentials.forEach { (credential, b) -> - var text = b.textEdit.text?.toString() ?: return@forEach - if (!credential.hideText) - text = text.trim() + if (credential is LoginInfo.FormField && b is LoginFormFieldItemBinding) { + var text = b.textEdit.text?.toString() ?: return@forEach + if (!credential.hideText) + text = text.trim() - if (credential.caseMode == LoginInfo.Credential.CaseMode.UPPER_CASE) - text = text.toUpperCase(Locale.getDefault()) - if (credential.caseMode == LoginInfo.Credential.CaseMode.LOWER_CASE) - text = text.toLowerCase(Locale.getDefault()) + if (credential.caseMode == LoginInfo.FormField.CaseMode.UPPER_CASE) + text = text.toUpperCase(Locale.getDefault()) + if (credential.caseMode == LoginInfo.FormField.CaseMode.LOWER_CASE) + text = text.toLowerCase(Locale.getDefault()) - credential.stripTextRegex?.let { - text = text.replace(it.toRegex(), "") + credential.stripTextRegex?.let { + text = text.replace(it.toRegex(), "") + } + + b.textEdit.setText(text) + + if (credential.isRequired && text.isBlank()) { + b.textLayout.error = app.getString(credential.emptyText) + hasErrors = true + return@forEach + } + + if (!text.matches(credential.validationRegex.toRegex())) { + b.textLayout.error = app.getString(credential.invalidText) + hasErrors = true + return@forEach + } + + payload.putString(credential.keyName, text) + arguments?.putString(credential.keyName, text) } - - b.textEdit.setText(text) - - if (credential.isRequired && text.isBlank()) { - b.textLayout.error = app.getString(credential.emptyText) - hasErrors = true - return@forEach + if (credential is LoginInfo.FormCheckbox && b is LoginFormCheckboxItemBinding) { + val checked = b.checkbox.isChecked + payload.putBoolean(credential.keyName, checked) + arguments?.putBoolean(credential.keyName, checked) } - - if (!text.matches(credential.validationRegex.toRegex())) { - b.textLayout.error = app.getString(credential.invalidText) - hasErrors = true - return@forEach - } - - payload.putString(credential.keyName, text) - arguments?.putString(credential.keyName, text) } if (hasErrors) 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 146fd05b..4ff09d5f 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 @@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel object LoginInfo { - private fun getEmailCredential(keyName: String) = Credential( + private fun getEmailCredential(keyName: String) = FormField( keyName = keyName, name = R.string.login_hint_email, icon = CommunityMaterial.Icon.cmd_at, @@ -24,9 +24,9 @@ object LoginInfo { errorCodes = mapOf(), isRequired = true, validationRegex = "([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ) - private fun getPasswordCredential(keyName: String) = Credential( + private fun getPasswordCredential(keyName: String) = FormField( keyName = keyName, name = R.string.login_hint_password, icon = CommunityMaterial.Icon2.cmd_lock_outline, @@ -94,7 +94,7 @@ object LoginInfo { hintText = R.string.login_mode_librus_jst_hint, guideText = R.string.login_mode_librus_jst_guide, credentials = listOf( - Credential( + FormField( keyName = "accountCode", name = R.string.login_hint_token, icon = CommunityMaterial.Icon.cmd_code_braces, @@ -103,9 +103,9 @@ object LoginInfo { errorCodes = mapOf(), isRequired = true, validationRegex = "[A-Z0-9_]+", - caseMode = Credential.CaseMode.UPPER_CASE + caseMode = FormField.CaseMode.UPPER_CASE ), - Credential( + FormField( keyName = "accountPin", name = R.string.login_hint_pin, icon = CommunityMaterial.Icon2.cmd_lock, @@ -114,7 +114,7 @@ object LoginInfo { errorCodes = mapOf(), isRequired = true, validationRegex = "[a-z0-9_]+", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ) ), errorCodes = mapOf( @@ -138,7 +138,7 @@ object LoginInfo { guideText = R.string.login_mode_vulcan_api_guide, isRecommended = true, credentials = listOf( - Credential( + FormField( keyName = "deviceToken", name = R.string.login_hint_token, icon = CommunityMaterial.Icon.cmd_code_braces, @@ -149,9 +149,9 @@ object LoginInfo { ), isRequired = true, validationRegex = "[A-Z0-9]{5,12}", - caseMode = Credential.CaseMode.UPPER_CASE + caseMode = FormField.CaseMode.UPPER_CASE ), - Credential( + FormField( keyName = "symbol", name = R.string.login_hint_symbol, icon = CommunityMaterial.Icon2.cmd_school, @@ -162,9 +162,9 @@ object LoginInfo { ), isRequired = true, validationRegex = "[a-z0-9_-]+", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ), - Credential( + FormField( keyName = "devicePin", name = R.string.login_hint_pin, icon = CommunityMaterial.Icon2.cmd_lock, @@ -175,7 +175,7 @@ object LoginInfo { ), isRequired = true, validationRegex = "[0-9]+", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ) ), errorCodes = mapOf( @@ -222,7 +222,7 @@ object LoginInfo { hintText = R.string.login_mode_mobidziennik_web_hint, guideText = R.string.login_mode_mobidziennik_web_guide, credentials = listOf( - Credential( + FormField( keyName = "username", name = R.string.login_hint_login_email, icon = CommunityMaterial.Icon.cmd_account_outline, @@ -231,9 +231,9 @@ object LoginInfo { errorCodes = mapOf(), isRequired = true, validationRegex = "^[a-z0-9_\\-@+.]+$", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ), - Credential( + FormField( keyName = "password", name = R.string.login_hint_password, icon = CommunityMaterial.Icon2.cmd_lock_outline, @@ -246,7 +246,7 @@ object LoginInfo { validationRegex = ".*", hideText = true ), - Credential( + FormField( keyName = "serverName", name = R.string.login_hint_address, icon = CommunityMaterial.Icon2.cmd_web, @@ -257,7 +257,7 @@ object LoginInfo { ), isRequired = true, validationRegex = "^[a-z0-9_\\-]+\$", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ) ), errorCodes = mapOf( @@ -280,7 +280,7 @@ object LoginInfo { hintText = R.string.login_mode_idziennik_web_hint, guideText = R.string.login_mode_idziennik_web_guide, credentials = listOf( - Credential( + FormField( keyName = "schoolName", name = R.string.login_hint_school_name, icon = CommunityMaterial.Icon2.cmd_school, @@ -291,9 +291,9 @@ object LoginInfo { ), isRequired = true, validationRegex = "^[a-z0-9_\\-.]+$", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ), - Credential( + FormField( keyName = "username", name = R.string.login_hint_username, icon = CommunityMaterial.Icon.cmd_account_outline, @@ -302,7 +302,7 @@ object LoginInfo { errorCodes = mapOf(), isRequired = true, validationRegex = "^[a-z0-9_\\-.]+$", - caseMode = Credential.CaseMode.LOWER_CASE + caseMode = FormField.CaseMode.LOWER_CASE ), getPasswordCredential("password") ), @@ -346,7 +346,7 @@ object LoginInfo { icon = R.drawable.login_mode_podlasie_api, guideText = R.string.login_mode_podlasie_api_guide, credentials = listOf( - Credential( + FormField( keyName = "apiToken", name = R.string.login_hint_token, icon = CommunityMaterial.Icon2.cmd_lock_outline, @@ -355,7 +355,15 @@ object LoginInfo { errorCodes = mapOf(), isRequired = true, validationRegex = "[a-zA-Z0-9]{10}", - caseMode = Credential.CaseMode.UNCHANGED + caseMode = FormField.CaseMode.UNCHANGED + ), + FormCheckbox( + keyName = "logoutDevices", + name = R.string.login_podlasie_logout_devices, + checked = false, + errorCodes = mapOf( + ERROR_LOGIN_PODLASIE_API_DEVICE_LIMIT to R.string.error_602_reason + ) ) ), errorCodes = mapOf() @@ -392,7 +400,7 @@ object LoginInfo { val isTesting: Boolean = false, val isPlatformSelection: Boolean = false, - val credentials: List, + val credentials: List, val errorCodes: Map ) @@ -409,11 +417,18 @@ object LoginInfo { val apiData: JsonObject ) - data class Credential( - val keyName: String, + open class BaseCredential( + open val keyName: String, + @StringRes + open val name: Int, + open val errorCodes: Map + ) + + data class FormField( + override val keyName: String, @StringRes - val name: Int, + override val name: Int, val icon: IIcon, @StringRes val placeholder: Int? = null, @@ -421,7 +436,7 @@ object LoginInfo { val emptyText: Int, @StringRes val invalidText: Int, - val errorCodes: Map, + override val errorCodes: Map, @StringRes val hintText: Int? = null, @@ -430,10 +445,18 @@ object LoginInfo { val caseMode: CaseMode = CaseMode.UNCHANGED, val hideText: Boolean = false, val stripTextRegex: String? = null - ) { + ) : BaseCredential(keyName, name, errorCodes) { enum class CaseMode { UNCHANGED, UPPER_CASE, LOWER_CASE } } + data class FormCheckbox( + override val keyName: String, + @StringRes + override val name: Int, + val checked: Boolean = false, + override val errorCodes: Map = mapOf() + ) : BaseCredential(keyName, name, errorCodes) + var chooserList: MutableList? = null var platformList: MutableMap> = mutableMapOf() } diff --git a/app/src/main/res/layout/login_form_checkbox_item.xml b/app/src/main/res/layout/login_form_checkbox_item.xml new file mode 100644 index 00000000..c28e9958 --- /dev/null +++ b/app/src/main/res/layout/login_form_checkbox_item.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/login_form_item.xml b/app/src/main/res/layout/login_form_field_item.xml similarity index 100% rename from app/src/main/res/layout/login_form_item.xml rename to app/src/main/res/layout/login_form_field_item.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eb594d6d..da00018f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1379,4 +1379,5 @@ Aktualizuj Dowiedz się więcej Stara nie zobaczy pał + Wyloguj z pozostałych urządzeń