During registration, persist time that call me is available.

Fixes #9926
master
Alan Evans 2020-08-19 12:40:26 -03:00 committed by Greyson Parrelli
parent d0681a5592
commit 220ebf93c7
6 changed files with 68 additions and 42 deletions

View File

@ -8,9 +8,11 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import java.util.concurrent.TimeUnit;
public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButton {
private int countDown;
private long countDownToTime;
@Nullable
private Listener listener;
@ -26,9 +28,14 @@ public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButt
super(context, attrs, defStyleAttr);
}
public void startCountDown(int countDown) {
this.countDown = countDown;
updateCountDown();
/**
* Starts a count down to the specified {@param time}.
*/
public void startCountDownTo(long time) {
if (time > 0) {
this.countDownToTime = time;
updateCountDown();
}
}
public void setCallEnabled() {
@ -38,23 +45,24 @@ public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButt
}
private void updateCountDown() {
if (countDown > 0) {
final long remainingMillis = countDownToTime - System.currentTimeMillis();
if (remainingMillis > 0) {
setEnabled(false);
setAlpha(0.5f);
countDown--;
int minutesRemaining = countDown / 60;
int secondsRemaining = countDown % 60;
int totalRemainingSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(remainingMillis);
int minutesRemaining = totalRemainingSeconds / 60;
int secondsRemaining = totalRemainingSeconds % 60;
setText(getResources().getString(R.string.RegistrationActivity_call_me_instead_available_in, minutesRemaining, secondsRemaining));
if (listener != null) {
listener.onRemaining(this, countDown);
listener.onRemaining(this, totalRemainingSeconds);
}
postDelayed(this::updateCountDown, 1000);
} else if (countDown == 0) {
postDelayed(this::updateCountDown, 250);
} else {
setCallEnabled();
}
}
@ -64,6 +72,6 @@ public class CallMeCountDownView extends androidx.appcompat.widget.AppCompatButt
}
public interface Listener {
void onRemaining(@NonNull CallMeCountDownView view, int remaining);
void onRemaining(@NonNull CallMeCountDownView view, int secondsRemaining);
}
}

View File

@ -29,7 +29,7 @@ public class AccountLockedFragment extends BaseRegistrationFragment {
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)))
);

View File

