Redesign FTUX to use Material Design 3.

This commit is contained in:
Nicholas 2023-01-23 16:30:57 -05:00 committed by Greyson Parrelli
parent 0303467c91
commit 150bbf181d
25 changed files with 842 additions and 918 deletions

View file

@ -0,0 +1,56 @@
package org.thoughtcrime.securesms.components.registration
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.button.MaterialButton
import org.thoughtcrime.securesms.R
import java.util.concurrent.TimeUnit
class ActionCountDownButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : MaterialButton(context, attrs, defStyle) {
private var countDownToTime: Long = 0
private var listener: Listener? = null
/**
* Starts a count down to the specified {@param time}.
*/
fun startCountDownTo(time: Long) {
if (time > 0) {
countDownToTime = time
updateCountDown()
}
}
fun setCallEnabled() {
setText(R.string.RegistrationActivity_call)
isEnabled = true
alpha = 1.0f
}
private fun updateCountDown() {
val remainingMillis = countDownToTime - System.currentTimeMillis()
if (remainingMillis > 0) {
isEnabled = false
alpha = 0.5f
val totalRemainingSeconds = TimeUnit.MILLISECONDS.toSeconds(remainingMillis).toInt()
val minutesRemaining = totalRemainingSeconds / 60
val secondsRemaining = totalRemainingSeconds % 60
text = resources.getString(R.string.RegistrationActivity_call_me_instead_available_in, minutesRemaining, secondsRemaining)
listener?.onRemaining(this, totalRemainingSeconds)
postDelayed({ updateCountDown() }, 250)
} else {
setCallEnabled()
}
}
fun setListener(listener: Listener?) {
this.listener = listener
}
interface Listener {
fun onRemaining(view: ActionCountDownButton, secondsRemaining: Int)
}
}

View file

@ -1,79 +0,0 @@
package org.thoughtcrime.securesms.components.registration;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.button.MaterialButton;
import org.thoughtcrime.securesms.R;
import java.util.concurrent.TimeUnit;
public class CallMeCountDownView extends MaterialButton {
private long countDownToTime;
@Nullable
private Listener listener;
public CallMeCountDownView(Context context) {
super(context);
}
public CallMeCountDownView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CallMeCountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* Starts a count down to the specified {@param time}.
*/
public void startCountDownTo(long time) {
if (time > 0) {
this.countDownToTime = time;
updateCountDown();
}
}
public void setCallEnabled() {
setText(R.string.RegistrationActivity_call);
setEnabled(true);
setAlpha(1.0f);
}
private void updateCountDown() {
final long remainingMillis = countDownToTime - System.currentTimeMillis();
if (remainingMillis > 0) {
setEnabled(false);
setAlpha(0.5f);
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, totalRemainingSeconds);
}
postDelayed(this::updateCountDown, 250);
} else {
setCallEnabled();
}
}
public void setListener(@Nullable Listener listener) {
this.listener = listener;
}
public interface Listener {
void onRemaining(@NonNull CallMeCountDownView view, int secondsRemaining);
}
}

View file

@ -1,139 +0,0 @@
package org.thoughtcrime.securesms.components.registration;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.OvershootInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import java.util.ArrayList;
import java.util.List;
public final class VerificationCodeView extends FrameLayout {
private final List<TextView> codes = new ArrayList<>(6);
private final List<View> containers = new ArrayList<>(6);
private OnCodeEnteredListener listener;
private int index;
public VerificationCodeView(Context context) {
super(context);
initialize(context);
}
public VerificationCodeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public VerificationCodeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public VerificationCodeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context);
}
private void initialize(@NonNull Context context) {
inflate(context, R.layout.verification_code_view, this);
codes.add(findViewById(R.id.code_zero));
codes.add(findViewById(R.id.code_one));
codes.add(findViewById(R.id.code_two));
codes.add(findViewById(R.id.code_three));
codes.add(findViewById(R.id.code_four));
codes.add(findViewById(R.id.code_five));
containers.add(findViewById(R.id.container_zero));
containers.add(findViewById(R.id.container_one));
containers.add(findViewById(R.id.container_two));
containers.add(findViewById(R.id.container_three));
containers.add(findViewById(R.id.container_four));
containers.add(findViewById(R.id.container_five));
}
@MainThread
public void setOnCompleteListener(OnCodeEnteredListener listener) {
this.listener = listener;
}
@MainThread
public void append(int value) {
if (index >= codes.size()) return;
setInactive(containers);
setActive(containers.get(index));
TextView codeView = codes.get(index++);
Animation translateIn = new TranslateAnimation(0, 0, codeView.getHeight(), 0);
translateIn.setInterpolator(new OvershootInterpolator());
translateIn.setDuration(500);
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setDuration(200);
AnimationSet animationSet = new AnimationSet(false);
animationSet.addAnimation(fadeIn);
animationSet.addAnimation(translateIn);
animationSet.reset();
animationSet.setStartTime(0);
codeView.setText(String.valueOf(value));
codeView.clearAnimation();
codeView.startAnimation(animationSet);
if (index == codes.size() && listener != null) {
listener.onCodeComplete(Stream.of(codes).map(TextView::getText).collect(Collectors.joining()));
}
}
@MainThread
public void delete() {
if (index <= 0) return;
codes.get(--index).setText("");
setInactive(containers);
setActive(containers.get(index));
}
@MainThread
public void clear() {
if (index != 0) {
Stream.of(codes).forEach(code -> code.setText(""));
index = 0;
}
setInactive(containers);
}
private static void setInactive(List<View> views) {
Stream.of(views).forEach(c -> c.setBackgroundResource(R.drawable.labeled_edit_text_background_inactive));
}
private static void setActive(@NonNull View container) {
container.setBackgroundResource(R.drawable.labeled_edit_text_background_active);
}
public interface OnCodeEnteredListener {
void onCodeComplete(@NonNull String code);
}
}

View file

@ -0,0 +1,52 @@
package org.thoughtcrime.securesms.components.registration
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import com.google.android.material.textfield.TextInputLayout
import org.thoughtcrime.securesms.R
class VerificationCodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) :
FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
private val containers: MutableList<TextInputLayout> = ArrayList(6)
private var listener: OnCodeEnteredListener? = null
private var index = 0
init {
inflate(context, R.layout.verification_code_view, this)
containers.add(findViewById(R.id.container_zero))
containers.add(findViewById(R.id.container_one))
containers.add(findViewById(R.id.container_two))
containers.add(findViewById(R.id.container_three))
containers.add(findViewById(R.id.container_four))
containers.add(findViewById(R.id.container_five))
}
fun setOnCompleteListener(listener: OnCodeEnteredListener?) {
this.listener = listener
}
fun append(digit: Int) {
if (index >= containers.size) return
containers[index++].editText?.setText(digit.toString())
if (index == containers.size) {
listener?.onCodeComplete(containers.joinToString("") { it.editText?.text.toString() })
}
}
fun delete() {
if (index <= 0) return
containers[--index].editText?.setText("")
}
fun clear() {
if (index != 0) {
containers.forEach { it.editText?.setText("") }
index = 0
}
}
interface OnCodeEnteredListener {
fun onCodeComplete(code: String)
}
}

View file

