parent
d0681a5592
commit
220ebf93c7
|
@ -8,9 +8,11 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButton {
|
public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButton {
|
||||||
|
|
||||||
private int countDown;
|
private long countDownToTime;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
|
|
||||||
|
@ -26,9 +28,14 @@ public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButt
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startCountDown(int countDown) {
|
/**
|
||||||
this.countDown = countDown;
|
* Starts a count down to the specified {@param time}.
|
||||||
updateCountDown();
|
*/
|
||||||
|
public void startCountDownTo(long time) {
|
||||||
|
if (time > 0) {
|
||||||
|
this.countDownToTime = time;
|
||||||
|
updateCountDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCallEnabled() {
|
public void setCallEnabled() {
|
||||||
|
@ -38,23 +45,24 @@ public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButt
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCountDown() {
|
private void updateCountDown() {
|
||||||
if (countDown > 0) {
|
final long remainingMillis = countDownToTime - System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (remainingMillis > 0) {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
setAlpha(0.5f);
|
setAlpha(0.5f);
|
||||||
|
|
||||||
countDown--;
|
int totalRemainingSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(remainingMillis);
|
||||||
|
int minutesRemaining = totalRemainingSeconds / 60;
|
||||||
int minutesRemaining = countDown / 60;
|
int secondsRemaining = totalRemainingSeconds % 60;
|
||||||
int secondsRemaining = countDown % 60;
|
|
||||||
|
|
||||||
setText(getResources().getString(R.string.RegistrationActivity_call_me_instead_available_in, minutesRemaining, secondsRemaining));
|
setText(getResources().getString(R.string.RegistrationActivity_call_me_instead_available_in, minutesRemaining, secondsRemaining));
|
||||||
|
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
listener.onRemaining(this, countDown);
|
listener.onRemaining(this, totalRemainingSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
postDelayed(this::updateCountDown, 1000);
|
postDelayed(this::updateCountDown, 250);
|
||||||
} else if (countDown == 0) {
|
} else {
|
||||||
setCallEnabled();
|
setCallEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +72,6 @@ public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButt
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
void onRemaining(@NonNull CallMeCountDownView view, int remaining);
|
void onRemaining(@NonNull CallMeCountDownView view, int secondsRemaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class AccountLockedFragment extends BaseRegistrationFragment {
|
||||||
|
|
||||||
TextView description = view.findViewById(R.id.account_locked_description);
|
TextView description = view.findViewById(R.id.account_locked_description);
|
||||||
|
|
||||||
getModel().getTimeRemaining().observe(getViewLifecycleOwner(),
|
getModel().getLockedTimeRemaining().observe(getViewLifecycleOwner(),
|
||||||
t -> description.setText(getString(R.string.AccountLockedFragment__your_account_has_been_locked_to_protect_your_privacy, durationToDays(t)))
|
t -> description.setText(getString(R.string.AccountLockedFragment__your_account_has_been_locked_to_protect_your_privacy, durationToDays(t)))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -85,12 +85,15 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
||||||
|
|
||||||
noCodeReceivedHelp.setOnClickListener(v -> sendEmailToSupport());
|
noCodeReceivedHelp.setOnClickListener(v -> sendEmailToSupport());
|
||||||
|
|
||||||
getModel().getSuccessfulCodeRequestAttempts().observe(this, (attempts) -> {
|
RegistrationViewModel model = getModel();
|
||||||
|
model.getSuccessfulCodeRequestAttempts().observe(this, (attempts) -> {
|
||||||
if (attempts >= 3) {
|
if (attempts >= 3) {
|
||||||
noCodeReceivedHelp.setVisibility(View.VISIBLE);
|
noCodeReceivedHelp.setVisibility(View.VISIBLE);
|
||||||
scrollView.postDelayed(() -> scrollView.smoothScrollTo(0, noCodeReceivedHelp.getBottom()), 15000);
|
scrollView.postDelayed(() -> scrollView.smoothScrollTo(0, noCodeReceivedHelp.getBottom()), 15000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
model.onStartEnterCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOnCodeFullyEnteredListener(VerificationCodeView verificationCodeView) {
|
private void setOnCodeFullyEnteredListener(VerificationCodeView verificationCodeView) {
|
||||||
|
@ -119,7 +122,7 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
|
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
|
||||||
model.setTimeRemaining(timeRemaining);
|
model.setLockedTimeRemaining(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) {
|
||||||
|
@ -131,7 +134,7 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onKbsRegistrationLockPinRequired(long timeRemaining, @NonNull TokenResponse tokenResponse, @NonNull String kbsStorageCredentials) {
|
public void onKbsRegistrationLockPinRequired(long timeRemaining, @NonNull TokenResponse tokenResponse, @NonNull String kbsStorageCredentials) {
|
||||||
model.setTimeRemaining(timeRemaining);
|
model.setLockedTimeRemaining(timeRemaining);
|
||||||
model.setStorageCredentials(kbsStorageCredentials);
|
model.setStorageCredentials(kbsStorageCredentials);
|
||||||
model.setKeyBackupCurrentToken(tokenResponse);
|
model.setKeyBackupCurrentToken(tokenResponse);
|
||||||
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
|
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
|
@ -170,7 +173,7 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
|
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
|
||||||
if (timeRemaining != null) {
|
if (timeRemaining != null) {
|
||||||
model.setTimeRemaining(timeRemaining);
|
model.setLockedTimeRemaining(timeRemaining);
|
||||||
}
|
}
|
||||||
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked());
|
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked());
|
||||||
}
|
}
|
||||||
|
@ -249,12 +252,12 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePhoneCallRequest() {
|
private void handlePhoneCallRequest() {
|
||||||
callMeCountDown.startCountDown(RegistrationConstants.SUBSEQUENT_CALL_AVAILABLE_AFTER);
|
|
||||||
|
|
||||||
RegistrationViewModel model = getModel();
|
RegistrationViewModel model = getModel();
|
||||||
String captcha = model.getCaptchaToken();
|
String captcha = model.getCaptchaToken();
|
||||||
model.clearCaptchaResponse();
|
model.clearCaptchaResponse();
|
||||||
|
|
||||||
|
model.onCallRequested();
|
||||||
|
|
||||||
NavController navController = Navigation.findNavController(callMeCountDown);
|
NavController navController = Navigation.findNavController(callMeCountDown);
|
||||||
|
|
||||||
RegistrationService registrationService = RegistrationService.getInstance(model.getNumber().getE164Number(), model.getRegistrationSecret());
|
RegistrationService registrationService = RegistrationService.getInstance(model.getNumber().getE164Number(), model.getRegistrationSecret());
|
||||||
|
@ -301,9 +304,10 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
getModel().getLiveNumber().observe(this, (s) -> header.setText(requireContext().getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, s.getFullFormattedNumber())));
|
RegistrationViewModel model = getModel();
|
||||||
|
model.getLiveNumber().observe(this, (s) -> header.setText(requireContext().getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, s.getFullFormattedNumber())));
|
||||||
|
|
||||||
callMeCountDown.startCountDown(RegistrationConstants.FIRST_CALL_AVAILABLE_AFTER);
|
model.getCanCallAtTime().observe(this, callAtTime -> callMeCountDown.startCountDownTo(callAtTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendEmailToSupport() {
|
private void sendEmailToSupport() {
|
||||||
|
|
|
@ -5,11 +5,8 @@ final class RegistrationConstants {
|
||||||
private RegistrationConstants() {
|
private RegistrationConstants() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static final int FIRST_CALL_AVAILABLE_AFTER = 64;
|
static final String TERMS_AND_CONDITIONS_URL = "https://signal.org/legal";
|
||||||
static final int SUBSEQUENT_CALL_AVAILABLE_AFTER = 300;
|
static final String SIGNAL_CAPTCHA_URL = "https://signalcaptchas.org/registration/generate.html";
|
||||||
static final String TERMS_AND_CONDITIONS_URL = "https://signal.org/legal";
|
static final String SIGNAL_CAPTCHA_SCHEME = "signalcaptcha://";
|
||||||
|
|
||||||
static final String SIGNAL_CAPTCHA_URL = "https://signalcaptchas.org/registration/generate.html";
|
|
||||||
static final String SIGNAL_CAPTCHA_SCHEME = "signalcaptcha://";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ 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.FeatureFlags;
|
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||||
|
@ -104,7 +103,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
||||||
PinKeyboardType keyboardType = getPinEntryKeyboardType().getOther();
|
PinKeyboardType keyboardType = getPinEntryKeyboardType().getOther();
|
||||||
keyboardToggle.setText(resolveKeyboardToggleText(keyboardType));
|
keyboardToggle.setText(resolveKeyboardToggleText(keyboardType));
|
||||||
|
|
||||||
getModel().getTimeRemaining()
|
getModel().getLockedTimeRemaining()
|
||||||
.observe(getViewLifecycleOwner(), t -> timeRemaining = t);
|
.observe(getViewLifecycleOwner(), t -> timeRemaining = t);
|
||||||
|
|
||||||
TokenResponse keyBackupCurrentToken = getModel().getKeyBackupCurrentToken();
|
TokenResponse keyBackupCurrentToken = getModel().getKeyBackupCurrentToken();
|
||||||
|
@ -180,7 +179,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
|
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
|
||||||
getModel().setTimeRemaining(timeRemaining);
|
getModel().setLockedTimeRemaining(timeRemaining);
|
||||||
|
|
||||||
cancelSpinning(pinButton);
|
cancelSpinning(pinButton);
|
||||||
pinEntry.getText().clear();
|
pinEntry.getText().clear();
|
||||||
|
@ -243,7 +242,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
|
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
|
||||||
if (timeRemaining != null) {
|
if (timeRemaining != null) {
|
||||||
model.setTimeRemaining(timeRemaining);
|
model.setLockedTimeRemaining(timeRemaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccountLocked();
|
onAccountLocked();
|
||||||
|
|
|
@ -14,11 +14,15 @@ import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public final class RegistrationViewModel extends ViewModel {
|
public final class RegistrationViewModel extends ViewModel {
|
||||||
|
|
||||||
private static final String TAG = Log.tag(RegistrationViewModel.class);
|
private static final String TAG = Log.tag(RegistrationViewModel.class);
|
||||||
|
|
||||||
|
private static final long FIRST_CALL_AVAILABLE_AFTER_MS = TimeUnit.SECONDS.toMillis(64);
|
||||||
|
private static final long SUBSEQUENT_CALL_AVAILABLE_AFTER_MS = TimeUnit.SECONDS.toMillis(300);
|
||||||
|
|
||||||
private final String secret;
|
private final String secret;
|
||||||
private final MutableLiveData<NumberViewState> number;
|
private final MutableLiveData<NumberViewState> number;
|
||||||
private final MutableLiveData<String> textCodeEntered;
|
private final MutableLiveData<String> textCodeEntered;
|
||||||
|
@ -28,8 +32,9 @@ public final class RegistrationViewModel extends ViewModel {
|
||||||
private final MutableLiveData<Boolean> restoreFlowShown;
|
private final MutableLiveData<Boolean> restoreFlowShown;
|
||||||
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 MutableLiveData<Long> timeRemaining;
|
private final MutableLiveData<Long> lockedTimeRemaining;
|
||||||
|
private final MutableLiveData<Long> canCallAtTime;
|
||||||
|
|
||||||
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));
|
||||||
|
@ -42,8 +47,9 @@ public final class RegistrationViewModel extends ViewModel {
|
||||||
restoreFlowShown = savedStateHandle.getLiveData("RESTORE_FLOW_SHOWN", false);
|
restoreFlowShown = savedStateHandle.getLiveData("RESTORE_FLOW_SHOWN", false);
|
||||||
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);
|
lockedTimeRemaining = savedStateHandle.getLiveData("TIME_REMAINING", 0L);
|
||||||
|
canCallAtTime = savedStateHandle.getLiveData("CAN_CALL_AT_TIME", 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -161,7 +167,7 @@ public final class RegistrationViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable TokenResponse getKeyBackupCurrentToken() {
|
public @Nullable TokenResponse getKeyBackupCurrentToken() {
|
||||||
String json = keyBackupcurrentTokenJson.getValue();
|
String json = keyBackupCurrentTokenJson.getValue();
|
||||||
if (json == null) return null;
|
if (json == null) return null;
|
||||||
try {
|
try {
|
||||||
return JsonUtil.fromJson(json, TokenResponse.class);
|
return JsonUtil.fromJson(json, TokenResponse.class);
|
||||||
|
@ -173,14 +179,26 @@ public final class RegistrationViewModel extends ViewModel {
|
||||||
|
|
||||||
public void setKeyBackupCurrentToken(TokenResponse tokenResponse) {
|
public void setKeyBackupCurrentToken(TokenResponse tokenResponse) {
|
||||||
String json = tokenResponse == null ? null : JsonUtil.toJson(tokenResponse);
|
String json = tokenResponse == null ? null : JsonUtil.toJson(tokenResponse);
|
||||||
keyBackupcurrentTokenJson.setValue(json);
|
keyBackupCurrentTokenJson.setValue(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Long> getTimeRemaining() {
|
public LiveData<Long> getLockedTimeRemaining() {
|
||||||
return timeRemaining;
|
return lockedTimeRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeRemaining(long timeRemaining) {
|
public LiveData<Long> getCanCallAtTime() {
|
||||||
this.timeRemaining.setValue(timeRemaining);
|
return canCallAtTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLockedTimeRemaining(long lockedTimeRemaining) {
|
||||||
|
this.lockedTimeRemaining.setValue(lockedTimeRemaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStartEnterCode() {
|
||||||
|
canCallAtTime.setValue(System.currentTimeMillis() + FIRST_CALL_AVAILABLE_AFTER_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCallRequested() {
|
||||||
|
canCallAtTime.setValue(System.currentTimeMillis() + SUBSEQUENT_CALL_AVAILABLE_AFTER_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue