Compare commits

...

3 Commits

Author SHA1 Message Date
Greyson Parrelli
508688e533 Bump version to 7.11.4 2024-07-22 11:26:57 -04:00
Greyson Parrelli
eef0f299b7 Fix welcome screen button after pressing back on transfer screen. 2024-07-22 11:22:16 -04:00
Nicholas Tinsley
f9e8ace5f7 Run registration UI callbacks in LifeycleScope. 2024-07-22 11:21:51 -04:00
5 changed files with 83 additions and 98 deletions

View File

@@ -22,8 +22,8 @@ plugins {
apply(from = "static-ips.gradle.kts") apply(from = "static-ips.gradle.kts")
val canonicalVersionCode = 1436 val canonicalVersionCode = 1436
val canonicalVersionName = "7.11.3" val canonicalVersionName = "7.11.4"
val currentHotfixVersion = 0 val currentHotfixVersion = 1
val maxHotfixVersions = 100 val maxHotfixVersions = 100
val keystores: Map<String, Properties?> = mapOf("debug" to loadKeystoreProperties("keystore.debug.properties")) val keystores: Map<String, Properties?> = mapOf("debug" to loadKeystoreProperties("keystore.debug.properties"))

View File

@@ -5,19 +5,9 @@
package org.thoughtcrime.securesms.registration.ui 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.PhoneNumberUtil
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber
fun PhoneNumber.toE164(): String { fun PhoneNumber.toE164(): String {
return PhoneNumberUtil.getInstance().format(this, PhoneNumberUtil.PhoneNumberFormat.E164) 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)
}

View File

@@ -11,10 +11,12 @@ import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.i18n.phonenumbers.PhoneNumberUtil import com.google.i18n.phonenumbers.PhoneNumberUtil
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import org.signal.core.util.logging.Log 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.fragments.SignalStrengthPhoneStateListener
import org.thoughtcrime.securesms.registration.ui.RegistrationCheckpoint import org.thoughtcrime.securesms.registration.ui.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.ui.RegistrationViewModel 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.concurrent.AssertedSuccessListener
import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.thoughtcrime.securesms.util.visible 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 TAG = Log.tag(EnterCodeFragment::class.java)
private val sharedViewModel by activityViewModels<RegistrationViewModel>() private val sharedViewModel by activityViewModels<RegistrationViewModel>()
private val bottomSheet = ContactSupportBottomSheetFragment()
private val binding: FragmentRegistrationEnterCodeBinding by ViewBinderDelegate(FragmentRegistrationEnterCodeBinding::bind) private val binding: FragmentRegistrationEnterCodeBinding by ViewBinderDelegate(FragmentRegistrationEnterCodeBinding::bind)
private lateinit var phoneStateListener: SignalStrengthPhoneStateListener private lateinit var phoneStateListener: SignalStrengthPhoneStateListener
private var autopilotCodeEntryActive = false private var autopilotCodeEntryActive = false
private val bottomSheet = ContactSupportBottomSheetFragment()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setDebugLogSubmitMultiTapView(binding.verifyHeader) setDebugLogSubmitMultiTapView(binding.verifyHeader)
phoneStateListener = SignalStrengthPhoneStateListener(this, PhoneStateCallback()) phoneStateListener = SignalStrengthPhoneStateListener(this, PhoneStateCallback())
@@ -134,34 +133,28 @@ class EnterCodeFragment : LoggingFragment(R.layout.fragment_registration_enter_c
} }
private fun handleSessionErrorResponse(result: RegistrationResult) { private fun handleSessionErrorResponse(result: RegistrationResult) {
if (isBindingInvalid()) { viewLifecycleOwner.lifecycleScope.launch {
Log.w(TAG, "Binding not valid, aborting! Result: $result") when (result) {
result.logCause() is VerificationCodeRequestResult.Success -> binding.keyboard.displaySuccess()
return is VerificationCodeRequestResult.RateLimited -> presentRateLimitedDialog()
} is VerificationCodeRequestResult.AttemptsExhausted -> presentAccountLocked()
when (result) { is VerificationCodeRequestResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining)
is VerificationCodeRequestResult.Success -> binding.keyboard.displaySuccess() else -> presentGenericError(result)
is VerificationCodeRequestResult.RateLimited -> presentRateLimitedDialog() }
is VerificationCodeRequestResult.AttemptsExhausted -> presentAccountLocked()
is VerificationCodeRequestResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining)
else -> presentGenericError(result)
} }
} }
private fun handleRegistrationErrorResponse(result: RegisterAccountResult) { private fun handleRegistrationErrorResponse(result: RegisterAccountResult) {
if (isBindingInvalid()) { viewLifecycleOwner.lifecycleScope.launch {
Log.w(TAG, "Binding not valid, aborting! Result: $result") when (result) {
result.logCause() is RegisterAccountResult.Success -> binding.keyboard.displaySuccess()
return is RegisterAccountResult.RegistrationLocked -> presentRegistrationLocked(result.timeRemaining)
} is RegisterAccountResult.AuthorizationFailed -> presentIncorrectCodeDialog()
when (result) { is RegisterAccountResult.AttemptsExhausted -> presentAccountLocked()
is RegisterAccountResult.Success -> binding.keyboard.displaySuccess() is RegisterAccountResult.RateLimited -> presentRateLimitedDialog()
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() { private fun presentIncorrectCodeDialog() {
sharedViewModel.incrementIncorrectCodeAttempts() 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()) { binding.keyboard.displayFailure().addListener(object : AssertedSuccessListener<Boolean?>() {
Log.w(TAG, "Binding not valid, aborting updating keyboard!") override fun onSuccess(result: Boolean?) {
return 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<Boolean?>() {
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) { private fun presentGenericError(requestResult: RegistrationResult) {

View File

@@ -25,6 +25,7 @@ import androidx.core.view.MenuProvider
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.gms.common.ConnectionResult 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.NumberParseException
import com.google.i18n.phonenumbers.PhoneNumberUtil import com.google.i18n.phonenumbers.PhoneNumberUtil
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber
import kotlinx.coroutines.launch
import org.signal.core.util.isNotNullOrBlank import org.signal.core.util.isNotNullOrBlank
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.LoggingFragment 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.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.ui.RegistrationState import org.thoughtcrime.securesms.registration.ui.RegistrationState
import org.thoughtcrime.securesms.registration.ui.RegistrationViewModel 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.ui.toE164
import org.thoughtcrime.securesms.registration.util.CountryPrefix import org.thoughtcrime.securesms.registration.util.CountryPrefix
import org.thoughtcrime.securesms.util.CommunicationActions import org.thoughtcrime.securesms.util.CommunicationActions
@@ -300,54 +301,59 @@ class EnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_registration_
} }
private fun handleErrorResponse(result: RegistrationResult) { private fun handleErrorResponse(result: RegistrationResult) {
if (isBindingInvalid()) { viewLifecycleOwner.lifecycleScope.launch {
Log.w(TAG, "Binding not valid, aborting! Result: $result") if (!result.isSuccess()) {
result.logCause() Log.i(TAG, "Handling error response.", result.getCause())
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)
} }
is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen) when (result) {
is VerificationCodeRequestResult.ImpossibleNumber -> { is RegistrationSessionCreationResult.Success,
MaterialAlertDialogBuilder(requireContext()).apply { is VerificationCodeRequestResult.Success -> Unit
setMessage(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber?.toE164()))
setPositiveButton(android.R.string.ok, null) is RegistrationSessionCreationResult.AttemptsExhausted,
show() is VerificationCodeRequestResult.AttemptsExhausted -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_rate_limited_to_service))
is VerificationCodeRequestResult.ChallengeRequired -> {
handleChallenges(result.challenges)
} }
}
is VerificationCodeRequestResult.InvalidTransportModeFailure -> { is VerificationCodeRequestResult.ExternalServiceFailure -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_unable_to_connect_to_service), skipToNextScreen)
MaterialAlertDialogBuilder(requireContext()).apply { is VerificationCodeRequestResult.ImpossibleNumber -> {
setMessage(R.string.RegistrationActivity_we_couldnt_send_you_a_verification_code) MaterialAlertDialogBuilder(requireContext()).apply {
setPositiveButton(R.string.RegistrationActivity_voice_call) { _, _ -> setMessage(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid, fragmentViewModel.phoneNumber?.toE164()))
sharedViewModel.requestVerificationCall(requireContext(), ::handleErrorResponse) 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))
} }
} }

View File

@@ -48,7 +48,6 @@ class WelcomeFragment : LoggingFragment(R.layout.fragment_registration_welcome)
} }
Activity.RESULT_CANCELED -> { Activity.RESULT_CANCELED -> {
Log.w(TAG, "Backup restoration canceled.") Log.w(TAG, "Backup restoration canceled.")
findNavController().popBackStack()
} }
else -> Log.w(TAG, "Backup restoration activity ended with unknown result code: $resultCode") else -> Log.w(TAG, "Backup restoration activity ended with unknown result code: $resultCode")
} }