diff --git a/app/src/main/java/org/thoughtcrime/securesms/delete/Country.java b/app/src/main/java/org/thoughtcrime/securesms/delete/Country.java index a785e633f7..bb83907baa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/delete/Country.java +++ b/app/src/main/java/org/thoughtcrime/securesms/delete/Country.java @@ -8,11 +8,13 @@ final class Country { private final String displayName; private final int code; private final String normalized; + private final String region; - Country(@NonNull String displayName, int code) { + Country(@NonNull String displayName, int code, @NonNull String region) { this.displayName = displayName; this.code = code; this.normalized = displayName.toLowerCase(); + this.region = region; } int getCode() { @@ -27,6 +29,10 @@ final class Country { return normalized; } + @NonNull String getRegion() { + return region; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountCountryPickerFragment.java b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountCountryPickerFragment.java index 9dd0655801..41e173b7b4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountCountryPickerFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountCountryPickerFragment.java @@ -16,7 +16,6 @@ import androidx.lifecycle.ViewModelProviders; import androidx.recyclerview.widget.RecyclerView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.text.AfterTextChanged; public class DeleteAccountCountryPickerFragment extends DialogFragment { @@ -61,7 +60,7 @@ public class DeleteAccountCountryPickerFragment extends DialogFragment { } private void onCountryPicked(@NonNull Country country) { - viewModel.onCountrySelected(country.getDisplayName(), country.getCode()); + viewModel.onRegionSelected(country.getRegion()); dismissAllowingStateLoss(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountFragment.java b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountFragment.java index 7426e10170..43832e7e28 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountFragment.java @@ -4,6 +4,7 @@ import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; @@ -64,8 +65,7 @@ public class DeleteAccountFragment extends Fragment { viewModel = ViewModelProviders.of(requireActivity(), new DeleteAccountViewModel.Factory(new DeleteAccountRepository())) .get(DeleteAccountViewModel.class); viewModel.getCountryDisplayName().observe(getViewLifecycleOwner(), this::setCountryDisplay); - viewModel.getRegionCode().observe(getViewLifecycleOwner(), this::setCountryFormatter); - viewModel.getCountryCode().observe(getViewLifecycleOwner(), this::setCountryCode); + viewModel.getRegionCode().observe(getViewLifecycleOwner(), this::handleRegionUpdated); viewModel.getEvents().observe(getViewLifecycleOwner(), this::handleEvent); initializeNumberInput(); @@ -87,9 +87,7 @@ public class DeleteAccountFragment extends Fragment { private @NonNull CharSequence buildBulletsText() { return new SpannableStringBuilder().append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_your_account_info_and_profile_photo))) .append("\n") - .append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_all_your_messages))) - .append("\n") - .append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__remove_you_from_all_signal_groups))); + .append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_all_your_messages))); } @SuppressLint("ClickableViewAccessibility") @@ -114,13 +112,10 @@ public class DeleteAccountFragment extends Fragment { } private void pickCountry() { + countryCode.clearFocus(); DeleteAccountCountryPickerFragment.show(requireFragmentManager()); } - private void setCountryCode(int countryCode) { - this.countryCode.setText(String.valueOf(countryCode)); - } - private void setCountryDisplay(@NonNull String regionDisplayName) { countrySpinnerAdapter.clear(); if (TextUtils.isEmpty(regionDisplayName)) { @@ -130,18 +125,20 @@ public class DeleteAccountFragment extends Fragment { } } - private void setCountryFormatter(@Nullable String regionCode) { + private void handleRegionUpdated(@Nullable String regionCode) { PhoneNumberUtil util = PhoneNumberUtil.getInstance(); countryFormatter = regionCode != null ? util.getAsYouTypeFormatter(regionCode) : null; reformatText(number.getText()); - if (!TextUtils.isEmpty(regionCode) && !regionCode.equals("ZZ")) { + if (!TextUtils.isEmpty(regionCode) && !"ZZ".equals(regionCode) && !countryCode.hasFocus()) { number.requestFocus(); int numberLength = number.getText().length(); number.getInput().setSelection(numberLength, numberLength); + + countryCode.setText(String.valueOf(util.getCountryCodeForRegion(regionCode))); } } @@ -202,11 +199,11 @@ public class DeleteAccountFragment extends Fragment { private void afterCountryCodeChanged(@Nullable Editable s) { if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) { - viewModel.onCountrySelected(null, 0); + viewModel.onCountrySelected(0); return; } - viewModel.onCountrySelected(null, Integer.parseInt(s.toString())); + viewModel.onCountrySelected(Integer.parseInt(s.toString())); } private void afterNumberChanged(@Nullable Editable s) { @@ -220,10 +217,10 @@ public class DeleteAccountFragment extends Fragment { private void handleEvent(@NonNull DeleteAccountViewModel.EventType eventType) { switch (eventType) { case NO_COUNTRY_CODE: - Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_country_code, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_country_code, Snackbar.LENGTH_SHORT).setTextColor(Color.WHITE).show(); break; case NO_NATIONAL_NUMBER: - Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_number, Snackbar.LENGTH_SHORT).show(); + Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_number, Snackbar.LENGTH_SHORT).setTextColor(Color.WHITE).show(); break; case NOT_A_MATCH: new AlertDialog.Builder(requireContext()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java index 393796a497..a8c9044876 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java @@ -28,6 +28,14 @@ class DeleteAccountRepository { .toList(); } + @NonNull String getRegionDisplayName(@NonNull String region) { + return PhoneNumberFormatter.getRegionDisplayName(region); + } + + int getRegionCountryCode(@NonNull String region) { + return PhoneNumberUtil.getInstance().getCountryCodeForRegion(region); + } + void deleteAccount(@NonNull Runnable onFailureToRemovePin, @NonNull Runnable onFailureToDeleteFromService, @NonNull Runnable onFailureToDeleteLocalData) @@ -66,7 +74,8 @@ class DeleteAccountRepository { private static @NonNull Country getCountryForRegion(@NonNull String region) { return new Country(PhoneNumberFormatter.getRegionDisplayName(region), - PhoneNumberUtil.getInstance().getCountryCodeForRegion(region)); + PhoneNumberUtil.getInstance().getCountryCodeForRegion(region), + region); } private static class RegionComparator implements Comparator { diff --git a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountViewModel.java index 80622383d2..e59ea09fc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountViewModel.java @@ -11,14 +11,15 @@ import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import com.annimon.stream.Stream; +import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber; +import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState; import org.thoughtcrime.securesms.util.DefaultValueLiveData; import org.thoughtcrime.securesms.util.SingleLiveEvent; -import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.util.List; @@ -27,30 +28,21 @@ public class DeleteAccountViewModel extends ViewModel { private final DeleteAccountRepository repository; private final List allCountries; private final LiveData> filteredCountries; - private final LiveData regionCode; - private final MutableLiveData countryCode; - private final MutableLiveData countryDisplayName; + private final MutableLiveData regionCode; + private final LiveData countryDisplayName; private final MutableLiveData nationalNumber; private final MutableLiveData query; private final SingleLiveEvent events; - private final LiveData numberViewState; public DeleteAccountViewModel(@NonNull DeleteAccountRepository repository) { this.repository = repository; this.allCountries = repository.getAllCountries(); - this.countryCode = new DefaultValueLiveData<>(NumberViewState.INITIAL.getCountryCode()); - this.nationalNumber = new DefaultValueLiveData<>(NumberViewState.INITIAL.getNationalNumber()); - this.countryDisplayName = new DefaultValueLiveData<>(NumberViewState.INITIAL.getCountryDisplayName()); + this.regionCode = new DefaultValueLiveData<>(PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY); + this.nationalNumber = new MutableLiveData<>(); this.query = new DefaultValueLiveData<>(""); - this.regionCode = Transformations.map(countryCode, this::mapCountryCodeToRegionCode); + this.countryDisplayName = Transformations.map(regionCode, repository::getRegionDisplayName); this.filteredCountries = Transformations.map(query, q -> Stream.of(allCountries).filter(country -> isMatch(q, country)).toList()); - - LiveData partialViewState = LiveDataUtil.combineLatest(countryCode, - countryDisplayName, - DeleteAccountViewModel::getPartialNumberViewState); - - this.numberViewState = LiveDataUtil.combineLatest(partialViewState, nationalNumber, DeleteAccountViewModel::getCompleteNumberViewState); - this.events = new SingleLiveEvent<>(); + this.events = new SingleLiveEvent<>(); } @NonNull LiveData> getFilteredCountries() { @@ -58,28 +50,19 @@ public class DeleteAccountViewModel extends ViewModel { } @NonNull LiveData getCountryDisplayName() { - return Transformations.distinctUntilChanged(Transformations.map(numberViewState, NumberViewState::getCountryDisplayName)); + return Transformations.distinctUntilChanged(countryDisplayName); } @NonNull LiveData getRegionCode() { return Transformations.distinctUntilChanged(regionCode); } - @NonNull LiveData getCountryCode() { - return Transformations.distinctUntilChanged(Transformations.map(numberViewState, NumberViewState::getCountryCode)); - } - @NonNull SingleLiveEvent getEvents() { return events; } @Nullable Long getNationalNumber() { - Long number = nationalNumber.getValue(); - if (number == null || number == NumberViewState.INITIAL.getNationalNumber()) { - return null; - } else { - return number; - } + return nationalNumber.getValue(); } void onQueryChanged(@NonNull String query) { @@ -93,7 +76,8 @@ public class DeleteAccountViewModel extends ViewModel { } void submit() { - Integer countryCode = this.countryCode.getValue(); + String region = this.regionCode.getValue(); + Integer countryCode = region != null ? repository.getRegionCountryCode(region) : null; Long nationalNumber = this.nationalNumber.getValue(); if (countryCode == null || countryCode == 0) { @@ -117,28 +101,31 @@ public class DeleteAccountViewModel extends ViewModel { } } - void onCountrySelected(@Nullable String countryDisplayName, int countryCode) { - if (countryDisplayName != null) { - this.countryDisplayName.setValue(countryDisplayName); - } + void onCountrySelected(int countryCode) { + String region = this.regionCode.getValue(); + List regions = PhoneNumberUtil.getInstance().getRegionCodesForCountryCode(countryCode); - this.countryCode.setValue(countryCode); + if (!regions.contains(region)) { + this.regionCode.setValue(PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(countryCode)); + } + } + + void onRegionSelected(@NonNull String region) { + this.regionCode.setValue(region); } void setNationalNumber(long nationalNumber) { this.nationalNumber.setValue(nationalNumber); - } - private @NonNull String mapCountryCodeToRegionCode(int countryCode) { - return PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(countryCode); - } - - private static @NonNull NumberViewState getPartialNumberViewState(int countryCode, @Nullable String countryDisplayName) { - return new NumberViewState.Builder().countryCode(countryCode).selectedCountryDisplayName(countryDisplayName).build(); - } - - private static @NonNull NumberViewState getCompleteNumberViewState(@NonNull NumberViewState partial, long nationalNumber) { - return partial.toBuilder().nationalNumber(nationalNumber).build(); + try { + String phoneNumberRegion = PhoneNumberUtil.getInstance() + .getRegionCodeForNumber(PhoneNumberUtil.getInstance().parse(String.valueOf(nationalNumber), + regionCode.getValue())); + if (phoneNumberRegion != null) { + regionCode.setValue(phoneNumberRegion); + } + } catch (NumberParseException ignored) { + } } private static boolean isMatch(@NonNull String query, @NonNull Country country) { diff --git a/app/src/main/res/layout/delete_account_fragment.xml b/app/src/main/res/layout/delete_account_fragment.xml index 701fdf7dd5..433eb5791d 100644 --- a/app/src/main/res/layout/delete_account_fragment.xml +++ b/app/src/main/res/layout/delete_account_fragment.xml @@ -1,123 +1,129 @@ - + android:layout_height="match_parent" + android:fillViewport="true"> - - - - - + android:layout_height="wrap_content"> - + - - - + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:text="@string/DeleteAccountFragment__deleting_your_account_will" + android:textAppearance="@style/Signal.Text.Body" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_warning" /> - - - - - + android:layout_marginStart="16dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="16dp" + android:textAppearance="@style/TextAppearance.Signal.Body2" + app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_notice" + tools:text="Some\nbullets\nhere" /> - - + android:layout_marginTop="30dp" + android:text="@string/DeleteAccountFragment__enter_your_phone_number" + android:textAppearance="@style/TextAppearance.Signal.Body1.Bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_bullets" /> - + \ No newline at end of file + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/delete_account_fragment_enter_phone_number"> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f3dd3866f..5786554bf8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2994,7 +2994,6 @@ Delete account Delete your account info and profile photo Delete all your messages - Remove you from all Signal groups No country code specified No number specified The phone number you entered doesn\'t match your account\'s.