Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b439322
adding debug flag for enabling the new combined signup/choose server …
ouchadam Mar 16, 2022
2cbbfca
adding helper for recalculating percentage heights within a constrain…
ouchadam Mar 23, 2022
7e5c3df
adding combined server selection/sign up fragment
ouchadam Mar 23, 2022
1198344
replacing hardcoded strings with resources
ouchadam Mar 28, 2022
585ac4b
extracting common textinputlayer interactions to their own extensions…
ouchadam Mar 28, 2022
c3ab895
adding missing inputType, fixes max lines and ime option not being ta…
ouchadam Mar 28, 2022
e8791fb
renaming reset action to also capture registering along with login
ouchadam Mar 28, 2022
53675b5
reducing the edit button min width in order to match designs
ouchadam Mar 28, 2022
bc4566d
temporarily hiding the server selection edit button whilst building o…
ouchadam Mar 28, 2022
12ae35f
reordering methods to match usage
ouchadam Mar 28, 2022
d302875
providing more context to screen opening function
ouchadam Mar 28, 2022
c83882d
updating debug switch copy to better reflect the feature
ouchadam Mar 28, 2022
aa5054c
defaulting the password field to the password type by default in xml
ouchadam Mar 28, 2022
32b54e1
using continuation copy for combined sign up SSO buttons
ouchadam Mar 28, 2022
4e215fa
adding changelog entry
ouchadam Mar 28, 2022
4c1c9a5
fixing new file header date
ouchadam Mar 30, 2022
468a81e
fixing type in debug key names
ouchadam Mar 31, 2022
c45c421
adding docs around the realigning of constraint layout child percentages
ouchadam Mar 31, 2022
801fb90
using a consistent name for the combined register screens and events
ouchadam Mar 31, 2022
13fb4e5
extracting hardcoded error message to the resources
ouchadam Mar 31, 2022
5120e7a
updating feature flag name to match feature name in other places
ouchadam Apr 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5277.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding combined account creation and server selection screen as part of the new FTUE
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
<item name="endIconTint">?vctr_content_secondary</item>
</style>

<style name="Widget.Vector.TextInputLayout.Username">
<item name="endIconMode">clear_text</item>
<item name="endIconTint">?vctr_content_secondary</item>
</style>

