mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 04:58:45 +00:00
Add support for pin entry sad paths.
This commit is contained in:
committed by
Greyson Parrelli
parent
afb9b76208
commit
62414e72b5
@@ -132,6 +132,11 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
|
||||
controller.prepopulateCountryCode();
|
||||
controller.setNumberAndCountryCode(viewModel.getNumber());
|
||||
showKeyboard(number.getEditText());
|
||||
|
||||
if (viewModel.hasUserSkippedReRegisterFlow() && viewModel.shouldAutoShowSmsConfirmDialog()) {
|
||||
viewModel.setAutoShowSmsConfirmDialog(false);
|
||||
ThreadUtil.runOnMainDelayed(() -> handleRegister(requireContext()), 250);
|
||||
}
|
||||
}
|
||||
|
||||
private void showKeyboard(View viewToFocus) {
|
||||
|
||||
@@ -7,45 +7,59 @@ import android.view.inputmethod.EditorInfo
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.LoggingFragment
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.FragmentRegistrationLockBinding
|
||||
import org.thoughtcrime.securesms.databinding.PinRestoreEntryFragmentBinding
|
||||
import org.thoughtcrime.securesms.lock.v2.KbsConstants
|
||||
import org.thoughtcrime.securesms.lock.v2.PinKeyboardType
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithRegistrationLockProcessor
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.ReRegisterWithPinViewModel
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import org.thoughtcrime.securesms.util.SupportEmailUtil
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
* Using a recovery password or restored KBS token attempt to register in the skip flow.
|
||||
*/
|
||||
class ReRegisterWithPinFragment : LoggingFragment(R.layout.fragment_registration_lock) {
|
||||
class ReRegisterWithPinFragment : LoggingFragment(R.layout.pin_restore_entry_fragment) {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ReRegisterWithPinFragment::class.java)
|
||||
}
|
||||
|
||||
private var _binding: FragmentRegistrationLockBinding? = null
|
||||
private val binding: FragmentRegistrationLockBinding
|
||||
private var _binding: PinRestoreEntryFragmentBinding? = null
|
||||
private val binding: PinRestoreEntryFragmentBinding
|
||||
get() = _binding!!
|
||||
|
||||
private val viewModel: RegistrationViewModel by activityViewModels()
|
||||
private val registrationViewModel: RegistrationViewModel by activityViewModels()
|
||||
private val reRegisterViewModel: ReRegisterWithPinViewModel by viewModels()
|
||||
|
||||
private val disposables = LifecycleDisposable()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_binding = FragmentRegistrationLockBinding.bind(view)
|
||||
_binding = PinRestoreEntryFragmentBinding.bind(view)
|
||||
|
||||
disposables.bindTo(viewLifecycleOwner.lifecycle)
|
||||
|
||||
RegistrationViewDelegate.setDebugLogSubmitMultiTapView(binding.kbsLockPinTitle)
|
||||
RegistrationViewDelegate.setDebugLogSubmitMultiTapView(binding.pinRestorePinTitle)
|
||||
|
||||
binding.kbsLockForgotPin.visibility = View.GONE
|
||||
binding.pinRestorePinDescription.setText(R.string.RegistrationLockFragment__enter_the_pin_you_created_for_your_account)
|
||||
|
||||
binding.kbsLockPinInput.imeOptions = EditorInfo.IME_ACTION_DONE
|
||||
binding.kbsLockPinInput.setOnEditorActionListener { v, actionId, _ ->
|
||||
binding.pinRestoreForgotPin.visibility = View.GONE
|
||||
binding.pinRestoreForgotPin.setOnClickListener { onNeedHelpClicked() }
|
||||
|
||||
binding.pinRestoreSkipButton.setOnClickListener { onSkipClicked() }
|
||||
|
||||
binding.pinRestorePinInput.imeOptions = EditorInfo.IME_ACTION_DONE
|
||||
binding.pinRestorePinInput.setOnEditorActionListener { v, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
ViewUtil.hideKeyboard(requireContext(), v!!)
|
||||
handlePinEntry()
|
||||
@@ -56,25 +70,31 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.fragment_registration
|
||||
|
||||
enableAndFocusPinEntry()
|
||||
|
||||
binding.kbsLockPinConfirm.setOnClickListener {
|
||||
ViewUtil.hideKeyboard(requireContext(), binding.kbsLockPinInput)
|
||||
binding.pinRestorePinConfirm.setOnClickListener {
|
||||
handlePinEntry()
|
||||
}
|
||||
|
||||
binding.kbsLockKeyboardToggle.setOnClickListener { v: View? ->
|
||||
binding.pinRestoreKeyboardToggle.setOnClickListener {
|
||||
val keyboardType: PinKeyboardType = getPinEntryKeyboardType()
|
||||
updateKeyboard(keyboardType.other)
|
||||
binding.kbsLockKeyboardToggle.setText(resolveKeyboardToggleText(keyboardType))
|
||||
binding.pinRestoreKeyboardToggle.setText(resolveKeyboardToggleText(keyboardType))
|
||||
}
|
||||
|
||||
val keyboardType: PinKeyboardType = getPinEntryKeyboardType().other
|
||||
binding.kbsLockKeyboardToggle.setText(resolveKeyboardToggleText(keyboardType))
|
||||
binding.pinRestoreKeyboardToggle.setText(resolveKeyboardToggleText(keyboardType))
|
||||
|
||||
reRegisterViewModel.updateTokenData(registrationViewModel.keyBackupCurrentToken)
|
||||
|
||||
disposables += reRegisterViewModel.triesRemaining.subscribe(this::updateTriesRemaining)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun handlePinEntry() {
|
||||
binding.kbsLockPinInput.isEnabled = false
|
||||
|
||||
val pin: String? = binding.kbsLockPinInput.text?.toString()
|
||||
val pin: String? = binding.pinRestorePinInput.text?.toString()
|
||||
|
||||
val trimmedLength = pin?.replace(" ", "")?.length ?: 0
|
||||
if (trimmedLength == 0) {
|
||||
@@ -89,39 +109,107 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.fragment_registration
|
||||
return
|
||||
}
|
||||
|
||||
binding.kbsLockPinConfirm.setSpinning()
|
||||
|
||||
disposables += viewModel.verifyReRegisterWithPin(pin!!)
|
||||
.subscribe { p ->
|
||||
if (p.hasResult()) {
|
||||
disposables += registrationViewModel.verifyReRegisterWithPin(pin!!)
|
||||
.doOnSubscribe {
|
||||
ViewUtil.hideKeyboard(requireContext(), binding.pinRestorePinInput)
|
||||
binding.pinRestorePinInput.isEnabled = false
|
||||
binding.pinRestorePinConfirm.setSpinning()
|
||||
}
|
||||
.doAfterTerminate {
|
||||
binding.pinRestorePinInput.isEnabled = true
|
||||
binding.pinRestorePinConfirm.cancelSpinning()
|
||||
}
|
||||
.subscribe { processor ->
|
||||
if (processor.hasResult()) {
|
||||
Log.i(TAG, "Successfully re-registered via skip flow")
|
||||
findNavController().safeNavigate(R.id.action_reRegisterWithPinFragment_to_registrationCompletePlaceHolderFragment)
|
||||
return@subscribe
|
||||
}
|
||||
|
||||
reRegisterViewModel.hasIncorrectGuess = true
|
||||
|
||||
if (processor is VerifyResponseWithRegistrationLockProcessor && processor.wrongPin()) {
|
||||
reRegisterViewModel.updateTokenData(processor.tokenData)
|
||||
if (processor.tokenData != null) {
|
||||
registrationViewModel.setKeyBackupTokenData(processor.tokenData)
|
||||
}
|
||||
return@subscribe
|
||||
} else if (processor.isKbsLocked()) {
|
||||
Log.w(TAG, "Unable to continue skip flow, KBS is locked")
|
||||
onAccountLocked()
|
||||
} else if (processor.isServerSentError()) {
|
||||
Log.i(TAG, "Error from server, not likely recoverable", processor.error)
|
||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
Log.w(TAG, "Unable to continue skip flow, resuming normal flow", p.error)
|
||||
// todo handle the various error conditions
|
||||
Toast.makeText(requireContext(), "retry or nav TODO ERROR See log", Toast.LENGTH_SHORT).show()
|
||||
binding.kbsLockPinInput.isEnabled = true
|
||||
Log.i(TAG, "Unexpected error occurred", processor.error)
|
||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTriesRemaining(triesRemaining: Int) {
|
||||
if (reRegisterViewModel.hasIncorrectGuess) {
|
||||
if (triesRemaining == 1 && !reRegisterViewModel.isLocalVerification) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.PinRestoreEntryFragment_incorrect_pin)
|
||||
.setMessage(resources.getQuantityString(R.plurals.PinRestoreEntryFragment_you_have_d_attempt_remaining, triesRemaining, triesRemaining))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
if (triesRemaining > 5) {
|
||||
binding.pinRestorePinInputLabel.setText(R.string.PinRestoreEntryFragment_incorrect_pin)
|
||||
} else {
|
||||
binding.pinRestorePinInputLabel.text = resources.getQuantityString(R.plurals.RegistrationLockFragment__incorrect_pin_d_attempts_remaining, triesRemaining, triesRemaining)
|
||||
}
|
||||
binding.pinRestoreForgotPin.visibility = View.VISIBLE
|
||||
} else {
|
||||
if (triesRemaining == 1) {
|
||||
binding.pinRestoreForgotPin.visibility = View.VISIBLE
|
||||
if (!reRegisterViewModel.isLocalVerification) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(resources.getQuantityString(R.plurals.PinRestoreEntryFragment_you_have_d_attempt_remaining, triesRemaining, triesRemaining))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (triesRemaining == 0) {
|
||||
Log.w(TAG, "Account locked. User out of attempts on KBS.")
|
||||
onAccountLocked()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAccountLocked() {
|
||||
val message = if (reRegisterViewModel.isLocalVerification) R.string.ReRegisterWithPinFragment_out_of_guesses_local else R.string.PinRestoreLockedFragment_youve_run_out_of_pin_guesses
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.PinRestoreEntryFragment_incorrect_pin)
|
||||
.setMessage(message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ReRegisterWithPinFragment_send_sms_code) { _, _ -> onSkipPinEntry() }
|
||||
.setNegativeButton(R.string.AccountLockedFragment__learn_more) { _, _ -> CommunicationActions.openBrowserLink(requireContext(), getString(R.string.PinRestoreLockedFragment_learn_more_url)) }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun enableAndFocusPinEntry() {
|
||||
binding.kbsLockPinInput.isEnabled = true
|
||||
binding.kbsLockPinInput.isFocusable = true
|
||||
if (binding.kbsLockPinInput.requestFocus()) {
|
||||
ServiceUtil.getInputMethodManager(binding.kbsLockPinInput.context).showSoftInput(binding.kbsLockPinInput, 0)
|
||||
binding.pinRestorePinInput.isEnabled = true
|
||||
binding.pinRestorePinInput.isFocusable = true
|
||||
if (binding.pinRestorePinInput.requestFocus()) {
|
||||
ServiceUtil.getInputMethodManager(binding.pinRestorePinInput.context).showSoftInput(binding.pinRestorePinInput, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPinEntryKeyboardType(): PinKeyboardType {
|
||||
val isNumeric = binding.kbsLockPinInput.inputType and InputType.TYPE_MASK_CLASS == InputType.TYPE_CLASS_NUMBER
|
||||
val isNumeric = binding.pinRestorePinInput.inputType and InputType.TYPE_MASK_CLASS == InputType.TYPE_CLASS_NUMBER
|
||||
return if (isNumeric) PinKeyboardType.NUMERIC else PinKeyboardType.ALPHA_NUMERIC
|
||||
}
|
||||
|
||||
private fun updateKeyboard(keyboard: PinKeyboardType) {
|
||||
val isAlphaNumeric = keyboard == PinKeyboardType.ALPHA_NUMERIC
|
||||
binding.kbsLockPinInput.inputType = if (isAlphaNumeric) InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD else InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
|
||||
binding.kbsLockPinInput.text.clear()
|
||||
binding.pinRestorePinInput.inputType = if (isAlphaNumeric) InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD else InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
|
||||
binding.pinRestorePinInput.text?.clear()
|
||||
}
|
||||
|
||||
@StringRes
|
||||
@@ -132,4 +220,41 @@ class ReRegisterWithPinFragment : LoggingFragment(R.layout.fragment_registration
|
||||
R.string.RegistrationLockFragment__enter_numeric_pin
|
||||
}
|
||||
}
|
||||
|
||||
private fun onNeedHelpClicked() {
|
||||
val message = if (reRegisterViewModel.isLocalVerification) R.string.ReRegisterWithPinFragment_need_help_local else R.string.PinRestoreEntryFragment_your_pin_is_a_d_digit_code
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.PinRestoreEntryFragment_need_help)
|
||||
.setMessage(getString(message, KbsConstants.MINIMUM_PIN_LENGTH))
|
||||
.setPositiveButton(R.string.PinRestoreEntryFragment_skip) { _, _ -> onSkipPinEntry() }
|
||||
.setNeutralButton(R.string.PinRestoreEntryFragment_contact_support) { _, _ ->
|
||||
val body = SupportEmailUtil.generateSupportEmailBody(requireContext(), R.string.ReRegisterWithPinFragment_support_email_subject, null, null)
|
||||
|
||||
CommunicationActions.openEmail(
|
||||
requireContext(),
|
||||
SupportEmailUtil.getSupportEmailAddress(requireContext()),
|
||||
getString(R.string.ReRegisterWithPinFragment_support_email_subject),
|
||||
body
|
||||
)
|
||||
}
|
||||
.setNegativeButton(R.string.PinRestoreEntryFragment_cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun onSkipClicked() {
|
||||
val message = if (reRegisterViewModel.isLocalVerification) R.string.ReRegisterWithPinFragment_skip_local else R.string.PinRestoreEntryFragment_if_you_cant_remember_your_pin
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.PinRestoreEntryFragment_skip_pin_entry)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.PinRestoreEntryFragment_skip) { _, _ -> onSkipPinEntry() }
|
||||
.setNegativeButton(R.string.PinRestoreEntryFragment_cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun onSkipPinEntry() {
|
||||
registrationViewModel.setUserSkippedReRegisterFlow(true)
|
||||
findNavController().safeNavigate(R.id.action_reRegisterWithPinFragment_to_enterPhoneNumberFragment)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.thoughtcrime.securesms.registration.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
||||
import org.thoughtcrime.securesms.pin.TokenData
|
||||
|
||||
/**
|
||||
* Used during re-registration flow when pin entry is required to skip SMS verification. Mostly tracks
|
||||
* guesses remaining in both the local and remote check flows.
|
||||
*/
|
||||
class ReRegisterWithPinViewModel : ViewModel() {
|
||||
var isLocalVerification: Boolean = false
|
||||
private set
|
||||
|
||||
var hasIncorrectGuess: Boolean = false
|
||||
|
||||
private val _triesRemaining: BehaviorSubject<Int> = BehaviorSubject.createDefault(10)
|
||||
val triesRemaining: Observable<Int> = _triesRemaining.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun updateTokenData(tokenData: TokenData?) {
|
||||
if (tokenData == null) {
|
||||
isLocalVerification = true
|
||||
if (hasIncorrectGuess) {
|
||||
_triesRemaining.onNext((_triesRemaining.value!! - 1).coerceAtLeast(0))
|
||||
}
|
||||
} else {
|
||||
_triesRemaining.onNext(tokenData.triesRemaining)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import org.whispersystems.signalservice.api.KbsPinData;
|
||||
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.IncorrectCodeException;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -54,6 +55,9 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
|
||||
private final RegistrationRepository registrationRepository;
|
||||
|
||||
private boolean userSkippedReRegisterFlow = false;
|
||||
private boolean autoShowSmsConfirmDialog = false;
|
||||
|
||||
public RegistrationViewModel(@NonNull SavedStateHandle savedStateHandle,
|
||||
boolean isReregister,
|
||||
@NonNull VerifyAccountRepository verifyAccountRepository,
|
||||
@@ -113,6 +117,25 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
return completed != null ? completed : false;
|
||||
}
|
||||
|
||||
public boolean hasUserSkippedReRegisterFlow() {
|
||||
return userSkippedReRegisterFlow;
|
||||
}
|
||||
|
||||
public void setUserSkippedReRegisterFlow(boolean userSkippedReRegisterFlow) {
|
||||
this.userSkippedReRegisterFlow = userSkippedReRegisterFlow;
|
||||
if (userSkippedReRegisterFlow) {
|
||||
setAutoShowSmsConfirmDialog(true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldAutoShowSmsConfirmDialog() {
|
||||
return autoShowSmsConfirmDialog;
|
||||
}
|
||||
|
||||
public void setAutoShowSmsConfirmDialog(boolean autoShowSmsConfirmDialog) {
|
||||
this.autoShowSmsConfirmDialog = autoShowSmsConfirmDialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<ServiceResponse<VerifyResponse>> verifyAccountWithoutRegistrationLock() {
|
||||
final String sessionId = getSessionId();
|
||||
@@ -216,7 +239,18 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
throw new IllegalStateException("Unable to get token or master key");
|
||||
}
|
||||
})
|
||||
.onErrorReturn(t -> new VerifyResponseWithoutKbs(ServiceResponse.forUnknownError(t)))
|
||||
.onErrorReturn(t -> new VerifyResponseWithRegistrationLockProcessor(ServiceResponse.forUnknownError(t), getKeyBackupCurrentToken()))
|
||||
.map(p -> {
|
||||
if (p instanceof VerifyResponseWithRegistrationLockProcessor) {
|
||||
VerifyResponseWithRegistrationLockProcessor lockProcessor = (VerifyResponseWithRegistrationLockProcessor) p;
|
||||
if (lockProcessor.wrongPin() && lockProcessor.getTokenData() != null) {
|
||||
TokenData newToken = TokenData.withResponse(lockProcessor.getTokenData(), lockProcessor.getTokenResponse());
|
||||
return new VerifyResponseWithRegistrationLockProcessor(lockProcessor.getResponse(), newToken);
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
})
|
||||
.doOnSuccess(p -> {
|
||||
if (p.hasResult()) {
|
||||
restoreFromStorageService();
|
||||
@@ -231,9 +265,13 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
{
|
||||
String localPinHash = SignalStore.kbsValues().getLocalPinHash();
|
||||
|
||||
if (hasRecoveryPassword() && localPinHash != null && PinHashing.verifyLocalPinHash(localPinHash, pin)) {
|
||||
Log.i(TAG, "Local pin matches input, attempting registration");
|
||||
return ReRegistrationData.canProceed(new KbsPinData(SignalStore.kbsValues().getOrCreateMasterKey(), SignalStore.kbsValues().getRegistrationLockTokenResponse()));
|
||||
if (hasRecoveryPassword() && localPinHash != null) {
|
||||
if (PinHashing.verifyLocalPinHash(localPinHash, pin)) {
|
||||
Log.i(TAG, "Local pin matches input, attempting registration");
|
||||
return ReRegistrationData.canProceed(new KbsPinData(SignalStore.kbsValues().getOrCreateMasterKey(), SignalStore.kbsValues().getRegistrationLockTokenResponse()));
|
||||
} else {
|
||||
throw new KeyBackupSystemWrongPinException(new TokenResponse(null, null, 0));
|
||||
}
|
||||
} else {
|
||||
TokenData data = getKeyBackupCurrentToken();
|
||||
if (data == null) {
|
||||
@@ -248,6 +286,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
}
|
||||
|
||||
setRecoveryPassword(kbsPinData.getMasterKey().deriveRegistrationRecoveryPassword());
|
||||
setKeyBackupTokenData(data);
|
||||
return ReRegistrationData.canProceed(kbsPinData);
|
||||
}
|
||||
}
|
||||
@@ -270,7 +309,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
return Single.just(processor);
|
||||
}
|
||||
})
|
||||
.<VerifyResponseProcessor>flatMap(processor -> {
|
||||
.flatMap(processor -> {
|
||||
if (processor.hasResult()) {
|
||||
VerifyResponse verifyResponse = processor.getResult();
|
||||
boolean setRegistrationLockEnabled = verifyResponse.getKbsData() != null;
|
||||
@@ -280,10 +319,7 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
}
|
||||
|
||||
return registrationRepository.registerAccount(registrationData, verifyResponse, setRegistrationLockEnabled)
|
||||
.map(r -> {
|
||||
return setRegistrationLockEnabled ? new VerifyResponseWithRegistrationLockProcessor(r, getKeyBackupCurrentToken())
|
||||
: new VerifyResponseWithoutKbs(r);
|
||||
});
|
||||
.map(r -> new VerifyResponseWithRegistrationLockProcessor(r, getKeyBackupCurrentToken()));
|
||||
} else {
|
||||
return Single.just(processor);
|
||||
}
|
||||
@@ -292,11 +328,14 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
}
|
||||
|
||||
public @NonNull Single<Boolean> canEnterSkipSmsFlow() {
|
||||
if (userSkippedReRegisterFlow) {
|
||||
return Single.just(false);
|
||||
}
|
||||
|
||||
return Single.just(hasRecoveryPassword())
|
||||
.flatMap(hasRecoveryPassword -> {
|
||||
if (hasRecoveryPassword) {
|
||||
Log.d(TAG, "Have valid recovery password but still checking kbs credentials as a backup");
|
||||
return checkForValidKbsAuthCredentials().map(unused -> true);
|
||||
return Single.just(true);
|
||||
} else {
|
||||
return checkForValidKbsAuthCredentials();
|
||||
}
|
||||
@@ -310,8 +349,9 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
||||
return kbsRepository.getToken(p.getValid())
|
||||
.flatMap(r -> {
|
||||
if (r.getResult().isPresent()) {
|
||||
setKeyBackupTokenData(r.getResult().get());
|
||||
return Single.just(true);
|
||||
TokenData tokenData = r.getResult().get();
|
||||
setKeyBackupTokenData(tokenData);
|
||||
return Single.just(tokenData.getTriesRemaining() > 0);
|
||||
} else {
|
||||
return Single.just(false);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
tools:viewBindingIgnore="true">
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -116,7 +116,9 @@
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim"
|
||||
app:popUpTo="@id/enterPhoneNumberFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
</fragment>
|
||||
|
||||
@@ -204,15 +206,6 @@
|
||||
android:name="org.thoughtcrime.securesms.registration.fragments.ReRegisterWithPinFragment"
|
||||
tools:layout="@layout/fragment_registration_lock">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_reRegisterWithPinFragment_to_enterCodeFragment"
|
||||
app:destination="@id/enterCodeFragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim"
|
||||
app:popUpTo="@+id/enterPhoneNumberFragment"/>
|
||||
|
||||
<action
|
||||
android:id="@+id/action_reRegisterWithPinFragment_to_registrationCompletePlaceHolderFragment"
|
||||
app:destination="@id/registrationCompletePlaceHolderFragment"
|
||||
@@ -222,6 +215,17 @@
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim"
|
||||
app:popUpTo="@+id/welcomeFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_reRegisterWithPinFragment_to_enterPhoneNumberFragment"
|
||||
app:destination="@id/enterPhoneNumberFragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim"
|
||||
app:popUpTo="@+id/reRegisterWithPinFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
||||
@@ -1596,6 +1596,17 @@
|
||||
<string name="PinRestoreLockedFragment_create_new_pin">Create new PIN</string>
|
||||
<string name="PinRestoreLockedFragment_learn_more_url" translatable="false">https://support.signal.org/hc/articles/360007059792</string>
|
||||
|
||||
<!-- Dialog button text indicating user wishes to send an sms code isntead of skipping it -->
|
||||
<string name="ReRegisterWithPinFragment_send_sms_code">Send SMS code</string>
|
||||
<!-- Email subject used when user contacts support about an issue with the reregister flow. -->
|
||||
<string name="ReRegisterWithPinFragment_support_email_subject">Signal Registration - Need Help with reregister PIN for Android</string>
|
||||
<!-- Dialog message shown in reregister flow when tapping a informational button to to learn about pins or contact support for help -->
|
||||
<string name="ReRegisterWithPinFragment_need_help_local">Your PIN is a %1$d+ digit code you created that can be numeric or alphanumeric.\n\nIf you can’t remember your PIN, you can create a new one.</string>
|
||||
<!-- Dialog message shown in reregister flow when user requests to skip this flow and return to the normal flow -->
|
||||
<string name="ReRegisterWithPinFragment_skip_local">If you can’t remember your PIN, you can create a new one.</string>
|
||||
<!-- Dialog message shown in reregister flow when user uses up all of their guesses for their pin and we are going to move on -->
|
||||
<string name="ReRegisterWithPinFragment_out_of_guesses_local">You\'ve run out of PIN guesses, but you can still access your Signal account by creating a new PIN.</string>
|
||||
|
||||
<!-- PinOptOutDialog -->
|
||||
<string name="PinOptOutDialog_warning">Warning</string>
|
||||
<string name="PinOptOutDialog_if_you_disable_the_pin_you_will_lose_all_data">If you disable the PIN, you will lose all data when you re-register Signal unless you manually back up and restore. You cannot turn on Registration Lock while the PIN is disabled.</string>
|
||||
@@ -3326,6 +3337,8 @@
|
||||
<!-- KbsLockFragment -->
|
||||
<string name="RegistrationLockFragment__enter_your_pin">Enter your PIN</string>
|
||||
<string name="RegistrationLockFragment__enter_the_pin_you_created">Enter the PIN you created for your account. This is different from your SMS verification code.</string>
|
||||
<!-- Info text shown above a pin entry text box describing what pin they should be entering. -->
|
||||
<string name="RegistrationLockFragment__enter_the_pin_you_created_for_your_account">Enter the PIN you created for your account.</string>
|
||||
<string name="RegistrationLockFragment__enter_alphanumeric_pin">Enter alphanumeric PIN</string>
|
||||
<string name="RegistrationLockFragment__enter_numeric_pin">Enter numeric PIN</string>
|
||||
<string name="RegistrationLockFragment__incorrect_pin_try_again">Incorrect PIN. Try again.</string>
|
||||
|
||||
Reference in New Issue
Block a user