diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/registration/ActionCountDownButton.kt b/app/src/main/java/org/thoughtcrime/securesms/components/registration/ActionCountDownButton.kt new file mode 100644 index 000000000..0c9994ce0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/registration/ActionCountDownButton.kt @@ -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) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java b/app/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java deleted file mode 100644 index 74ccc98fc..000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/registration/CallMeCountDownView.java +++ /dev/null @@ -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); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java b/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java deleted file mode 100644 index 466f36eac..000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.java +++ /dev/null @@ -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 codes = new ArrayList<>(6); - private final List 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 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); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.kt new file mode 100644 index 000000000..184973ef7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/registration/VerificationCodeView.kt @@ -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 = 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) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberEnterPhoneNumberFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberEnterPhoneNumberFragment.kt index 1c644412a..65609ac21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberEnterPhoneNumberFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberEnterPhoneNumberFragment.kt @@ -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) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseKbsPinFragment.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseKbsPinFragment.java index b9f0f0e9f..8655a2ab9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseKbsPinFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/BaseKbsPinFragment.java @@ -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 extends LoggingFragment { +public abstract class BaseKbsPinFragment 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 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 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 extends return keyboardToggle; } - protected TextView getConfirm() { + protected CircularProgressMaterialButton getConfirm() { return confirm; } @@ -173,8 +157,6 @@ abstract class BaseKbsPinFragment 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(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java deleted file mode 100644 index c3c0265d8..000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.java +++ /dev/null @@ -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 { - - 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); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.kt new file mode 100644 index 000000000..92172b779 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinFragment.kt @@ -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() { + + 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) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java index ea0a9d2a7..0c852ce91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/lock/v2/ConfirmKbsPinViewModel.java @@ -16,8 +16,8 @@ final class ConfirmKbsPinViewModel extends ViewModel implements BaseKbsPinViewMo private final DefaultValueLiveData userEntry = new DefaultValueLiveData<>(KbsPin.EMPTY); private final DefaultValueLiveData keyboard = new DefaultValueLiveData<>(PinKeyboardType.NUMERIC); - private final DefaultValueLiveData saveAnimation = new DefaultValueLiveData<>(SaveAnimation.NONE); - private final DefaultValueLiveData