@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNum
import org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNumberViewModel.ContinueStatus
import org.thoughtcrime.securesms.registration.fragments.CountryPickerFragment
import org.thoughtcrime.securesms.registration.fragments.CountryPickerFragmentArgs
import org.thoughtcrime.securesms.registration.util.RegistrationNumberInputController
import org.thoughtcrime.securesms.registration.util.ChangeNumberInputController
import org.thoughtcrime.securesms.util.Dialogs
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@ -54,13 +54,13 @@ class ChangeNumberEnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_c
oldNumberCountryCode = view.findViewById(R.id.change_number_enter_phone_number_old_number_country_code)
oldNumber = view.findViewById(R.id.change_number_enter_phone_number_old_number_number)
val oldController = RegistrationNumberInputController(
val oldController = ChangeNumberInputController(
requireContext(),
oldNumberCountryCode,
oldNumber,
oldNumberCountrySpinner,
false,
object : RegistrationNumberInputController.Callbacks {
object : ChangeNumberInputController.Callbacks {
override fun onNumberFocused() {
scrollView.postDelayed({ scrollView.smoothScrollTo(0, oldNumber.bottom) }, 250)
}
@ -91,13 +91,13 @@ class ChangeNumberEnterPhoneNumberFragment : LoggingFragment(R.layout.fragment_c
newNumberCountryCode = view.findViewById(R.id.change_number_enter_phone_number_new_number_country_code)
newNumber = view.findViewById(R.id.change_number_enter_phone_number_new_number_number)
val newController = RegistrationNumberInputController(
val newController = ChangeNumberInputController(
requireContext(),
newNumberCountryCode,
newNumber,
newNumberCountrySpinner,
true,
object : RegistrationNumberInputController.Callbacks {
object : ChangeNumberInputController.Callbacks {
override fun onNumberFocused() {
scrollView.postDelayed({ scrollView.smoothScrollTo(0, newNumber.bottom) }, 250)
}

View file

@ -17,10 +17,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.airbnb.lottie.LottieAnimationView;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.R;
@ -29,19 +25,18 @@ import org.thoughtcrime.securesms.pin.PinOptOutDialog;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton;
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends LoggingFragment {
public abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends LoggingFragment {
private TextView title;
private LearnMoreTextView description;
private EditText input;
private TextView label;
private TextView keyboardToggle;
private TextView confirm;
private LottieAnimationView lottieProgress;
private LottieAnimationView lottieEnd;
private ViewModel viewModel;
private TextView title;
private LearnMoreTextView description;
private EditText input;
private TextView label;
private TextView keyboardToggle;
private CircularProgressMaterialButton confirm;
private ViewModel viewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@ -78,10 +73,6 @@ abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.BaseKbsPinFragment__learn_more_url));
});
Toolbar toolbar = view.findViewById(R.id.kbs_pin_toolbar);
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(null);
initializeListeners();
}
@ -137,13 +128,6 @@ abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends
return input;
}
protected LottieAnimationView getLottieProgress() {
return lottieProgress;
}
protected LottieAnimationView getLottieEnd() {
return lottieEnd;
}
protected TextView getLabel() {
return label;
@ -153,7 +137,7 @@ abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends
return keyboardToggle;
}
protected TextView getConfirm() {
protected CircularProgressMaterialButton getConfirm() {
return confirm;
}
@ -173,8 +157,6 @@ abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel> extends
label = view.findViewById(R.id.edit_kbs_pin_input_label);
keyboardToggle = view.findViewById(R.id.edit_kbs_pin_keyboard_toggle);
confirm = view.findViewById(R.id.edit_kbs_pin_confirm);
lottieProgress = view.findViewById(R.id.edit_kbs_pin_lottie_progress);
lottieEnd = view.findViewById(R.id.edit_kbs_pin_lottie_end);
initializeViewStates();
}

View file

@ -1,189 +0,0 @@
package org.thoughtcrime.securesms.lock.v2;
import android.animation.Animator;
import android.app.Activity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.RawRes;
import androidx.appcompat.app.AlertDialog;
import androidx.autofill.HintConstants;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.lifecycle.ViewModelProvider;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.animation.AnimationRepeatListener;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.megaphone.Megaphones;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.SpanUtil;
import java.util.Objects;
public class ConfirmKbsPinFragment extends BaseKbsPinFragment<ConfirmKbsPinViewModel> {
private ConfirmKbsPinViewModel viewModel;
@Override
protected void initializeViewStates() {
ConfirmKbsPinFragmentArgs args = ConfirmKbsPinFragmentArgs.fromBundle(requireArguments());
if (args.getIsPinChange()) {
initializeViewStatesForPinChange();
} else {
initializeViewStatesForPinCreate();
}
ViewCompat.setAutofillHints(getInput(), HintConstants.AUTOFILL_HINT_NEW_PASSWORD);
}
@Override
protected ConfirmKbsPinViewModel initializeViewModel() {
ConfirmKbsPinFragmentArgs args = ConfirmKbsPinFragmentArgs.fromBundle(requireArguments());
KbsPin userEntry = Objects.requireNonNull(args.getUserEntry());
PinKeyboardType keyboard = args.getKeyboard();
ConfirmKbsPinRepository repository = new ConfirmKbsPinRepository();
ConfirmKbsPinViewModel.Factory factory = new ConfirmKbsPinViewModel.Factory(userEntry, keyboard, repository);
viewModel = new ViewModelProvider(this, factory).get(ConfirmKbsPinViewModel.class);
viewModel.getLabel().observe(getViewLifecycleOwner(), this::updateLabel);
viewModel.getSaveAnimation().observe(getViewLifecycleOwner(), this::updateSaveAnimation);
return viewModel;
}
private void initializeViewStatesForPinCreate() {
getTitle().setText(R.string.CreateKbsPinFragment__create_your_pin);
getDescription().setText(R.string.ConfirmKbsPinFragment__confirm_your_pin);
getKeyboardToggle().setVisibility(View.INVISIBLE);
getLabel().setText("");
getDescription().setLearnMoreVisible(false);
}
private void initializeViewStatesForPinChange() {
getTitle().setText(R.string.CreateKbsPinFragment__create_a_new_pin);
getDescription().setText(R.string.ConfirmKbsPinFragment__confirm_your_pin);
getDescription().setLearnMoreVisible(false);
getKeyboardToggle().setVisibility(View.INVISIBLE);
getLabel().setText("");
}
private void updateLabel(@NonNull ConfirmKbsPinViewModel.Label label) {
switch (label) {
case EMPTY:
getLabel().setText("");
break;
case CREATING_PIN:
getLabel().setText(R.string.ConfirmKbsPinFragment__creating_pin);
getInput().setEnabled(false);
break;
case RE_ENTER_PIN:
getLabel().setText(R.string.ConfirmKbsPinFragment__re_enter_your_pin);
break;
case PIN_DOES_NOT_MATCH:
getLabel().setText(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.red_500),
getString(R.string.ConfirmKbsPinFragment__pins_dont_match)));
getInput().getText().clear();
break;
}
}
private void updateSaveAnimation(@NonNull ConfirmKbsPinViewModel.SaveAnimation animation) {
updateAnimationAndInputVisibility(animation);
LottieAnimationView lottieProgress = getLottieProgress();
switch (animation) {
case NONE:
lottieProgress.cancelAnimation();
break;
case LOADING:
lottieProgress.setAnimation(R.raw.lottie_kbs_loading);
lottieProgress.setRepeatMode(LottieDrawable.RESTART);
lottieProgress.setRepeatCount(LottieDrawable.INFINITE);
lottieProgress.playAnimation();
break;
case SUCCESS:
startEndAnimationOnNextProgressRepetition(R.raw.lottie_kbs_success, new AnimationCompleteListener() {
@Override
public void onAnimationEnd(Animator animation) {
requireActivity().setResult(Activity.RESULT_OK);
closeNavGraphBranch();
RegistrationUtil.maybeMarkRegistrationComplete(requireContext());
StorageSyncHelper.scheduleSyncForDataChange();
}
});
break;
case FAILURE:
startEndAnimationOnNextProgressRepetition(R.raw.lottie_kbs_fail, new AnimationCompleteListener() {
@Override
public void onAnimationEnd(Animator animation) {
RegistrationUtil.maybeMarkRegistrationComplete(requireContext());
displayFailedDialog();
}
});
break;
}
}
private void startEndAnimationOnNextProgressRepetition(@RawRes int lottieAnimationId,
@NonNull AnimationCompleteListener listener)
{
LottieAnimationView lottieProgress = getLottieProgress();
LottieAnimationView lottieEnd = getLottieEnd();
lottieEnd.setAnimation(lottieAnimationId);
lottieEnd.removeAllAnimatorListeners();
lottieEnd.setRepeatCount(0);
lottieEnd.addAnimatorListener(listener);
if (lottieProgress.isAnimating()) {
lottieProgress.addAnimatorListener(new AnimationRepeatListener(animator ->
hideProgressAndStartEndAnimation(lottieProgress, lottieEnd)
));
} else {
hideProgressAndStartEndAnimation(lottieProgress, lottieEnd);
}
}
private void hideProgressAndStartEndAnimation(@NonNull LottieAnimationView lottieProgress,
@NonNull LottieAnimationView lottieEnd)
{
viewModel.onLoadingAnimationComplete();
lottieProgress.setVisibility(View.GONE);
lottieEnd.setVisibility(View.VISIBLE);
lottieEnd.playAnimation();
}
private void updateAnimationAndInputVisibility(ConfirmKbsPinViewModel.SaveAnimation saveAnimation) {
if (saveAnimation == ConfirmKbsPinViewModel.SaveAnimation.NONE) {
getInput().setVisibility(View.VISIBLE);
getLottieProgress().setVisibility(View.GONE);
} else {
getInput().setVisibility(View.GONE);
getLottieProgress().setVisibility(View.VISIBLE);
}
}
private void displayFailedDialog() {
new AlertDialog.Builder(requireContext()).setTitle(R.string.ConfirmKbsPinFragment__pin_creation_failed)
.setMessage(R.string.ConfirmKbsPinFragment__your_pin_was_not_saved)
.setCancelable(false)
.setPositiveButton(R.string.ok, (d, w) -> {
d.dismiss();
markMegaphoneSeenIfNecessary();
requireActivity().setResult(Activity.RESULT_CANCELED);
closeNavGraphBranch();
})
.show();
}
private void markMegaphoneSeenIfNecessary() {
ApplicationDependencies.getMegaphoneRepository().markSeen(Megaphones.Event.PINS_FOR_ALL);
}
}

View file

@ -0,0 +1,123 @@
package org.thoughtcrime.securesms.lock.v2
import android.app.Activity
import android.content.DialogInterface
import android.view.View
import androidx.autofill.HintConstants
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.lock.v2.ConfirmKbsPinViewModel.SaveAnimation
import org.thoughtcrime.securesms.megaphone.Megaphones
import org.thoughtcrime.securesms.registration.RegistrationUtil
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.SpanUtil
internal class ConfirmKbsPinFragment : BaseKbsPinFragment<ConfirmKbsPinViewModel>() {
override fun initializeViewStates() {
val args = ConfirmKbsPinFragmentArgs.fromBundle(requireArguments())
if (args.isPinChange) {
initializeViewStatesForPinChange()
} else {
initializeViewStatesForPinCreate()
}
ViewCompat.setAutofillHints(input, HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
}
override fun initializeViewModel(): ConfirmKbsPinViewModel {
val args = ConfirmKbsPinFragmentArgs.fromBundle(requireArguments())
val userEntry = args.userEntry!!
val keyboard = args.keyboard
val repository = ConfirmKbsPinRepository()
val factory = ConfirmKbsPinViewModel.Factory(userEntry, keyboard, repository)
val viewModel = ViewModelProvider(this, factory)[ConfirmKbsPinViewModel::class.java]
viewModel.label.observe(viewLifecycleOwner) { label: ConfirmKbsPinViewModel.LabelState -> updateLabel(label) }
viewModel.saveAnimation.observe(viewLifecycleOwner) { animation: SaveAnimation -> updateSaveAnimation(animation) }
return viewModel
}
private fun initializeViewStatesForPinCreate() {
title.setText(R.string.ConfirmKbsPinFragment__confirm_your_pin)
description.setText(R.string.ConfirmKbsPinFragment__re_enter_the_pin_you_just_created)
keyboardToggle.visibility = View.INVISIBLE
description.setLearnMoreVisible(false)
label.text = ""
confirm.isEnabled = true
}
private fun initializeViewStatesForPinChange() {
title.setText(R.string.ConfirmKbsPinFragment__confirm_your_pin)
description.setText(R.string.ConfirmKbsPinFragment__re_enter_the_pin_you_just_created)
description.setLearnMoreVisible(false)
keyboardToggle.visibility = View.INVISIBLE
label.text = ""
confirm.isEnabled = true
}
private fun updateLabel(labelState: ConfirmKbsPinViewModel.LabelState) {
when (labelState) {
ConfirmKbsPinViewModel.LabelState.EMPTY -> label.text = ""
ConfirmKbsPinViewModel.LabelState.CREATING_PIN -> {
label.setText(R.string.ConfirmKbsPinFragment__creating_pin)
input.isEnabled = false
}
ConfirmKbsPinViewModel.LabelState.RE_ENTER_PIN -> label.setText(R.string.ConfirmKbsPinFragment__re_enter_your_pin)
ConfirmKbsPinViewModel.LabelState.PIN_DOES_NOT_MATCH -> {
label.text = SpanUtil.color(
ContextCompat.getColor(requireContext(), R.color.red_500),
getString(R.string.ConfirmKbsPinFragment__pins_dont_match)
)
input.text.clear()
}
}
}
private fun updateSaveAnimation(animation: SaveAnimation) {
updateInputVisibility(animation)
when (animation) {
SaveAnimation.NONE -> confirm.cancelSpinning()
SaveAnimation.LOADING -> confirm.setSpinning()
SaveAnimation.SUCCESS -> {
confirm.cancelSpinning()
requireActivity().setResult(Activity.RESULT_OK)
closeNavGraphBranch()
RegistrationUtil.maybeMarkRegistrationComplete(requireContext())
StorageSyncHelper.scheduleSyncForDataChange()
}
SaveAnimation.FAILURE -> {
confirm.cancelSpinning()
RegistrationUtil.maybeMarkRegistrationComplete(requireContext())
displayFailedDialog()
}
}
}
private fun updateInputVisibility(saveAnimation: SaveAnimation) {
if (saveAnimation == SaveAnimation.NONE) {
input.visibility = View.VISIBLE
} else {
input.visibility = View.GONE
}
}
private fun displayFailedDialog() {
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.ConfirmKbsPinFragment__pin_creation_failed)
.setMessage(R.string.ConfirmKbsPinFragment__your_pin_was_not_saved)
.setCancelable(false)
.setPositiveButton(R.string.ok) { d: DialogInterface, w: Int ->
d.dismiss()
markMegaphoneSeenIfNecessary()
requireActivity().setResult(Activity.RESULT_CANCELED)
closeNavGraphBranch()
}
.show()
}
private fun markMegaphoneSeenIfNecessary() {
ApplicationDependencies.getMegaphoneRepository().markSeen(Megaphones.Event.PINS_FOR_ALL)
}
}

View file

@ -16,8 +16,8 @@ final class ConfirmKbsPinViewModel extends ViewModel implements BaseKbsPinViewMo
private final DefaultValueLiveData<KbsPin> userEntry = new DefaultValueLiveData<>(KbsPin.EMPTY);
private final DefaultValueLiveData<PinKeyboardType> keyboard = new DefaultValueLiveData<>(PinKeyboardType.NUMERIC);
private final DefaultValueLiveData<SaveAnimation> saveAnimation = new DefaultValueLiveData<>(SaveAnimation.NONE);
private final DefaultValueLiveData<Label> label = new DefaultValueLiveData<>(Label.RE_ENTER_PIN);
private final DefaultValueLiveData<SaveAnimation> saveAnimation = new DefaultValueLiveData<>(SaveAnimation.NONE);
private final DefaultValueLiveData<LabelState> label = new DefaultValueLiveData<>(LabelState.EMPTY);
private final KbsPin pinToConfirm;
@ -35,29 +35,25 @@ final class ConfirmKbsPinViewModel extends ViewModel implements BaseKbsPinViewMo
return Transformations.distinctUntilChanged(saveAnimation);
}
LiveData<Label> getLabel() {
LiveData<LabelState> getLabel() {
return Transformations.distinctUntilChanged(label);
}
@Override
public void confirm() {
KbsPin userEntry = this.userEntry.getValue();
this.userEntry.setValue(KbsPin.EMPTY);
if (pinToConfirm.toString().equals(userEntry.toString())) {
this.label.setValue(Label.CREATING_PIN);
this.label.setValue(LabelState.CREATING_PIN);
this.saveAnimation.setValue(SaveAnimation.LOADING);
repository.setPin(pinToConfirm, this.keyboard.getValue(), this::handleResult);
} else {
this.label.setValue(Label.PIN_DOES_NOT_MATCH);
this.userEntry.setValue(KbsPin.EMPTY);
this.label.setValue(LabelState.PIN_DOES_NOT_MATCH);
}
}
void onLoadingAnimationComplete() {
this.label.setValue(Label.EMPTY);
}
@Override
public LiveData<KbsPin> getUserEntry() {
return userEntry;
@ -91,7 +87,7 @@ final class ConfirmKbsPinViewModel extends ViewModel implements BaseKbsPinViewMo
}
}
enum Label {
enum LabelState {
RE_ENTER_PIN,
PIN_DOES_NOT_MATCH,
CREATING_PIN,

View file

@ -1,112 +0,0 @@
package org.thoughtcrime.securesms.lock.v2;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.PluralsRes;
import androidx.autofill.HintConstants;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
public class CreateKbsPinFragment extends BaseKbsPinFragment<CreateKbsPinViewModel> {
@Override
protected void initializeViewStates() {
CreateKbsPinFragmentArgs args = CreateKbsPinFragmentArgs.fromBundle(requireArguments());
if (args.getIsPinChange()) {
initializeViewStatesForPinChange(args.getIsForgotPin());
} else {
initializeViewStatesForPinCreate();
}
getLabel().setText(getPinLengthRestrictionText(R.plurals.CreateKbsPinFragment__pin_must_be_at_least_digits));
getConfirm().setEnabled(false);
ViewCompat.setAutofillHints(getInput(), HintConstants.AUTOFILL_HINT_NEW_PASSWORD);
}
private void initializeViewStatesForPinChange(boolean isForgotPin) {
getTitle().setText(R.string.CreateKbsPinFragment__create_a_new_pin);
getDescription().setText(R.string.CreateKbsPinFragment__you_can_choose_a_new_pin_as_long_as_this_device_is_registered);
getDescription().setLearnMoreVisible(true);
}
private void initializeViewStatesForPinCreate() {
getTitle().setText(R.string.CreateKbsPinFragment__create_your_pin);
getDescription().setText(R.string.CreateKbsPinFragment__pins_keep_information_stored_with_signal_encrypted);
getDescription().setLearnMoreVisible(true);
}
@Override
protected CreateKbsPinViewModel initializeViewModel() {
CreateKbsPinViewModel viewModel = new ViewModelProvider(this).get(CreateKbsPinViewModel.class);
CreateKbsPinFragmentArgs args = CreateKbsPinFragmentArgs.fromBundle(requireArguments());
viewModel.getNavigationEvents().observe(getViewLifecycleOwner(), e -> onConfirmPin(e.getUserEntry(), e.getKeyboard(), args.getIsPinChange()));
viewModel.getErrorEvents().observe(getViewLifecycleOwner(), e -> {
if (e == CreateKbsPinViewModel.PinErrorEvent.WEAK_PIN) {
getLabel().setText(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.red_500),
getString(R.string.CreateKbsPinFragment__choose_a_stronger_pin)));
shake(getInput(), () -> getInput().getText().clear());
} else {
throw new AssertionError("Unexpected PIN error!");
}
});
viewModel.getKeyboard().observe(getViewLifecycleOwner(), k -> {
getLabel().setText(getLabelText(k));
getInput().getText().clear();
});
return viewModel;
}
private void onConfirmPin(@NonNull KbsPin userEntry, @NonNull PinKeyboardType keyboard, boolean isPinChange) {
CreateKbsPinFragmentDirections.ActionConfirmPin action = CreateKbsPinFragmentDirections.actionConfirmPin();
action.setUserEntry(userEntry);
action.setKeyboard(keyboard);
action.setIsPinChange(isPinChange);
SafeNavigation.safeNavigate(Navigation.findNavController(requireView()), action);
}
private String getLabelText(@NonNull PinKeyboardType keyboard) {
if (keyboard == PinKeyboardType.ALPHA_NUMERIC) {
return getPinLengthRestrictionText(R.plurals.CreateKbsPinFragment__pin_must_be_at_least_characters);
} else {
return getPinLengthRestrictionText(R.plurals.CreateKbsPinFragment__pin_must_be_at_least_digits);
}
}
private String getPinLengthRestrictionText(@PluralsRes int plurals) {
return requireContext().getResources().getQuantityString(plurals, KbsConstants.MINIMUM_PIN_LENGTH, KbsConstants.MINIMUM_PIN_LENGTH);
}
private static void shake(@NonNull EditText view, @NonNull Runnable afterwards) {
TranslateAnimation shake = new TranslateAnimation(0, 30, 0, 0);
shake.setDuration(50);
shake.setRepeatCount(7);
shake.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
afterwards.run();
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
view.startAnimation(shake);
}
}

View file

@ -0,0 +1,101 @@
package org.thoughtcrime.securesms.lock.v2
import android.view.animation.Animation
import android.view.animation.TranslateAnimation
import android.widget.EditText
import androidx.annotation.PluralsRes
import androidx.autofill.HintConstants
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation.findNavController
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinViewModel.NavigationEvent
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinViewModel.PinErrorEvent
import org.thoughtcrime.securesms.util.SpanUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate
class CreateKbsPinFragment : BaseKbsPinFragment<CreateKbsPinViewModel?>() {
override fun initializeViewStates() {
val args = CreateKbsPinFragmentArgs.fromBundle(requireArguments())
if (args.isPinChange) {
initializeViewStatesForPinChange(args.isForgotPin)
} else {
initializeViewStatesForPinCreate()
}
label.text = getPinLengthRestrictionText(R.plurals.CreateKbsPinFragment__pin_must_be_at_least_digits)
confirm.isEnabled = false
ViewCompat.setAutofillHints(input, HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
}
private fun initializeViewStatesForPinChange(isForgotPin: Boolean) {
title.setText(R.string.CreateKbsPinFragment__create_a_new_pin)
description.setText(R.string.CreateKbsPinFragment__you_can_choose_a_new_pin_as_long_as_this_device_is_registered)
description.setLearnMoreVisible(true)
}
private fun initializeViewStatesForPinCreate() {
title.setText(R.string.CreateKbsPinFragment__create_your_pin)
description.setText(R.string.CreateKbsPinFragment__pins_can_help_you_restore_your_account)
description.setLearnMoreVisible(true)
}
override fun initializeViewModel(): CreateKbsPinViewModel {
val viewModel = ViewModelProvider(this)[CreateKbsPinViewModel::class.java]
val args = CreateKbsPinFragmentArgs.fromBundle(requireArguments())
viewModel.navigationEvents.observe(viewLifecycleOwner) { e: NavigationEvent -> onConfirmPin(e.userEntry, e.keyboard, args.isPinChange) }
viewModel.errorEvents.observe(viewLifecycleOwner) { e: PinErrorEvent ->
if (e == PinErrorEvent.WEAK_PIN) {
label.text = SpanUtil.color(
ContextCompat.getColor(requireContext(), R.color.red_500),
getString(R.string.CreateKbsPinFragment__choose_a_stronger_pin)
)
shake(input) { input.text.clear() }
} else {
throw AssertionError("Unexpected PIN error!")
}
}
viewModel.keyboard.observe(viewLifecycleOwner) { k: PinKeyboardType ->
label.text = getLabelText(k)
input.text.clear()
}
return viewModel
}
private fun onConfirmPin(userEntry: KbsPin, keyboard: PinKeyboardType, isPinChange: Boolean) {
val action = CreateKbsPinFragmentDirections.actionConfirmPin()
action.userEntry = userEntry
action.keyboard = keyboard
action.isPinChange = isPinChange
findNavController(requireView()).safeNavigate(action)
}
private fun getLabelText(keyboard: PinKeyboardType): String {
return if (keyboard == PinKeyboardType.ALPHA_NUMERIC) {
getPinLengthRestrictionText(R.plurals.CreateKbsPinFragment__pin_must_be_at_least_characters)
} else {
getPinLengthRestrictionText(R.plurals.CreateKbsPinFragment__pin_must_be_at_least_digits)
}
}
private fun getPinLengthRestrictionText(@PluralsRes plurals: Int): String {
return resources.getQuantityString(plurals, KbsConstants.MINIMUM_PIN_LENGTH, KbsConstants.MINIMUM_PIN_LENGTH)
}
companion object {
private fun shake(view: EditText, afterwards: Runnable) {
val shake = TranslateAnimation(0F, 30F, 0F, 0F)
shake.duration = 50
shake.repeatCount = 7
shake.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
afterwards.run()
}
override fun onAnimationRepeat(animation: Animation) {}
})
view.startAnimation(shake)
}
}
}

