Fix phone number formatter in Registration V2.

This commit is contained in:
Nicholas Tinsley
2024-04-19 17:57:56 -04:00
committed by Cody Henthorne
parent b771a21518
commit 6ad72f00af
11 changed files with 73 additions and 37 deletions

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.registration.v2.ui.shared
package org.thoughtcrime.securesms.registration.v2.ui
/**
* An ordered list of checkpoints of the registration process.

View File

@@ -12,7 +12,6 @@ import androidx.activity.viewModels
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.BaseActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel
/**
* Activity to hold the entire registration process.

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.registration.v2.ui.shared
package org.thoughtcrime.securesms.registration.v2.ui
import com.google.i18n.phonenumbers.Phonenumber

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.registration.v2.ui.shared
package org.thoughtcrime.securesms.registration.v2.ui
import android.content.Context
import androidx.lifecycle.ViewModel
@@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.registration.RegistrationData
import org.thoughtcrime.securesms.registration.RegistrationUtil
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
import org.thoughtcrime.securesms.registration.v2.ui.toE164
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.dualsim.MccMncProducer

View File

@@ -24,8 +24,8 @@ import org.thoughtcrime.securesms.profiles.edit.CreateProfileActivity
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView
import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
/**
* The final screen of account registration, where the user enters their verification code.

View File

@@ -21,9 +21,9 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.registration.compose.GrantPermissionsScreen
import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2State
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
import org.thoughtcrime.securesms.util.BackupUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate

View File

@@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.registration.v2.ui.phonenumber
import android.content.Context
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuInflater
@@ -38,9 +39,9 @@ import org.thoughtcrime.securesms.databinding.FragmentRegistrationEnterPhoneNumb
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView
import org.thoughtcrime.securesms.registration.util.CountryPrefix
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2State
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
import org.thoughtcrime.securesms.registration.v2.ui.toE164
import org.thoughtcrime.securesms.util.PlayServicesUtil
import org.thoughtcrime.securesms.util.SpanUtil
@@ -63,8 +64,11 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
private lateinit var phoneNumberInputLayout: TextInputEditText
private lateinit var spinnerView: MaterialAutoCompleteTextView
private var currentPhoneNumberFormatter: TextWatcher? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setDebugLogSubmitMultiTapView(binding.verifyHeader)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
@@ -80,25 +84,14 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
R.layout.registration_country_code_dropdown_item,
fragmentViewModel.supportedCountryPrefixes
)
setDebugLogSubmitMultiTapView(binding.verifyHeader)
binding.registerButton.setOnClickListener { onRegistrationButtonClicked() }
binding.toolbar.title = null
binding.toolbar.title = ""
val activity = requireActivity() as AppCompatActivity
activity.setSupportActionBar(binding.toolbar)
requireActivity().addMenuProvider(UseProxyMenuProvider(), viewLifecycleOwner)
val existingPhoneNumber = sharedViewModel.uiState.value?.phoneNumber
if (existingPhoneNumber != null) {
fragmentViewModel.restoreState(existingPhoneNumber)
fragmentViewModel.phoneNumber()?.let {
phoneNumberInputLayout.setText(it.nationalNumber.toString())
}
} else if (spinnerView.editableText.isBlank()) {
spinnerView.setText(fragmentViewModel.countryPrefix().toString())
}
sharedViewModel.uiState.observe(viewLifecycleOwner) { sharedState ->
presentRegisterButton(sharedState)
presentProgressBar(sharedState.inProgress, sharedState.isReRegister)
@@ -108,6 +101,19 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
}
fragmentViewModel.uiState.observe(viewLifecycleOwner) { fragmentState ->
fragmentState.phoneNumberFormatter?.let {
if (it != currentPhoneNumberFormatter) {
currentPhoneNumberFormatter?.let { oldWatcher ->
Log.d(TAG, "Removing current phone number formatter in fragment")
phoneNumberInputLayout.removeTextChangedListener(oldWatcher)
}
phoneNumberInputLayout.addTextChangedListener(it)
currentPhoneNumberFormatter = it
Log.d(TAG, "Updating phone number formatter in fragment")
}
}
if (fragmentViewModel.isEnteredNumberValid(fragmentState)) {
sharedViewModel.setPhoneNumber(fragmentViewModel.parsePhoneNumber(fragmentState))
} else {
@@ -121,12 +127,26 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
initializeInputFields()
val existingPhoneNumber = sharedViewModel.uiState.value?.phoneNumber
if (existingPhoneNumber != null) {
fragmentViewModel.restoreState(existingPhoneNumber)
fragmentViewModel.phoneNumber()?.let {
phoneNumberInputLayout.setText(it.nationalNumber.toString())
}
} else if (spinnerView.editableText.isBlank()) {
spinnerView.setText(fragmentViewModel.countryPrefix().toString())
}
ViewUtil.focusAndShowKeyboard(phoneNumberInputLayout)
}
private fun initializeInputFields() {
binding.countryCode.editText?.addTextChangedListener { s ->
val countryCode: Int = s.toString().toInt()
fragmentViewModel.setCountry(countryCode)
}
phoneNumberInputLayout.addTextChangedListener {
// TODO [regv2]: country code as you type formatter
fragmentViewModel.setPhoneNumber(it?.toString())
}
phoneNumberInputLayout.onFocusChangeListener = View.OnFocusChangeListener { _: View?, hasFocus: Boolean ->
@@ -155,7 +175,6 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
}
fragmentViewModel.supportedCountryPrefixes.firstOrNull { it.toString() == s.toString() }?.let {
// TODO [regv2]: setCountryFormatter(it.regionCode)
fragmentViewModel.setCountry(it.digits)
val numberLength: Int = phoneNumberInputLayout.text?.length ?: 0
phoneNumberInputLayout.setSelection(numberLength, numberLength)

View File

@@ -5,10 +5,12 @@
package org.thoughtcrime.securesms.registration.v2.ui.phonenumber
import android.text.TextWatcher
/**
* State holder for the phone number entry screen, including phone number and Play Services errors.
*/
data class EnterPhoneNumberV2State(val countryPrefixIndex: Int, val phoneNumber: String, val error: Error = Error.NONE) {
data class EnterPhoneNumberV2State(val countryPrefixIndex: Int, val phoneNumber: String, val phoneNumberFormatter: TextWatcher? = null, val error: Error = Error.NONE) {
companion object {
@JvmStatic

View File

@@ -5,13 +5,18 @@
package org.thoughtcrime.securesms.registration.v2.ui.phonenumber
import android.telephony.PhoneNumberFormattingTextWatcher
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.registration.util.CountryPrefix
@@ -51,6 +56,18 @@ class EnterPhoneNumberV2ViewModel : ViewModel() {
store.update {
it.copy(countryPrefixIndex = matchingIndex)
}
viewModelScope.launch {
withContext(Dispatchers.Default) {
val regionCode = PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(digits)
val textWatcher = PhoneNumberFormattingTextWatcher(regionCode)
store.update {
Log.d(TAG, "Updating phone number formatter in state")
it.copy(phoneNumberFormatter = textWatcher)
}
}
}
}
fun parsePhoneNumber(state: EnterPhoneNumberV2State): PhoneNumber {

View File

@@ -9,7 +9,6 @@ import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.NavHostFragment
@@ -21,9 +20,10 @@ import org.thoughtcrime.securesms.databinding.FragmentRegistrationWelcomeV2Bindi
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView
import org.thoughtcrime.securesms.registration.fragments.WelcomePermissions
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
import org.thoughtcrime.securesms.registration.v2.ui.grantpermissions.GrantPermissionsV2Fragment
import org.thoughtcrime.securesms.registration.v2.ui.shared.RegistrationV2ViewModel
import org.thoughtcrime.securesms.util.BackupUtil
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -33,7 +33,6 @@ import kotlin.jvm.optionals.getOrNull
* First screen that is displayed on the very first app launch.
*/
class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome_v2) {
private val TAG = Log.tag(WelcomeV2Fragment::class.java)
private val sharedViewModel by activityViewModels<RegistrationV2ViewModel>()
private val binding: FragmentRegistrationWelcomeV2Binding by ViewBinderDelegate(FragmentRegistrationWelcomeV2Binding::bind)
@@ -44,7 +43,6 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome
setDebugLogSubmitMultiTapView(binding.title)
binding.welcomeContinueButton.setOnClickListener { onContinueClicked() }
binding.welcomeTermsButton.setOnClickListener { onTermsClicked() }
binding.welcomeTransferOrRestore.setOnClickListener { onRestoreFromBackupClicked() }
}
private fun onContinueClicked() {
@@ -65,12 +63,8 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome
NavHostFragment.findNavController(this).safeNavigate(WelcomeV2FragmentDirections.actionSkipRestore())
}
private fun onRestoreFromBackupClicked() {
Toast.makeText(requireContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show()
}
private fun onTermsClicked() {
Toast.makeText(requireContext(), "Not yet implemented.", Toast.LENGTH_SHORT).show()
CommunicationActions.openBrowserLink(requireContext(), TERMS_AND_CONDITIONS_URL)
}
private fun maybePrefillE164() {
@@ -87,4 +81,9 @@ class WelcomeV2Fragment : LoggingFragment(R.layout.fragment_registration_welcome
Log.i(TAG, "No phone permission.")
}
}
companion object {
private val TAG = Log.tag(WelcomeV2Fragment::class.java)
private const val TERMS_AND_CONDITIONS_URL = "https://signal.org/legal"
}
}

View File

@@ -55,6 +55,7 @@
app:layout_goneMarginBottom="@dimen/registration_button_bottom_margin"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" />
<!-- TODO [regv2]: delete this -->
<com.google.android.material.button.MaterialButton
android:id="@+id/welcome_transfer_or_restore"
style="@style/Signal.Widget.Button.Large.Secondary"