@ -85,12 +85,15 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
noCodeReceivedHelp.setOnClickListener(v -> sendEmailToSupport());
getModel().getSuccessfulCodeRequestAttempts().observe(this, (attempts) -> {
RegistrationViewModel model = getModel();
model.getSuccessfulCodeRequestAttempts().observe(this, (attempts) -> {
if (attempts >= 3) {
noCodeReceivedHelp.setVisibility(View.VISIBLE);
scrollView.postDelayed(() -> scrollView.smoothScrollTo(0, noCodeReceivedHelp.getBottom()), 15000);
}
});
model.onStartEnterCode();
}
private void setOnCodeFullyEnteredListener(VerificationCodeView verificationCodeView) {
@ -119,7 +122,7 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
@Override
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
model.setTimeRemaining(timeRemaining);
model.setLockedTimeRemaining(timeRemaining);
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean r) {
@ -131,7 +134,7 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
@Override
public void onKbsRegistrationLockPinRequired(long timeRemaining, @NonNull TokenResponse tokenResponse, @NonNull String kbsStorageCredentials) {
model.setTimeRemaining(timeRemaining);
model.setLockedTimeRemaining(timeRemaining);
model.setStorageCredentials(kbsStorageCredentials);
model.setKeyBackupCurrentToken(tokenResponse);
keyboard.displayLocked().addListener(new AssertedSuccessListener<Boolean>() {
@ -170,7 +173,7 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
@Override
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
if (timeRemaining != null) {
model.setTimeRemaining(timeRemaining);
model.setLockedTimeRemaining(timeRemaining);
}
Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked());
}
@ -249,12 +252,12 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
}
private void handlePhoneCallRequest() {
callMeCountDown.startCountDown(RegistrationConstants.SUBSEQUENT_CALL_AVAILABLE_AFTER);
RegistrationViewModel model = getModel();
String captcha = model.getCaptchaToken();
model.clearCaptchaResponse();
model.onCallRequested();
NavController navController = Navigation.findNavController(callMeCountDown);
RegistrationService registrationService = RegistrationService.getInstance(model.getNumber().getE164Number(), model.getRegistrationSecret());
@ -301,9 +304,10 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
public void 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() {

View File

@ -5,11 +5,8 @@ final class RegistrationConstants {
private RegistrationConstants() {
}
static final int FIRST_CALL_AVAILABLE_AFTER = 64;
static final int SUBSEQUENT_CALL_AVAILABLE_AFTER = 300;
static final String TERMS_AND_CONDITIONS_URL = "https://signal.org/legal";
static final String SIGNAL_CAPTCHA_URL = "https://signalcaptchas.org/registration/generate.html";
static final String SIGNAL_CAPTCHA_SCHEME = "signalcaptcha://";
static final String TERMS_AND_CONDITIONS_URL = "https://signal.org/legal";
static final String SIGNAL_CAPTCHA_URL = "https://signalcaptchas.org/registration/generate.html";
static final String SIGNAL_CAPTCHA_SCHEME = "signalcaptcha://";
}

View File

@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.registration.service.CodeVerificationRequest;
import org.thoughtcrime.securesms.registration.service.RegistrationService;
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
@ -104,7 +103,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
PinKeyboardType keyboardType = getPinEntryKeyboardType().getOther();
keyboardToggle.setText(resolveKeyboardToggleText(keyboardType));
getModel().getTimeRemaining()
getModel().getLockedTimeRemaining()
.observe(getViewLifecycleOwner(), t -> timeRemaining = t);
TokenResponse keyBackupCurrentToken = getModel().getKeyBackupCurrentToken();
@ -180,7 +179,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
@Override
public void onV1RegistrationLockPinRequiredOrIncorrect(long timeRemaining) {
getModel().setTimeRemaining(timeRemaining);
getModel().setLockedTimeRemaining(timeRemaining);
cancelSpinning(pinButton);
pinEntry.getText().clear();
@ -243,7 +242,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment {
@Override
public void onKbsAccountLocked(@Nullable Long timeRemaining) {
if (timeRemaining != null) {
model.setTimeRemaining(timeRemaining);
model.setLockedTimeRemaining(timeRemaining);
}
onAccountLocked();

View File

@ -14,11 +14,15 @@ import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse
import org.whispersystems.signalservice.internal.util.JsonUtil;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public final class RegistrationViewModel extends ViewModel {
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 MutableLiveData<NumberViewState> number;
private final MutableLiveData<String> textCodeEntered;
@ -28,8 +32,9 @@ public final class RegistrationViewModel extends ViewModel {
private final MutableLiveData<Boolean> restoreFlowShown;
private final MutableLiveData<Integer> successfulCodeRequestAttempts;
private final MutableLiveData<LocalCodeRequestRateLimiter> requestLimiter;
private final MutableLiveData<String> keyBackupcurrentTokenJson;
private final MutableLiveData<Long> timeRemaining;
private final MutableLiveData<String> keyBackupCurrentTokenJson;
private final MutableLiveData<Long> lockedTimeRemaining;
private final MutableLiveData<Long> canCallAtTime;
public RegistrationViewModel(@NonNull SavedStateHandle savedStateHandle) {
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);
successfulCodeRequestAttempts = savedStateHandle.getLiveData("SUCCESSFUL_CODE_REQUEST_ATTEMPTS", 0);
requestLimiter = savedStateHandle.getLiveData("REQUEST_RATE_LIMITER", new LocalCodeRequestRateLimiter(60_000));
keyBackupcurrentTokenJson = savedStateHandle.getLiveData("KBS_TOKEN");
timeRemaining = savedStateHandle.getLiveData("TIME_REMAINING", 0L);
keyBackupCurrentTokenJson = savedStateHandle.getLiveData("KBS_TOKEN");
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) {
@ -161,7 +167,7 @@ public final class RegistrationViewModel extends ViewModel {
}
public @Nullable TokenResponse getKeyBackupCurrentToken() {
String json = keyBackupcurrentTokenJson.getValue();
String json = keyBackupCurrentTokenJson.getValue();
if (json == null) return null;
try {
return JsonUtil.fromJson(json, TokenResponse.class);
@ -173,14 +179,26 @@ public final class RegistrationViewModel extends ViewModel {
public void setKeyBackupCurrentToken(TokenResponse tokenResponse) {
String json = tokenResponse == null ? null : JsonUtil.toJson(tokenResponse);
keyBackupcurrentTokenJson.setValue(json);
keyBackupCurrentTokenJson.setValue(json);
}
public LiveData<Long> getTimeRemaining() {
return timeRemaining;
public LiveData<Long> getLockedTimeRemaining() {
return lockedTimeRemaining;
}
public void setTimeRemaining(long timeRemaining) {
this.timeRemaining.setValue(timeRemaining);
public LiveData<Long> getCanCallAtTime() {
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);
}
}