diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java index 23bc6b2523..0d8213f23b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/RegistrationLockDialog.java @@ -50,6 +50,7 @@ import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.text.AfterTextChanged; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.RegistrationLockData; import org.whispersystems.signalservice.api.kbs.HashedPin; import org.whispersystems.signalservice.api.kbs.MasterKey; @@ -305,7 +306,7 @@ public final class RegistrationLockDialog { TextSecurePreferences.setRegistrationLockLastReminderTime(context, System.currentTimeMillis()); TextSecurePreferences.setRegistrationLockNextReminderInterval(context, RegistrationLockReminders.INITIAL_INTERVAL); return true; - } catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException e) { + } catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException | KeyBackupSystemNoDataException e) { Log.w(TAG, e); return false; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java index fe893f9609..2e9168df67 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinRepository.java @@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.RegistrationLockData; import org.whispersystems.signalservice.api.kbs.HashedPin; import org.whispersystems.signalservice.api.kbs.MasterKey; @@ -59,7 +60,7 @@ final class ConfirmKbsPinRepository { ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.PINS_FOR_ALL); return PinSetResult.SUCCESS; - } catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException e) { + } catch (IOException | UnauthenticatedResponseException | KeyBackupServicePinException | KeyBackupSystemNoDataException e) { Log.w(TAG, e); return PinSetResult.FAILURE; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java index 1003e81b5b..ca72416e34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/RegistrationPinV2MigrationJob.java @@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.RegistrationLockData; import org.whispersystems.signalservice.api.kbs.HashedPin; import org.whispersystems.signalservice.api.kbs.MasterKey; @@ -55,7 +56,7 @@ public final class RegistrationPinV2MigrationJob extends BaseJob { } @Override - protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException { + protected void onRun() throws IOException, UnauthenticatedResponseException, KeyBackupServicePinException, KeyBackupSystemNoDataException { if (!TextSecurePreferences.isV1RegistrationLockEnabled(context)) { Log.i(TAG, "Registration lock disabled"); return; diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/AccountLockedFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/AccountLockedFragment.java index 9254a12bab..14543f52d0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/AccountLockedFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/AccountLockedFragment.java @@ -11,11 +11,12 @@ import android.widget.TextView; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; import org.thoughtcrime.securesms.R; -public class AccountLockedFragment extends Fragment { +import java.util.concurrent.TimeUnit; + +public class AccountLockedFragment extends BaseRegistrationFragment { @Override public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -24,14 +25,16 @@ public class AccountLockedFragment extends Fragment { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - AccountLockedFragmentArgs args = AccountLockedFragmentArgs.fromBundle(requireArguments()); + super.onViewCreated(view, savedInstanceState); TextView description = view.findViewById(R.id.account_locked_description); - description.setText(getString(R.string.AccountLockedFragment__your_account_has_been_locked_to_protect_your_privacy, args.getTimeRemaining())); + getModel().getTimeRemaining().observe(getViewLifecycleOwner(), + t -> description.setText(getString(R.string.AccountLockedFragment__your_account_has_been_locked_to_protect_your_privacy, durationToDays(t))) + ); - view.findViewById(R.id.account_locked_next).setOnClickListener(this::onNextClicked); - view.findViewById(R.id.account_locked_learn_more).setOnClickListener(this::onLearnMoreClicked); + view.findViewById(R.id.account_locked_next).setOnClickListener(v -> onNext()); + view.findViewById(R.id.account_locked_learn_more).setOnClickListener(v -> learnMore()); requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) { @Override @@ -41,16 +44,18 @@ public class AccountLockedFragment extends Fragment { }); } - private void onNextClicked(@NonNull View unused) { - onNext(); + private void learnMore() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(getString(R.string.AccountLockedFragment__learn_more_url))); + startActivity(intent); } - private void onLearnMoreClicked(@NonNull View unused) { - Intent intent = new Intent(Intent.ACTION_VIEW); + private static long durationToDays(Long duration) { + return duration != null ? getLockoutDays(duration) : 7; + } - intent.setData(Uri.parse(getString(R.string.AccountLockedFragment__learn_more_url))); - - startActivity(intent); + private static int getLockoutDays(long timeRemainingMs) { + return (int) TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1; } private void onNext() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterCodeFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterCodeFragment.java index 0641458004..fe7728b7dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterCodeFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/EnterCodeFragment.java @@ -171,11 +171,11 @@ 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); + public void onKbsAccountLocked(@Nullable Long timeRemaining) { + if (timeRemaining != null) { + model.setTimeRemaining(timeRemaining); + } + Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked()); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java index 3b79b78429..b0085fd37d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java @@ -192,7 +192,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment { if (triesRemaining == 0) { Log.w(TAG, "Account locked. User out of attempts on KBS."); - lockAccount(timeRemaining); + onAccountLocked(); return; } @@ -226,10 +226,12 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment { } @Override - public void onKbsAccountLocked(long timeRemaining) { - getModel().setTimeRemaining(timeRemaining); + public void onKbsAccountLocked(@Nullable Long timeRemaining) { + if (timeRemaining != null) { + model.setTimeRemaining(timeRemaining); + } - lockAccount(timeRemaining); + onAccountLocked(); } @Override @@ -254,10 +256,8 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment { return (int) TimeUnit.MILLISECONDS.toDays(timeRemainingMs) + 1; } - private void lockAccount(long timeRemaining) { - RegistrationLockFragmentDirections.ActionAccountLocked action = RegistrationLockFragmentDirections.actionAccountLocked(timeRemaining); - - Navigation.findNavController(requireView()).navigate(action); + private void onAccountLocked() { + Navigation.findNavController(requireView()).navigate(RegistrationLockFragmentDirections.actionAccountLocked()); } private void updateKeyboard(@NonNull PinKeyboardType keyboard) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java b/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java index a2968f2925..9ce60e3769 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/service/CodeVerificationRequest.java @@ -35,6 +35,7 @@ import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.RegistrationLockData; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.kbs.HashedPin; @@ -94,6 +95,9 @@ public final class CodeVerificationRequest { kbsToken = kbsTokenResponse; verifyAccount(context, credentials, code, pin, kbsTokenResponse, basicStorageCredentials, fcmToken); return Result.SUCCESS; + } catch (KeyBackupSystemNoDataException e) { + Log.w(TAG, "No data found on KBS"); + return Result.KBS_ACCOUNT_LOCKED; } catch (KeyBackupSystemWrongPinException e) { kbsToken = e.getTokenResponse(); return Result.KBS_WRONG_PIN; @@ -156,7 +160,7 @@ public final class CodeVerificationRequest { break; case KBS_ACCOUNT_LOCKED: Log.w(TAG, "KBS Account is locked"); - callback.onKbsAccountLocked(lockedException.getTimeRemaining()); + callback.onKbsAccountLocked(lockedException != null ? lockedException.getTimeRemaining() : null); break; } } @@ -184,7 +188,7 @@ public final class CodeVerificationRequest { @Nullable TokenResponse kbsTokenResponse, @Nullable String kbsStorageCredentials, @Nullable String fcmToken) - throws IOException, KeyBackupSystemWrongPinException + throws IOException, KeyBackupSystemWrongPinException, KeyBackupSystemNoDataException { boolean isV2KbsPin = kbsTokenResponse != null; int registrationId = KeyHelper.generateRegistrationId(false); @@ -284,7 +288,7 @@ public final class CodeVerificationRequest { private static @Nullable RegistrationLockData restoreMasterKey(@Nullable String pin, @Nullable String basicStorageCredentials, @NonNull TokenResponse tokenResponse) - throws IOException, KeyBackupSystemWrongPinException + throws IOException, KeyBackupSystemWrongPinException, KeyBackupSystemNoDataException { if (pin == null) return null; @@ -304,7 +308,7 @@ public final class CodeVerificationRequest { if (kbsData != null) { Log.i(TAG, "Found registration lock token on KBS."); } else { - Log.i(TAG, "No KBS data found."); + throw new AssertionError("Null not expected"); } return kbsData; } catch (UnauthenticatedResponseException e) { @@ -340,11 +344,11 @@ public final class CodeVerificationRequest { void onIncorrectKbsRegistrationLockPin(@NonNull TokenResponse kbsTokenResponse); /** - * V2 (KBS) pin is set, but there is no data on KBS + * V2 (KBS) pin is set, but there is no data on KBS. * - * @param timeRemaining Time until pin expires and number can be reused. + * @param timeRemaining Non-null if known. */ - void onKbsAccountLocked(long timeRemaining); + void onKbsAccountLocked(@Nullable Long timeRemaining); void onRateLimited(); diff --git a/app/src/main/res/navigation/registration.xml b/app/src/main/res/navigation/registration.xml index c21d3d6465..01d3343c2b 100644 --- a/app/src/main/res/navigation/registration.xml +++ b/app/src/main/res/navigation/registration.xml @@ -157,12 +157,7 @@ android:id="@+id/accountLockedFragment" android:name="org.thoughtcrime.securesms.registration.fragments.AccountLockedFragment" android:label="fragment_account_locked" - tools:layout="@layout/account_locked_fragment"> - - - + tools:layout="@layout/account_locked_fragment"/>