mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-09 14:28:34 +00:00
Bring KBS fragment source into RegistrationLockFragment and handle account locked.
This commit is contained in:
parent
fb82420376
commit
1ea6838db6
@ -31,7 +31,6 @@ import org.thoughtcrime.securesms.registration.service.CodeVerificationRequest;
|
|||||||
import org.thoughtcrime.securesms.registration.service.RegistrationCodeRequest;
|
import org.thoughtcrime.securesms.registration.service.RegistrationCodeRequest;
|
||||||
import org.thoughtcrime.securesms.registration.service.RegistrationService;
|
import org.thoughtcrime.securesms.registration.service.RegistrationService;
|
||||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
|
||||||
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
|
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||||
|
|
||||||
@ -122,30 +121,38 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIncorrectRegistrationLockPin(long timeRemaining, String storageCredentials) {
|
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
|
||||||
model.setStorageCredentials(storageCredentials);
|
model.setTimeRemaining(timeRemaining);
|
||||||
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
|
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean r) {
|
public void onSuccess(Boolean r) {
|
||||||
if (FeatureFlags.pinsForAll()) {
|
Navigation.findNavController(requireView())
|
||||||
Navigation.findNavController(requireView())
|
.navigate(EnterCodeFragmentDirections.actionRequireKbsLockPin(timeRemaining));
|
||||||
.navigate(EnterCodeFragmentDirections.actionRequireKbsLockPin(timeRemaining));
|
|
||||||
} else {
|
|
||||||
Navigation.findNavController(requireView())
|
|
||||||
.navigate(EnterCodeFragmentDirections.actionRequireRegistrationLockPin(timeRemaining));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse triesRemaining) {
|
public void onKbsRegistrationLockPinRequired(long timeRemaining, @NonNull TokenResponse tokenResponse, @NonNull String kbsStorageCredentials) {
|
||||||
// Unexpected, because at this point, no pin has been provided by the user.
|
model.setTimeRemaining(timeRemaining);
|
||||||
throw new AssertionError();
|
model.setStorageCredentials(kbsStorageCredentials);
|
||||||
|
model.setKeyBackupCurrentToken(tokenResponse);
|
||||||
|
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean r) {
|
||||||
|
Navigation.findNavController(requireView())
|
||||||
|
.navigate(EnterCodeFragmentDirections.actionRequireKbsLockPin(timeRemaining));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTooManyAttempts() {
|
public void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse tokenResponse) {
|
||||||
|
throw new AssertionError("Unexpected, user has made no pin guesses");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRateLimited() {
|
||||||
keyboard.displayFailure().addListener(new AssertedSuccessListener<Boolean>() {
|
keyboard.displayFailure().addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean r) {
|
public void onSuccess(Boolean r) {
|
||||||
@ -163,6 +170,14 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKbsAccountLocked(long timeRemaining) {
|
||||||
|
model.setTimeRemaining(timeRemaining);
|
||||||
|
RegistrationLockFragmentDirections.ActionAccountLocked action = RegistrationLockFragmentDirections.actionAccountLocked(timeRemaining);
|
||||||
|
|
||||||
|
Navigation.findNavController(requireView()).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError() {
|
public void onError() {
|
||||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show();
|
Toast.makeText(requireContext(), R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show();
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.registration.fragments;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.navigation.Navigation;
|
|
||||||
|
|
||||||
import com.dd.CircularProgressButton;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.thoughtcrime.securesms.lock.v2.KbsKeyboardType;
|
|
||||||
import org.thoughtcrime.securesms.registration.service.CodeVerificationRequest;
|
|
||||||
import org.thoughtcrime.securesms.registration.service.RegistrationService;
|
|
||||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public final class KbsLockFragment extends BaseRegistrationFragment {
|
|
||||||
|
|
||||||
private EditText pinEntry;
|
|
||||||
private CircularProgressButton pinButton;
|
|
||||||
private TextView errorLabel;
|
|
||||||
private TextView keyboardToggle;
|
|
||||||
private long timeRemaining;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.kbs_lock_fragment, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
|
|
||||||
setDebugLogSubmitMultiTapView(view.findViewById(R.id.kbs_lock_pin_title));
|
|
||||||
|
|
||||||
pinEntry = view.findViewById(R.id.kbs_lock_pin_input);
|
|
||||||
pinButton = view.findViewById(R.id.kbs_lock_pin_confirm);
|
|
||||||
errorLabel = view.findViewById(R.id.kbs_lock_pin_input_label);
|
|
||||||
keyboardToggle = view.findViewById(R.id.kbs_lock_keyboard_toggle);
|
|
||||||
|
|
||||||
View pinForgotButton = view.findViewById(R.id.kbs_lock_forgot_pin);
|
|
||||||
|
|
||||||
timeRemaining = KbsLockFragmentArgs.fromBundle(requireArguments()).getTimeRemaining();
|
|
||||||
|
|
||||||
pinForgotButton.setOnClickListener(v -> handleForgottenPin(timeRemaining));
|
|
||||||
|
|
||||||
pinEntry.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
|
||||||
pinEntry.setOnEditorActionListener((v, actionId, event) -> {
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
|
||||||
hideKeyboard(requireContext(), v);
|
|
||||||
handlePinEntry();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
pinButton.setOnClickListener((v) -> {
|
|
||||||
hideKeyboard(requireContext(), pinEntry);
|
|
||||||
handlePinEntry();
|
|
||||||
});
|
|
||||||
|
|
||||||
keyboardToggle.setOnClickListener((v) -> {
|
|
||||||
KbsKeyboardType keyboardType = getPinEntryKeyboardType();
|
|
||||||
|
|
||||||
updateKeyboard(keyboardType);
|
|
||||||
keyboardToggle.setText(resolveKeyboardToggleText(keyboardType));
|
|
||||||
});
|
|
||||||
|
|
||||||
RegistrationViewModel model = getModel();
|
|
||||||
model.getTokenResponseCredentialsPair().observe(getViewLifecycleOwner(), pair -> {
|
|
||||||
if (pair.first().getTries() == 0) {
|
|
||||||
lockAccount();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
model.onRegistrationLockFragmentCreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private KbsKeyboardType getPinEntryKeyboardType() {
|
|
||||||
boolean isNumeric = (pinEntry.getImeOptions() & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_NUMBER;
|
|
||||||
|
|
||||||
return isNumeric ? KbsKeyboardType.NUMERIC : KbsKeyboardType.ALPHA_NUMERIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePinEntry() {
|
|
||||||
final String pin = pinEntry.getText().toString();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(pin) || TextUtils.isEmpty(pin.replace(" ", ""))) {
|
|
||||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_you_must_enter_your_registration_lock_PIN, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegistrationViewModel model = getModel();
|
|
||||||
RegistrationService registrationService = RegistrationService.getInstance(model.getNumber().getE164Number(), model.getRegistrationSecret());
|
|
||||||
String storageCredentials = model.getBasicStorageCredentials();
|
|
||||||
TokenResponse tokenResponse = model.getKeyBackupCurrentToken();
|
|
||||||
|
|
||||||
setSpinning(pinButton);
|
|
||||||
|
|
||||||
registrationService.verifyAccount(requireActivity(),
|
|
||||||
model.getFcmToken(),
|
|
||||||
model.getTextCodeEntered(),
|
|
||||||
pin, storageCredentials, tokenResponse,
|
|
||||||
|
|
||||||
new CodeVerificationRequest.VerifyCallback() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccessfulRegistration() {
|
|
||||||
cancelSpinning(pinButton);
|
|
||||||
SignalStore.kbsValues().setKeyboardType(getPinEntryKeyboardType());
|
|
||||||
|
|
||||||
Navigation.findNavController(requireView()).navigate(KbsLockFragmentDirections.actionSuccessfulRegistration());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIncorrectRegistrationLockPin(long timeRemaining, String storageCredentials) {
|
|
||||||
model.setStorageCredentials(storageCredentials);
|
|
||||||
cancelSpinning(pinButton);
|
|
||||||
|
|
||||||
pinEntry.setText("");
|
|
||||||
errorLabel.setText(R.string.KbsLockFragment__incorrect_pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse tokenResponse) {
|
|
||||||
cancelSpinning(pinButton);
|
|
||||||
|
|
||||||
model.setKeyBackupCurrentToken(tokenResponse);
|
|
||||||
|
|
||||||
int triesRemaining = tokenResponse.getTries();
|
|
||||||
|
|
||||||
if (triesRemaining == 0) {
|
|
||||||
lockAccount();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triesRemaining == 3) {
|
|
||||||
long daysRemaining = getLockoutDays(timeRemaining);
|
|
||||||
|
|
||||||
new AlertDialog.Builder(requireContext())
|
|
||||||
.setTitle(R.string.KbsLockFragment__incorrect_pin)
|
|
||||||
.setMessage(getString(R.string.KbsLockFragment__you_have_d_attempts_remaining, triesRemaining, daysRemaining, daysRemaining))
|
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triesRemaining > 5) {
|
|
||||||
errorLabel.setText(R.string.KbsLockFragment__incorrect_pin_try_again);
|
|
||||||
} else {
|
|
||||||
errorLabel.setText(getString(R.string.KbsLockFragment__incorrect_pin_d_attempts_remaining, triesRemaining));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTooManyAttempts() {
|
|
||||||
cancelSpinning(pinButton);
|
|
||||||
|
|
||||||
new AlertDialog.Builder(requireContext())
|
|
||||||
.setTitle(R.string.RegistrationActivity_too_many_attempts)
|
|
||||||
.setMessage(R.string.RegistrationActivity_you_have_made_too_many_incorrect_registration_lock_pin_attempts_please_try_again_in_a_day)
|
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError() {
|
|
||||||
cancelSpinning(pinButton);
|
|
||||||
|
|
||||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_error_connecting_to_service, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleForgottenPin(long timeRemainingMs) {
|
|
||||||
new AlertDialog.Builder(requireContext())
|
|
||||||
.setTitle(R.string.KbsLockFragment__forgot_your_pin)
|
|
||||||
.setMessage(getString(R.string.KbsLockFragment__for_your_privacy_and_security_there_is_no_way_to_recover, getLockoutDays(timeRemainingMs)))
|
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getLockoutDays(long timeRemainingMs) {
|
|
||||||
return TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void lockAccount() {
|
|
||||||
KbsLockFragmentDirections.ActionAccountLocked action = KbsLockFragmentDirections.actionAccountLocked(timeRemaining);
|
|
||||||
|
|
||||||
Navigation.findNavController(requireView()).navigate(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateKeyboard(@NonNull KbsKeyboardType keyboard) {
|
|
||||||
boolean isAlphaNumeric = keyboard == KbsKeyboardType.ALPHA_NUMERIC;
|
|
||||||
|
|
||||||
pinEntry.setInputType(isAlphaNumeric ? InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
|
|
||||||
: InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
private @StringRes int resolveKeyboardToggleText(@NonNull KbsKeyboardType keyboard) {
|
|
||||||
if (keyboard == KbsKeyboardType.ALPHA_NUMERIC) {
|
|
||||||
return R.string.KbsLockFragment__enter_alphanumeric_pin;
|
|
||||||
} else {
|
|
||||||
return R.string.KbsLockFragment__enter_numeric_pin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ import androidx.navigation.ActivityNavigator;
|
|||||||
|
|
||||||
import org.thoughtcrime.securesms.MainActivity;
|
import org.thoughtcrime.securesms.MainActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
||||||
import org.thoughtcrime.securesms.lock.v2.PinUtil;
|
import org.thoughtcrime.securesms.lock.v2.PinUtil;
|
||||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
||||||
|
@ -1,32 +1,33 @@
|
|||||||
package org.thoughtcrime.securesms.registration.fragments;
|
package org.thoughtcrime.securesms.registration.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
|
|
||||||
import com.dd.CircularProgressButton;
|
import com.dd.CircularProgressButton;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
import org.thoughtcrime.securesms.lock.v2.KbsKeyboardType;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.registration.service.CodeVerificationRequest;
|
import org.thoughtcrime.securesms.registration.service.CodeVerificationRequest;
|
||||||
import org.thoughtcrime.securesms.registration.service.RegistrationService;
|
import org.thoughtcrime.securesms.registration.service.RegistrationService;
|
||||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
||||||
@ -35,11 +36,12 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
|
|
||||||
private EditText pinEntry;
|
private EditText pinEntry;
|
||||||
private CircularProgressButton pinButton;
|
private CircularProgressButton pinButton;
|
||||||
|
private TextView errorLabel;
|
||||||
|
private TextView keyboardToggle;
|
||||||
private long timeRemaining;
|
private long timeRemaining;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.fragment_registration_lock, container, false);
|
return inflater.inflate(R.layout.fragment_registration_lock, container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,39 +49,19 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
setDebugLogSubmitMultiTapView(view.findViewById(R.id.verify_header));
|
setDebugLogSubmitMultiTapView(view.findViewById(R.id.kbs_lock_pin_title));
|
||||||
|
|
||||||
pinEntry = view.findViewById(R.id.pin);
|
pinEntry = view.findViewById(R.id.kbs_lock_pin_input);
|
||||||
pinButton = view.findViewById(R.id.pinButton);
|
pinButton = view.findViewById(R.id.kbs_lock_pin_confirm);
|
||||||
|
errorLabel = view.findViewById(R.id.kbs_lock_pin_input_label);
|
||||||
|
keyboardToggle = view.findViewById(R.id.kbs_lock_keyboard_toggle);
|
||||||
|
|
||||||
View clarificationLabel = view.findViewById(R.id.clarification_label);
|
View pinForgotButton = view.findViewById(R.id.kbs_lock_forgot_pin);
|
||||||
View subHeader = view.findViewById(R.id.verify_subheader);
|
|
||||||
View pinForgotButton = view.findViewById(R.id.forgot_button);
|
|
||||||
|
|
||||||
String code = getModel().getTextCodeEntered();
|
|
||||||
|
|
||||||
timeRemaining = RegistrationLockFragmentArgs.fromBundle(requireArguments()).getTimeRemaining();
|
timeRemaining = RegistrationLockFragmentArgs.fromBundle(requireArguments()).getTimeRemaining();
|
||||||
|
|
||||||
pinForgotButton.setOnClickListener(v -> handleForgottenPin(timeRemaining));
|
pinForgotButton.setOnClickListener(v -> handleForgottenPin(timeRemaining));
|
||||||
|
|
||||||
pinEntry.addTextChangedListener(new TextWatcher() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
boolean matchesTextCode = s != null && s.toString().equals(code);
|
|
||||||
clarificationLabel.setVisibility(matchesTextCode ? View.VISIBLE : View.INVISIBLE);
|
|
||||||
subHeader.setVisibility(matchesTextCode ? View.INVISIBLE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pinEntry.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
pinEntry.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
pinEntry.setOnEditorActionListener((v, actionId, event) -> {
|
pinEntry.setOnEditorActionListener((v, actionId, event) -> {
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
@ -95,35 +77,21 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
handlePinEntry();
|
handlePinEntry();
|
||||||
});
|
});
|
||||||
|
|
||||||
RegistrationViewModel model = getModel();
|
keyboardToggle.setOnClickListener((v) -> {
|
||||||
model.getTokenResponseCredentialsPair()
|
KbsKeyboardType keyboardType = getPinEntryKeyboardType();
|
||||||
.observe(this, pair -> {
|
|
||||||
TokenResponse token = pair.first();
|
|
||||||
String credentials = pair.second();
|
|
||||||
updateContinueText(token, credentials);
|
|
||||||
});
|
|
||||||
|
|
||||||
model.onRegistrationLockFragmentCreate();
|
updateKeyboard(keyboardType);
|
||||||
|
keyboardToggle.setText(resolveKeyboardToggleText(keyboardType));
|
||||||
|
});
|
||||||
|
|
||||||
|
getModel().getTimeRemaining()
|
||||||
|
.observe(getViewLifecycleOwner(), t -> timeRemaining = t);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateContinueText(@Nullable TokenResponse tokenResponse, @Nullable String storageCredentials) {
|
private KbsKeyboardType getPinEntryKeyboardType() {
|
||||||
if (tokenResponse == null) {
|
boolean isNumeric = (pinEntry.getImeOptions() & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_NUMBER;
|
||||||
if (storageCredentials == null) {
|
|
||||||
pinButton.setIdleText(getString(R.string.RegistrationActivity_continue));
|
return isNumeric ? KbsKeyboardType.NUMERIC : KbsKeyboardType.ALPHA_NUMERIC;
|
||||||
} else {
|
|
||||||
// TODO: This is the case where we can determine they are locked out
|
|
||||||
// no token, but do have storage credentials. Might want to change text.
|
|
||||||
pinButton.setIdleText(getString(R.string.RegistrationActivity_continue));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int triesRemaining = tokenResponse.getTries();
|
|
||||||
if (triesRemaining == 1) {
|
|
||||||
pinButton.setIdleText(getString(R.string.RegistrationActivity_continue_last_attempt));
|
|
||||||
} else {
|
|
||||||
pinButton.setIdleText(getString(R.string.RegistrationActivity_continue_d_attempts_left, triesRemaining));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pinButton.setText(pinButton.getIdleText());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePinEntry() {
|
private void handlePinEntry() {
|
||||||
@ -134,58 +102,79 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegistrationViewModel model = getModel();
|
RegistrationViewModel model = getModel();
|
||||||
RegistrationService registrationService = RegistrationService.getInstance(model.getNumber().getE164Number(), model.getRegistrationSecret());
|
RegistrationService registrationService = RegistrationService.getInstance(model.getNumber().getE164Number(), model.getRegistrationSecret());
|
||||||
String storageCredentials = model.getBasicStorageCredentials();
|
TokenResponse tokenResponse = model.getKeyBackupCurrentToken();
|
||||||
TokenResponse tokenResponse = model.getKeyBackupCurrentToken();
|
String basicStorageCredentials = model.getBasicStorageCredentials();
|
||||||
|
|
||||||
setSpinning(pinButton);
|
setSpinning(pinButton);
|
||||||
|
|
||||||
registrationService.verifyAccount(requireActivity(),
|
registrationService.verifyAccount(requireActivity(),
|
||||||
model.getFcmToken(),
|
model.getFcmToken(),
|
||||||
model.getTextCodeEntered(),
|
model.getTextCodeEntered(),
|
||||||
pin, storageCredentials, tokenResponse,
|
pin,
|
||||||
|
basicStorageCredentials,
|
||||||
|
tokenResponse,
|
||||||
|
|
||||||
new CodeVerificationRequest.VerifyCallback() {
|
new CodeVerificationRequest.VerifyCallback() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccessfulRegistration() {
|
public void onSuccessfulRegistration() {
|
||||||
cancelSpinning(pinButton);
|
cancelSpinning(pinButton);
|
||||||
|
SignalStore.kbsValues().setKeyboardType(getPinEntryKeyboardType());
|
||||||
|
|
||||||
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionSuccessfulRegistration());
|
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionSuccessfulRegistration());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIncorrectRegistrationLockPin(long timeRemaining, String storageCredentials) {
|
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
|
||||||
model.setStorageCredentials(storageCredentials);
|
getModel().setTimeRemaining(timeRemaining);
|
||||||
cancelSpinning(pinButton);
|
|
||||||
|
|
||||||
pinEntry.setText("");
|
cancelSpinning(pinButton);
|
||||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_incorrect_registration_lock_pin, Toast.LENGTH_LONG).show();
|
pinEntry.getText().clear();
|
||||||
|
|
||||||
|
errorLabel.setText(R.string.KbsLockFragment__incorrect_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKbsRegistrationLockPinRequired(long timeRemaining, @NonNull TokenResponse kbsTokenResponse, @NonNull String kbsStorageCredentials) {
|
||||||
|
throw new AssertionError("Not expected after a pin guess");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse tokenResponse) {
|
public void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse tokenResponse) {
|
||||||
cancelSpinning(pinButton);
|
cancelSpinning(pinButton);
|
||||||
|
pinEntry.getText().clear();
|
||||||
|
|
||||||
model.setKeyBackupCurrentToken(tokenResponse);
|
model.setKeyBackupCurrentToken(tokenResponse);
|
||||||
|
|
||||||
int triesRemaining = tokenResponse.getTries();
|
int triesRemaining = tokenResponse.getTries();
|
||||||
|
|
||||||
if (triesRemaining == 0) {
|
if (triesRemaining == 0) {
|
||||||
handleForgottenPin(timeRemaining);
|
Log.w(TAG, "Account locked. User out of attempts on KBS.");
|
||||||
|
lockAccount(timeRemaining);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new AlertDialog.Builder(requireContext())
|
if (triesRemaining == 3) {
|
||||||
.setTitle(R.string.RegistrationActivity_pin_incorrect)
|
long daysRemaining = getLockoutDays(timeRemaining);
|
||||||
.setMessage(getString(R.string.RegistrationActivity_you_have_d_tries_remaining, triesRemaining))
|
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
new AlertDialog.Builder(requireContext())
|
||||||
.show();
|
.setTitle(R.string.KbsLockFragment__incorrect_pin)
|
||||||
|
.setMessage(getString(R.string.KbsLockFragment__you_have_d_attempts_remaining, triesRemaining, daysRemaining, daysRemaining))
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (triesRemaining > 5) {
|
||||||
|
errorLabel.setText(R.string.KbsLockFragment__incorrect_pin_try_again);
|
||||||
|
} else {
|
||||||
|
errorLabel.setText(getString(R.string.KbsLockFragment__incorrect_pin_d_attempts_remaining, triesRemaining));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTooManyAttempts() {
|
public void onRateLimited() {
|
||||||
cancelSpinning(pinButton);
|
cancelSpinning(pinButton);
|
||||||
|
|
||||||
new AlertDialog.Builder(requireContext())
|
new AlertDialog.Builder(requireContext())
|
||||||
@ -195,6 +184,13 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKbsAccountLocked(long timeRemaining) {
|
||||||
|
getModel().setTimeRemaining(timeRemaining);
|
||||||
|
|
||||||
|
lockAccount(timeRemaining);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError() {
|
public void onError() {
|
||||||
cancelSpinning(pinButton);
|
cancelSpinning(pinButton);
|
||||||
@ -206,9 +202,34 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
|||||||
|
|
||||||
private void handleForgottenPin(long timeRemainingMs) {
|
private void handleForgottenPin(long timeRemainingMs) {
|
||||||
new AlertDialog.Builder(requireContext())
|
new AlertDialog.Builder(requireContext())
|
||||||
.setTitle(R.string.RegistrationActivity_oh_no)
|
.setTitle(R.string.KbsLockFragment__forgot_your_pin)
|
||||||
.setMessage(getString(R.string.RegistrationActivity_registration_of_this_phone_number_will_be_possible_without_your_registration_lock_pin_after_seven_days_have_passed, (TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1)))
|
.setMessage(getString(R.string.KbsLockFragment__for_your_privacy_and_security_there_is_no_way_to_recover, getLockoutDays(timeRemainingMs)))
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long getLockoutDays(long timeRemainingMs) {
|
||||||
|
return TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lockAccount(long timeRemaining) {
|
||||||
|
RegistrationLockFragmentDirections.ActionAccountLocked action = RegistrationLockFragmentDirections.actionAccountLocked(timeRemaining);
|
||||||
|
|
||||||
|
Navigation.findNavController(requireView()).navigate(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateKeyboard(@NonNull KbsKeyboardType keyboard) {
|
||||||
|
boolean isAlphaNumeric = keyboard == KbsKeyboardType.ALPHA_NUMERIC;
|
||||||
|
|
||||||
|
pinEntry.setInputType(isAlphaNumeric ? InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||||
|
: InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @StringRes static int resolveKeyboardToggleText(@NonNull KbsKeyboardType keyboard) {
|
||||||
|
if (keyboard == KbsKeyboardType.ALPHA_NUMERIC) {
|
||||||
|
return R.string.KbsLockFragment__enter_alphanumeric_pin;
|
||||||
|
} else {
|
||||||
|
return R.string.KbsLockFragment__enter_numeric_pin;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,16 +53,12 @@ public final class CodeVerificationRequest {
|
|||||||
|
|
||||||
private static final String TAG = Log.tag(CodeVerificationRequest.class);
|
private static final String TAG = Log.tag(CodeVerificationRequest.class);
|
||||||
|
|
||||||
static TokenResponse getToken(@Nullable String basicStorageCredentials) throws IOException {
|
|
||||||
if (basicStorageCredentials == null) return null;
|
|
||||||
return ApplicationDependencies.getKeyBackupService().getToken(basicStorageCredentials);
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum Result {
|
private enum Result {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
PIN_LOCKED,
|
PIN_LOCKED,
|
||||||
KBS_WRONG_PIN,
|
KBS_WRONG_PIN,
|
||||||
RATE_LIMITED,
|
RATE_LIMITED,
|
||||||
|
KBS_ACCOUNT_LOCKED,
|
||||||
ERROR
|
ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,17 +83,39 @@ public final class CodeVerificationRequest {
|
|||||||
{
|
{
|
||||||
new AsyncTask<Void, Void, Result>() {
|
new AsyncTask<Void, Void, Result>() {
|
||||||
|
|
||||||
private volatile LockedException lockedException;
|
private volatile LockedException lockedException;
|
||||||
private volatile KeyBackupSystemWrongPinException keyBackupSystemWrongPinException;
|
private volatile TokenResponse kbsToken;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Result doInBackground(Void... voids) {
|
protected Result doInBackground(Void... voids) {
|
||||||
|
final boolean pinSupplied = pin != null;
|
||||||
|
final boolean tryKbs = kbsTokenResponse != null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
verifyAccount(context, credentials, code, pin, basicStorageCredentials, kbsTokenResponse, fcmToken);
|
kbsToken = kbsTokenResponse;
|
||||||
|
verifyAccount(context, credentials, code, pin, kbsTokenResponse, basicStorageCredentials, fcmToken);
|
||||||
return Result.SUCCESS;
|
return Result.SUCCESS;
|
||||||
|
} catch (KeyBackupSystemWrongPinException e) {
|
||||||
|
kbsToken = e.getTokenResponse();
|
||||||
|
return Result.KBS_WRONG_PIN;
|
||||||
} catch (LockedException e) {
|
} catch (LockedException e) {
|
||||||
|
if (pinSupplied && tryKbs) {
|
||||||
|
throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!");
|
||||||
|
}
|
||||||
|
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
lockedException = e;
|
lockedException = e;
|
||||||
|
if (e.getBasicStorageCredentials() != null) {
|
||||||
|
try {
|
||||||
|
kbsToken = getToken(e.getBasicStorageCredentials());
|
||||||
|
if (kbsToken == null || kbsToken.getTries() == 0) {
|
||||||
|
return Result.KBS_ACCOUNT_LOCKED;
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return Result.ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
return Result.PIN_LOCKED;
|
return Result.PIN_LOCKED;
|
||||||
} catch (RateLimitException e) {
|
} catch (RateLimitException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
@ -105,9 +123,6 @@ public final class CodeVerificationRequest {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return Result.ERROR;
|
return Result.ERROR;
|
||||||
} catch (KeyBackupSystemWrongPinException e) {
|
|
||||||
keyBackupSystemWrongPinException = e;
|
|
||||||
return Result.KBS_WRONG_PIN;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,22 +134,41 @@ public final class CodeVerificationRequest {
|
|||||||
callback.onSuccessfulRegistration();
|
callback.onSuccessfulRegistration();
|
||||||
break;
|
break;
|
||||||
case PIN_LOCKED:
|
case PIN_LOCKED:
|
||||||
callback.onIncorrectRegistrationLockPin(lockedException.getTimeRemaining(), lockedException.getBasicStorageCredentials());
|
if (kbsToken != null) {
|
||||||
|
if (lockedException.getBasicStorageCredentials() == null) {
|
||||||
|
throw new AssertionError("KBS Token set, but no storage credentials supplied.");
|
||||||
|
}
|
||||||
|
Log.w(TAG, "Reg Locked: V2 pin needed for registration");
|
||||||
|
callback.onKbsRegistrationLockPinRequired(lockedException.getTimeRemaining(), kbsToken, lockedException.getBasicStorageCredentials());
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Reg Locked: V1 pin needed for registration");
|
||||||
|
callback.onV1RegistrationLockPinRequiredOrIncorrect(lockedException.getTimeRemaining());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RATE_LIMITED:
|
case RATE_LIMITED:
|
||||||
callback.onTooManyAttempts();
|
callback.onRateLimited();
|
||||||
break;
|
break;
|
||||||
case ERROR:
|
case ERROR:
|
||||||
callback.onError();
|
callback.onError();
|
||||||
break;
|
break;
|
||||||
case KBS_WRONG_PIN:
|
case KBS_WRONG_PIN:
|
||||||
callback.onIncorrectKbsRegistrationLockPin(keyBackupSystemWrongPinException.getTokenResponse());
|
Log.w(TAG, "KBS Pin was wrong");
|
||||||
|
callback.onIncorrectKbsRegistrationLockPin(kbsToken);
|
||||||
|
break;
|
||||||
|
case KBS_ACCOUNT_LOCKED:
|
||||||
|
Log.w(TAG, "KBS Account is locked");
|
||||||
|
callback.onKbsAccountLocked(lockedException.getTimeRemaining());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TokenResponse getToken(@Nullable String basicStorageCredentials) throws IOException {
|
||||||
|
if (basicStorageCredentials == null) return null;
|
||||||
|
return ApplicationDependencies.getKeyBackupService().getToken(basicStorageCredentials);
|
||||||
|
}
|
||||||
|
|
||||||
private static void handleSuccessfulRegistration(@NonNull Context context) {
|
private static void handleSuccessfulRegistration(@NonNull Context context) {
|
||||||
JobManager jobManager = ApplicationDependencies.getJobManager();
|
JobManager jobManager = ApplicationDependencies.getJobManager();
|
||||||
jobManager.add(new DirectoryRefreshJob(false));
|
jobManager.add(new DirectoryRefreshJob(false));
|
||||||
@ -148,11 +182,12 @@ public final class CodeVerificationRequest {
|
|||||||
@NonNull Credentials credentials,
|
@NonNull Credentials credentials,
|
||||||
@NonNull String code,
|
@NonNull String code,
|
||||||
@Nullable String pin,
|
@Nullable String pin,
|
||||||
@Nullable String basicStorageCredentials,
|
|
||||||
@Nullable TokenResponse kbsTokenResponse,
|
@Nullable TokenResponse kbsTokenResponse,
|
||||||
|
@Nullable String kbsStorageCredentials,
|
||||||
@Nullable String fcmToken)
|
@Nullable String fcmToken)
|
||||||
throws IOException, KeyBackupSystemWrongPinException
|
throws IOException, KeyBackupSystemWrongPinException
|
||||||
{
|
{
|
||||||
|
boolean isV2KbsPin = kbsTokenResponse != null;
|
||||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||||
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context);
|
byte[] unidentifiedAccessKey = UnidentifiedAccessUtil.getSelfUnidentifiedAccessKey(context);
|
||||||
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
|
||||||
@ -161,10 +196,10 @@ public final class CodeVerificationRequest {
|
|||||||
SessionUtil.archiveAllSessions(context);
|
SessionUtil.archiveAllSessions(context);
|
||||||
|
|
||||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, credentials.getE164number(), credentials.getPassword());
|
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, credentials.getE164number(), credentials.getPassword());
|
||||||
RegistrationLockData kbsData = restoreMasterKey(pin, basicStorageCredentials, kbsTokenResponse);
|
RegistrationLockData kbsData = isV2KbsPin ? restoreMasterKey(pin, kbsStorageCredentials, kbsTokenResponse) : null;
|
||||||
String registrationLock = kbsData != null ? kbsData.getMasterKey().deriveRegistrationLock() : null;
|
String registrationLock = kbsData != null ? kbsData.getMasterKey().deriveRegistrationLock() : null;
|
||||||
boolean present = fcmToken != null;
|
boolean present = fcmToken != null;
|
||||||
String pinForServer = basicStorageCredentials == null ? pin : null;
|
String pinForServer = isV2KbsPin ? null : pin;
|
||||||
|
|
||||||
UUID uuid = accountManager.verifyAccountWithCode(code, null, registrationId, !present,
|
UUID uuid = accountManager.verifyAccountWithCode(code, null, registrationId, !present,
|
||||||
pinForServer, registrationLock,
|
pinForServer, registrationLock,
|
||||||
@ -251,14 +286,13 @@ public final class CodeVerificationRequest {
|
|||||||
|
|
||||||
private static @Nullable RegistrationLockData restoreMasterKey(@Nullable String pin,
|
private static @Nullable RegistrationLockData restoreMasterKey(@Nullable String pin,
|
||||||
@Nullable String basicStorageCredentials,
|
@Nullable String basicStorageCredentials,
|
||||||
@Nullable TokenResponse tokenResponse)
|
@NonNull TokenResponse tokenResponse)
|
||||||
throws IOException, KeyBackupSystemWrongPinException
|
throws IOException, KeyBackupSystemWrongPinException
|
||||||
{
|
{
|
||||||
if (pin == null) return null;
|
if (pin == null) return null;
|
||||||
|
|
||||||
if (basicStorageCredentials == null) {
|
if (basicStorageCredentials == null) {
|
||||||
Log.i(TAG, "No storage credentials supplied, pin is not on KBS");
|
throw new AssertionError("Cannot restore KBS key, no storage credentials supplied");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
|
KeyBackupService keyBackupService = ApplicationDependencies.getKeyBackupService();
|
||||||
@ -290,13 +324,32 @@ public final class CodeVerificationRequest {
|
|||||||
void onSuccessfulRegistration();
|
void onSuccessfulRegistration();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The account is locked with a V1 (non-KBS) pin.
|
||||||
|
*
|
||||||
* @param timeRemaining Time until pin expires and number can be reused.
|
* @param timeRemaining Time until pin expires and number can be reused.
|
||||||
*/
|
*/
|
||||||
void onIncorrectRegistrationLockPin(long timeRemaining, String storageCredentials);
|
void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The account is locked with a V2 (KBS) pin. Called before any user pin guesses.
|
||||||
|
*/
|
||||||
|
void onKbsRegistrationLockPinRequired(long timeRemaining, @NonNull TokenResponse kbsTokenResponse, @NonNull String kbsStorageCredentials);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The account is locked with a V2 (KBS) pin. Called after a user pin guess.
|
||||||
|
* <p>
|
||||||
|
* i.e. an attempt has likely been used.
|
||||||
|
*/
|
||||||
void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse kbsTokenResponse);
|
void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse kbsTokenResponse);
|
||||||
|
|
||||||
void onTooManyAttempts();
|
/**
|
||||||
|
* V2 (KBS) pin is set, but there is no data on KBS
|
||||||
|
*
|
||||||
|
* @param timeRemaining Time until pin expires and number can be reused.
|
||||||
|
*/
|
||||||
|
void onKbsAccountLocked(long timeRemaining);
|
||||||
|
|
||||||
|
void onRateLimited();
|
||||||
|
|
||||||
void onError();
|
void onError();
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,4 @@ public final class RegistrationService {
|
|||||||
{
|
{
|
||||||
CodeVerificationRequest.verifyAccount(activity, credentials, fcmToken, code, pin, basicStorageCredentials, tokenResponse, callback);
|
CodeVerificationRequest.verifyAccount(activity, credentials, fcmToken, code, pin, basicStorageCredentials, tokenResponse, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable TokenResponse getToken(@Nullable String basicStorageCredentials) throws IOException {
|
|
||||||
return CodeVerificationRequest.getToken(basicStorageCredentials);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,10 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.SavedStateHandle;
|
import androidx.lifecycle.SavedStateHandle;
|
||||||
import androidx.lifecycle.Transformations;
|
|
||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.registration.service.RegistrationService;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataPair;
|
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
||||||
|
|
||||||
@ -34,8 +29,7 @@ public final class RegistrationViewModel extends ViewModel {
|
|||||||
private final MutableLiveData<Integer> successfulCodeRequestAttempts;
|
private final MutableLiveData<Integer> successfulCodeRequestAttempts;
|
||||||
private final MutableLiveData<LocalCodeRequestRateLimiter> requestLimiter;
|
private final MutableLiveData<LocalCodeRequestRateLimiter> requestLimiter;
|
||||||
private final MutableLiveData<String> keyBackupcurrentTokenJson;
|
private final MutableLiveData<String> keyBackupcurrentTokenJson;
|
||||||
private final LiveData<TokenResponse> keyBackupcurrentToken;
|
private final MutableLiveData<Long> timeRemaining;
|
||||||
private final LiveData<Pair<TokenResponse, String>> tokenResponseCredentialsPair;
|
|
||||||
|
|
||||||
public RegistrationViewModel(@NonNull SavedStateHandle savedStateHandle) {
|
public RegistrationViewModel(@NonNull SavedStateHandle savedStateHandle) {
|
||||||
secret = loadValue(savedStateHandle, "REGISTRATION_SECRET", Util.getSecret(18));
|
secret = loadValue(savedStateHandle, "REGISTRATION_SECRET", Util.getSecret(18));
|
||||||
@ -49,19 +43,7 @@ public final class RegistrationViewModel extends ViewModel {
|
|||||||
successfulCodeRequestAttempts = savedStateHandle.getLiveData("SUCCESSFUL_CODE_REQUEST_ATTEMPTS", 0);
|
successfulCodeRequestAttempts = savedStateHandle.getLiveData("SUCCESSFUL_CODE_REQUEST_ATTEMPTS", 0);
|
||||||
requestLimiter = savedStateHandle.getLiveData("REQUEST_RATE_LIMITER", new LocalCodeRequestRateLimiter(60_000));
|
requestLimiter = savedStateHandle.getLiveData("REQUEST_RATE_LIMITER", new LocalCodeRequestRateLimiter(60_000));
|
||||||
keyBackupcurrentTokenJson = savedStateHandle.getLiveData("KBS_TOKEN");
|
keyBackupcurrentTokenJson = savedStateHandle.getLiveData("KBS_TOKEN");
|
||||||
|
timeRemaining = savedStateHandle.getLiveData("TIME_REMAINING", 0L);
|
||||||
keyBackupcurrentToken = Transformations.map(keyBackupcurrentTokenJson, json ->
|
|
||||||
{
|
|
||||||
if (json == null) return null;
|
|
||||||
try {
|
|
||||||
return JsonUtil.fromJson(json, TokenResponse.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tokenResponseCredentialsPair = new LiveDataPair<>(keyBackupcurrentToken, basicStorageCredentials);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T loadValue(@NonNull SavedStateHandle savedStateHandle, @NonNull String key, @NonNull T initialValue) {
|
private static <T> T loadValue(@NonNull SavedStateHandle savedStateHandle, @NonNull String key, @NonNull T initialValue) {
|
||||||
@ -179,26 +161,26 @@ public final class RegistrationViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable TokenResponse getKeyBackupCurrentToken() {
|
public @Nullable TokenResponse getKeyBackupCurrentToken() {
|
||||||
return keyBackupcurrentToken.getValue();
|
String json = keyBackupcurrentTokenJson.getValue();
|
||||||
|
if (json == null) return null;
|
||||||
|
try {
|
||||||
|
return JsonUtil.fromJson(json, TokenResponse.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyBackupCurrentToken(TokenResponse tokenResponse) {
|
public void setKeyBackupCurrentToken(TokenResponse tokenResponse) {
|
||||||
keyBackupcurrentTokenJson.setValue(tokenResponse == null ? null : JsonUtil.toJson(tokenResponse));
|
String json = tokenResponse == null ? null : JsonUtil.toJson(tokenResponse);
|
||||||
|
keyBackupcurrentTokenJson.setValue(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Pair<TokenResponse, String>> getTokenResponseCredentialsPair() {
|
public LiveData<Long> getTimeRemaining() {
|
||||||
return tokenResponseCredentialsPair;
|
return timeRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRegistrationLockFragmentCreate() {
|
public void setTimeRemaining(long timeRemaining) {
|
||||||
SimpleTask.run(() -> {
|
this.timeRemaining.setValue(timeRemaining);
|
||||||
RegistrationService registrationService = RegistrationService.getInstance(getNumber().getE164Number(), getRegistrationSecret());
|
|
||||||
try {
|
|
||||||
return registrationService.getToken(getBasicStorageCredentials());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, this::setKeyBackupCurrentToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,113 +1,95 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:fillViewport="true"
|
|
||||||
tools:context=".registration.fragments.RegistrationLockFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<TextView
|
||||||
|
android:id="@+id/kbs_lock_pin_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp">
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:text="@string/KbsLockFragment__enter_your_pin"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Title1"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/kbs_lock_keyboard_toggle"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.20" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/clarification_label"
|
android:id="@+id/kbs_lock_pin_description"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="32dp"
|
android:layout_marginStart="27dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginEnd="32dp"
|
android:layout_marginEnd="27dp"
|
||||||
android:gravity="center"
|
android:gravity="center_horizontal"
|
||||||
android:text="@string/registration_activity__the_registration_lock_pin_is_not_the_same_as_the_sms_verification_code_you_just_received_please_enter_the_pin_you_previously_configured_in_the_application"
|
android:minHeight="66dp"
|
||||||
android:textColor="#73B7F0"
|
android:text="@string/KbsLockFragment__enter_the_pin_you_created"
|
||||||
android:visibility="invisible"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:textColor="@color/core_grey_60"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_title" />
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/verify_header"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
<EditText
|
||||||
android:id="@+id/verify_header"
|
android:id="@+id/kbs_lock_pin_input"
|
||||||
style="@style/Signal.Text.Headline.Registration"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_gravity="center_horizontal"
|
||||||
android:layout_gravity="center"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginStart="32dp"
|
android:gravity="center_horizontal"
|
||||||
android:layout_marginTop="40dp"
|
android:inputType="numberPassword"
|
||||||
android:layout_marginEnd="32dp"
|
android:minWidth="210dp"
|
||||||
android:gravity="center"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:text="@string/RegistrationActivity_registration_lock_pin"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_description" />
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/verify_subheader"
|
android:id="@+id/kbs_lock_pin_input_label"
|
||||||
style="@style/Signal.Text.Body.Registration"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginStart="32dp"
|
android:gravity="center_horizontal"
|
||||||
android:layout_marginTop="16dp"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_marginEnd="32dp"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:gravity="center"
|
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_input"
|
||||||
android:text="@string/RegistrationActivity_this_phone_number_has_registration_lock_enabled_please_enter_the_registration_lock_pin"
|
tools:text="@string/KbsLockFragment__incorrect_pin_try_again" />
|
||||||
app:layout_constraintTop_toBottomOf="@+id/verify_header"
|
|
||||||
tools:layout_editor_absoluteX="0dp" />
|
|
||||||
|
|
||||||
<TextView
|
<Button
|
||||||
android:id="@+id/forgot_button"
|
android:id="@+id/kbs_lock_forgot_pin"
|
||||||
android:layout_width="wrap_content"
|
style="@style/Widget.AppCompat.Button.Borderless.Colored"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginStart="32dp"
|
||||||
android:padding="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/registration_activity__forgot_pin"
|
android:layout_marginEnd="32dp"
|
||||||
android:textColor="@color/blue_400"
|
android:text="@string/KbsLockFragment__forgot_pin"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:textColor="@color/signal_primary"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/pinButton" />
|
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_input" />
|
||||||
|
|
||||||
<com.dd.CircularProgressButton
|
<Button
|
||||||
android:id="@+id/pinButton"
|
android:id="@+id/kbs_lock_keyboard_toggle"
|
||||||
style="@style/Button.Registration"
|
style="@style/Widget.AppCompat.Button.Borderless.Colored"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="32dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginEnd="32dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:textColor="@color/signal_primary"
|
||||||
app:cpb_textIdle="@string/RegistrationActivity_continue"
|
app:layout_constraintBottom_toTopOf="@id/kbs_lock_pin_confirm"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
tools:text="Create Alphanumeric Pin" />
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textInputLayout" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.dd.CircularProgressButton
|
||||||
android:id="@+id/textInputLayout"
|
android:id="@+id/kbs_lock_pin_confirm"
|
||||||
android:layout_width="match_parent"
|
style="@style/Button.Registration"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_marginStart="32dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginStart="32dp"
|
||||||
android:layout_marginEnd="32dp"
|
android:layout_marginEnd="32dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_marginBottom="16dp"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
app:cpb_textIdle="@string/RegistrationActivity_continue"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/clarification_label">
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
android:id="@+id/pin"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:hint="@string/registration_activity__registration_lock_pin"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="numberPassword" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,95 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/kbs_lock_pin_title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:text="@string/KbsLockFragment__enter_your_pin"
|
|
||||||
android:textAppearance="@style/TextAppearance.Signal.Title1"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/kbs_lock_keyboard_toggle"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.20" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/kbs_lock_pin_description"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="27dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="27dp"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:minHeight="66dp"
|
|
||||||
android:text="@string/KbsLockFragment__enter_the_pin_you_created"
|
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
|
||||||
android:textColor="@color/core_grey_60"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_title" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/kbs_lock_pin_input"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:inputType="numberPassword"
|
|
||||||
android:minWidth="210dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_description" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/kbs_lock_pin_input_label"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_input"
|
|
||||||
tools:text="@string/KbsLockFragment__incorrect_pin_try_again" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/kbs_lock_forgot_pin"
|
|
||||||
style="@style/Widget.AppCompat.Button.Borderless.Colored"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:text="@string/KbsLockFragment__forgot_pin"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_input" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/kbs_lock_keyboard_toggle"
|
|
||||||
style="@style/Widget.AppCompat.Button.Borderless.Colored"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:textColor="@color/signal_primary"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/kbs_lock_pin_confirm"
|
|
||||||
tools:text="Create Alphanumeric Pin" />
|
|
||||||
|
|
||||||
<com.dd.CircularProgressButton
|
|
||||||
android:id="@+id/kbs_lock_pin_confirm"
|
|
||||||
style="@style/Button.Registration"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
app:cpb_textIdle="@string/RegistrationActivity_continue"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -81,19 +81,9 @@
|
|||||||
android:label="fragment_enter_code"
|
android:label="fragment_enter_code"
|
||||||
tools:layout="@layout/fragment_registration_enter_code">
|
tools:layout="@layout/fragment_registration_enter_code">
|
||||||
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_requireRegistrationLockPin"
|
|
||||||
app:destination="@id/registrationLockFragment"
|
|
||||||
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/welcomeFragment"
|
|
||||||
app:popUpToInclusive="true" />
|
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_requireKbsLockPin"
|
android:id="@+id/action_requireKbsLockPin"
|
||||||
app:destination="@id/kbsLockFragment"
|
app:destination="@id/registrationLockFragment"
|
||||||
app:enterAnim="@anim/nav_default_enter_anim"
|
app:enterAnim="@anim/nav_default_enter_anim"
|
||||||
app:exitAnim="@anim/nav_default_exit_anim"
|
app:exitAnim="@anim/nav_default_exit_anim"
|
||||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||||
@ -119,18 +109,9 @@
|
|||||||
app:destination="@id/registrationCompletePlaceHolderFragment"
|
app:destination="@id/registrationCompletePlaceHolderFragment"
|
||||||
app:popUpTo="@+id/welcomeFragment"
|
app:popUpTo="@+id/welcomeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/registrationLockFragment"
|
|
||||||
android:name="org.thoughtcrime.securesms.registration.fragments.RegistrationLockFragment"
|
|
||||||
android:label="fragment_registration_lock"
|
|
||||||
tools:layout="@layout/fragment_registration_lock">
|
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_successfulRegistration"
|
android:id="@+id/action_accountLocked"
|
||||||
app:destination="@id/registrationCompletePlaceHolderFragment"
|
app:destination="@id/accountLockedFragment"
|
||||||
app:enterAnim="@anim/nav_default_enter_anim"
|
app:enterAnim="@anim/nav_default_enter_anim"
|
||||||
app:exitAnim="@anim/nav_default_exit_anim"
|
app:exitAnim="@anim/nav_default_exit_anim"
|
||||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||||
@ -138,17 +119,13 @@
|
|||||||
app:popUpTo="@+id/welcomeFragment"
|
app:popUpTo="@+id/welcomeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
<argument
|
|
||||||
android:name="timeRemaining"
|
|
||||||
app:argType="long" />
|
|
||||||
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/kbsLockFragment"
|
android:id="@+id/registrationLockFragment"
|
||||||
android:name="org.thoughtcrime.securesms.registration.fragments.KbsLockFragment"
|
android:name="org.thoughtcrime.securesms.registration.fragments.RegistrationLockFragment"
|
||||||
android:label="fragment_kbs_lock"
|
android:label="fragment_kbs_lock"
|
||||||
tools:layout="@layout/kbs_lock_fragment">
|
tools:layout="@layout/fragment_registration_lock">
|
||||||
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_successfulRegistration"
|
android:id="@+id/action_successfulRegistration"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user