mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 12:38:33 +00:00
Update country picker for findBy and changeNumber.
This commit is contained in:
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.changenumber;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SimpleAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.loaders.CountryListLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ChangeNumberCountryPickerFragment extends ListFragment implements LoaderManager.LoaderCallbacks<ArrayList<Map<String, String>>> {
|
||||
|
||||
public static final String KEY_COUNTRY = "country";
|
||||
public static final String KEY_COUNTRY_CODE = "country_code";
|
||||
|
||||
private EditText countryFilter;
|
||||
private ChangeNumberViewModel model;
|
||||
private String resultKey;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
return inflater.inflate(R.layout.fragment_registration_country_picker, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
if (getArguments() != null) {
|
||||
ChangeNumberCountryPickerFragmentArgs arguments = ChangeNumberCountryPickerFragmentArgs.fromBundle(requireArguments());
|
||||
resultKey = arguments.getResultKey();
|
||||
}
|
||||
|
||||
if (resultKey == null) {
|
||||
model = new ViewModelProvider(requireActivity()).get(ChangeNumberViewModel.class);
|
||||
}
|
||||
|
||||
countryFilter = view.findViewById(R.id.country_search);
|
||||
|
||||
countryFilter.addTextChangedListener(new FilterWatcher());
|
||||
LoaderManager.getInstance(this).initLoader(0, null, this).forceLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(@NonNull ListView listView, @NonNull View view, int position, long id) {
|
||||
Map<String, String> item = (Map<String, String>) getListAdapter().getItem(position);
|
||||
|
||||
int countryCode = Integer.parseInt(item.get("country_code").replace("+", ""));
|
||||
String countryName = item.get("country_name");
|
||||
|
||||
if (resultKey == null) {
|
||||
model.setNewCountry(countryCode, countryName);
|
||||
} else {
|
||||
Bundle result = new Bundle();
|
||||
result.putString(KEY_COUNTRY, countryName);
|
||||
result.putInt(KEY_COUNTRY_CODE, countryCode);
|
||||
getParentFragmentManager().setFragmentResult(resultKey, result);
|
||||
}
|
||||
|
||||
NavHostFragment.findNavController(this).navigateUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Loader<ArrayList<Map<String, String>>> onCreateLoader(int id, @Nullable Bundle args) {
|
||||
return new CountryListLoader(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<ArrayList<Map<String, String>>> loader,
|
||||
@NonNull ArrayList<Map<String, String>> results)
|
||||
{
|
||||
((TextView) getListView().getEmptyView()).setText(
|
||||
R.string.country_selection_fragment__no_matching_countries);
|
||||
String[] from = { "country_name", "country_code" };
|
||||
int[] to = { R.id.country_name, R.id.country_code };
|
||||
|
||||
setListAdapter(new SimpleAdapter(getActivity(), results, R.layout.country_list_item, from, to));
|
||||
|
||||
applyFilter(countryFilter.getText());
|
||||
}
|
||||
|
||||
private void applyFilter(@NonNull CharSequence text) {
|
||||
SimpleAdapter listAdapter = (SimpleAdapter) getListAdapter();
|
||||
|
||||
if (listAdapter != null) {
|
||||
listAdapter.getFilter().filter(text);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<ArrayList<Map<String, String>>> loader) {
|
||||
setListAdapter(null);
|
||||
}
|
||||
|
||||
private class FilterWatcher implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
applyFilter(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,15 @@ import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.signal.core.util.getParcelableCompat
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.LoggingFragment
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.ViewBinderDelegate
|
||||
import org.thoughtcrime.securesms.databinding.FragmentChangeNumberEnterPhoneNumberBinding
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
import org.thoughtcrime.securesms.registration.util.ChangeNumberInputController
|
||||
import org.thoughtcrime.securesms.registrationv3.ui.countrycode.CountryCodeFragment
|
||||
import org.thoughtcrime.securesms.util.Dialogs
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
@@ -65,15 +68,15 @@ class ChangeNumberEnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_c
|
||||
override fun onNumberInputDone(view: View) = Unit
|
||||
|
||||
override fun onPickCountry(view: View) {
|
||||
findNavController().safeNavigate(ChangeNumberEnterPhoneNumberFragmentDirections.actionEnterPhoneNumberChangeFragmentToCountryPickerFragment(OLD_NUMBER_COUNTRY_SELECT))
|
||||
findNavController().safeNavigate(ChangeNumberEnterPhoneNumberFragmentDirections.actionEnterPhoneNumberChangeFragmentToCountryPickerFragment(OLD_NUMBER_COUNTRY_SELECT, viewModel.oldCountry))
|
||||
}
|
||||
|
||||
override fun setNationalNumber(number: String) {
|
||||
viewModel.setOldNationalNumber(number)
|
||||
}
|
||||
|
||||
override fun setCountry(countryCode: Int) {
|
||||
viewModel.setOldCountry(countryCode)
|
||||
override fun setCountry(country: Country) {
|
||||
viewModel.setOldCountry(country)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -96,25 +99,27 @@ class ChangeNumberEnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_c
|
||||
}
|
||||
|
||||
override fun onPickCountry(view: View) {
|
||||
findNavController().safeNavigate(ChangeNumberEnterPhoneNumberFragmentDirections.actionEnterPhoneNumberChangeFragmentToCountryPickerFragment(NEW_NUMBER_COUNTRY_SELECT))
|
||||
findNavController().safeNavigate(ChangeNumberEnterPhoneNumberFragmentDirections.actionEnterPhoneNumberChangeFragmentToCountryPickerFragment(NEW_NUMBER_COUNTRY_SELECT, viewModel.newCountry))
|
||||
}
|
||||
|
||||
override fun setNationalNumber(number: String) {
|
||||
viewModel.setNewNationalNumber(number)
|
||||
}
|
||||
|
||||
override fun setCountry(countryCode: Int) {
|
||||
viewModel.setNewCountry(countryCode)
|
||||
override fun setCountry(country: Country) {
|
||||
viewModel.setNewCountry(country)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
parentFragmentManager.setFragmentResultListener(OLD_NUMBER_COUNTRY_SELECT, this) { _: String, bundle: Bundle ->
|
||||
viewModel.setOldCountry(bundle.getInt(ChangeNumberCountryPickerFragment.KEY_COUNTRY_CODE), bundle.getString(ChangeNumberCountryPickerFragment.KEY_COUNTRY))
|
||||
val country = bundle.getParcelableCompat(CountryCodeFragment.RESULT_COUNTRY, Country::class.java)!!
|
||||
viewModel.setOldCountry(country)
|
||||
}
|
||||
|
||||
parentFragmentManager.setFragmentResultListener(NEW_NUMBER_COUNTRY_SELECT, this) { _: String, bundle: Bundle ->
|
||||
viewModel.setNewCountry(bundle.getInt(ChangeNumberCountryPickerFragment.KEY_COUNTRY_CODE), bundle.getString(ChangeNumberCountryPickerFragment.KEY_COUNTRY))
|
||||
val country = bundle.getParcelableCompat(CountryCodeFragment.RESULT_COUNTRY, Country::class.java)!!
|
||||
viewModel.setNewCountry(country)
|
||||
}
|
||||
|
||||
viewModel.liveOldNumberState.observe(viewLifecycleOwner, oldController::updateNumber)
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.components.settings.app.changenumber
|
||||
|
||||
import org.thoughtcrime.securesms.registration.data.network.Challenge
|
||||
import org.thoughtcrime.securesms.registration.data.network.VerificationCodeRequestResult
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState
|
||||
import org.whispersystems.signalservice.api.svr.Svr3Credentials
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||
@@ -32,7 +33,9 @@ data class ChangeNumberState(
|
||||
val captchaToken: String? = null,
|
||||
val challengesRequested: List<Challenge> = emptyList(),
|
||||
val challengesPresented: Set<Challenge> = emptySet(),
|
||||
val allowedToRequestCode: Boolean = false
|
||||
val allowedToRequestCode: Boolean = false,
|
||||
val oldCountry: Country? = null,
|
||||
val newCountry: Country? = null
|
||||
) {
|
||||
val challengesRemaining: List<Challenge> = challengesRequested.filterNot { it in challengesPresented }
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.registration.data.network.SessionMetadataResul
|
||||
import org.thoughtcrime.securesms.registration.data.network.VerificationCodeRequestResult
|
||||
import org.thoughtcrime.securesms.registration.sms.SmsRetrieverReceiver
|
||||
import org.thoughtcrime.securesms.registration.ui.RegistrationViewModel
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.SvrAuthCredentialSet
|
||||
import org.thoughtcrime.securesms.util.dualsim.MccMncProducer
|
||||
@@ -100,15 +101,24 @@ class ChangeNumberViewModel : ViewModel() {
|
||||
val svrTriesRemaining: Int
|
||||
get() = store.value.svrTriesRemaining
|
||||
|
||||
val oldCountry: Country?
|
||||
get() = store.value.oldCountry
|
||||
|
||||
val newCountry: Country?
|
||||
get() = store.value.newCountry
|
||||
|
||||
fun setOldNationalNumber(updatedNumber: String) {
|
||||
store.update {
|
||||
it.copy(oldPhoneNumber = oldNumberState.toBuilder().nationalNumber(updatedNumber).build())
|
||||
}
|
||||
}
|
||||
|
||||
fun setOldCountry(countryCode: Int, country: String? = null) {
|
||||
fun setOldCountry(country: Country) {
|
||||
store.update {
|
||||
it.copy(oldPhoneNumber = oldNumberState.toBuilder().selectedCountryDisplayName(country).countryCode(countryCode).build())
|
||||
it.copy(
|
||||
oldPhoneNumber = oldNumberState.toBuilder().selectedCountryDisplayName(country.name).countryCode(country.countryCode).build(),
|
||||
oldCountry = country
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +128,12 @@ class ChangeNumberViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun setNewCountry(countryCode: Int, country: String? = null) {
|
||||
fun setNewCountry(country: Country) {
|
||||
store.update {
|
||||
it.copy(number = number.toBuilder().selectedCountryDisplayName(country).countryCode(countryCode).build())
|
||||
it.copy(
|
||||
number = number.toBuilder().selectedCountryDisplayName(country.name).countryCode(country.countryCode).build(),
|
||||
newCountry = country
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -26,8 +25,6 @@ import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
@@ -49,14 +46,9 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -83,7 +75,9 @@ import org.thoughtcrime.securesms.components.settings.app.usernamelinks.main.Use
|
||||
import org.thoughtcrime.securesms.invites.InviteActions
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberVisualTransformation
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.registration.util.CountryPrefix
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.CountryCodeState
|
||||
import org.thoughtcrime.securesms.registrationv3.ui.countrycode.Screen
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter
|
||||
@@ -160,7 +154,7 @@ class FindByActivity : PassphraseRequiredActivity() {
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectCountryPrefixClick = {
|
||||
onSelectCountryClick = {
|
||||
navController.navigate("select-country-prefix")
|
||||
},
|
||||
onQrCodeScanClicked = {
|
||||
@@ -171,24 +165,21 @@ class FindByActivity : PassphraseRequiredActivity() {
|
||||
}
|
||||
|
||||
composable("select-country-prefix") {
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(id = R.string.FindByActivity__select_country_code),
|
||||
onNavigationClick = { navController.popBackStack() },
|
||||
navigationIconPainter = painterResource(id = R.drawable.symbol_arrow_left_24)
|
||||
) { paddingValues ->
|
||||
SelectCountryScreen(
|
||||
paddingValues = paddingValues,
|
||||
searchEntry = state.countryPrefixSearchEntry,
|
||||
onSearchEntryChanged = viewModel::onCountryPrefixSearchEntryChanged,
|
||||
supportedCountryPrefixes = state.supportedCountryPrefixes,
|
||||
onCountryPrefixSelected = {
|
||||
Screen(
|
||||
state = CountryCodeState(
|
||||
query = state.query,
|
||||
filteredList = state.filteredCountries,
|
||||
countryList = state.supportedCountries
|
||||
),
|
||||
title = stringResource(R.string.FindByActivity__select_country_code),
|
||||
onSearch = { search -> viewModel.filterCountries(search) },
|
||||
onDismissed = { navController.popBackStack() },
|
||||
onClick = { country ->
|
||||
viewModel.onCountrySelected(country)
|
||||
navController.popBackStack()
|
||||
viewModel.onCountryPrefixSelected(it)
|
||||
viewModel.onCountryPrefixSearchEntryChanged("")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dialog("invalid-entry") {
|
||||
val title = if (state.mode == FindByMode.USERNAME) {
|
||||
@@ -201,8 +192,8 @@ class FindByActivity : PassphraseRequiredActivity() {
|
||||
stringResource(id = R.string.FindByActivity__s_is_not_a_valid_username, state.userEntry)
|
||||
} else {
|
||||
val formattedNumber = remember(state.userEntry) {
|
||||
val cleansed = state.userEntry.removePrefix(state.selectedCountryPrefix.digits.toString())
|
||||
PhoneNumberFormatter.formatE164(state.selectedCountryPrefix.digits.toString(), cleansed)
|
||||
val cleansed = state.userEntry.removePrefix(state.selectedCountry.countryCode.toString())
|
||||
PhoneNumberFormatter.formatE164(state.selectedCountry.countryCode.toString(), cleansed)
|
||||
}
|
||||
stringResource(id = R.string.FindByActivity__s_is_not_a_valid_phone_number, formattedNumber)
|
||||
}
|
||||
@@ -240,8 +231,8 @@ class FindByActivity : PassphraseRequiredActivity() {
|
||||
stringResource(id = R.string.FindByActivity__s_is_not_a_signal_user, state.userEntry)
|
||||
} else {
|
||||
val formattedNumber = remember(state.userEntry) {
|
||||
val cleansed = state.userEntry.removePrefix(state.selectedCountryPrefix.digits.toString())
|
||||
PhoneNumberFormatter.formatE164(state.selectedCountryPrefix.digits.toString(), cleansed)
|
||||
val cleansed = state.userEntry.removePrefix(state.selectedCountry.countryCode.toString())
|
||||
PhoneNumberFormatter.formatE164(state.selectedCountry.countryCode.toString(), cleansed)
|
||||
}
|
||||
stringResource(id = R.string.FindByActivity__s_is_not_a_signal_user_would, formattedNumber)
|
||||
}
|
||||
@@ -303,7 +294,7 @@ private fun Content(
|
||||
state: FindByState,
|
||||
onUserEntryChanged: (String) -> Unit,
|
||||
onNextClick: () -> Unit,
|
||||
onSelectCountryPrefixClick: () -> Unit,
|
||||
onSelectCountryClick: () -> Unit,
|
||||
onQrCodeScanClicked: () -> Unit
|
||||
) {
|
||||
val placeholderLabel = remember(state.mode) {
|
||||
@@ -338,8 +329,8 @@ private fun Content(
|
||||
val visualTransformation = if (state.mode == FindByMode.USERNAME) {
|
||||
VisualTransformation.None
|
||||
} else {
|
||||
remember(state.selectedCountryPrefix) {
|
||||
PhoneNumberVisualTransformation(state.selectedCountryPrefix.regionCode)
|
||||
remember(state.selectedCountry.countryCode) {
|
||||
PhoneNumberVisualTransformation(state.selectedCountry.regionCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +346,8 @@ private fun Content(
|
||||
{
|
||||
PhoneNumberEntryPrefix(
|
||||
enabled = !state.isLookupInProgress,
|
||||
selectedCountryPrefix = state.selectedCountryPrefix,
|
||||
onSelectCountryPrefixClick = onSelectCountryPrefixClick
|
||||
selectedCountry = state.selectedCountry,
|
||||
onSelectCountryClick = onSelectCountryClick
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -448,8 +439,8 @@ private fun Content(
|
||||
@Composable
|
||||
private fun PhoneNumberEntryPrefix(
|
||||
enabled: Boolean,
|
||||
selectedCountryPrefix: CountryPrefix,
|
||||
onSelectCountryPrefixClick: () -> Unit
|
||||
selectedCountry: Country,
|
||||
onSelectCountryClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -459,10 +450,10 @@ private fun PhoneNumberEntryPrefix(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(1000.dp))
|
||||
.clickable(onClick = onSelectCountryPrefixClick, enabled = enabled)
|
||||
.clickable(onClick = onSelectCountryClick, enabled = enabled)
|
||||
) {
|
||||
Text(
|
||||
text = selectedCountryPrefix.toString(),
|
||||
text = "+${selectedCountry.countryCode}",
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp, top = 6.dp, bottom = 6.dp)
|
||||
)
|
||||
@@ -486,113 +477,6 @@ private fun PhoneNumberEntryPrefix(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SelectCountryScreen(
|
||||
paddingValues: PaddingValues,
|
||||
searchEntry: String,
|
||||
onSearchEntryChanged: (String) -> Unit,
|
||||
onCountryPrefixSelected: (CountryPrefix) -> Unit,
|
||||
supportedCountryPrefixes: List<CountryPrefix>
|
||||
) {
|
||||
val focusRequester = remember {
|
||||
FocusRequester()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
) {
|
||||
TextFields.TextField(
|
||||
value = searchEntry,
|
||||
onValueChange = onSearchEntryChanged,
|
||||
placeholder = { Text(text = stringResource(id = R.string.FindByActivity__search)) },
|
||||
shape = RoundedCornerShape(32.dp),
|
||||
colors = TextFieldDefaults.colors(
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
errorIndicatorColor = Color.Transparent,
|
||||
cursorColor = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 10.dp)
|
||||
.focusRequester(focusRequester)
|
||||
.heightIn(min = 44.dp),
|
||||
contentPadding = TextFieldDefaults.contentPaddingWithoutLabel(top = 10.dp, bottom = 10.dp)
|
||||
)
|
||||
|
||||
LazyColumn {
|
||||
items(
|
||||
items = supportedCountryPrefixes
|
||||
) {
|
||||
CountryPrefixRowItem(
|
||||
searchTerm = searchEntry,
|
||||
countryPrefix = it,
|
||||
onClick = { onCountryPrefixSelected(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CountryPrefixRowItem(
|
||||
searchTerm: String,
|
||||
countryPrefix: CountryPrefix,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val regionDisplayName = remember(countryPrefix.regionCode, Locale.current) {
|
||||
PhoneNumberFormatter.getRegionDisplayName(countryPrefix.regionCode).orElse(countryPrefix.regionCode)
|
||||
}
|
||||
|
||||
if (searchTerm.isNotBlank() && !regionDisplayName.contains(searchTerm, ignoreCase = true)) {
|
||||
return
|
||||
}
|
||||
|
||||
val highlightedName: AnnotatedString = remember(regionDisplayName, searchTerm) {
|
||||
if (searchTerm.isBlank()) {
|
||||
AnnotatedString(regionDisplayName)
|
||||
} else {
|
||||
buildAnnotatedString {
|
||||
append(regionDisplayName)
|
||||
|
||||
val startIndex = regionDisplayName.indexOf(searchTerm, ignoreCase = true)
|
||||
|
||||
addStyle(
|
||||
style = SpanStyle(
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
start = startIndex,
|
||||
end = startIndex + searchTerm.length
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = spacedBy((-2).dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = dimensionResource(id = CoreUiR.dimen.gutter))
|
||||
.padding(top = 16.dp, bottom = 14.dp)
|
||||
) {
|
||||
Text(
|
||||
text = highlightedName
|
||||
)
|
||||
|
||||
Text(
|
||||
text = countryPrefix.toString(),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Light Theme", group = "content - phone", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark Theme", group = "content - phone", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
@@ -606,7 +490,7 @@ private fun ContentPreviewPhoneNumber() {
|
||||
),
|
||||
onUserEntryChanged = {},
|
||||
onNextClick = {},
|
||||
onSelectCountryPrefixClick = {},
|
||||
onSelectCountryClick = {},
|
||||
onQrCodeScanClicked = {}
|
||||
)
|
||||
}
|
||||
@@ -625,23 +509,8 @@ private fun ContentPreviewUsername() {
|
||||
),
|
||||
onUserEntryChanged = {},
|
||||
onNextClick = {},
|
||||
onSelectCountryPrefixClick = {},
|
||||
onSelectCountryClick = {},
|
||||
onQrCodeScanClicked = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Light Theme", group = "select country", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark Theme", group = "select country", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun SelectCountryScreenPreview() {
|
||||
Previews.Preview {
|
||||
SelectCountryScreen(
|
||||
paddingValues = PaddingValues(0.dp),
|
||||
searchEntry = "",
|
||||
onSearchEntryChanged = {},
|
||||
supportedCountryPrefixes = FindByState(mode = FindByMode.PHONE_NUMBER).supportedCountryPrefixes,
|
||||
onCountryPrefixSelected = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import com.google.i18n.phonenumbers.NumberParseException
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||
import org.signal.core.util.orNull
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.registration.util.CountryPrefix
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.CountryUtils
|
||||
|
||||
/**
|
||||
* State for driving find by number/username screen.
|
||||
@@ -17,12 +18,11 @@ import org.thoughtcrime.securesms.registration.util.CountryPrefix
|
||||
data class FindByState(
|
||||
val mode: FindByMode,
|
||||
val userEntry: String = "",
|
||||
val supportedCountryPrefixes: List<CountryPrefix> = PhoneNumberUtil.getInstance().supportedCallingCodes
|
||||
.map { CountryPrefix(it, PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(it)) }
|
||||
.sortedBy { it.digits.toString() },
|
||||
val selectedCountryPrefix: CountryPrefix = supportedCountryPrefixes.first(),
|
||||
val countryPrefixSearchEntry: String = "",
|
||||
val isLookupInProgress: Boolean = false
|
||||
val supportedCountries: List<Country> = CountryUtils.getCountries(),
|
||||
val filteredCountries: List<Country> = emptyList(),
|
||||
val selectedCountry: Country = supportedCountries.first(),
|
||||
val isLookupInProgress: Boolean = false,
|
||||
val query: String = ""
|
||||
) {
|
||||
companion object {
|
||||
fun startingState(self: Recipient, mode: FindByMode): FindByState {
|
||||
@@ -36,7 +36,7 @@ data class FindByState(
|
||||
|
||||
val state = FindByState(mode = mode)
|
||||
return state.copy(
|
||||
selectedCountryPrefix = state.supportedCountryPrefixes.firstOrNull { it.digits == countryCode } ?: state.supportedCountryPrefixes.first()
|
||||
selectedCountry = state.supportedCountries.firstOrNull { it.countryCode == countryCode } ?: state.supportedCountries.first()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.signal.core.util.concurrent.safeBlockingGet
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientRepository
|
||||
import org.thoughtcrime.securesms.registration.util.CountryPrefix
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil
|
||||
|
||||
class FindByViewModel(
|
||||
@@ -40,12 +40,8 @@ class FindByViewModel(
|
||||
internalState.value = state.value.copy(userEntry = cleansed)
|
||||
}
|
||||
|
||||
fun onCountryPrefixSearchEntryChanged(searchEntry: String) {
|
||||
internalState.value = state.value.copy(countryPrefixSearchEntry = searchEntry)
|
||||
}
|
||||
|
||||
fun onCountryPrefixSelected(countryPrefix: CountryPrefix) {
|
||||
internalState.value = state.value.copy(selectedCountryPrefix = countryPrefix)
|
||||
fun onCountrySelected(country: Country) {
|
||||
internalState.value = state.value.copy(selectedCountry = country)
|
||||
}
|
||||
|
||||
suspend fun onNextClicked(context: Context): FindByResult {
|
||||
@@ -80,7 +76,7 @@ class FindByViewModel(
|
||||
@WorkerThread
|
||||
private fun performPhoneLookup(context: Context): FindByResult {
|
||||
val stateSnapshot = state.value
|
||||
val countryCode = stateSnapshot.selectedCountryPrefix.digits
|
||||
val countryCode = stateSnapshot.selectedCountry.countryCode
|
||||
val nationalNumber = stateSnapshot.userEntry.removePrefix(countryCode.toString())
|
||||
|
||||
val e164 = "+$countryCode$nationalNumber"
|
||||
@@ -92,4 +88,22 @@ class FindByViewModel(
|
||||
is RecipientRepository.LookupResult.Success -> FindByResult.Success(result.recipientId)
|
||||
}
|
||||
}
|
||||
|
||||
fun filterCountries(filterBy: String) {
|
||||
if (filterBy.isEmpty()) {
|
||||
internalState.value = state.value.copy(
|
||||
query = filterBy,
|
||||
filteredCountries = emptyList()
|
||||
)
|
||||
} else {
|
||||
internalState.value = state.value.copy(
|
||||
query = filterBy,
|
||||
filteredCountries = state.value.supportedCountries.filter { country: Country ->
|
||||
country.name.contains(filterBy, ignoreCase = true) ||
|
||||
country.countryCode.toString().contains(filterBy) ||
|
||||
(filterBy.equals("usa", ignoreCase = true) && country.name.equals("United States", ignoreCase = true))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ object CountryUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun countryToEmoji(countryCode: String): String {
|
||||
return if (countryCode.isNotEmpty()) {
|
||||
countryCode
|
||||
|
||||
@@ -21,7 +21,10 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.LabeledEditText;
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.Country;
|
||||
import org.thoughtcrime.securesms.registration.ui.countrycode.CountryUtils;
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState;
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||
|
||||
/**
|
||||
* Handle the logic and formatting of phone number input specifically for change number flows.
|
||||
@@ -150,7 +153,7 @@ public final class ChangeNumberInputController {
|
||||
|
||||
private void setCountryDisplay(@Nullable String regionDisplayName) {
|
||||
countrySpinnerAdapter.clear();
|
||||
if (regionDisplayName == null) {
|
||||
if (regionDisplayName == null || regionDisplayName.isEmpty()) {
|
||||
countrySpinnerAdapter.add(context.getString(R.string.RegistrationActivity_select_your_country));
|
||||
} else {
|
||||
countrySpinnerAdapter.add(regionDisplayName);
|
||||
@@ -245,7 +248,8 @@ public final class ChangeNumberInputController {
|
||||
}
|
||||
|
||||
if (!isUpdating) {
|
||||
callbacks.setCountry(countryCode);
|
||||
Country country = new Country(CountryUtils.countryToEmoji(regionCode), PhoneNumberFormatter.getRegionDisplayName(regionCode).orElse(""), countryCode, regionCode);
|
||||
callbacks.setCountry(country);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +273,6 @@ public final class ChangeNumberInputController {
|
||||
|
||||
void setNationalNumber(@NonNull String number);
|
||||
|
||||
void setCountry(int countryCode);
|
||||
void setCountry(@NonNull Country country);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ class CountryCodeFragment : ComposeFragment() {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(CountryCodeFragment::class.java)
|
||||
const val RESULT_KEY = "result_key"
|
||||
const val REQUEST_KEY_COUNTRY = "request_key_country"
|
||||
const val REQUEST_COUNTRY = "country"
|
||||
const val RESULT_COUNTRY = "country"
|
||||
@@ -86,6 +87,8 @@ class CountryCodeFragment : ComposeFragment() {
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
val resultKey = arguments?.getString(RESULT_KEY) ?: REQUEST_KEY_COUNTRY
|
||||
|
||||
Screen(
|
||||
state = state,
|
||||
title = stringResource(R.string.CountryCodeFragment__your_country),
|
||||
@@ -93,7 +96,7 @@ class CountryCodeFragment : ComposeFragment() {
|
||||
onDismissed = { findNavController().popBackStack() },
|
||||
onClick = { country ->
|
||||
setFragmentResult(
|
||||
REQUEST_KEY_COUNTRY,
|
||||
resultKey,
|
||||
bundleOf(
|
||||
RESULT_COUNTRY to country
|
||||
)
|
||||
|
||||
@@ -58,13 +58,18 @@
|
||||
|
||||
<fragment
|
||||
android:id="@+id/countryPickerFragment"
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNumberCountryPickerFragment"
|
||||
android:name="org.thoughtcrime.securesms.registrationv3.ui.countrycode.CountryCodeFragment"
|
||||
tools:layout="@layout/fragment_registration_country_picker">
|
||||
|
||||
<argument
|
||||
android:name="result_key"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
|
||||
<argument
|
||||
android:name="country"
|
||||
app:argType="org.thoughtcrime.securesms.registration.ui.countrycode.Country"
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
||||
Reference in New Issue
Block a user