refactor: download wizard details form #163
5 changed files with 147 additions and 30 deletions
|
@ -1,10 +1,13 @@
|
||||||
package im.angry.openeuicc.ui.wizard
|
package im.angry.openeuicc.ui.wizard
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Patterns
|
import android.text.InputType
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
|
@ -48,8 +51,13 @@ class DownloadWizardDetailsFragment : DownloadWizardActivity.DownloadWizardStepF
|
||||||
matchingId = view.requireViewById(R.id.profile_download_code)
|
matchingId = view.requireViewById(R.id.profile_download_code)
|
||||||
confirmationCode = view.requireViewById(R.id.profile_download_confirmation_code)
|
confirmationCode = view.requireViewById(R.id.profile_download_confirmation_code)
|
||||||
imei = view.requireViewById(R.id.profile_download_imei)
|
imei = view.requireViewById(R.id.profile_download_imei)
|
||||||
smdp.editText!!.addTextChangedListener {
|
smdp.editText!!.addTextChangedListener { updateInputCompleteness() }
|
||||||
updateInputCompleteness()
|
matchingId.editText!!.addTextChangedListener { updateInputCompleteness() }
|
||||||
|
confirmationCode.editText!!.addTextChangedListener { updateInputCompleteness() }
|
||||||
|
imei.editText!!.addTextChangedListener { updateInputCompleteness() }
|
||||||
|
confirmationCode.setEndIconOnClickListener {
|
||||||
|
onConfirmationCodeEndIconClick(confirmationCode)
|
||||||
|
validate()
|
||||||
}
|
}
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
@ -69,7 +77,84 @@ class DownloadWizardDetailsFragment : DownloadWizardActivity.DownloadWizardStepF
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateInputCompleteness() {
|
private fun updateInputCompleteness() {
|
||||||
inputComplete = Patterns.DOMAIN_NAME.matcher(smdp.editText!!.text).matches()
|
validate()
|
||||||
|
val layouts = arrayOf(smdp, matchingId, confirmationCode, imei)
|
||||||
|
for (layout in layouts) layout.isErrorEnabled = layout.error != null
|
||||||
|
inputComplete = layouts.all { it.error == null }
|
||||||
refreshButtons()
|
refreshButtons()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun validate() {
|
||||||
|
smdp.error = smdp.editText!!.text?.let {
|
||||||
|
if (it.isEmpty()) return@let getString(R.string.download_wizard_details_error_address_required)
|
||||||
|
if (it.contains("://")) return@let getString(R.string.download_wizard_details_error_cannot_url)
|
||||||
|
val (host, port) = splitHostPort(it)
|
||||||
|
if (isFQDN(host) && port in 1..65535) return@let null
|
||||||
|
getString(R.string.download_wizard_details_error_address_incorrect_format)
|
||||||
|
}
|
||||||
|
matchingId.error = matchingId.editText!!.text?.let {
|
||||||
|
if (isMatchingID(it)) return@let null
|
||||||
|
getString(R.string.download_wizard_details_error_matching_id_incorrect_format)
|
||||||
|
}
|
||||||
|
confirmationCode.error = confirmationCode.editText!!.let {
|
||||||
|
if (it.text.isEmpty()) return@let null
|
||||||
|
val passed = when (it.inputType and EditorInfo.TYPE_MASK_CLASS) {
|
||||||
|
EditorInfo.TYPE_CLASS_NUMBER -> it.text.all(Char::isDigit)
|
||||||
|
EditorInfo.TYPE_CLASS_TEXT -> true
|
||||||
|
else -> return@let null
|
||||||
|
}
|
||||||
|
if (passed) return@let null
|
||||||
|
getString(R.string.download_wizard_details_error_confirmation_code_incorrect_format)
|
||||||
|
}
|
||||||
|
imei.error = imei.editText!!.text?.let {
|
||||||
|
if (it.isEmpty()) return@let null
|
||||||
|
if (it.length == 15 && it.all(Char::isDigit) && luhnValid(it)) return@let null
|
||||||
|
getString(R.string.download_wizard_details_error_imei_incorrect_format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onConfirmationCodeEndIconClick(layout: TextInputLayout) {
|
||||||
|
val editText = layout.editText ?: return
|
||||||
|
fun getDrawable(@DrawableRes resId: Int) =
|
||||||
|
ContextCompat.getDrawable(requireActivity(), resId)
|
||||||
|
editText.inputType = when (editText.inputType and EditorInfo.TYPE_MASK_CLASS) {
|
||||||
|
EditorInfo.TYPE_CLASS_NUMBER -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||||
|
EditorInfo.TYPE_CLASS_TEXT -> InputType.TYPE_CLASS_NUMBER
|
||||||
|
else -> EditorInfo.TYPE_NULL
|
||||||
|
}
|
||||||
|
layout.endIconDrawable = when (editText.inputType and EditorInfo.TYPE_MASK_CLASS) {
|
||||||
|
EditorInfo.TYPE_CLASS_NUMBER -> getDrawable(R.drawable.ic_format_number)
|
||||||
|
EditorInfo.TYPE_CLASS_TEXT -> getDrawable(R.drawable.ic_format_text)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun splitHostPort(input: CharSequence): Pair<CharSequence, Int?> {
|
||||||
|
val portIndex = input.lastIndexOf(':')
|
||||||
|
if (portIndex == -1) return Pair(input, 443)
|
||||||
|
return Pair(
|
||||||
|
input.slice(0 until portIndex),
|
||||||
|
input.slice(portIndex + 1 until input.length).toString().toIntOrNull()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isFQDN(input: CharSequence): Boolean {
|
||||||
|
if (input.isEmpty() || input.length > 255) return false
|
||||||
|
if (!input.contains('.')) return false
|
||||||
|
for (label in input.split('.')) {
|
||||||
|
if (label.isEmpty() || label.length > 63) return false
|
||||||
|
if (label.all { it.isLetterOrDigit() || it == '-' }) continue
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isMatchingID(input: CharSequence) =
|
||||||
|
input.isEmpty() || input.all { it.isLetterOrDigit() || it == '-' }
|
||||||
|
|
||||||
|
private fun luhnValid(input: CharSequence) = input
|
||||||
|
.map(Char::digitToInt)
|
||||||
|
.mapIndexed { index, digit -> if (index % 2 == 0) digit else digit * 2 }
|
||||||
|
.sumOf { if (it > 9) it - 9 else it }
|
||||||
|
.rem(10) == 0
|
11
app-common/src/main/res/drawable/ic_format_number.xml
Normal file
11
app-common/src/main/res/drawable/ic_format_number.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M7 15H5.5v-4.5H4V9h3v6zm6.5-1.5h-3v-1h2c0.55 0 1-0.45 1-1V10c0-0.55-0.45-1-1-1H9v1.5h3v1h-2c-0.55 0-1 0.45-1 1V15h4.5v-1.5zm6 0.5v-4c0-0.55-0.45-1-1-1H15v1.5h3v1h-2v1h2v1h-3V15h3.5c0.55 0 1-0.45 1-1z" />
|
||||||
|
</vector>
|
11
app-common/src/main/res/drawable/ic_format_text.xml
Normal file
11
app-common/src/main/res/drawable/ic_format_text.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M21 11h-1.5v-0.5h-2v3h2V13H21v1c0 0.55-0.45 1-1 1h-3c-0.55 0-1-0.45-1-1v-4c0-0.55 0.45-1 1-1h3c0.55 0 1 0.45 1 1v1zM8 10v5H6.5v-1.5h-2V15H3v-5c0-0.55 0.45-1 1-1h3c0.55 0 1 0.45 1 1zm-1.5 0.5h-2V12h2v-1.5zm7 1.5c0.55 0 1 0.45 1 1v1c0 0.55-0.45 1-1 1h-4V9h4c0.55 0 1 0.45 1 1v1c0 0.55-0.45 1-1 1zM11 10.5v0.75h2V10.5h-2zm2 2.25h-2v0.75h2v-0.75z" />
|
||||||
|
</vector>
|
|
@ -11,18 +11,18 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/download_wizard_details_title"
|
android:id="@+id/download_wizard_details_title"
|
||||||
android:text="@string/download_wizard_details"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:layout_marginStart="60dp"
|
android:layout_marginStart="60dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
android:layout_marginEnd="60dp"
|
android:layout_marginEnd="60dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
android:layout_marginBottom="20dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:gravity="center_horizontal"
|
||||||
|
android:text="@string/download_wizard_details"
|
||||||
|
android:textSize="20sp"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
@ -32,10 +32,11 @@
|
||||||
android:hint="@string/profile_download_server">
|
android:hint="@string/profile_download_server">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:maxLines="1"
|
|
||||||
android:inputType="text"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:inputType="textVisiblePassword"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:typeface="monospace" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
@ -43,14 +44,14 @@
|
||||||
android:id="@+id/profile_download_code"
|
android:id="@+id/profile_download_code"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/profile_download_code"
|
android:hint="@string/profile_download_code">
|
||||||
app:passwordToggleEnabled="true">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:maxLines="1"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:inputType="textPassword" />
|
android:inputType="textVisiblePassword"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:typeface="monospace" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
@ -59,13 +60,16 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/profile_download_confirmation_code"
|
android:hint="@string/profile_download_confirmation_code"
|
||||||
app:passwordToggleEnabled="true">
|
app:endIconCheckable="true"
|
||||||
|
app:endIconDrawable="@drawable/ic_format_number"
|
||||||
|
app:endIconMode="custom">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:maxLines="1"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:inputType="textPassword" />
|
android:inputType="number"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:typeface="monospace" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
@ -75,29 +79,29 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
android:layout_marginBottom="6dp"
|
android:layout_marginBottom="6dp"
|
||||||
android:hint="@string/profile_download_imei"
|
android:hint="@string/profile_download_imei">
|
||||||
app:passwordToggleEnabled="true">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:maxLines="1"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:inputType="numberPassword" />
|
android:inputType="number"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:typeface="monospace" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.helper.widget.Flow
|
<androidx.constraintlayout.helper.widget.Flow
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginHorizontal="20dp"
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:orientation="vertical"
|
||||||
app:constraint_referenced_ids="profile_download_server,profile_download_code,profile_download_confirmation_code,profile_download_imei"
|
app:constraint_referenced_ids="profile_download_server,profile_download_code,profile_download_confirmation_code,profile_download_imei"
|
||||||
app:flow_verticalGap="16dp"
|
app:flow_verticalGap="16dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintTop_toBottomOf="@id/download_wizard_details_title"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constrainedWidth="true" />
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/download_wizard_details_title" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,12 @@
|
||||||
<string name="download_wizard_method_clipboard">Load from Clipboard</string>
|
<string name="download_wizard_method_clipboard">Load from Clipboard</string>
|
||||||
<string name="download_wizard_method_manual">Enter manually</string>
|
<string name="download_wizard_method_manual">Enter manually</string>
|
||||||
<string name="download_wizard_details">Input or confirm details for downloading your eSIM:</string>
|
<string name="download_wizard_details">Input or confirm details for downloading your eSIM:</string>
|
||||||
|
<string name="download_wizard_details_error_address_required">Server address is required</string>
|
||||||
|
<string name="download_wizard_details_error_cannot_url">Server address not is URL</string>
|
||||||
|
<string name="download_wizard_details_error_address_incorrect_format">Incorrect Server address</string>
|
||||||
|
<string name="download_wizard_details_error_matching_id_incorrect_format">Incorrect Matching ID format</string>
|
||||||
|
<string name="download_wizard_details_error_confirmation_code_incorrect_format">Incorrect Confirmation Code format</string>
|
||||||
|
<string name="download_wizard_details_error_imei_incorrect_format">Incorrect IMEI format</string>
|
||||||
<string name="download_wizard_progress">Downloading your eSIM…</string>
|
<string name="download_wizard_progress">Downloading your eSIM…</string>
|
||||||
<string name="download_wizard_progress_step_preparing">Preparing</string>
|
<string name="download_wizard_progress_step_preparing">Preparing</string>
|
||||||
<string name="download_wizard_progress_step_connecting">Establishing connection to server</string>
|
<string name="download_wizard_progress_step_connecting">Establishing connection to server</string>
|
||||||
|
|
Loading…
Add table
Reference in a new issue