View file

@ -21,7 +21,7 @@ import org.greenrobot.eventbus.ThreadMode;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.registration.CallMeCountDownView;
import org.thoughtcrime.securesms.components.registration.ActionCountDownButton;
import org.thoughtcrime.securesms.components.registration.VerificationCodeView;
import org.thoughtcrime.securesms.components.registration.VerificationPinKeyboard;
import org.thoughtcrime.securesms.registration.ReceivedSmsEvent;
@ -55,13 +55,11 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
private static final String TAG = Log.tag(BaseEnterSmsCodeFragment.class);
private ScrollView scrollView;
private TextView header;
private TextView subheader;
private VerificationCodeView verificationCodeView;
private VerificationPinKeyboard keyboard;
private CallMeCountDownView callMeCountDown;
private ActionCountDownButton callMeCountDown;
private View wrongNumber;
private View noCodeReceivedHelp;
private View serviceWarning;
private boolean autoCompleting;
private ViewModel viewModel;
@ -80,13 +78,11 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
setDebugLogSubmitMultiTapView(view.findViewById(R.id.verify_header));
scrollView = view.findViewById(R.id.scroll_view);
header = view.findViewById(R.id.verify_header);
subheader = view.findViewById(R.id.verification_subheader);
verificationCodeView = view.findViewById(R.id.code);
keyboard = view.findViewById(R.id.keyboard);
callMeCountDown = view.findViewById(R.id.call_me_count_down);
wrongNumber = view.findViewById(R.id.wrong_number);
noCodeReceivedHelp = view.findViewById(R.id.no_code);
serviceWarning = view.findViewById(R.id.cell_service_warning);
new SignalStrengthPhoneStateListener(this, this);
@ -106,14 +102,12 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
}
});
noCodeReceivedHelp.setOnClickListener(v -> sendEmailToSupport());
disposables.bindTo(getViewLifecycleOwner().getLifecycle());
viewModel = getViewModel();
viewModel.getSuccessfulCodeRequestAttempts().observe(getViewLifecycleOwner(), (attempts) -> {
if (attempts >= 3) {
noCodeReceivedHelp.setVisibility(View.VISIBLE);
scrollView.postDelayed(() -> scrollView.smoothScrollTo(0, noCodeReceivedHelp.getBottom()), 15000);
// TODO Add bottom sheet for help
}
});
@ -330,7 +324,7 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
public void onResume() {
super.onResume();
header.setText(requireContext().getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, viewModel.getNumber().getFullFormattedNumber()));
subheader.setText(requireContext().getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, viewModel.getNumber().getFullFormattedNumber()));
viewModel.getCanCallAtTime().observe(getViewLifecycleOwner(), callAtTime -> callMeCountDown.startCountDownTo(callAtTime));
}
@ -348,40 +342,11 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
@Override
public void onNoCellSignalPresent() {
if (serviceWarning.getVisibility() == View.VISIBLE) {
return;
}
serviceWarning.setVisibility(View.VISIBLE);
serviceWarning.animate()
.alpha(1)
.setListener(null)
.start();
scrollView.postDelayed(() -> {
if (serviceWarning.getVisibility() == View.VISIBLE) {
scrollView.smoothScrollTo(0, serviceWarning.getBottom());
}
}, 1000);
// TODO animate in bottom sheet
}
@Override
public void onCellSignalPresent() {
if (serviceWarning.getVisibility() != View.VISIBLE) {
return;
}
serviceWarning.animate()
.alpha(0)
.setListener(new Animator.AnimatorListener() {
@Override public void onAnimationEnd(Animator animation) {
serviceWarning.setVisibility(View.GONE);
}
@Override public void onAnimationStart(Animator animation) {}
@Override public void onAnimationCancel(Animator animation) {}
@Override public void onAnimationRepeat(Animator animation) {}
})
.start();
// TODO animate away bottom sheet
}
}

