Fix phone number autofill in Registration V2.

This commit is contained in:
Nicholas Tinsley 2024-06-05 14:33:18 -04:00 committed by Cody Henthorne
parent 380c33642c
commit 220d3877a2
5 changed files with 62 additions and 42 deletions

View file

@ -5,7 +5,10 @@
package org.thoughtcrime.securesms.registration.v2.ui
import com.google.i18n.phonenumbers.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.google.i18n.phonenumbers.Phonenumber
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.registration.v2.data.network.Challenge
import org.whispersystems.signalservice.internal.push.AuthCredentials
@ -16,7 +19,7 @@ import org.whispersystems.signalservice.internal.push.AuthCredentials
data class RegistrationV2State(
val sessionId: String? = null,
val enteredCode: String = "",
val phoneNumber: Phonenumber.PhoneNumber? = null,
val phoneNumber: Phonenumber.PhoneNumber? = fetchExistingE164FromValues(),
val inProgress: Boolean = false,
val isReRegister: Boolean = false,
val recoveryPassword: String? = SignalStore.svr().getRecoveryPassword(),
@ -43,4 +46,22 @@ data class RegistrationV2State(
val networkError: Throwable? = null
) {
val challengesRemaining: List<Challenge> = challengesRequested.filterNot { it in challengesPresented }
companion object {
private val TAG = Log.tag(RegistrationV2State::class)
private fun fetchExistingE164FromValues(): Phonenumber.PhoneNumber? {
val existingE164 = SignalStore.registrationValues().sessionE164
if (existingE164 != null) {
try {
return PhoneNumberUtil.getInstance().parse(existingE164, null)
} catch (ex: NumberParseException) {
Log.w(TAG, "Could not parse stored E164.", ex)
return null
}
} else {
return null
}
}
}
}

View file

@ -10,8 +10,6 @@ import android.content.Context
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
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
@ -106,19 +104,8 @@ class RegistrationV2ViewModel : ViewModel() {
val isReregister: Boolean
get() = store.value.isReRegister
init {
val existingE164 = SignalStore.registrationValues().sessionE164
if (existingE164 != null) {
try {
val existingPhoneNumber = PhoneNumberUtil.getInstance().parse(existingE164, null)
if (existingPhoneNumber != null) {
setPhoneNumber(existingPhoneNumber)
}
} catch (ex: NumberParseException) {
Log.w(TAG, "Could not parse stored E164.", ex)
}
}
}
val phoneNumber: Phonenumber.PhoneNumber?
get() = store.value.phoneNumber
fun maybePrefillE164(context: Context) {
Log.v(TAG, "maybePrefillE164()")

View file

@ -19,6 +19,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.compose.ComposeFragment
@ -101,7 +102,7 @@ class GrantPermissionsV2Fragment : ComposeFragment() {
private fun proceedToNextScreen() {
when (welcomeAction) {
WelcomeAction.CONTINUE -> NavHostFragment.findNavController(this).safeNavigate(GrantPermissionsV2FragmentDirections.actionEnterPhoneNumber())
WelcomeAction.CONTINUE -> findNavController().safeNavigate(GrantPermissionsV2FragmentDirections.actionEnterPhoneNumber())
WelcomeAction.RESTORE_BACKUP -> {
val restoreIntent = RestoreActivity.getIntentForRestore(requireActivity())
launchRestoreActivity.launch(restoreIntent)

View file

@ -130,15 +130,7 @@ 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")
}
bindPhoneNumberFormatter(it)
}
if (fragmentViewModel.isEnteredNumberValid(fragmentState)) {
@ -154,19 +146,33 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
initializeInputFields()
val existingPhoneNumber = sharedViewModel.uiState.value?.phoneNumber
val existingPhoneNumber = sharedViewModel.phoneNumber
if (existingPhoneNumber != null) {
fragmentViewModel.restoreState(existingPhoneNumber)
fragmentViewModel.phoneNumber()?.let {
phoneNumberInputLayout.setText(it.nationalNumber.toString())
spinnerView.setText(existingPhoneNumber.countryCode.toString())
fragmentViewModel.formatter?.let {
bindPhoneNumberFormatter(it)
}
phoneNumberInputLayout.setText(existingPhoneNumber.nationalNumber.toString())
} else {
spinnerView.setText(fragmentViewModel.countryPrefix().toString())
}
spinnerView.setText(fragmentViewModel.countryPrefix().toString())
ViewUtil.focusAndShowKeyboard(phoneNumberInputLayout)
}
private fun bindPhoneNumberFormatter(formatter: TextWatcher) {
if (formatter != currentPhoneNumberFormatter) {
currentPhoneNumberFormatter?.let { oldWatcher ->
Log.d(TAG, "Removing current phone number formatter in fragment")
phoneNumberInputLayout.removeTextChangedListener(oldWatcher)
}
phoneNumberInputLayout.addTextChangedListener(formatter)
currentPhoneNumberFormatter = formatter
Log.d(TAG, "Updating phone number formatter in fragment")
}
}
private fun handleChallenges(remainingChallenges: List<Challenge>) {
when (remainingChallenges.first()) {
Challenge.CAPTCHA -> moveToCaptcha()
@ -187,11 +193,13 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
phoneNumberInputLayout.addTextChangedListener {
fragmentViewModel.setPhoneNumber(it?.toString())
}
phoneNumberInputLayout.onFocusChangeListener = View.OnFocusChangeListener { _: View?, hasFocus: Boolean ->
if (hasFocus) {
binding.scrollView.postDelayed({ binding.scrollView.smoothScrollTo(0, binding.registerButton.bottom) }, 250)
}
}
phoneNumberInputLayout.imeOptions = EditorInfo.IME_ACTION_DONE
phoneNumberInputLayout.setOnEditorActionListener { v: TextView?, actionId: Int, _: KeyEvent? ->
if (actionId == EditorInfo.IME_ACTION_DONE && v != null) {
@ -298,7 +306,7 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen)
is VerificationCodeRequestResult.ImpossibleNumber -> {
MaterialAlertDialogBuilder(requireContext()).apply {
setMessage(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber()?.toE164()))
setMessage(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber?.toE164()))
setPositiveButton(android.R.string.ok, null)
show()
}
@ -369,7 +377,7 @@ class EnterPhoneNumberV2Fragment : LoggingFragment(R.layout.fragment_registratio
Dialogs.showAlertDialog(
requireContext(),
getString(R.string.RegistrationActivity_invalid_number),
getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber()?.toE164())
getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber?.toE164())
)
}
}

View file

@ -6,6 +6,7 @@
package org.thoughtcrime.securesms.registration.v2.ui.phonenumber
import android.telephony.PhoneNumberFormattingTextWatcher
import android.text.TextWatcher
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
@ -31,6 +32,17 @@ class EnterPhoneNumberV2ViewModel : ViewModel() {
private val store = MutableStateFlow(EnterPhoneNumberV2State())
val uiState = store.asLiveData()
val formatter: TextWatcher?
get() = store.value.phoneNumberFormatter
val phoneNumber: PhoneNumber?
get() = try {
parsePhoneNumber(store.value)
} catch (ex: NumberParseException) {
Log.w(TAG, "Could not parse phone number in current state.", ex)
null
}
val supportedCountryPrefixes: List<CountryPrefix> = PhoneNumberUtil.getInstance().supportedCallingCodes
.map { CountryPrefix(it, PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(it)) }
.sortedBy { it.digits }
@ -45,15 +57,6 @@ class EnterPhoneNumberV2ViewModel : ViewModel() {
return supportedCountryPrefixes[store.value.countryPrefixIndex]
}
fun phoneNumber(): PhoneNumber? {
return try {
parsePhoneNumber(store.value)
} catch (ex: NumberParseException) {
Log.w(TAG, "Could not parse phone number in current state.", ex)
null
}
}
fun setPhoneNumber(phoneNumber: String?) {
store.update { it.copy(phoneNumber = phoneNumber ?: "") }
}