<style name="Widget.Vector.TextInputLayout.Form">
<item name="boxStrokeColor">@color/form_edit_text_stroke_color_selector</item>
<item name="android:textColorHint">@color/form_edit_text_hint_color_selector</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.onboardingPersonalize,
factory = VectorFeatures::isOnboardingPersonalizeEnabled
),
createBooleanFeature(
label = "FTUE Combined register",
key = DebugFeatureKeys.onboardingCombinedRegister,
factory = VectorFeatures::isOnboardingCombinedRegisterEnabled
),
createBooleanFeature(
label = "Live location sharing",
key = DebugFeatureKeys.liveLocationSharing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class DebugVectorFeatures(
override fun isOnboardingPersonalizeEnabled(): Boolean = read(DebugFeatureKeys.onboardingPersonalize)
?: vectorFeatures.isOnboardingPersonalizeEnabled()

override fun isOnboardingCombinedRegisterEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedRegister)
?: vectorFeatures.isOnboardingCombinedRegisterEnabled()

override fun isLiveLocationEnabled(): Boolean = read(DebugFeatureKeys.liveLocationSharing)
?: vectorFeatures.isLiveLocationEnabled()

Expand Down Expand Up @@ -107,7 +110,8 @@ private fun <T : Enum<T>> enumPreferencesKey(type: KClass<T>) = stringPreference
object DebugFeatureKeys {
val onboardingAlreadyHaveAnAccount = booleanPreferencesKey("onboarding-already-have-an-account")
val onboardingSplashCarousel = booleanPreferencesKey("onboarding-splash-carousel")
val onboardingUseCase = booleanPreferencesKey("onbboarding-splash-carousel")
val onboardingPersonalize = booleanPreferencesKey("onbboarding-personalize")
val onboardingUseCase = booleanPreferencesKey("onboarding-splash-carousel")
val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@

package im.vector.app.core.extensions

import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.children
import androidx.core.view.doOnLayout
import kotlin.math.roundToInt

fun ConstraintLayout.updateConstraintSet(block: (ConstraintSet) -> Unit) {
ConstraintSet().let {
Expand All @@ -26,3 +30,21 @@ fun ConstraintLayout.updateConstraintSet(block: (ConstraintSet) -> Unit) {
it.applyTo(this)
}
}

/**
* Helper to recalculate all ConstraintLayout child views with percentage based height against the parent's height.
* This is helpful when using a ConstraintLayout within a ScrollView as any percentages will use the total scrolling size
* instead of the viewport/ScrollView height
*/
fun ConstraintLayout.realignPercentagesToParent() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be interesting to have a comment explaining the purpose of the method? If my understanding is correct it only updates the children views according to the height but there is no mention of that in the function name. Do you think the current naming is sufficient?

Copy link
Contributor Author

@ouchadam ouchadam Mar 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great point, will do! the tl;dr is that constraint layouts within scrollviews use the total scrolling height rather than the viewport, which means percentages change size depending on how much they can scroll

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doOnLayout {
val rootHeight = (parent as View).height
children.forEach { child ->
val params = child.layoutParams as ConstraintLayout.LayoutParams
if (params.matchConstraintPercentHeight != 1.0f) {
params.height = (rootHeight * params.matchConstraintPercentHeight).roundToInt()
child.layoutParams = params
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.core.extensions

import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.flow.map
import reactivecircus.flowbinding.android.widget.textChanges

fun TextInputLayout.editText() = this.editText!!

/**
* Detect if a field starts or ends with spaces
*/
fun TextInputLayout.hasSurroundingSpaces() = editText().text.toString().let { it.trim() != it }

fun TextInputLayout.hasContentFlow(mapper: (CharSequence) -> CharSequence = { it }) = editText().textChanges().map { mapper(it).isNotEmpty() }

fun TextInputLayout.content() = editText().text.toString()
2 changes: 2 additions & 0 deletions vector/src/main/java/im/vector/app/features/VectorFeatures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface VectorFeatures {
fun isOnboardingSplashCarouselEnabled(): Boolean
fun isOnboardingUseCaseEnabled(): Boolean
fun isOnboardingPersonalizeEnabled(): Boolean
fun isOnboardingCombinedRegisterEnabled(): Boolean
fun isLiveLocationEnabled(): Boolean

enum class OnboardingVariant {
Expand All @@ -40,5 +41,6 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isOnboardingSplashCarouselEnabled() = true
override fun isOnboardingUseCaseEnabled() = true
override fun isOnboardingPersonalizeEnabled() = false
override fun isOnboardingCombinedRegisterEnabled() = false
override fun isLiveLocationEnabled(): Boolean = BuildConfig.ENABLE_LIVE_LOCATION_SHARING
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
error++
}
if (isSignupMode && isNumericOnlyUserIdForbidden && login.isDigitsOnly()) {
views.loginFieldTil.error = "The homeserver does not accept username with only digits."
views.loginFieldTil.error = getString(R.string.error_forbidden_digits_only_username)
error++
}
if (password.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) :
LinearLayout(context, attrs, defStyle) {

interface InteractionListener {
fun interface InteractionListener {
fun onProviderSelected(id: String?)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ sealed interface OnboardingAction : VectorViewModelAction {

// Login or Register, depending on the signMode
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
data class Register(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerning the naming, I think we should stick to either register or signUp to be consistent accross the app. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100% agree, would favour using register & login.

For now I'll rename the code introduced by the PR rather than renaming the entire onboarding package as it will cause a lot of noise in the diff

object StopEmailValidationCheck : OnboardingAction

data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
Expand All @@ -51,7 +52,7 @@ sealed interface OnboardingAction : VectorViewModelAction {
object ResetHomeServerType : ResetAction
object ResetHomeServerUrl : ResetAction
object ResetSignMode : ResetAction
object ResetLogin : ResetAction
object ResetAuthenticationAttempt : ResetAction
object ResetResetPassword : ResetAction

// Homeserver history
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {

object OpenUseCaseSelection : OnboardingViewEvents()
object OpenServerSelection : OnboardingViewEvents()
object OpenCombinedRegister : OnboardingViewEvents()
data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
object OnLoginFlowRetrieved : OnboardingViewEvents()
data class OnSignModeSelected(val signMode: SignMode) : OnboardingViewEvents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class OnboardingViewModel @AssistedInject constructor(
is OnboardingAction.InitWith -> handleInitWith(action)
is OnboardingAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
is OnboardingAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
is OnboardingAction.Register -> handleRegisterWith(action).also { lastAction = action }
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
is OnboardingAction.ResetPassword -> handleResetPassword(action)
Expand Down Expand Up @@ -276,7 +277,7 @@ class OnboardingViewModel @AssistedInject constructor(
}
}

private fun handleRegisterWith(action: OnboardingAction.LoginOrRegister) {
private fun handleRegisterWith(action: OnboardingAction.Register) {
reAuthHelper.data = action.password
handleRegisterAction(RegisterAction.CreateAccount(
action.username,
Expand Down Expand Up @@ -312,7 +313,7 @@ class OnboardingViewModel @AssistedInject constructor(
}
}
}
OnboardingAction.ResetSignMode -> {
OnboardingAction.ResetSignMode -> {
setState {
copy(
isLoading = false,
Expand All @@ -322,13 +323,13 @@ class OnboardingViewModel @AssistedInject constructor(
)
}
}
OnboardingAction.ResetLogin -> {
OnboardingAction.ResetAuthenticationAttempt -> {
viewModelScope.launch {
authenticationService.cancelPendingLoginOrRegistration()
setState { copy(isLoading = false) }
}
}
OnboardingAction.ResetResetPassword -> {
OnboardingAction.ResetResetPassword -> {
setState {
copy(
isLoading = false,
Expand Down Expand Up @@ -356,7 +357,13 @@ class OnboardingViewModel @AssistedInject constructor(

private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) {
setState { copy(useCase = action.useCase) }
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
when (vectorFeatures.isOnboardingCombinedRegisterEnabled()) {
true -> {
handle(OnboardingAction.UpdateHomeServer(matrixOrgUrl))
OnboardingViewEvents.OpenCombinedRegister
}
false -> _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
}
}

private fun resetUseCase() {
Expand Down Expand Up @@ -459,7 +466,7 @@ class OnboardingViewModel @AssistedInject constructor(
when (state.signMode) {
SignMode.Unknown -> error("Developer error, invalid sign mode")
SignMode.SignIn -> handleLogin(action)
SignMode.SignUp -> handleRegisterWith(action)
SignMode.SignUp -> handleRegisterWith(OnboardingAction.Register(action.username, action.password, action.initialDeviceName))
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 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.
Expand Down Expand Up @@ -191,7 +191,7 @@ class FtueAuthCaptchaFragment @Inject constructor(
}

override fun resetViewModel() {
viewModel.handle(OnboardingAction.ResetLogin)
viewModel.handle(OnboardingAction.ResetAuthenticationAttempt)
}

override fun updateWithState(state: OnboardingViewState) {
Expand Down
Loading