Merge pull request #4927 from vector-im/feature/adm/ftue-usecase

FTUE Use case UI/UX
This commit is contained in:
Adam Brown 2022-01-13 14:48:49 +00:00 committed by GitHub
commit a208b48c15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 448 additions and 35 deletions

View file

@ -36,13 +36,18 @@ class DebugFeaturesStateFactory @Inject constructor(
),
createBooleanFeature(
label = "FTUE Splash - I already have an account",
factory = VectorFeatures::isAlreadyHaveAccountSplashEnabled,
key = DebugFeatureKeys.alreadyHaveAnAccount
key = DebugFeatureKeys.onboardingAlreadyHaveAnAccount,
factory = VectorFeatures::isOnboardingAlreadyHaveAccountSplashEnabled
),
createBooleanFeature(
label = "FTUE Splash - Carousel",
factory = VectorFeatures::isSplashCarouselEnabled,
key = DebugFeatureKeys.splashCarousel
label = "FTUE Splash - carousel",
key = DebugFeatureKeys.onboardingSplashCarousel,
factory = VectorFeatures::isOnboardingSplashCarouselEnabled
),
createBooleanFeature(
label = "FTUE Use Case",
key = DebugFeatureKeys.onboardingUseCase,
factory = VectorFeatures::isOnboardingUseCaseEnabled
)
))
}

View file