View file

@ -11,9 +11,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProvider;
@ -27,6 +28,7 @@ import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.tasks.Task;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
@ -35,7 +37,6 @@ import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.LabeledEditText;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.registration.VerifyAccountRepository.Mode;
import org.thoughtcrime.securesms.registration.util.RegistrationNumberInputController;
@ -51,6 +52,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.navigation.SafeNavigation;
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@ -64,10 +66,9 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
private static final String TAG = Log.tag(EnterPhoneNumberFragment.class);
private LabeledEditText countryCode;
private LabeledEditText number;
private TextInputLayout countryCode;
private TextInputLayout number;
private CircularProgressMaterialButton register;
private Spinner countrySpinner;
private View cancel;
private ScrollView scrollView;
private RegistrationViewModel viewModel;
@ -91,20 +92,16 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
setDebugLogSubmitMultiTapView(view.findViewById(R.id.verify_header));
countryCode = view.findViewById(R.id.country_code);
number = view.findViewById(R.id.number);
countrySpinner = view.findViewById(R.id.country_spinner);
cancel = view.findViewById(R.id.cancel_button);
scrollView = view.findViewById(R.id.scroll_view);
register = view.findViewById(R.id.registerButton);
countryCode = view.findViewById(R.id.country_code);
number = view.findViewById(R.id.number);
cancel = view.findViewById(R.id.cancel_button);
scrollView = view.findViewById(R.id.scroll_view);
register = view.findViewById(R.id.registerButton);
RegistrationNumberInputController controller = new RegistrationNumberInputController(requireContext(),
countryCode,
number,
countrySpinner,
true,
this);
this,
Objects.requireNonNull(number.getEditText()),
countryCode);
register.setOnClickListener(v -> handleRegister(requireContext()));
disposables.bindTo(getViewLifecycleOwner().getLifecycle());
@ -125,7 +122,10 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
Toolbar toolbar = view.findViewById(R.id.toolbar);
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(null);
final ActionBar supportActionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setTitle(null);
}
}
@Override
@ -144,13 +144,13 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
}
private void handleRegister(@NonNull Context context) {
if (TextUtils.isEmpty(countryCode.getText())) {
if (TextUtils.isEmpty(countryCode.getEditText().getText())) {
showErrorDialog(context, getString(R.string.RegistrationActivity_you_must_specify_your_country_code));
return;
}
if (TextUtils.isEmpty(this.number.getText())) {
showErrorDialog(context, getString(R.string.RegistrationActivity_you_must_specify_your_phone_number));
if (TextUtils.isEmpty(this.number.getEditText().getText())) {
showErrorDialog(context, getString(R.string.RegistrationActivity_please_enter_a_valid_phone_number_to_register));
return;
}
@ -184,8 +184,8 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
disableAllEntries();
if (fcmSupported) {
SmsRetrieverClient client = SmsRetriever.getClient(context);
Task<Void> task = client.startSmsRetriever();
SmsRetrieverClient client = SmsRetriever.getClient(context);
Task<Void> task = client.startSmsRetriever();
AtomicBoolean handled = new AtomicBoolean(false);
Debouncer debouncer = new Debouncer(TimeUnit.SECONDS.toMillis(5));
@ -224,14 +224,12 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
private void disableAllEntries() {
countryCode.setEnabled(false);
number.setEnabled(false);
countrySpinner.setEnabled(false);
cancel.setVisibility(View.GONE);
}
private void enableAllEntries() {
countryCode.setEnabled(true);
number.setEnabled(true);
countrySpinner.setEnabled(true);
if (viewModel.isReregister()) {
cancel.setVisibility(View.VISIBLE);
}
@ -283,22 +281,12 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
scrollView.postDelayed(() -> scrollView.smoothScrollTo(0, register.getBottom()), 250);
}
@Override
public void onNumberInputNext(@NonNull View view) {
// Intentionally left blank
}
@Override
public void onNumberInputDone(@NonNull View view) {
ViewUtil.hideKeyboard(requireContext(), view);
handleRegister(requireContext());
}
@Override
public void onPickCountry(@NonNull View view) {
SafeNavigation.safeNavigate(Navigation.findNavController(view), R.id.action_pickCountry);
}
@Override
public void setNationalNumber(@NonNull String number) {
viewModel.setNationalNumber(number);
@ -325,8 +313,8 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
d.dismiss();
})
.setPositiveButton(R.string.yes, (d, i) -> {
countryCode.setText(String.valueOf(phoneNumber.getCountryCode()));
number.setText(String.valueOf(phoneNumber.getNationalNumber()));
countryCode.getEditText().setText(String.valueOf(phoneNumber.getCountryCode()));
number.getEditText().setText(String.valueOf(phoneNumber.getNationalNumber()));
requestVerificationCode(mode);
d.dismiss();
})
@ -357,9 +345,9 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
R.string.RegistrationActivity_a_verification_code_will_be_sent_to,
e164number,
() -> {
ViewUtil.hideKeyboard(context, number.getInput());
ViewUtil.hideKeyboard(context, number.getEditText());
onConfirmed.run();
},
() -> number.focusAndMoveCursorToEndAndOpenKeyboard());
() -> ViewUtil.focusAndMoveCursorToEndAndOpenKeyboard(this.number.getEditText()));
}
}

