From f9e8ace5f77de5d8df185219be9a7929be477e95 Mon Sep 17 00:00:00 2001 From: Nicholas Tinsley Date: Tue, 16 Jul 2024 11:18:29 -0400 Subject: [PATCH] Run registration UI callbacks in LifeycleScope. --- .../registration/ui/RegistrationExtensions.kt | 10 -- .../ui/entercode/EnterCodeFragment.kt | 72 ++++++-------- .../phonenumber/EnterPhoneNumberFragment.kt | 94 ++++++++++--------- 3 files changed, 81 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationExtensions.kt index a4e097ca34..6cd4c70d11 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/RegistrationExtensions.kt @@ -5,19 +5,9 @@ package org.thoughtcrime.securesms.registration.ui -import androidx.fragment.app.Fragment -import androidx.lifecycle.Lifecycle import com.google.i18n.phonenumbers.PhoneNumberUtil import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber fun PhoneNumber.toE164(): String { return PhoneNumberUtil.getInstance().format(this, PhoneNumberUtil.PhoneNumberFormat.E164) } - -fun Fragment.isBindingInvalid(): Boolean { - if (view == null) { - return false - } - - return viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED) -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt index df984e1677..a7df623a3f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/entercode/EnterCodeFragment.kt @@ -11,10 +11,12 @@ import android.view.View import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.i18n.phonenumbers.PhoneNumberUtil +import kotlinx.coroutines.launch import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.signal.core.util.logging.Log @@ -31,7 +33,6 @@ import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegat import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener import org.thoughtcrime.securesms.registration.ui.RegistrationCheckpoint import org.thoughtcrime.securesms.registration.ui.RegistrationViewModel -import org.thoughtcrime.securesms.registration.ui.isBindingInvalid import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.visible @@ -48,17 +49,15 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c private val TAG = Log.tag(EnterCodeFragment::class.java) private val sharedViewModel by activityViewModels() + private val bottomSheet = ContactSupportBottomSheetFragment() private val binding: FragmentRegistrationEnterCodeBinding by ViewBinderDelegate(FragmentRegistrationEnterCodeBinding::bind) private lateinit var phoneStateListener: SignalStrengthPhoneStateListener private var autopilotCodeEntryActive = false - private val bottomSheet = ContactSupportBottomSheetFragment() - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setDebugLogSubmitMultiTapView(binding.verifyHeader) phoneStateListener = SignalStrengthPhoneStateListener(this, PhoneStateCallback()) @@ -134,34 +133,28 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c } private fun handleSessionErrorResponse(result: RegistrationResult) { - if (isBindingInvalid()) { - Log.w(TAG, "Binding not valid, aborting! Result: $result") - result.logCause() - return - } - when (result) { - is VerificationCodeRequestResult.Success -> binding.keyboard.displaySuccess() - is VerificationCodeRequestResult.RateLimited -> presentRateLimitedDialog() - is VerificationCodeRequestResult.AttemptsExhausted -> presentAccountLocked() - is VerificationCodeRequestResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining) - else -> presentGenericError(result) + viewLifecycleOwner.lifecycleScope.launch { + when (result) { + is VerificationCodeRequestResult.Success -> binding.keyboard.displaySuccess() + is VerificationCodeRequestResult.RateLimited -> presentRateLimitedDialog() + is VerificationCodeRequestResult.AttemptsExhausted -> presentAccountLocked() + is VerificationCodeRequestResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining) + else -> presentGenericError(result) + } } } private fun handleRegistrationErrorResponse(result: RegisterAccountResult) { - if (isBindingInvalid()) { - Log.w(TAG, "Binding not valid, aborting! Result: $result") - result.logCause() - return - } - when (result) { - is RegisterAccountResult.Success -> binding.keyboard.displaySuccess() - is RegisterAccountResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining) - is RegisterAccountResult.AuthorizationFailed -> presentIncorrectCodeDialog() - is RegisterAccountResult.AttemptsExhausted -> presentAccountLocked() - is RegisterAccountResult.RateLimited -> presentRateLimitedDialog() + viewLifecycleOwner.lifecycleScope.launch { + when (result) { + is RegisterAccountResult.Success -> binding.keyboard.displaySuccess() + is RegisterAccountResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining) + is RegisterAccountResult.AuthorizationFailed -> presentIncorrectCodeDialog() + is RegisterAccountResult.AttemptsExhausted -> presentAccountLocked() + is RegisterAccountResult.RateLimited -> presentRateLimitedDialog() - else -> presentGenericError(result) + else -> presentGenericError(result) + } } } @@ -209,22 +202,19 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c private fun presentIncorrectCodeDialog() { sharedViewModel.incrementIncorrectCodeAttempts() - Toast.makeText(requireContext(), R.string.RegistrationActivity_incorrect_code, Toast.LENGTH_LONG).show() + viewLifecycleOwner.lifecycleScope.launch { + Toast.makeText(requireContext(), R.string.RegistrationActivity_incorrect_code, Toast.LENGTH_LONG).show() - if (isBindingInvalid()) { - Log.w(TAG, "Binding not valid, aborting updating keyboard!") - return + binding.keyboard.displayFailure().addListener(object : AssertedSuccessListener() { + override fun onSuccess(result: Boolean?) { + binding.callMeCountDown.visibility = View.VISIBLE + binding.resendSmsCountDown.visibility = View.VISIBLE + binding.wrongNumber.visibility = View.VISIBLE + binding.code.clear() + binding.keyboard.displayKeyboard() + } + }) } - - binding.keyboard.displayFailure().addListener(object : AssertedSuccessListener() { - override fun onSuccess(result: Boolean?) { - binding.callMeCountDown.visibility = View.VISIBLE - binding.resendSmsCountDown.visibility = View.VISIBLE - binding.wrongNumber.visibility = View.VISIBLE - binding.code.clear() - binding.keyboard.displayKeyboard() - } - }) } private fun presentGenericError(requestResult: RegistrationResult) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt index 8f46537d93..09aa3554b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/ui/phonenumber/EnterPhoneNumberFragment.kt @@ -25,6 +25,7 @@ import androidx.core.view.MenuProvider import androidx.core.widget.addTextChangedListener import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import com.google.android.gms.common.ConnectionResult @@ -35,6 +36,7 @@ import com.google.android.material.textfield.TextInputEditText import com.google.i18n.phonenumbers.NumberParseException import com.google.i18n.phonenumbers.PhoneNumberUtil import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber +import kotlinx.coroutines.launch import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.LoggingFragment @@ -51,7 +53,6 @@ import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegat import org.thoughtcrime.securesms.registration.ui.RegistrationCheckpoint import org.thoughtcrime.securesms.registration.ui.RegistrationState import org.thoughtcrime.securesms.registration.ui.RegistrationViewModel -import org.thoughtcrime.securesms.registration.ui.isBindingInvalid import org.thoughtcrime.securesms.registration.ui.toE164 import org.thoughtcrime.securesms.registration.util.CountryPrefix import org.thoughtcrime.securesms.util.CommunicationActions @@ -300,54 +301,59 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_ } private fun handleErrorResponse(result: RegistrationResult) { - if (isBindingInvalid()) { - Log.w(TAG, "Binding not valid, aborting! Result: $result") - result.logCause() - return - } - if (!result.isSuccess()) { - Log.i(TAG, "Handling error response.", result.getCause()) - } - when (result) { - is RegistrationSessionCreationResult.Success, - is VerificationCodeRequestResult.Success -> Unit - is RegistrationSessionCreationResult.AttemptsExhausted, - is VerificationCodeRequestResult.AttemptsExhausted -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_service)) - is VerificationCodeRequestResult.ChallengeRequired -> { - handleChallenges(result.challenges) + viewLifecycleOwner.lifecycleScope.launch { + if (!result.isSuccess()) { + Log.i(TAG, "Handling error response.", result.getCause()) } - 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())) - setPositiveButton(android.R.string.ok, null) - show() + when (result) { + is RegistrationSessionCreationResult.Success, + is VerificationCodeRequestResult.Success -> Unit + + is RegistrationSessionCreationResult.AttemptsExhausted, + is VerificationCodeRequestResult.AttemptsExhausted -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_service)) + + is VerificationCodeRequestResult.ChallengeRequired -> { + handleChallenges(result.challenges) } - } - is VerificationCodeRequestResult.InvalidTransportModeFailure -> { - MaterialAlertDialogBuilder(requireContext()).apply { - setMessage(R.string.RegistrationActivity_we_couldnt_send_you_a_verification_code) - setPositiveButton(R.string.RegistrationActivity_voice_call) { _, _ -> - sharedViewModel.requestVerificationCall(requireContext(), ::handleErrorResponse) + + 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())) + setPositiveButton(android.R.string.ok, null) + show() } - setNegativeButton(R.string.RegistrationActivity_cancel, null) - show() } + + is VerificationCodeRequestResult.InvalidTransportModeFailure -> { + MaterialAlertDialogBuilder(requireContext()).apply { + setMessage(R.string.RegistrationActivity_we_couldnt_send_you_a_verification_code) + setPositiveButton(R.string.RegistrationActivity_voice_call) { _, _ -> + sharedViewModel.requestVerificationCall(requireContext(), ::handleErrorResponse) + } + setNegativeButton(R.string.RegistrationActivity_cancel, null) + show() + } + } + + is RegistrationSessionCreationResult.MalformedRequest, + is VerificationCodeRequestResult.MalformedRequest -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) + + is VerificationCodeRequestResult.MustRetry -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) + is VerificationCodeRequestResult.NonNormalizedNumber -> handleNonNormalizedNumberError(result.originalNumber, result.normalizedNumber, fragmentViewModel.mode) + is RegistrationSessionCreationResult.RateLimited -> { + Log.i(TAG, "Session creation rate limited! Next attempt: ${result.timeRemaining.milliseconds}") + presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_try_again, result.timeRemaining.milliseconds.toString())) + } + + is VerificationCodeRequestResult.RateLimited -> { + Log.i(TAG, "Code request rate limited! Next attempt: ${result.timeRemaining.milliseconds}") + presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_try_again, result.timeRemaining.milliseconds.toString())) + } + + is VerificationCodeRequestResult.TokenNotAccepted -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_we_need_to_verify_that_youre_human)) { _, _ -> moveToCaptcha() } + else -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service)) } - is RegistrationSessionCreationResult.MalformedRequest, - is VerificationCodeRequestResult.MalformedRequest -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) - is VerificationCodeRequestResult.MustRetry -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) - is VerificationCodeRequestResult.NonNormalizedNumber -> handleNonNormalizedNumberError(result.originalNumber, result.normalizedNumber, fragmentViewModel.mode) - is RegistrationSessionCreationResult.RateLimited -> { - Log.i(TAG, "Session creation rate limited! Next attempt: ${result.timeRemaining.milliseconds}") - presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_try_again, result.timeRemaining.milliseconds.toString())) - } - is VerificationCodeRequestResult.RateLimited -> { - Log.i(TAG, "Code request rate limited! Next attempt: ${result.timeRemaining.milliseconds}") - presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_try_again, result.timeRemaining.milliseconds.toString())) - } - is VerificationCodeRequestResult.TokenNotAccepted -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_we_need_to_verify_that_youre_human)) { _, _ -> moveToCaptcha() } - else -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service)) } }