@ -43,10 +43,13 @@ class DebugVectorFeatures(
return readPreferences().getEnum<VectorFeatures.OnboardingVariant>() ?: vectorFeatures.onboardingVariant()
}
override fun isAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.alreadyHaveAnAccount)
?: vectorFeatures.isAlreadyHaveAccountSplashEnabled()
override fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.onboardingAlreadyHaveAnAccount)
?: vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()
override fun isSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.splashCarousel) ?: vectorFeatures.isSplashCarouselEnabled()
override fun isOnboardingSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.onboardingSplashCarousel)
?: vectorFeatures.isOnboardingSplashCarouselEnabled()
override fun isOnboardingUseCaseEnabled(): Boolean = read(DebugFeatureKeys.onboardingUseCase) ?: vectorFeatures.isOnboardingUseCaseEnabled()
fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
if (value == null) {
@ -96,6 +99,7 @@ private inline fun <reified T : Enum<T>> enumPreferencesKey() = enumPreferencesK
private fun <T : Enum<T>> enumPreferencesKey(type: KClass<T>) = stringPreferencesKey("enum-${type.simpleName}")
object DebugFeatureKeys {
val alreadyHaveAnAccount = booleanPreferencesKey("already-have-an-account")
val splashCarousel = booleanPreferencesKey("splash-carousel")
val onboardingAlreadyHaveAnAccount = booleanPreferencesKey("onboarding-already-have-an-account")
val onboardingSplashCarousel = booleanPreferencesKey("onboarding-splash-carousel")
val onboardingUseCase = booleanPreferencesKey("onbboarding-splash-carousel")
}

View file

@ -104,6 +104,7 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthServerSelectionFragmen
import im.vector.app.features.onboarding.ftueauth.FtueAuthSignUpSignInSelectionFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthSplashCarouselFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthSplashFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthUseCaseFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthWaitForEmailFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthWebFragment
import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsFragment
@ -449,6 +450,11 @@ interface FragmentModule {
@FragmentKey(FtueAuthSplashCarouselFragment::class)
fun bindFtueAuthSplashCarouselFragment(fragment: FtueAuthSplashCarouselFragment): Fragment
@Binds
@IntoMap
@FragmentKey(FtueAuthUseCaseFragment::class)
fun bindFtueAuthUseCaseFragment(fragment: FtueAuthUseCaseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(FtueAuthWaitForEmailFragment::class)

View file

@ -82,7 +82,7 @@ fun TextView.setTextWithColoredPart(@StringRes fullTextRes: Int,
fun TextView.setTextWithColoredPart(fullText: String,
coloredPart: String,
@AttrRes colorAttribute: Int = R.attr.colorPrimary,
underline: Boolean = false,
underline: Boolean = true,
onClick: (() -> Unit)? = null) {
val color = ThemeUtils.getColor(context, colorAttribute)
@ -101,7 +101,6 @@ fun TextView.setTextWithColoredPart(fullText: String,
override fun updateDrawState(ds: TextPaint) {
ds.color = color
ds.isUnderlineText = !underline
}
}
setSpan(clickableSpan, index, index + coloredPart.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

View file

@ -21,10 +21,9 @@ import im.vector.app.BuildConfig
interface VectorFeatures {
fun onboardingVariant(): OnboardingVariant
fun isAlreadyHaveAccountSplashEnabled(): Boolean
fun isSplashCarouselEnabled(): Boolean
fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean
fun isOnboardingSplashCarouselEnabled(): Boolean
fun isOnboardingUseCaseEnabled(): Boolean
enum class OnboardingVariant {
LEGACY,
@ -35,6 +34,7 @@ interface VectorFeatures {
class DefaultVectorFeatures : VectorFeatures {
override fun onboardingVariant(): VectorFeatures.OnboardingVariant = BuildConfig.ONBOARDING_VARIANT
override fun isAlreadyHaveAccountSplashEnabled() = true
override fun isSplashCarouselEnabled() = false
override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true
override fun isOnboardingSplashCarouselEnabled() = false
override fun isOnboardingUseCaseEnabled() = false
}

View file

@ -0,0 +1,24 @@
/*
* 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
enum class FtueUseCase {
FRIENDS_FAMILY,
TEAMS,
COMMUNITIES,
SKIP
}

View file

@ -31,6 +31,8 @@ sealed class OnboardingAction : VectorViewModelAction {
data class UpdateServerType(val serverType: ServerType) : OnboardingAction()
data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction()
data class UpdateUseCase(val useCase: FtueUseCase) : OnboardingAction()
object ResetUseCase : OnboardingAction()
data class UpdateSignMode(val signMode: SignMode) : OnboardingAction()
data class LoginWithToken(val loginToken: String) : OnboardingAction()
data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction()

View file

@ -34,6 +34,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {
// Navigation event
object OpenUseCaseSelection : OnboardingViewEvents()
object OpenServerSelection : OnboardingViewEvents()
data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
object OnLoginFlowRetrieved : OnboardingViewEvents()

View file

@ -35,6 +35,7 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ensureTrailingSlash
import im.vector.app.features.VectorFeatures
import im.vector.app.features.login.HomeServerConnectionConfigFactory
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.login.LoginMode
@ -71,7 +72,8 @@ class OnboardingViewModel @AssistedInject constructor(
private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
private val reAuthHelper: ReAuthHelper,
private val stringProvider: StringProvider,
private val homeServerHistoryService: HomeServerHistoryService
private val homeServerHistoryService: HomeServerHistoryService,
private val vectorFeatures: VectorFeatures
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
@AssistedFactory
@ -123,6 +125,8 @@ class OnboardingViewModel @AssistedInject constructor(
when (action) {
is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow)
is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow)
is OnboardingAction.UpdateUseCase -> handleUpdateUseCase()
OnboardingAction.ResetUseCase -> resetUseCase()
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
is OnboardingAction.InitWith -> handleInitWith(action)
@ -154,15 +158,28 @@ class OnboardingViewModel @AssistedInject constructor(
if (homeServerConnectionConfig == null) {
// Url is invalid, in this case, just use the regular flow
Timber.w("Url from config url was invalid: $configUrl")
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
continueToPageAfterSplash(onboardingFlow)
} else {
getLoginFlow(homeServerConnectionConfig, ServerType.Other)
}
} else {
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
continueToPageAfterSplash(onboardingFlow)
}
}
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
val nextOnboardingStep = when (onboardingFlow) {
OnboardingFlow.SignUp -> if (vectorFeatures.isOnboardingUseCaseEnabled()) {
OnboardingViewEvents.OpenUseCaseSelection
} else {
OnboardingViewEvents.OpenServerSelection
}
OnboardingFlow.SignIn,
OnboardingFlow.SignInSignUp -> OnboardingViewEvents.OpenServerSelection
}
_viewEvents.post(nextOnboardingStep)
}
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
// It happens when we get the login flow, or during direct authentication.
// So alter the homeserver config and retrieve again the login flow
@ -441,6 +458,15 @@ class OnboardingViewModel @AssistedInject constructor(
}
}
private fun handleUpdateUseCase() {
// TODO act on the use case selection
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
}
private fun resetUseCase() {
// TODO remove stored use case
}
private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) {
setState {
copy(

View file

@ -69,7 +69,7 @@ class FtueAuthSplashCarouselFragment @Inject constructor(
views.loginSplashSubmit.debouncedClicks { getStarted() }
views.loginSplashAlreadyHaveAccount.apply {
isVisible = vectorFeatures.isAlreadyHaveAccountSplashEnabled()
isVisible = vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()
debouncedClicks { alreadyHaveAnAccount() }
}
@ -111,7 +111,7 @@ class FtueAuthSplashCarouselFragment @Inject constructor(
}
private fun getStarted() {
val getStartedFlow = if (vectorFeatures.isAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp
val getStartedFlow = if (vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp
viewModel.handle(OnboardingAction.OnGetStarted(resetLoginConfig = false, onboardingFlow = getStartedFlow))
}

View file

@ -55,7 +55,7 @@ class FtueAuthSplashFragment @Inject constructor(
private fun setupViews() {
views.loginSplashSubmit.debouncedClicks { getStarted() }
views.loginSplashAlreadyHaveAccount.apply {
isVisible = vectorFeatures.isAlreadyHaveAccountSplashEnabled()
isVisible = vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()
debouncedClicks { alreadyHaveAnAccount() }
}
@ -70,7 +70,7 @@ class FtueAuthSplashFragment @Inject constructor(
}
private fun getStarted() {
val getStartedFlow = if (vectorFeatures.isAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp
val getStartedFlow = if (vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp
viewModel.handle(OnboardingAction.OnGetStarted(resetLoginConfig = false, onboardingFlow = getStartedFlow))
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2021 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 android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.annotation.StringRes
import im.vector.app.R
import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.databinding.FragmentFtueAuthUseCaseBinding
import im.vector.app.features.login.ServerType
import im.vector.app.features.onboarding.FtueUseCase
import im.vector.app.features.onboarding.OnboardingAction
import javax.inject.Inject
class FtueAuthUseCaseFragment @Inject constructor() : AbstractFtueAuthFragment<FragmentFtueAuthUseCaseBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueAuthUseCaseBinding {
return FragmentFtueAuthUseCaseBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupViews()
}
private fun setupViews() {
views.useCaseOptionOne.setUseCase(R.string.ftue_auth_use_case_option_one, FtueUseCase.FRIENDS_FAMILY)
views.useCaseOptionTwo.setUseCase(R.string.ftue_auth_use_case_option_two, FtueUseCase.TEAMS)
views.useCaseOptionThree.setUseCase(R.string.ftue_auth_use_case_option_three, FtueUseCase.COMMUNITIES)
views.useCaseSkip.setTextWithColoredPart(
fullTextRes = R.string.ftue_auth_use_case_skip,
coloredTextRes = R.string.ftue_auth_use_case_skip_partial,
underline = false,
colorAttribute = R.attr.colorAccent,
onClick = { viewModel.handle(OnboardingAction.UpdateUseCase(FtueUseCase.SKIP)) }
)
views.useCaseConnectToServer.setOnClickListener {
viewModel.handle(OnboardingAction.UpdateServerType(ServerType.Other))
}
}
override fun resetViewModel() {
viewModel.handle(OnboardingAction.ResetUseCase)
}
private fun TextView.setUseCase(@StringRes label: Int, useCase: FtueUseCase) {
setText(label)
debouncedClicks {
viewModel.handle(OnboardingAction.UpdateUseCase(useCase))
}
}
}

View file

@ -113,7 +113,7 @@ class FtueAuthVariant(
}
private fun addFirstFragment() {
val splashFragment = when (vectorFeatures.isSplashCarouselEnabled()) {
val splashFragment = when (vectorFeatures.isOnboardingSplashCarouselEnabled()) {
true -> FtueAuthSplashCarouselFragment::class.java
else -> FtueAuthSplashFragment::class.java
}
@ -155,13 +155,16 @@ class FtueAuthVariant(
activity.addFragmentToBackstack(views.loginFragmentContainer,
FtueAuthServerSelectionFragment::class.java,
option = { ft ->
activity.findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// Disable transition of text
// findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// No transition here now actually
// findViewById<View?>(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// TODO Disabled because it provokes a flickering
// ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
if (vectorFeatures.isOnboardingUseCaseEnabled()) {
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
} else {
activity.findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// TODO Disabled because it provokes a flickering
// Disable transition of text
// findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// No transition here now actually
// findViewById<View?>(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
}
})
is OnboardingViewEvents.OnServerSelectionDone -> onServerSelectionDone(viewEvents)
is OnboardingViewEvents.OnSignModeSelected -> onSignModeSelected(viewEvents)
@ -212,6 +215,11 @@ class FtueAuthVariant(
is OnboardingViewEvents.Loading ->
// This is handled by the Fragments
Unit
OnboardingViewEvents.OpenUseCaseSelection -> {
activity.addFragmentToBackstack(views.loginFragmentContainer,
FtueAuthUseCaseFragment::class.java,
option = commonOption)
}
}.exhaustive
}

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="30"
android:viewportHeight="30">
<path
android:pathData="M15,30C23.2843,30 30,23.2843 30,15C30,6.7157 23.2843,0 15,0C6.7157,0 0,6.7157 0,15C0,23.2843 6.7157,30 15,30Z"
android:fillColor="#0DBD8B"/>
<path
android:pathData="M5.25,5.25h19.5v19.5h-19.5z"
android:fillColor="#0DBD8B"/>
<path
android:pathData="M24.75,15C24.75,20.3848 20.3848,24.75 15,24.75C9.6152,24.75 5.25,20.3848 5.25,15C5.25,9.6152 9.6152,5.25 15,5.25C20.3848,5.25 24.75,9.6152 24.75,15Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M20.447,20.529L21.1924,18.2288C21.2005,18.2039 21.2046,18.1779 21.2046,18.1518V17.495C21.2046,17.4299 21.1792,17.3674 21.1339,17.3208L20.4576,16.6251C20.4387,16.6057 20.4168,16.5895 20.3927,16.5771L18.9143,15.8168C18.8902,15.8044 18.8683,15.7882 18.8494,15.7687L18.176,15.0761C18.1289,15.0277 18.0643,15.0004 17.9967,15.0004H15.2501C15.112,15.0004 15.0001,14.8884 15.0001,14.7504V13.6549C15.0001,13.5168 14.8881,13.4049 14.7501,13.4049H12.9234C12.7853,13.4049 12.6734,13.293 12.6734,13.1549V12.1161C12.6734,11.9578 12.8187,11.8393 12.9737,11.8712L16.2508,12.5454C16.4059,12.5773 16.5512,12.4588 16.5512,12.3005V11.1132C16.5512,11.0481 16.5766,10.9856 16.622,10.9389L18.7085,8.7928C18.8028,8.6958 18.8028,8.5413 18.7085,8.4443L17.3653,7.0627C17.3399,7.0366 17.3091,7.0164 17.2751,7.0034L16.3697,6.6585C15.4822,6.3204 14.5072,6.2893 13.6,6.5701L12.6734,6.8569C11.0705,7.3793 9.8105,8.631 9.2774,10.2303L9.0046,11.0488C8.9393,11.2446 8.9006,11.4482 8.8895,11.6543L8.8014,13.2957C8.7977,13.3653 8.8232,13.4333 8.8718,13.4833L10.273,14.9246C10.3201,14.973 10.3848,15.0004 10.4523,15.0004H11.8373C11.8771,15.0004 11.9163,15.0098 11.9516,15.028L13.3133,15.7283C13.3966,15.7712 13.4489,15.857 13.4489,15.9506V18.1337C13.4489,18.1716 13.4575,18.209 13.4741,18.243L14.1561,19.646C14.198,19.7321 14.2853,19.7867 14.381,19.7867H15.67C15.7375,19.7867 15.8022,19.814 15.8493,19.8624L16.5512,20.5844L17.2988,21.3534C17.3173,21.3724 17.3327,21.3944 17.3444,21.4183L17.9466,22.6572C18.0224,22.8131 18.2299,22.8465 18.3507,22.7222L18.8779,22.1799L19.6535,21.3822L20.3884,20.6262C20.4152,20.5987 20.4352,20.5655 20.447,20.529Z"
android:fillColor="#0DBD8B"/>
</vector>

View file

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="30"
android:viewportHeight="30">
<path
android:pathData="M15,30C23.2843,30 30,23.2843 30,15C30,6.7157 23.2843,0 15,0C6.7157,0 0,6.7157 0,15C0,23.2843 6.7157,30 15,30Z"
android:fillColor="#0DBD8B"/>
<path
android:pathData="M6,6h18v18h-18z"
android:fillColor="#0DBD8B"/>
<path
android:pathData="M23.1,15C23.1,19.4735 19.4735,23.1 15,23.1C13.5189,23.1 12.1307,22.7025 10.9361,22.0083L8.2645,22.8328C7.4955,23.0701 6.7755,22.3481 7.0149,21.5797L7.8678,18.8427C7.2504,17.6993 6.9,16.3906 6.9,15C6.9,10.5265 10.5265,6.9 15,6.9C19.4735,6.9 23.1,10.5265 23.1,15ZM12.3,15C12.3,15.4971 11.8971,15.9 11.4,15.9C10.903,15.9 10.5,15.4971 10.5,15C10.5,14.503 10.903,14.1 11.4,14.1C11.8971,14.1 12.3,14.503 12.3,15ZM15,15.9C15.4971,15.9 15.9,15.4971 15.9,15C15.9,14.503 15.4971,14.1 15,14.1C14.503,14.1 14.1,14.503 14.1,15C14.1,15.4971 14.503,15.9 15,15.9ZM19.5,15C19.5,15.4971 19.0971,15.9 18.6,15.9C18.103,15.9 17.7,15.4971 17.7,15C17.7,14.503 18.103,14.1 18.6,14.1C19.0971,14.1 19.5,14.503 19.5,15Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="71dp"
android:height="70dp"
android:viewportWidth="71"
android:viewportHeight="70">
<path
android:pathData="M36,70C55.33,70 71,54.33 71,35C71,15.67 55.33,0 36,0C16.67,0 1,15.67 1,35C1,54.33 16.67,70 36,70Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M45.1656,29.34C45.1656,37.8427 38.253,44.7357 29.726,44.7357C27.313,44.7357 25.0292,44.1837 22.9948,43.1993L17.427,44.9344C15.369,45.5756 13.4329,43.6543 14.0668,41.6001L15.7976,35.9909C14.8291,33.9777 14.2865,31.7221 14.2865,29.34C14.2865,20.8373 21.199,13.9445 29.726,13.9445C38.253,13.9445 45.1656,20.8373 45.1656,29.34ZM33.1657,49.3666C31.124,49.3666 29.1751,48.9695 27.3921,48.2479C29.9841,53.098 35.0977,56.3978 40.9823,56.3978C43.3877,56.3978 45.6642,55.8465 47.6924,54.8632L53.2381,56.5959C55.2954,57.2385 57.233,55.3188 56.6005,53.2641L54.8737,47.6537C55.8396,45.6411 56.3805,43.3858 56.3805,41.0044C56.3805,35.1755 53.1397,30.1033 48.3611,27.4903C49.4395,29.4606 50.0621,31.6572 50.0621,33.9732C50.0621,42.4748 41.6698,49.3666 33.1657,49.3666Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="30"
android:viewportHeight="30">
<path
android:pathData="M15,30C23.2843,30 30,23.2843 30,15C30,6.7157 23.2843,0 15,0C6.7157,0 0,6.7157 0,15C0,23.2843 6.7157,30 15,30Z"
android:fillColor="#0DBD8B"/>
<path
android:pathData="M15,23.6667C19.7864,23.6667 23.6666,19.7865 23.6666,15C23.6666,10.2136 19.7864,6.3334 15,6.3334C10.2135,6.3334 6.3333,10.2136 6.3333,15C6.3333,19.7865 10.2135,23.6667 15,23.6667ZM14.2779,13.0139C14.2779,14.3103 13.3079,15.3612 12.1113,15.3612C10.9146,15.3612 9.9446,14.3103 9.9446,13.0139C9.9446,11.7176 10.9146,10.6667 12.1113,10.6667C13.3079,10.6667 14.2779,11.7176 14.2779,13.0139ZM18.7963,16.2239C19.8447,16.2239 20.6945,15.3032 20.6945,14.1675C20.6945,13.0318 19.8447,12.1112 18.7963,12.1112C17.748,12.1112 16.8982,13.0318 16.8982,14.1675C16.8982,15.3032 17.748,16.2239 18.7963,16.2239ZM12.9465,16.5264C14.5339,16.8741 15.7221,18.2883 15.7221,19.98L15.7221,22.322H11.3888L8.1905,18.9723C8.8842,17.4783 10.3789,16.4445 12.111,16.4445C12.3968,16.4445 12.6761,16.4727 12.9465,16.5264ZM17.2221,22.3219H18.7965L22.0067,18.9722C21.3339,17.8909 20.1476,17.1729 18.7965,17.1729C18.0326,17.1729 17.3213,17.4024 16.7258,17.7974C17.0439,18.4575 17.2221,19.1978 17.2221,19.98L17.2221,22.3219Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<group>
<clip-path
android:pathData="M15,23.6667C19.7864,23.6667 23.6666,19.7865 23.6666,15C23.6666,10.2136 19.7864,6.3334 15,6.3334C10.2135,6.3334 6.3333,10.2136 6.3333,15C6.3333,19.7865 10.2135,23.6667 15,23.6667ZM14.2779,13.0139C14.2779,14.3103 13.3079,15.3612 12.1113,15.3612C10.9146,15.3612 9.9446,14.3103 9.9446,13.0139C9.9446,11.7176 10.9146,10.6667 12.1113,10.6667C13.3079,10.6667 14.2779,11.7176 14.2779,13.0139ZM18.7963,16.2239C19.8447,16.2239 20.6945,15.3032 20.6945,14.1675C20.6945,13.0318 19.8447,12.1112 18.7963,12.1112C17.748,12.1112 16.8982,13.0318 16.8982,14.1675C16.8982,15.3032 17.748,16.2239 18.7963,16.2239ZM12.9465,16.5264C14.5339,16.8741 15.7221,18.2883 15.7221,19.98L15.7221,22.322H11.3888L8.1905,18.9723C8.8842,17.4783 10.3789,16.4445 12.111,16.4445C12.3968,16.4445 12.6761,16.4727 12.9465,16.5264ZM17.2221,22.3219H18.7965L22.0067,18.9722C21.3339,17.8909 20.1476,17.1729 18.7965,17.1729C18.0326,17.1729 17.3213,17.4024 16.7258,17.7974C17.0439,18.4575 17.2221,19.1978 17.2221,19.98L17.2221,22.3219Z"
android:fillType="evenOdd"/>
<path
android:pathData="M15.7221,19.98H14.3887V19.98L15.7221,19.98ZM12.9465,16.5264L13.2318,15.2239L13.2191,15.2212L13.2063,15.2186L12.9465,16.5264ZM15.7221,22.322V23.6553H17.0554L17.0554,22.322L15.7221,22.322ZM11.3888,22.322L10.4244,23.2427L10.8183,23.6553H11.3888V22.322ZM8.1905,18.9723L6.9812,18.4108L6.5983,19.2355L7.2262,19.8931L8.1905,18.9723ZM18.7965,22.3219V23.6552H19.3655L19.7591,23.2445L18.7965,22.3219ZM17.2221,22.3219L15.8888,22.3219L15.8888,23.6552H17.2221V22.3219ZM22.0067,18.9722L22.9693,19.8948L23.6858,19.1471L23.1388,18.2679L22.0067,18.9722ZM16.7258,17.7974L15.9889,16.6862L15.0201,17.3288L15.5246,18.3761L16.7258,17.7974ZM17.2221,19.98H15.8887V19.98L17.2221,19.98ZM22.3333,15C22.3333,19.0501 19.0501,22.3334 15,22.3334V25C20.5228,25 25,20.5229 25,15H22.3333ZM15,7.6667C19.0501,7.6667 22.3333,10.95 22.3333,15H25C25,9.4772 20.5228,5 15,5V7.6667ZM7.6666,15C7.6666,10.95 10.9499,7.6667 15,7.6667V5C9.4771,5 5,9.4772 5,15H7.6666ZM15,22.3334C10.9499,22.3334 7.6666,19.0501 7.6666,15H5C5,20.5229 9.4771,25 15,25V22.3334ZM12.1113,16.6945C14.1427,16.6945 15.6113,14.9441 15.6113,13.0139H12.9446C12.9446,13.6764 12.473,14.0278 12.1113,14.0278V16.6945ZM8.6113,13.0139C8.6113,14.9441 10.0798,16.6945 12.1113,16.6945V14.0278C11.7495,14.0278 11.2779,13.6764 11.2779,13.0139H8.6113ZM12.1113,9.3334C10.0798,9.3334 8.6113,11.0837 8.6113,13.0139H11.2779C11.2779,12.3515 11.7495,12 12.1113,12V9.3334ZM15.6113,13.0139C15.6113,11.0837 14.1427,9.3334 12.1113,9.3334V12C12.473,12 12.9446,12.3515 12.9446,13.0139H15.6113ZM19.3612,14.1675C19.3612,14.6693 19.0098,14.8905 18.7963,14.8905V17.5572C20.6795,17.5572 22.0278,15.9371 22.0278,14.1675H19.3612ZM18.7963,13.4445C19.0098,13.4445 19.3612,13.6657 19.3612,14.1675H22.0278C22.0278,12.398 20.6795,10.7779 18.7963,10.7779V13.4445ZM18.2315,14.1675C18.2315,13.6657 18.5829,13.4445 18.7963,13.4445V10.7779C16.9132,10.7779 15.5649,12.398 15.5649,14.1675H18.2315ZM18.7963,14.8905C18.5829,14.8905 18.2315,14.6693 18.2315,14.1675H15.5649C15.5649,15.9371 16.9132,17.5572 18.7963,17.5572V14.8905ZM17.0554,19.98C17.0554,17.6486 15.4181,15.7028 13.2318,15.2239L12.6612,17.8289C13.6496,18.0453 14.3887,18.9279 14.3887,19.98H17.0554ZM17.0554,22.322L17.0554,19.98L14.3887,19.98L14.3888,22.322L17.0554,22.322ZM11.3888,23.6553H15.7221V20.9886H11.3888V23.6553ZM7.2262,19.8931L10.4244,23.2427L12.3531,21.4012L9.1549,18.0515L7.2262,19.8931ZM12.111,15.1112C9.8314,15.1112 7.8811,16.4725 6.9812,18.4108L9.3999,19.5338C9.8873,18.484 10.9265,17.7779 12.111,17.7779V15.1112ZM13.2063,15.2186C12.8509,15.148 12.4846,15.1112 12.111,15.1112V17.7779C12.309,17.7779 12.5014,17.7973 12.6866,17.8342L13.2063,15.2186ZM18.7965,20.9886H17.2221V23.6552H18.7965V20.9886ZM21.044,18.0497L17.8339,21.3994L19.7591,23.2445L22.9693,19.8948L21.044,18.0497ZM18.7965,18.5062C19.661,18.5062 20.4311,18.9638 20.8746,19.6766L23.1388,18.2679C22.2366,16.8179 20.6343,15.8395 18.7965,15.8395V18.5062ZM17.4628,18.9085C17.8477,18.6532 18.304,18.5062 18.7965,18.5062V15.8395C17.7611,15.8395 16.795,16.1516 15.9889,16.6862L17.4628,18.9085ZM18.5554,19.98C18.5554,18.9932 18.3302,18.0555 17.927,17.2187L15.5246,18.3761C15.7575,18.8595 15.8887,19.4023 15.8887,19.98H18.5554ZM18.5554,22.3219L18.5554,19.98L15.8887,19.98L15.8888,22.3219L18.5554,22.3219Z"
android:fillColor="#ffffff"/>
</group>
<path
android:pathData="M22.9166,15C22.9166,19.3723 19.3722,22.9167 15,22.9167C10.6277,22.9167 7.0833,19.3723 7.0833,15C7.0833,10.6278 10.6277,7.0834 15,7.0834C19.3722,7.0834 22.9166,10.6278 22.9166,15Z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
</vector>

View file

@ -190,7 +190,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSpace4" />
<TextView
<Button
android:id="@+id/loginSplashAlreadyHaveAccount"
style="@style/Widget.Vector.Button.Text.Login"
android:layout_width="0dp"

View file

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/LoginFormScrollView"
android:layout_height="match_parent"
android:background="?android:colorBackground"
android:fillViewport="true"
android:paddingTop="0dp"
android:paddingBottom="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/layout_vertical_margin"
android:paddingBottom="@dimen/layout_vertical_margin">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/useCaseGutterStart"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_start_percent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/useCaseGutterEnd"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
<ImageView
android:id="@+id/useCaseHeaderIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:layout_marginTop="52dp"
android:src="@drawable/ic_onboarding_use_case_icon"
app:layout_constraintBottom_toTopOf="@id/useCaseHeaderTitle"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/useCaseHeaderTitle"
style="@style/Widget.Vector.TextView.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:text="@string/ftue_auth_use_case_title"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="@id/useCaseHeaderSubtitle"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/useCaseHeaderIcon" />
<TextView
android:id="@+id/useCaseHeaderSubtitle"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="@string/ftue_auth_use_case_subtitle"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toTopOf="@id/titleContentSpacing"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/useCaseHeaderTitle" />
<Space
android:id="@+id/titleContentSpacing"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/useCaseOptionOne"
app:layout_constraintHeight_percent="0.05"
app:layout_constraintTop_toBottomOf="@id/useCaseHeaderSubtitle" />
<TextView
android:id="@+id/useCaseOptionOne"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_login_server_selector"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:padding="16dp"
android:text="@string/ftue_auth_use_case_option_one"
android:textColor="?vctr_content_primary"
app:drawableStartCompat="@drawable/ic_friends_and_family"
app:layout_constraintBottom_toTopOf="@id/useCaseOptionTwo"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/titleContentSpacing" />
<TextView
android:id="@+id/useCaseOptionTwo"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_login_server_selector"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:padding="16dp"
android:text="@string/ftue_auth_use_case_option_two"
android:textColor="?vctr_content_primary"
app:drawableStartCompat="@drawable/ic_teams"
app:layout_constraintBottom_toTopOf="@id/useCaseOptionThree"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/useCaseOptionOne" />
<TextView
android:id="@+id/useCaseOptionThree"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_login_server_selector"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:padding="16dp"
android:text="@string/ftue_auth_use_case_option_three"
android:textColor="?vctr_content_primary"
app:drawableStartCompat="@drawable/ic_communities"
app:layout_constraintBottom_toTopOf="@id/useCaseSkip"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/useCaseOptionTwo" />
<TextView
android:id="@+id/useCaseSkip"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:clickable="true"
android:gravity="center"
app:layout_constraintBottom_toTopOf="@id/contentFooterSpacing"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/useCaseOptionThree"
tools:text="Not sure yet? You can skip this question" />
<Space
android:id="@+id/contentFooterSpacing"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/useCaseFooter"
app:layout_constraintHeight_min="16dp"
app:layout_constraintTop_toBottomOf="@id/useCaseSkip" />
<TextView
android:id="@+id/useCaseFooter"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/ftue_auth_use_case_join_existing_server"
app:layout_constraintBottom_toTopOf="@id/useCaseConnectToServer"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/contentFooterSpacing" />
<Button
android:id="@+id/useCaseConnectToServer"
style="@style/Widget.Vector.Button.Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ftue_auth_use_case_connect_to_server"
android:textAllCaps="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/useCaseGutterEnd"
app:layout_constraintStart_toStartOf="@id/useCaseGutterStart"
app:layout_constraintTop_toBottomOf="@id/useCaseFooter" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -69,7 +69,7 @@
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
app:layout_constraintTop_toBottomOf="@id/loginSplashButtonsSpace" />
<TextView
<Button
android:id="@+id/loginSplashAlreadyHaveAccount"
style="@style/Widget.Vector.Button.Text.Login"
android:layout_width="0dp"

View file

@ -14,4 +14,15 @@
<!-- Temporary string -->
<string name="template_not_implemented" translatable="false">Not implemented yet in ${app_name}</string>
<!-- WIP strings, will move to strings.xml when signed off -->
<string name="ftue_auth_use_case_title" translatable="false">Who will you chat to the most?</string>
<string name="ftue_auth_use_case_subtitle" translatable="false">We\'ll help you get connected.</string>
<string name="ftue_auth_use_case_option_one" translatable="false">Friends and family</string>
<string name="ftue_auth_use_case_option_two" translatable="false">Teams</string>
<string name="ftue_auth_use_case_option_three" translatable="false">Communities</string>
<string name="ftue_auth_use_case_skip" translatable="false">Not sure yet? %s</string>
<string name="ftue_auth_use_case_skip_partial" translatable="false">You can skip this question</string>
<string name="ftue_auth_use_case_join_existing_server" translatable="false">Looking to join an existing server?</string>
<string name="ftue_auth_use_case_connect_to_server" translatable="false">Connect to server</string>
</resources>