1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-19 22:49:08 -05:00

Add custom register host field on login screen (#2221)

This commit is contained in:
Mikołaj Pich 2023-06-01 23:22:10 +02:00 committed by GitHub
parent c1b86674c2
commit 5306044173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 2564 additions and 28 deletions

View File

@ -196,7 +196,7 @@ ext {
}
dependencies {
implementation 'io.github.wulkanowy:sdk:2.0.7'
implementation 'io.github.wulkanowy:sdk:2.0.8-SNAPSHOT'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,7 @@ import javax.inject.Singleton
AutoMigration(from = 47, to = 48),
AutoMigration(from = 51, to = 52),
AutoMigration(from = 54, to = 55, spec = Migration55::class),
AutoMigration(from = 55, to = 56),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@ -57,7 +58,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 55
const val VERSION_SCHEMA = 56
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),

View File

@ -19,6 +19,9 @@ data class Student(
@ColumnInfo(name = "scrapper_base_url")
val scrapperBaseUrl: String,
@ColumnInfo(name = "scrapper_domain_suffix", defaultValue = "")
val scrapperDomainSuffix: String,
@ColumnInfo(name = "mobile_base_url")
val mobileBaseUrl: String,

View File

@ -55,6 +55,7 @@ fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
fun RegisterStudent.mapToStudentWithSemesters(
user: RegisterUser,
scrapperDomainSuffix: String,
symbol: RegisterSymbol,
unit: RegisterUnit,
colors: List<Long>,
@ -76,6 +77,7 @@ fun RegisterStudent.mapToStudentWithSemesters(
studentName = "$studentName $studentSurname",
loginMode = user.loginMode.name,
scrapperBaseUrl = user.scrapperBaseUrl.orEmpty(),
scrapperDomainSuffix = scrapperDomainSuffix,
mobileBaseUrl = symbol.hebeBaseUrl.orEmpty(),
certificateKey = symbol.keyId.orEmpty(),
privateKey = symbol.privatePem.orEmpty(),

View File

@ -43,22 +43,14 @@ class StudentRepository @Inject constructor(
.getStudentsFromHebe(token, pin, symbol, "")
.mapToPojo(null)
suspend fun getStudentsScrapper(
email: String,
password: String,
scrapperBaseUrl: String,
symbol: String
): RegisterUser = sdk
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
.mapToPojo(password)
suspend fun getUserSubjectsFromScrapper(
email: String,
password: String,
scrapperBaseUrl: String,
domainSuffix: String,
symbol: String
): RegisterUser = sdk
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
.mapToPojo(password)
suspend fun getStudentsHybrid(

View File

@ -6,5 +6,6 @@ data class LoginData(
val login: String,
val password: String,
val baseUrl: String,
val domainSuffix: String,
val symbol: String?,
) : Serializable

View File

@ -5,6 +5,7 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.ArrayAdapter
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@ -55,6 +56,9 @@ class LoginAdvancedFragment :
get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString()))
.orEmpty()
override val formDomainSuffix: String
get() = binding.loginFormDomainSuffix.text.toString().trim()
override val formHostSymbol: String
get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString()))
.orEmpty()
@ -279,6 +283,7 @@ class LoginAdvancedFragment :
loginFormUsernameLayout.visibility = VISIBLE
loginFormPassLayout.visibility = VISIBLE
loginFormHostLayout.visibility = VISIBLE
loginFormDomainSuffixLayout.isVisible = true
loginFormPinLayout.visibility = GONE
loginFormSymbolLayout.visibility = VISIBLE
loginFormTokenLayout.visibility = GONE
@ -290,6 +295,7 @@ class LoginAdvancedFragment :
loginFormUsernameLayout.visibility = VISIBLE
loginFormPassLayout.visibility = VISIBLE
loginFormHostLayout.visibility = VISIBLE
loginFormDomainSuffixLayout.isVisible = true
loginFormPinLayout.visibility = GONE
loginFormSymbolLayout.visibility = VISIBLE
loginFormTokenLayout.visibility = GONE
@ -301,6 +307,7 @@ class LoginAdvancedFragment :
loginFormUsernameLayout.visibility = GONE
loginFormPassLayout.visibility = GONE
loginFormHostLayout.visibility = GONE
loginFormDomainSuffixLayout.isVisible = false
loginFormPinLayout.visibility = VISIBLE
loginFormSymbolLayout.visibility = VISIBLE
loginFormTokenLayout.visibility = VISIBLE

View File

@ -154,6 +154,7 @@ class LoginAdvancedPresenter @Inject constructor(
login = view?.formUsernameValue.orEmpty().trim(),
password = view?.formPassValue.orEmpty().trim(),
baseUrl = view?.formHostValue.orEmpty().trim(),
domainSuffix = view?.formDomainSuffix.orEmpty().trim(),
symbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(),
)
when (it.data.symbols.size) {
@ -186,6 +187,7 @@ class LoginAdvancedPresenter @Inject constructor(
val email = view?.formUsernameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty()
val domainSuffix = view?.formDomainSuffix.orEmpty()
val pin = view?.formPinValue.orEmpty()
val symbol = view?.formSymbolValue.orEmpty()
@ -193,8 +195,8 @@ class LoginAdvancedPresenter @Inject constructor(
return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
Sdk.Mode.HEBE -> studentRepository.getStudentsApi(pin, symbol, token)
Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(
email, password, endpoint, symbol
Sdk.Mode.SCRAPPER -> studentRepository.getUserSubjectsFromScrapper(
email, password, endpoint, domainSuffix, symbol
)
Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(

View File

@ -12,6 +12,8 @@ interface LoginAdvancedView : BaseView {
val formHostValue: String
val formDomainSuffix: String
val formHostSymbol: String
val formLoginType: String

View File

@ -45,6 +45,9 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString()))
.orEmpty()
override val formDomainSuffix: String
get() = binding.loginFormDomainSuffix.text.toString()
override val formHostSymbol: String
get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString()))
.orEmpty()
@ -204,6 +207,10 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
binding.loginFormContainer.visibility = if (show) VISIBLE else GONE
}
override fun showDomainSuffixInput(show: Boolean) {
binding.loginFormDomainSuffixLayout.isVisible = show
}
override fun showOtherOptionsButton(show: Boolean) {
binding.loginFormAdvancedButton.isVisible = show
}
@ -256,6 +263,7 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
override fun onResume() {
super.onResume()
presenter.updateUsernameLabel()
presenter.updateCustomDomainSuffixVisibility()
}
override fun openEmail(lastError: String) {

View File

@ -1,8 +1,13 @@
package io.github.wulkanowy.ui.modules.login.form
import androidx.core.net.toUri
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceLoading
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
@ -56,10 +61,17 @@ class LoginFormPresenter @Inject constructor(
} else if (formUsernameValue == "jan@fakelog.cf" && formPassValue == "jan123") {
setCredentials("", "")
}
updateCustomDomainSuffixVisibility()
updateUsernameLabel()
}
}
fun updateCustomDomainSuffixVisibility() {
view?.run {
showDomainSuffixInput("customSuffix" in formHostValue)
}
}
fun updateUsernameLabel() {
view?.run {
setUsernameLabel(if ("email" !in formHostValue) nicknameLabel else emailLabel)
@ -91,6 +103,7 @@ class LoginFormPresenter @Inject constructor(
val email = view?.formUsernameValue.orEmpty().trim()
val password = view?.formPassValue.orEmpty().trim()
val host = view?.formHostValue.orEmpty().trim()
val domainSuffix = view?.formDomainSuffix.orEmpty().trim()
val symbol = view?.formHostSymbol.orEmpty().trim()
if (!validateCredentials(email, password, host)) return
@ -100,6 +113,7 @@ class LoginFormPresenter @Inject constructor(
email = email,
password = password,
scrapperBaseUrl = host,
domainSuffix = domainSuffix,
symbol = symbol
)
}
@ -112,7 +126,7 @@ class LoginFormPresenter @Inject constructor(
}
}
.onResourceSuccess {
val loginData = LoginData(email, password, host, symbol)
val loginData = LoginData(email, password, host, domainSuffix, symbol)
when (it.symbols.size) {
0 -> view?.navigateToSymbol(loginData)
else -> view?.navigateToStudentSelect(loginData, it)

View File

@ -14,6 +14,8 @@ interface LoginFormView : BaseView {
val formHostValue: String
val formDomainSuffix: String
val formHostSymbol: String
val nicknameLabel: String
@ -56,6 +58,8 @@ interface LoginFormView : BaseView {
fun showContent(show: Boolean)
fun showDomainSuffixInput(show: Boolean)
fun showOtherOptionsButton(show: Boolean)
fun showVersion()

View File

@ -241,6 +241,7 @@ class LoginStudentSelectPresenter @Inject constructor(
item.student.mapToStudentWithSemesters(
user = registerUser,
symbol = item.symbol,
scrapperDomainSuffix = loginData.domainSuffix,
unit = item.unit,
colors = appInfo.defaultColorsForAvatar,
)

View File

@ -62,6 +62,7 @@ class LoginSymbolPresenter @Inject constructor(
email = loginData.login,
password = loginData.password,
scrapperBaseUrl = loginData.baseUrl,
domainSuffix = loginData.domainSuffix,
symbol = loginData.symbol.orEmpty(),
)
}.onEach { user ->

View File

@ -16,6 +16,7 @@ fun Sdk.init(student: Student): Sdk {
mobileBaseUrl = student.mobileBaseUrl
} else {
scrapperBaseUrl = student.scrapperBaseUrl
domainSuffix = student.scrapperDomainSuffix
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
}

View File

@ -172,7 +172,7 @@
android:layout_marginBottom="16dp"
android:hint="@string/login_host_hint"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/loginFormTokenLayout"
app:layout_constraintBottom_toTopOf="@+id/loginFormDomainSuffixLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormPassLayout">
@ -185,6 +185,31 @@
tools:ignore="Deprecated,LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormDomainSuffixLayout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_domain_suffix_hint"
app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@+id/loginFormSymbolLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormDomainSuffix"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textShortMessage"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormTokenLayout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
@ -200,7 +225,7 @@
app:layout_constraintBottom_toTopOf="@+id/loginFormSymbolLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout">
app:layout_constraintTop_toBottomOf="@+id/loginFormDomainSuffixLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormToken"

View File

@ -239,6 +239,32 @@
tools:ignore="Deprecated,LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormDomainSuffixLayout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_domain_suffix_hint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormDomainSuffix"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:importantForAutofill="no"
android:inputType="text"
android:maxLines="1"
tools:targetApi="o" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/loginFormAdvancedButton"
style="@style/Widget.Material3.Button.OutlinedButton"
@ -264,7 +290,7 @@
android:layout_marginEnd="24dp"
android:text="@string/login_sign_in"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" />
app:layout_constraintTop_toBottomOf="@+id/loginFormDomainSuffixLayout" />
<TextView
android:id="@+id/loginFormPrivacyLink"

View File

@ -20,6 +20,7 @@
<item>Gmina Ulan-Majorat - Platforma oświatowa</item>
<item>Gmina Ozorków - Platforma edukacyjna</item>
<item>Gmina Łopiennik Górny - Platforma oświatowa</item>
<item>Custom domain suffix</item>
<item>Fakelog</item>
</string-array>
<string-array name="hosts_values">
@ -42,6 +43,7 @@
<item>https://vulcan.net.pl/?login</item>
<item>https://vulcan.net.pl/?login</item>
<item>https://vulcan.net.pl/?login</item>
<item>https://vulcan.net.pl/?email&amp;customSuffix</item>
<item>https://fakelog.cf/?email</item>
</string-array>
<string-array name="hosts_symbols">
@ -64,6 +66,7 @@
<item>gminaulanmajorat</item>
<item>gminaozorkow</item>
<item>gminalopiennikgorny</item>
<item>Default</item>
<item>powiatwulkanowy</item>
</string-array>
</resources>

View File

@ -42,6 +42,7 @@
<string name="login_login_pesel_email_hint">Login, PESEL or e-mail</string>
<string name="login_password_hint">Password</string>
<string name="login_host_hint">UONET+ register variant</string>
<string name="login_domain_suffix_hint">Custom domain suffix</string>
<string name="login_type_api">Mobile API</string>
<string name="login_type_scrapper">Scraper</string>
<string name="login_type_hybrid">Hybrid</string>

View File

@ -50,6 +50,7 @@ fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalD
fun getStudentEntity(mode: Sdk.Mode = Sdk.Mode.HEBE) = Student(
scrapperBaseUrl = "http://fakelog.cf",
scrapperDomainSuffix = "",
email = "jan@fakelog.cf",
certificateKey = "",
classId = 0,

View File

@ -296,6 +296,7 @@ class GradeRepositoryTest {
private fun createGrades(grades: List<SdkGrade>): Grades = Grades(
details = grades,
summary = listOf(),
descriptive = emptyList(),
isAverage = false,
isPoints = false,
isForAdults = false,

View File

@ -201,6 +201,7 @@ class GetMailboxByStudentUseCaseTest {
schoolShortName: String = "test",
) = Student(
scrapperBaseUrl = "http://fakelog.cf",
scrapperDomainSuffix = "",
email = "jan@fakelog.cf",
certificateKey = "",
classId = 0,

View File

@ -47,6 +47,7 @@ class GradeAverageProviderTest {
private val student = Student(
scrapperBaseUrl = "",
scrapperDomainSuffix = "",
mobileBaseUrl = "",
loginType = "",
loginMode = "SCRAPPER",

View File

@ -130,7 +130,7 @@ class LoginFormPresenterTest {
@Test
fun loginTest() {
coEvery {
repository.getUserSubjectsFromScrapper(any(), any(), any(), any())
repository.getUserSubjectsFromScrapper(any(), any(), any(), any(), any())
} returns registerUser
every { loginFormView.formUsernameValue } returns "@"
@ -149,7 +149,7 @@ class LoginFormPresenterTest {
@Test
fun loginEmptyTest() {
coEvery {
repository.getUserSubjectsFromScrapper(any(), any(), any(), any())
repository.getUserSubjectsFromScrapper(any(), any(), any(), any(), any())
} returns registerUser
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
@ -167,7 +167,7 @@ class LoginFormPresenterTest {
@Test
fun loginEmptyTwiceTest() {
coEvery {
repository.getUserSubjectsFromScrapper(any(), any(), any(), any())
repository.getUserSubjectsFromScrapper(any(), any(), any(), any(), any())
} returns registerUser
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
@ -187,12 +187,7 @@ class LoginFormPresenterTest {
fun loginErrorTest() {
val testException = IOException("test")
coEvery {
repository.getUserSubjectsFromScrapper(
any(),
any(),
any(),
any()
)
repository.getUserSubjectsFromScrapper(any(), any(), any(), any(), any())
} throws testException
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"

View File

@ -55,6 +55,7 @@ class LoginStudentSelectPresenterTest {
password = "",
baseUrl = "",
symbol = null,
domainSuffix = "",
)
private val subject = RegisterStudent(