From 220d3877a2a91d9f79b3b0d7aa5993d54ccd5fa3 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Wed, 5 Jun 2024 14:33:18 -0400 Subject: [PATCH] Fix phone number autofill in Registration V2. --- .../registration/v2/ui/RegistrationV2State.kt | 23 ++++++++++- .../v2/ui/RegistrationV2ViewModel.kt | 17 +------- .../GrantPermissionsV2Fragment.kt | 3 +- .../phonenumber/EnterPhoneNumberV2Fragment.kt | 40 +++++++++++-------- .../EnterPhoneNumberV2ViewModel.kt | 21 +++++----- 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt index 80fd051252..4c187e515c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2State.kt @@ -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 = 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 + } + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt index 845f0fda9d..f141860bc2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/RegistrationV2ViewModel.kt @@ -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()") diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt index 2cec49dbf0..232f615dfc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/grantpermissions/GrantPermissionsV2Fragment.kt @@ -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) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt index ad7e2b48ad..d7c1c7e1c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2Fragment.kt @@ -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) { 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()) ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt index 8883e79f01..6ddf80c906 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/v2/ui/phonenumber/EnterPhoneNumberV2ViewModel.kt @@ -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 = 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 ?: "") } }