From 76173090587401016cb00f3e4c412532f5b18af6 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 19 May 2022 16:40:27 +0100 Subject: [PATCH] hooking up, styling and applying copy to the phone number fragment --- .../im/vector/app/core/di/FragmentModule.kt | 6 ++ .../ftueauth/FtueAuthEmailEntryFragment.kt | 8 +-- .../ftueauth/FtueAuthPhoneEntryFragment.kt | 42 ++++++++----- .../onboarding/ftueauth/FtueAuthVariant.kt | 13 ++-- .../onboarding/ftueauth/PhoneNumberParser.kt | 47 ++++++++++++++ .../src/main/res/drawable/ic_ftue_phone.xml | 11 ++++ .../res/layout/fragment_ftue_phone_input.xml | 62 +++++++++---------- vector/src/main/res/values/donottranslate.xml | 4 ++ 8 files changed, 138 insertions(+), 55 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/ftueauth/PhoneNumberParser.kt create mode 100644 vector/src/main/res/drawable/ic_ftue_phone.xml diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index e76f0ad672..143818f3e5 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -110,6 +110,7 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFrag import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyWaitForEmailFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthPersonalizationCompleteFragment +import im.vector.app.features.onboarding.ftueauth.FtueAuthPhoneEntryFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordMailConfirmationFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordSuccessFragment @@ -509,6 +510,11 @@ interface FragmentModule { @FragmentKey(FtueAuthEmailEntryFragment::class) fun bindFtueAuthEmailEntryFragment(fragment: FtueAuthEmailEntryFragment): Fragment + @Binds + @IntoMap + @FragmentKey(FtueAuthPhoneEntryFragment::class) + fun bindFtueAuthPhoneEntryFragment(fragment: FtueAuthPhoneEntryFragment): Fragment + @Binds @IntoMap @FragmentKey(FtueAuthChooseDisplayNameFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthEmailEntryFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthEmailEntryFragment.kt index 7d8dab73ec..1d85c75fa1 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthEmailEntryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthEmailEntryFragment.kt @@ -26,16 +26,16 @@ import im.vector.app.core.extensions.clearErrorOnChange import im.vector.app.core.extensions.content import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.setOnImeDoneListener -import im.vector.app.databinding.FragmentFtuePhoneInputBinding +import im.vector.app.databinding.FragmentFtueEmailInputBinding import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.RegisterAction import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import javax.inject.Inject -class FtueAuthEmailEntryFragment @Inject constructor() : AbstractFtueAuthFragment() { +class FtueAuthEmailEntryFragment @Inject constructor() : AbstractFtueAuthFragment() { - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtuePhoneInputBinding { - return FragmentFtuePhoneInputBinding.inflate(inflater, container, false) + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueEmailInputBinding { + return FragmentFtueEmailInputBinding.inflate(inflater, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneEntryFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneEntryFragment.kt index 5f9a962aa7..70c56bacb8 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneEntryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneEntryFragment.kt @@ -21,13 +21,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.lifecycle.lifecycleScope +import im.vector.app.R import im.vector.app.core.extensions.associateContentStateWith import im.vector.app.core.extensions.autofillPhoneNumber import im.vector.app.core.extensions.content import im.vector.app.core.extensions.editText -import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.setOnImeDoneListener -import im.vector.app.databinding.FragmentFtueEmailInputBinding +import im.vector.app.databinding.FragmentFtuePhoneInputBinding import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.RegisterAction import kotlinx.coroutines.flow.launchIn @@ -36,10 +36,12 @@ import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import reactivecircus.flowbinding.android.widget.textChanges import javax.inject.Inject -class FtueAuthPhoneEntryFragment @Inject constructor() : AbstractFtueAuthFragment() { +class FtueAuthPhoneEntryFragment @Inject constructor( + private val phoneNumberParser: PhoneNumberParser +) : AbstractFtueAuthFragment() { - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueEmailInputBinding { - return FragmentFtueEmailInputBinding.inflate(inflater, container, false) + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtuePhoneInputBinding { + return FragmentFtuePhoneInputBinding.inflate(inflater, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -48,27 +50,35 @@ class FtueAuthPhoneEntryFragment @Inject constructor() : AbstractFtueAuthFragmen } private fun setupViews() { - views.emailEntryInput.associateContentStateWith(button = views.emailEntrySubmit) - views.emailEntryInput.setOnImeDoneListener { updateEmail() } - views.emailEntrySubmit.debouncedClicks { updateEmail() } + views.phoneEntryInput.associateContentStateWith(button = views.phoneEntrySubmit) + views.phoneEntryInput.setOnImeDoneListener { updatePhoneNumber() } + views.phoneEntrySubmit.debouncedClicks { updatePhoneNumber() } - views.emailEntryInput.editText().textChanges() + views.phoneEntryInput.editText().textChanges() .onEach { - views.emailEntryInput.error = null - views.emailEntrySubmit.isEnabled = it.isEmail() + views.phoneEntryInput.error = null + views.phoneEntrySubmit.isEnabled = it.isNotBlank() } .launchIn(viewLifecycleOwner.lifecycleScope) - views.emailEntryInput.autofillPhoneNumber() + views.phoneEntryInput.autofillPhoneNumber() } - private fun updateEmail() { - val email = views.emailEntryInput.content() - viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AddThreePid(RegisterThreePid.Email(email)))) + private fun updatePhoneNumber() { + val number = views.phoneEntryInput.content() + + when (val result = phoneNumberParser.parseInternationalNumber(number)) { + PhoneNumberParser.Result.ErrorInvalidNumber -> views.phoneEntryInput.error = getString(R.string.login_msisdn_error_other) + PhoneNumberParser.Result.ErrorMissingInternationalCode -> views.phoneEntryInput.error = getString(R.string.login_msisdn_error_not_international) + is PhoneNumberParser.Result.Success -> { + val (countryCode, phoneNumber) = result + viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AddThreePid(RegisterThreePid.Msisdn(phoneNumber, countryCode)))) + } + } } override fun onError(throwable: Throwable) { - views.emailEntryInput.error = errorFormatter.toHumanReadable(throwable) + views.phoneEntryInput.error = errorFormatter.toHumanReadable(throwable) } override fun resetViewModel() { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index 6ac922cece..3fa1854dd5 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -395,10 +395,15 @@ class FtueAuthVariant( } private fun onMsisdn(stage: Stage) { - addRegistrationStageFragmentToBackstack( - FtueAuthGenericTextInputFormFragment::class.java, - FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory), - ) + when { + vectorFeatures.isOnboardingCombinedRegisterEnabled() -> addRegistrationStageFragmentToBackstack( + FtueAuthPhoneEntryFragment::class.java + ) + else -> addRegistrationStageFragmentToBackstack( + FtueAuthGenericTextInputFormFragment::class.java, + FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory), + ) + } } private fun onEmail(stage: Stage) { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/PhoneNumberParser.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/PhoneNumberParser.kt new file mode 100644 index 0000000000..3858f9d5b4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/PhoneNumberParser.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.onboarding.ftueauth + +import com.google.i18n.phonenumbers.NumberParseException +import com.google.i18n.phonenumbers.PhoneNumberUtil +import javax.inject.Inject + +class PhoneNumberParser @Inject constructor() { + + fun parseInternationalNumber(rawPhoneNumber: String): Result { + return when { + rawPhoneNumber.doesNotStartWith("+") -> Result.ErrorMissingInternationalCode + else -> parseNumber(rawPhoneNumber) + } + } + + private fun parseNumber(rawPhoneNumber: String) = try { + val instance = PhoneNumberUtil.getInstance() + val phoneNumber = instance.parse(rawPhoneNumber, null) + Result.Success(instance.getRegionCodeForCountryCode(phoneNumber.countryCode), rawPhoneNumber) + } catch (e: NumberParseException) { + Result.ErrorInvalidNumber + } + + sealed interface Result { + object ErrorMissingInternationalCode : Result + object ErrorInvalidNumber : Result + data class Success(val countryCode: String, val phoneNumber: String) : Result + } + + private fun String.doesNotStartWith(input: String) = !startsWith(input) +} diff --git a/vector/src/main/res/drawable/ic_ftue_phone.xml b/vector/src/main/res/drawable/ic_ftue_phone.xml new file mode 100644 index 0000000000..53884d6d96 --- /dev/null +++ b/vector/src/main/res/drawable/ic_ftue_phone.xml @@ -0,0 +1,11 @@ + + + diff --git a/vector/src/main/res/layout/fragment_ftue_phone_input.xml b/vector/src/main/res/layout/fragment_ftue_phone_input.xml index 0cfcfea7cc..32b54bcef3 100644 --- a/vector/src/main/res/layout/fragment_ftue_phone_input.xml +++ b/vector/src/main/res/layout/fragment_ftue_phone_input.xml @@ -13,14 +13,14 @@ android:layout_height="wrap_content"> + app:layout_constraintBottom_toTopOf="@id/phoneEntryHeaderSubtitle" + app:layout_constraintEnd_toEndOf="@id/phoneEntryGutterEnd" + app:layout_constraintStart_toStartOf="@id/phoneEntryGutterStart" + app:layout_constraintTop_toBottomOf="@id/phoneEntryHeaderIcon" /> + app:layout_constraintEnd_toEndOf="@id/phoneEntryGutterEnd" + app:layout_constraintStart_toStartOf="@id/phoneEntryGutterStart" + app:layout_constraintTop_toBottomOf="@id/phoneEntryHeaderTitle" /> + app:layout_constraintTop_toBottomOf="@id/phoneEntryHeaderSubtitle" /> @@ -108,22 +108,22 @@ android:id="@+id/entrySpacing" android:layout_width="match_parent" android:layout_height="0dp" - app:layout_constraintBottom_toTopOf="@id/emailEntrySubmit" + app:layout_constraintBottom_toTopOf="@id/phoneEntrySubmit" app:layout_constraintHeight_percent="0.03" - app:layout_constraintTop_toBottomOf="@id/emailEntryInput" + app:layout_constraintTop_toBottomOf="@id/phoneEntryInput" app:layout_constraintVertical_bias="0" app:layout_constraintVertical_chainStyle="packed" />