View file

@ -24,9 +24,9 @@ import org.thoughtcrime.securesms.components.LabeledEditText;
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState;
/**
* Handle the logic and formatting of phone number input for registration/change number flows.
* Handle the logic and formatting of phone number input specifically for change number flows.
*/
public final class RegistrationNumberInputController {
public final class ChangeNumberInputController {
private final Context context;
private final LabeledEditText countryCode;
@ -38,12 +38,12 @@ public final class RegistrationNumberInputController {
private AsYouTypeFormatter countryFormatter;
private boolean isUpdating = true;
public RegistrationNumberInputController(@NonNull Context context,
@NonNull LabeledEditText countryCode,
@NonNull LabeledEditText number,
@NonNull Spinner countrySpinner,
boolean lastInput,
@NonNull Callbacks callbacks)
public ChangeNumberInputController(@NonNull Context context,
@NonNull LabeledEditText countryCode,
@NonNull LabeledEditText number,
@NonNull Spinner countrySpinner,
boolean lastInput,
@NonNull Callbacks callbacks)
{
this.context = context;
this.countryCode = countryCode;

View file

@ -0,0 +1,155 @@
package org.thoughtcrime.securesms.registration.util
import android.content.Context
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.View
import android.view.View.OnFocusChangeListener
import android.view.inputmethod.EditorInfo
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.TextView
import com.google.android.material.textfield.MaterialAutoCompleteTextView
import com.google.android.material.textfield.TextInputLayout
import com.google.i18n.phonenumbers.AsYouTypeFormatter
import com.google.i18n.phonenumbers.PhoneNumberUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState
/**
* Handle the logic and formatting of phone number input specifically for registration number the flow.
*/
class RegistrationNumberInputController(
val context: Context,
val callbacks: Callbacks,
private val phoneNumberInputLayout: EditText,
countryCodeInputLayout: TextInputLayout
) {
private val spinnerView: MaterialAutoCompleteTextView = countryCodeInputLayout.editText as MaterialAutoCompleteTextView
private val supportedCountryPrefixes: List<CountryPrefix> = PhoneNumberUtil.getInstance().supportedCallingCodes
.map { CountryPrefix(it, PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(it)) }
.sortedBy { it.digits }
private val spinnerAdapter: ArrayAdapter<CountryPrefix> = ArrayAdapter<CountryPrefix>(context, R.layout.registration_country_code_dropdown_item, supportedCountryPrefixes)
private var countryFormatter: AsYouTypeFormatter? = null
private var isUpdating = true
init {
setUpNumberInput()
spinnerView.setAdapter(spinnerAdapter)
spinnerView.addTextChangedListener(CountryCodeEntryListener())
}
private fun advanceToPhoneNumberInput() {
if (!isUpdating) {
phoneNumberInputLayout.requestFocus()
}
val numberLength: Int = phoneNumberInputLayout.text?.length ?: 0
phoneNumberInputLayout.setSelection(numberLength, numberLength)
}
private fun setUpNumberInput() {
phoneNumberInputLayout.addTextChangedListener(NumberChangedListener())
phoneNumberInputLayout.onFocusChangeListener = OnFocusChangeListener { v: View?, hasFocus: Boolean ->
if (hasFocus) {
callbacks.onNumberFocused()
}
}
phoneNumberInputLayout.imeOptions = EditorInfo.IME_ACTION_DONE
phoneNumberInputLayout.setOnEditorActionListener { v: TextView?, actionId: Int, _: KeyEvent? ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
callbacks.onNumberInputDone(v!!)
return@setOnEditorActionListener true
}
false
}
}
fun updateNumber(numberViewState: NumberViewState) {
val countryCode = numberViewState.countryCode
isUpdating = true
val regionCode = PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(countryCode)
setCountryFormatter(regionCode)
isUpdating = false
}
private fun setCountryFormatter(regionCode: String?) {
val util = PhoneNumberUtil.getInstance()
countryFormatter = if (regionCode != null) util.getAsYouTypeFormatter(regionCode) else null
reformatText(phoneNumberInputLayout.text)
}
private fun reformatText(editable: Editable): String? {
if (TextUtils.isEmpty(editable)) {
return null
}
val countryFormatter: AsYouTypeFormatter = countryFormatter ?: return null
countryFormatter.clear()
var formattedNumber: String? = null
val justDigits = StringBuilder()
for (character in editable) {
if (Character.isDigit(character)) {
formattedNumber = countryFormatter.inputDigit(character)
justDigits.append(character)
}
}
if (formattedNumber != null && editable.toString() != formattedNumber) {
editable.replace(0, editable.length, formattedNumber)
}
return if (justDigits.isEmpty()) {
null
} else justDigits.toString()
}
inner class NumberChangedListener : TextWatcher {
override fun afterTextChanged(s: Editable) {
val number: String = reformatText(s) ?: return
if (!isUpdating) {
callbacks.setNationalNumber(number)
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
}
inner class CountryCodeEntryListener : TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (s.isNullOrEmpty()) {
return
}
if (s[0] != '+') {
s.insert(0, "+")
}
supportedCountryPrefixes.firstOrNull { it.toString() == s.toString() }?.let {
setCountryFormatter(it.regionCode)
callbacks.setCountry(it.digits)
advanceToPhoneNumberInput()
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
}
interface Callbacks {
fun onNumberFocused()
fun onNumberInputDone(view: View)
fun setNationalNumber(number: String)
fun setCountry(countryCode: Int)
}
}
data class CountryPrefix(val digits: Int, val regionCode: String) {
override fun toString(): String {
return "+$digits"
}
}

View file

@ -1,71 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:fillViewport="true">
android:fillViewport="true"
tools:viewBindingIgnore="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/kbs_pin_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="parent" />
<TextView
android:id="@+id/edit_kbs_pin_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.Signal.Title1"
app:layout_constraintBottom_toTopOf="@id/edit_kbs_pin_keyboard_toggle"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="24dp"
android:textAppearance="@style/Signal.Text.HeadlineMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.20"
tools:text="@string/CreateKbsPinFragment__create_your_pin" />
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
android:id="@+id/edit_kbs_pin_description"
style="@style/Signal.Text.BodyLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="27dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="27dp"
android:gravity="center_horizontal"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:minHeight="66dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/signal_text_secondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_pin_title"
tools:text="@string/CreateKbsPinFragment__pins_keep_information_stored_with_signal_encrypted" />
tools:text="@string/CreateKbsPinFragment__pins_can_help_you_restore_your_account" />
<EditText
android:id="@+id/edit_kbs_pin_input"
android:layout_width="wrap_content"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_kbs_textinputlayout"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="12dp"
android:gravity="center_horizontal"
android:inputType="numberPassword"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:minWidth="210dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_pin_description" />
app:layout_constraintTop_toBottomOf="@+id/edit_kbs_pin_description">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/edit_kbs_pin_lottie_progress"
android:layout_width="57dp"
android:layout_height="57dp"
android:layout_gravity="center_horizontal"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_pin_description" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_kbs_pin_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:imeOptions="actionDone"
android:inputType="numberPassword" />
</com.google.android.material.textfield.TextInputLayout>
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/edit_kbs_pin_lottie_end"
@ -85,7 +80,7 @@
android:gravity="center_horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_pin_input"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_textinputlayout"
app:layout_goneMarginTop="65dp"
tools:text="PIN must be at least 4 digits" />
@ -94,22 +89,25 @@
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_pin_input_label"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toTopOf="@id/edit_kbs_pin_confirm"
app:layout_constraintVertical_bias="1.0"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_pin_input_label"
app:layout_constraintVertical_bias="0.0"
tools:layout_editor_absoluteX="32dp"
tools:text="Create Alphanumeric Pin" />
<com.google.android.material.button.MaterialButton
<org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
android:id="@+id/edit_kbs_pin_confirm"
style="@style/Signal.Widget.Button.Large.Tonal"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="16dp"
android:text="@string/BaseKbsPinFragment__next"
app:layout_constraintBottom_toBottomOf="parent" />
app:circularProgressMaterialButton__label="@string/BaseKbsPinFragment__next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View file

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".registration.fragments.EnterSmsCodeFragment">
tools:context=".registration.fragments.EnterSmsCodeFragment"
tools:viewBindingIgnore="true">
<ScrollView
android:id="@+id/scroll_view"
@ -25,52 +25,45 @@
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_toEndOf="@id/call_me_count_down"
android:layout_marginStart="22dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/registrationactivity_text_view_padding"
android:hyphenationFrequency="normal"
android:maxWidth="150dp"
android:text="@string/RegistrationActivity_wrong_number"
android:textColor="@color/signal_text_secondary"
android:textColor="@color/signal_colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/call_me_count_down"
app:layout_constraintTop_toBottomOf="@+id/code" />
<com.google.android.material.button.MaterialButton
android:id="@+id/no_code"
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_toEndOf="@id/call_me_count_down"
android:hyphenationFrequency="normal"
android:text="@string/RegistrationActivity_contact_signal_support"
android:textColor="@color/signal_text_secondary"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/call_me_count_down"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/wrong_number"
tools:visibility="visible" />
app:layout_constraintTop_toBottomOf="@+id/verification_subheader" />
<TextView
android:id="@+id/verify_header"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="32dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="32dp"
android:gravity="center"
android:gravity="start"
android:text="@string/RegistrationActivity_verification_code"
android:textAppearance="@style/Signal.Text.HeadlineMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/verification_subheader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/verify_header"
app:layout_constraintTop_toBottomOf="@+id/verify_header"
tools:text="@string/RegistrationActivity_enter_the_code_we_sent_to_s" />
<org.thoughtcrime.securesms.components.registration.CallMeCountDownView
<org.thoughtcrime.securesms.components.registration.ActionCountDownButton
android:id="@+id/call_me_count_down"
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="wrap_content"
@ -79,66 +72,39 @@
android:hyphenationFrequency="normal"
android:maxWidth="150dp"
android:textColor="@color/signal_text_secondary"
app:layout_constraintEnd_toStartOf="@+id/wrong_number"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/resend_sms_count_down"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/wrong_number"
app:layout_constraintTop_toBottomOf="@+id/code"
app:layout_constraintVertical_bias="1.0"
tools:text="@string/RegistrationActivity_call" />
<org.thoughtcrime.securesms.components.registration.ActionCountDownButton
android:id="@+id/resend_sms_count_down"
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/code"
android:hyphenationFrequency="normal"
android:maxWidth="150dp"
android:textColor="@color/signal_text_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/code"
app:layout_constraintVertical_bias="1.0"
tools:text="@string/RegistrationActivity_resend_code" />
<org.thoughtcrime.securesms.components.registration.VerificationCodeView
android:id="@+id/code"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/verify_header" />
<LinearLayout
android:id="@+id/cell_service_warning"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:alpha="0"
android:background="@drawable/registration_no_cell_service_warning_border"
android:gravity="center"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:text="@string/RegistrationActivity_make_sure_your_phone_has_a_cellular_signal"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/no_code"
tools:alpha="1"
tools:visibility="visible">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_margin="2dp"
android:importantForAccessibility="no"
app:srcCompat="@drawable/ic_error_outline_14"
app:tint="@color/core_ultramarine" />
<TextView
style="@style/Signal.Text.Body.Registration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/RegistrationActivity_make_sure_your_phone_has_a_cellular_signal"
android:textAlignment="center"
android:textColor="@color/core_ultramarine"
android:textSize="12sp" />
</LinearLayout>
app:layout_constraintTop_toBottomOf="@id/wrong_number" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -147,6 +113,7 @@
<org.thoughtcrime.securesms.components.registration.VerificationPinKeyboard
android:id="@+id/keyboard"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
tools:visibility="visible" />
</LinearLayout>

View file

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".registration.fragments.EnterPhoneNumberFragment">
tools:context=".registration.fragments.EnterPhoneNumberFragment"
tools:viewBindingIgnore="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@ -19,59 +19,57 @@
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/country_spinner_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="32dp"
android:background="@drawable/labeled_edit_text_background_inactive"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/verify_subheader">
<Spinner
android:id="@+id/country_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textAlignment="viewStart" />
</FrameLayout>
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginStart="12dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:layout_marginEnd="12dp"
android:layoutDirection="ltr"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/country_spinner_frame">
app:layout_constraintTop_toBottomOf="@id/verify_subheader">
<org.thoughtcrime.securesms.components.LabeledEditText
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/country_code"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_weight="1"
app:labeledEditText_background="@color/white"
app:labeledEditText_textLayout="@layout/country_code_text" />
style="@style/Widget.Signal.TextInputLayout.Registration.Dropdown"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:errorEnabled="false"
app:hintEnabled="false">
<org.thoughtcrime.securesms.components.LabeledEditText
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:digits="+1234567890"
android:drawablePadding="-24dp"
android:hint="@string/Registration_country_code_entry_hint"
android:imeOptions="actionNext"
android:maxLength="4"
android:maxLines="1"
android:padding="0dp"
android:singleLine="true"
tools:text="+1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/number"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
app:labeledEditText_background="@color/white"
app:labeledEditText_label="@string/RegistrationActivity_phone_number_description"
app:labeledEditText_textLayout="@layout/phone_text" />
android:layout_weight="1"
android:hint="@string/RegistrationActivity_phone_number_description">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="phone" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<TextView
@ -81,8 +79,7 @@
android:layout_gravity="center"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:gravity="center"
android:text="@string/RegistrationActivity_enter_your_phone_number_to_get_started"
android:text="@string/RegistrationActivity_enter_your_phone_number"
android:textAppearance="@style/Signal.Text.HeadlineMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
@ -91,14 +88,14 @@
<TextView
android:id="@+id/verify_subheader"
style="@style/Signal.Text.Body.Registration"
style="@style/Signal.Text.BodyLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:gravity="center"
android:text="@string/RegistrationActivity_you_will_receive_a_verification_code"
android:textColor="@color/core_grey_60"
app:layout_constraintTop_toBottomOf="@+id/verify_header"
tools:layout_editor_absoluteX="0dp" />
@ -126,14 +123,12 @@
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="16dp"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal"
app:circularProgressMaterialButton__label="@string/RegistrationActivity_next"
app:circularProgressMaterialButton__label="@string/RegistrationActivity_continue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout"
app:layout_constraintVertical_bias="0" />
app:layout_constraintVertical_bias="0"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
android:fillViewport="true"
tools:viewBindingIgnore="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@ -54,8 +54,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/Signal.Text.BodyMedium"
android:gravity="center_horizontal"
android:textAppearance="@style/Signal.Text.BodyMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/kbs_lock_pin_input"
@ -78,25 +78,26 @@
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toTopOf="@id/kbs_lock_pin_confirm"
app:layout_constraintTop_toBottomOf="@id/kbs_lock_forgot_pin"
app:layout_constraintVertical_bias="1.0"
app:layout_constraintVertical_bias="0.0"
tools:layout_editor_absoluteX="32dp"
tools:text="Create Alphanumeric Pin" />
<org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
android:id="@+id/kbs_lock_pin_confirm"
android:layout_width="0dp"
style="@style/Signal.Widget.Button.Large.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="16dp"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Primary"
app:circularProgressMaterialButton__label="@string/RegistrationActivity_continue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View file

@ -15,39 +15,53 @@
android:id="@+id/pin_restore_pin_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/RegistrationLockFragment__enter_your_pin"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="24dp"
android:textAppearance="@style/Signal.Text.HeadlineMedium"
app:layout_constraintBottom_toTopOf="@id/pin_restore_keyboard_toggle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@string/RegistrationLockFragment__enter_your_pin"
app:layout_constraintBottom_toTopOf="@id/pin_restore_pin_description"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.20" />
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="@+id/pin_restore_pin_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="27dp"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="27dp"
android:gravity="center_horizontal"
android:layout_marginEnd="24dp"
android:minHeight="66dp"
android:text="@string/RegistrationLockFragment__enter_the_pin_you_created"
android:textAppearance="@style/Signal.Text.BodyLarge"
android:textColor="@color/core_grey_60"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/pin_restore_pin_title" />
<EditText
android:id="@+id/pin_restore_pin_input"
android:layout_width="wrap_content"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_kbs_textinputlayout"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="12dp"
android:gravity="center_horizontal"
android:inputType="numberPassword"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:minWidth="210dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/pin_restore_pin_description" />
app:layout_constraintTop_toBottomOf="@+id/pin_restore_pin_description">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/pin_restore_pin_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="numberPassword" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/pin_restore_pin_input_label"
@ -58,7 +72,7 @@
android:textAppearance="@style/Signal.Text.BodyMedium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/pin_restore_pin_input"
app:layout_constraintTop_toBottomOf="@id/edit_kbs_textinputlayout"
tools:text="@string/RegistrationLockFragment__incorrect_pin_try_again" />
<com.google.android.material.button.MaterialButton
@ -80,25 +94,25 @@
style="@style/Signal.Widget.Button.Large.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintBottom_toTopOf="@id/pin_restore_pin_confirm"
app:layout_constraintTop_toBottomOf="@id/pin_restore_forgot_pin"
app:layout_constraintVertical_bias="1.0"
app:layout_constraintVertical_bias="0.0"
tools:layout_editor_absoluteX="32dp"
tools:text="Create Alphanumeric Pin" />
<org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
android:id="@+id/pin_restore_pin_confirm"
android:layout_width="0dp"
style="@style/Signal.Widget.Button.Large.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="16dp"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Primary"
app:circularProgressMaterialButton__label="@string/RegistrationActivity_continue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:materialThemeOverlay="@style/ThemeOverlay.Signal.CircularProgressIndicator.Tonal" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pin_restore_skip_button"

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text1"
style="?android:attr/spinnerDropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:gravity="center"
android:singleLine="true" />

View file

@ -8,76 +8,85 @@
android:layoutDirection="ltr"
tools:viewBindingIgnore="true">
<FrameLayout
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/container_zero"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/labeled_edit_text_background_inactive"
app:errorEnabled="false"
app:hintEnabled="false"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toStartOf="@+id/container_one"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/code_zero"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:gravity="center"
android:importantForAccessibility="yes"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
android:padding="0dp"
android:textSize="16sp"
tools:text="0" />
</FrameLayout>
</com.google.android.material.textfield.TextInputLayout>
<FrameLayout
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/container_one"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:background="@drawable/labeled_edit_text_background_inactive"
app:errorEnabled="false"
app:hintEnabled="false"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toStartOf="@+id/container_two"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/container_zero"
app:layout_constraintTop_toTopOf="parent">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/code_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:gravity="center"
android:importantForAccessibility="yes"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
android:padding="0dp"
android:textAppearance="@style/Signal.Text.BodyLarge"
tools:text="1" />
</FrameLayout>
</com.google.android.material.textfield.TextInputLayout>
<FrameLayout
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/container_two"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:background="@drawable/labeled_edit_text_background_inactive"
app:errorEnabled="false"
app:hintEnabled="false"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toStartOf="@+id/separator"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/container_one"
app:layout_constraintTop_toTopOf="parent">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/code_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:gravity="center"
android:importantForAccessibility="yes"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
android:padding="0dp"
android:textAppearance="@style/Signal.Text.BodyLarge"
tools:text="2" />
</FrameLayout>
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/separator"
@ -87,83 +96,92 @@
android:layout_marginStart="4dp"
android:importantForAccessibility="no"
android:text="-"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/container_three"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/container_two"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<FrameLayout
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/container_three"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:background="@drawable/labeled_edit_text_background_inactive"
app:errorEnabled="false"
app:hintEnabled="false"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toStartOf="@+id/container_four"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/separator"
app:layout_constraintTop_toTopOf="parent">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/code_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:gravity="center"
android:importantForAccessibility="yes"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
android:padding="0dp"
android:textAppearance="@style/Signal.Text.BodyLarge"
tools:text="3" />
</FrameLayout>
</com.google.android.material.textfield.TextInputLayout>
<FrameLayout
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/container_four"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:background="@drawable/labeled_edit_text_background_inactive"
app:errorEnabled="false"
app:hintEnabled="false"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toStartOf="@+id/container_five"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/container_three"
app:layout_constraintTop_toTopOf="parent">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/code_four"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:gravity="center"
android:importantForAccessibility="yes"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
android:padding="0dp"
android:textAppearance="@style/Signal.Text.BodyLarge"
tools:text="4" />
</FrameLayout>
</com.google.android.material.textfield.TextInputLayout>
<FrameLayout
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/container_five"
style="@style/Widget.Signal.TextInputLayout.Registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:background="@drawable/labeled_edit_text_background_inactive"
app:errorEnabled="false"
app:hintEnabled="false"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/container_four"
app:layout_constraintTop_toTopOf="parent">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/code_five"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="48dp"
android:layout_height="56dp"
android:layout_gravity="center"
android:textColor="@color/signal_inverse_primary"
android:textSize="28sp"
android:gravity="center"
android:padding="0dp"
android:textAppearance="@style/Signal.Text.BodyLarge"
tools:text="5" />
</FrameLayout>
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -219,4 +219,5 @@
<dimen name="media_preview_lottie_button_dimen">36dp</dimen>
<dimen name="media_rail_item_size">46dp</dimen>
<dimen name="media_rail_thumbnail_size">44dp</dimen>
<dimen name="registrationactivity_text_view_padding">32dp</dimen>
</resources>

View file

@ -1696,9 +1696,7 @@
<string name="RegistrationActivity_you_must_specify_your_country_code">You must specify your
country code
</string>
<string name="RegistrationActivity_you_must_specify_your_phone_number">You must specify your
phone number
</string>
<string name="RegistrationActivity_please_enter_a_valid_phone_number_to_register">Please enter a valid phone number to register.</string>
<string name="RegistrationActivity_invalid_number">Invalid number</string>
<string name="RegistrationActivity_the_number_you_specified_s_is_invalid">The number you
specified (%s) is invalid.
@ -1729,15 +1727,17 @@
<string name="RegistrationActivity_next">Next</string>
<string name="RegistrationActivity_continue">Continue</string>
<string name="RegistrationActivity_take_privacy_with_you_be_yourself_in_every_message">Take privacy with you.\nBe yourself in every message.</string>
<string name="RegistrationActivity_enter_your_phone_number_to_get_started">Enter your phone number to get started</string>
<string name="RegistrationActivity_enter_your_phone_number">Enter your phone number</string>
<string name="RegistrationActivity_you_will_receive_a_verification_code">You will receive a verification code. Carrier rates may apply.</string>
<string name="RegistrationActivity_you_will_receive_a_verification_code">You will receive a verification code.\nCarrier rates may apply.</string>
<string name="RegistrationActivity_enter_the_code_we_sent_to_s">Enter the code we sent to %s</string>
<string name="RegistrationActivity_make_sure_your_phone_has_a_cellular_signal">Make sure your phone has a cellular signal to receive your SMS or call</string>
<string name="RegistrationActivity_phone_number_description">Phone number</string>
<string name="RegistrationActivity_country_code_description">Country code</string>
<string name="RegistrationActivity_country_code_hint">Country</string>
<string name="RegistrationActivity_call">Call</string>
<string name="RegistrationActivity_verification_code">Verification Code</string>
<string name="RegistrationActivity_resend_code">Resend Code</string>
<!-- RegistrationLockV2Dialog -->
<string name="RegistrationLockV2Dialog_turn_on_registration_lock">Turn on Registration Lock?</string>
@ -3180,11 +3180,13 @@
<string name="CreateKbsPinFragment__create_a_new_pin">Create a new PIN</string>
<string name="CreateKbsPinFragment__you_can_choose_a_new_pin_as_long_as_this_device_is_registered">You can change your PIN as long as this device is registered.</string>
<string name="CreateKbsPinFragment__create_your_pin">Create your PIN</string>
<string name="CreateKbsPinFragment__pins_keep_information_stored_with_signal_encrypted">PINs keep information stored with Signal encrypted so only you can access it. Your profile, settings, and contacts will restore when you reinstall. You won\'t need your PIN to open the app.</string>
<string name="CreateKbsPinFragment__pins_can_help_you_restore_your_account">PINs can help you restore your account and keep your info encrypted with Signal. </string>
<string name="CreateKbsPinFragment__choose_a_stronger_pin">Choose a stronger PIN</string>
<!-- ConfirmKbsPinFragment -->
<string name="ConfirmKbsPinFragment__pins_dont_match">PINs don\'t match. Try again.</string>
<!-- Prompt for the user to repeat entering the PIN in order to help them remember it correctly. -->
<string name="ConfirmKbsPinFragment__re_enter_the_pin_you_just_created">Re-enter the PIN you just created.</string>
<string name="ConfirmKbsPinFragment__confirm_your_pin">Confirm your PIN.</string>
<string name="ConfirmKbsPinFragment__pin_creation_failed">PIN creation failed</string>
<string name="ConfirmKbsPinFragment__your_pin_was_not_saved">Your PIN was not saved. We\'ll prompt you to create a PIN later.</string>
@ -3324,7 +3326,7 @@
<!-- Error message shown if a very large attachment is encountered during the backup creation and causes the backup to fail -->
<string name="LocalBackupJobApi29_your_backup_contains_a_very_large_file">Your backup contains a very large file that cannot be backed up. Please delete it and create a new backup.</string>
<string name="LocalBackupJobApi29_tap_to_manage_backups">Tap to manage backups.</string>
<string name="RegistrationActivity_wrong_number">Wrong number</string>
<string name="RegistrationActivity_wrong_number">Wrong number?</string>
<string name="RegistrationActivity_call_me_instead_available_in">Call me instead \n (Available in %1$02d:%2$02d)</string>
<string name="RegistrationActivity_contact_signal_support">Contact Signal Support</string>
<string name="RegistrationActivity_code_support_subject">Signal Registration - Verification Code for Android</string>
@ -5467,6 +5469,7 @@
<string name="GroupStoryEducationSheet__all_group_chat_members">All group chat members can view story replies.</string>
<!-- Button label to dismiss sheet -->
<string name="GroupStoryEducationSheet__next">Next</string>
<string name="Registration_country_code_entry_hint">+0</string>
<!-- EOF -->

View file

@ -500,6 +500,25 @@
<item name="hintTextAppearance">@style/Signal.Text.LabelMedium</item>
</style>
<style name="Widget.Signal.TextInputLayout.Registration" parent="Widget.MaterialComponents.TextInputLayout.FilledBox">
<item name="boxBackgroundColor">@color/signal_colorSurfaceVariant</item>
<item name="boxCornerRadiusTopEnd">4dp</item>
<item name="boxCornerRadiusTopStart">4dp</item>
<item name="hintTextColor">@color/signal_colorPrimary</item>
<item name="android:textColorHint">@color/signal_colorPrimary</item>
</style>
<style name="Widget.Signal.TextInputLayout.Registration.Dropdown" parent="Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu">
<item name="android:paddingStart">12dp</item>
<item name="android:paddingEnd">6dp</item>
<item name="boxBackgroundColor">@color/signal_colorSurfaceVariant</item>
<item name="boxCornerRadiusTopEnd">2dp</item>
<item name="boxCornerRadiusTopStart">2dp</item>
<item name="hintTextColor">@color/signal_colorPrimary</item>
<item name="android:textColorHint">@color/signal_colorPrimary</item>
</style>
<style name="Widget.MaterialComponents.Button.TextButton.Dialog">
<item name="android:minWidth">@dimen/mtrl_btn_dialog_btn_min_width</item>
<!-- Allow buttons to span more than one line so that the end isn't cut