diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index a5daeddc7ac..54d783fc85d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3641,4 +3641,10 @@
Message in %s
Message in room
Room/Space
+
+ You can no longer create an account with %1$s using this app
+ Download %1$s to use %2$s for your account or choose a different homeserver.
+ Download %1$s
+ Faster, more secure, and packed with powerful collaboration tools.
+
diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml
index 9d8645a7076..5e9bf9062cb 100644
--- a/library/ui-styles/src/main/res/values/colors.xml
+++ b/library/ui-styles/src/main/res/values/colors.xml
@@ -158,4 +158,25 @@
#EEF8F4
#1D292A
+
+
+ #FFF7F6
+ #3E0000
+
+
+ #FFC5BC
+ #710000
+
+
+ #D51928
+ #FD3E3C
+
+
+ #D51928
+ #FD3E3C
+
+
+ #1B1D22
+ #EBEEF2
+
diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml
index 9afa14caed0..39ec830abda 100644
--- a/library/ui-styles/src/main/res/values/theme_dark.xml
+++ b/library/ui-styles/src/main/res/values/theme_dark.xml
@@ -55,6 +55,13 @@
- ?vctr_system
- ?vctr_notice_secondary
+
+ - @color/vctr_bg_critical_subtle_dark
+ - @color/vctr_border_critical_subtle_dark
+ - @color/vctr_icon_critical_primary_dark
+ - @color/vctr_text_critical_primary_dark
+ - @color/vctr_text_primary_dark
+
- @color/element_accent_dark
- @color/element_accent_dark
diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml
index 23782ee34b2..bd397d1bc68 100644
--- a/library/ui-styles/src/main/res/values/theme_light.xml
+++ b/library/ui-styles/src/main/res/values/theme_light.xml
@@ -55,6 +55,13 @@
- ?vctr_system
- ?vctr_notice_secondary
+
+ - @color/vctr_bg_critical_subtle_light
+ - @color/vctr_border_critical_subtle_light
+ - @color/vctr_icon_critical_primary_light
+ - @color/vctr_text_critical_primary_light
+ - @color/vctr_text_primary_light
+
- @color/element_accent_light
- @color/element_accent_light
diff --git a/vector-config/src/main/java/im/vector/app/config/Config.kt b/vector-config/src/main/java/im/vector/app/config/Config.kt
index 1bd21dd08e8..5e0196767f2 100644
--- a/vector-config/src/main/java/im/vector/app/config/Config.kt
+++ b/vector-config/src/main/java/im/vector/app/config/Config.kt
@@ -97,4 +97,15 @@ object Config {
val ER_DEBUG_ANALYTICS_CONFIG = DEBUG_ANALYTICS_CONFIG.copy(sentryEnvironment = "element-r")
val SHOW_UNVERIFIED_SESSIONS_ALERT_AFTER_MILLIS = 7.days.inWholeMilliseconds // 1 Week
+
+ /**
+ * Sunsetting the application.
+ * Fork maintainers can use this to inform users about their new application if any. Note that you probably also want
+ * to replace the resource `replacement_app_icon` too.
+ */
+ val sunsetConfig: SunsetConfig = SunsetConfig.Enabled(
+ learnMoreLink = "https://element.io/app-for-productivity",
+ replacementApplicationName = "Element X",
+ replacementApplicationId = "io.element.android.x",
+ )
}
diff --git a/vector-config/src/main/java/im/vector/app/config/SunsetConfig.kt b/vector-config/src/main/java/im/vector/app/config/SunsetConfig.kt
new file mode 100644
index 00000000000..f012ac32a9f
--- /dev/null
+++ b/vector-config/src/main/java/im/vector/app/config/SunsetConfig.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2025 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.config
+
+sealed interface SunsetConfig {
+ /**
+ * Sunsetting the application is disabled.
+ */
+ data object Disabled : SunsetConfig
+
+ /**
+ * Sunsetting the application is enabled and can be configured by implementing this class.
+ */
+ data class Enabled(
+ /**
+ * The URL target to learn more.
+ */
+ val learnMoreLink: String,
+
+ /**
+ * The replacement application ID.
+ * Example: for Element application, the replacement application ID is the id of Element X: "Element X".
+ */
+ val replacementApplicationName: String,
+
+ /**
+ * The replacement application ID.
+ * Example: for Element App, the replacement application ID is the id of Element X: "io.element.android.x".
+ */
+ val replacementApplicationId: String,
+ ) : SunsetConfig
+}
diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt
index 82271ca23c1..4db340a7230 100644
--- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt
@@ -32,6 +32,7 @@ import androidx.core.app.ShareCompat
import androidx.core.content.FileProvider
import androidx.core.content.getSystemService
import im.vector.app.R
+import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.themes.ThemeUtils
import im.vector.lib.strings.CommonStrings
@@ -367,13 +368,21 @@ private fun addToGallery(savedFile: File, mediaMimeType: String?, context: Conte
}
/**
- * Open the play store to the provided application Id, default to this app.
+ * Open the play store or the F-Droid to the provided application Id, default to this app.
*/
-fun openPlayStore(activity: Activity, appId: String) {
+fun openApplicationStore(
+ activity: Activity,
+ buildMeta: BuildMeta,
+ appId: String = buildMeta.applicationId,
+) {
try {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appId")))
} catch (activityNotFoundException: ActivityNotFoundException) {
- activity.safeStartActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appId")))
+ if (buildMeta.flavorDescription == "FDroid") {
+ activity.safeStartActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://f-droid.org/packages/$appId")))
+ } else {
+ activity.safeStartActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appId")))
+ }
}
}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/MasSupportRequiredException.kt b/vector/src/main/java/im/vector/app/features/onboarding/MasSupportRequiredException.kt
new file mode 100644
index 00000000000..ca6090bd055
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/onboarding/MasSupportRequiredException.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2025 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
+
+class MasSupportRequiredException : Exception("Please use replacement app")
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 27f1727641c..8ed6272d507 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -12,6 +12,8 @@ import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import im.vector.app.config.Config
+import im.vector.app.config.SunsetConfig
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
@@ -761,7 +763,13 @@ class OnboardingViewModel @AssistedInject constructor(
}
OnboardingFlow.SignUp -> {
updateSignMode(SignMode.SignUp)
- internalRegisterAction(RegisterAction.StartRegistration)
+ if (authResult.selectedHomeserver.hasOidcCompatibilityFlow && Config.sunsetConfig is SunsetConfig.Enabled) {
+ // Navigate to the screen to create an account, it will show the error
+ setState { copy(isLoading = false) }
+ _viewEvents.post(OnboardingViewEvents.OpenCombinedRegister)
+ } else {
+ internalRegisterAction(RegisterAction.StartRegistration)
+ }
}
OnboardingFlow.SignInSignUp,
null -> {
@@ -775,9 +783,17 @@ class OnboardingViewModel @AssistedInject constructor(
private suspend fun onHomeServerEdited(config: HomeServerConnectionConfig, serverTypeOverride: ServerType?, authResult: StartAuthenticationResult) {
when (awaitState().onboardingFlow) {
- OnboardingFlow.SignUp -> internalRegisterAction(RegisterAction.StartRegistration) {
- updateServerSelection(config, serverTypeOverride, authResult)
- _viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
+ OnboardingFlow.SignUp -> {
+ if (authResult.selectedHomeserver.hasOidcCompatibilityFlow && Config.sunsetConfig is SunsetConfig.Enabled) {
+ // An error is displayed now
+ setState { copy(isLoading = false) }
+ _viewEvents.post(OnboardingViewEvents.Failure(MasSupportRequiredException()))
+ } else {
+ internalRegisterAction(RegisterAction.StartRegistration) {
+ updateServerSelection(config, serverTypeOverride, authResult)
+ _viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
+ }
+ }
}
OnboardingFlow.SignIn -> {
updateServerSelection(config, serverTypeOverride, authResult)
@@ -924,7 +940,10 @@ private fun LoginMode.supportsSignModeScreen(): Boolean {
return when (this) {
LoginMode.Password,
is LoginMode.SsoAndPassword -> true
- is LoginMode.Sso,
+ is LoginMode.Sso -> {
+ // In this case, an error will be displayed in the next screen
+ hasOidcCompatibilityFlow
+ }
LoginMode.Unknown,
LoginMode.Unsupported -> false
}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
index 5a971514b68..9b322b626b9 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
@@ -12,6 +12,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
import androidx.autofill.HintConstants
import androidx.core.text.isDigitsOnly
import androidx.core.view.isVisible
@@ -19,6 +20,9 @@ import androidx.lifecycle.lifecycleScope
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.config.Config
+import im.vector.app.config.SunsetConfig
import im.vector.app.core.extensions.clearErrorOnChange
import im.vector.app.core.extensions.content
import im.vector.app.core.extensions.editText
@@ -31,6 +35,9 @@ import im.vector.app.core.extensions.realignPercentagesToParent
import im.vector.app.core.extensions.setOnFocusLostListener
import im.vector.app.core.extensions.setOnImeDoneListener
import im.vector.app.core.extensions.toReducedUrl
+import im.vector.app.core.resources.BuildMeta
+import im.vector.app.core.utils.openApplicationStore
+import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentFtueCombinedRegisterBinding
import im.vector.app.features.login.LoginMode
import im.vector.app.features.login.SSORedirectRouterActivity
@@ -52,12 +59,14 @@ import org.matrix.android.sdk.api.failure.isRegistrationDisabled
import org.matrix.android.sdk.api.failure.isUsernameInUse
import org.matrix.android.sdk.api.failure.isWeakPassword
import reactivecircus.flowbinding.android.widget.textChanges
+import javax.inject.Inject
private const val MINIMUM_PASSWORD_LENGTH = 8
@AndroidEntryPoint
class FtueAuthCombinedRegisterFragment :
AbstractSSOFtueAuthFragment() {
+ @Inject lateinit var buildMeta: BuildMeta
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueCombinedRegisterBinding {
return FragmentFtueCombinedRegisterBinding.inflate(inflater, container, false)
@@ -181,7 +190,8 @@ class FtueAuthCombinedRegisterFragment :
}
private fun setupUi(state: OnboardingViewState) {
- views.selectedServerName.text = state.selectedHomeserver.userFacingUrl.toReducedUrl()
+ val serverName = state.selectedHomeserver.userFacingUrl.toReducedUrl()
+ views.selectedServerName.text = serverName
if (state.isLoading) {
// Ensure password is hidden
@@ -201,6 +211,47 @@ class FtueAuthCombinedRegisterFragment :
is LoginMode.SsoAndPassword -> renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode)
else -> hideSsoProviders()
}
+
+ (Config.sunsetConfig as? SunsetConfig.Enabled)?.let { config ->
+ val isMasSupportRequired = state.selectedHomeserver.hasOidcCompatibilityFlow
+ views.serverSelectionSpacing.isVisible = !isMasSupportRequired
+ views.serverSelectionDivider.isVisible = !isMasSupportRequired
+ views.chooseServerCardErrorMas.isVisible = isMasSupportRequired
+ views.chooseServerCardDownloadReplacementApp.isVisible = isMasSupportRequired
+
+ if (isMasSupportRequired) {
+ views.chooseServerCardErrorMas.findViewById(R.id.view_card_error_title).text =
+ getString(CommonStrings.error_mas_not_supported_title, serverName)
+ views.chooseServerCardErrorMas.findViewById(R.id.view_card_error_subtitle).text =
+ getString(
+ CommonStrings.error_mas_not_supported_subtitle,
+ config.replacementApplicationName,
+ serverName,
+ )
+ views.chooseServerCardDownloadReplacementApp.findViewById(R.id.view_download_replacement_app_title).text =
+ getString(CommonStrings.view_download_replacement_app_title, config.replacementApplicationName)
+
+ views.chooseServerCardDownloadReplacementApp.debouncedClicks {
+ openApplicationStore(
+ activity = requireActivity(),
+ buildMeta = buildMeta,
+ appId = config.replacementApplicationId,
+ )
+ }
+ views.chooseServerCardDownloadReplacementApp.findViewById(R.id.view_download_replacement_app_learn_more)?.debouncedClicks {
+ openUrlInChromeCustomTab(
+ context = requireContext(),
+ session = null,
+ url = config.learnMoreLink,
+ )
+ }
+
+ // Disable form
+ views.createAccountInput.isEnabled = false
+ views.createAccountPasswordInput.isEnabled = false
+ views.createAccountSubmit.isEnabled = false
+ }
+ }
}
private fun renderSsoProviders(deviceId: String?, loginMode: LoginMode) {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt
index 3f34c6ea686..e43de86c13c 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt
@@ -11,7 +11,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.TextView
+import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.config.Config
+import im.vector.app.config.SunsetConfig
import im.vector.app.core.extensions.associateContentStateWith
import im.vector.app.core.extensions.clearErrorOnChange
import im.vector.app.core.extensions.content
@@ -20,20 +25,26 @@ import im.vector.app.core.extensions.realignPercentagesToParent
import im.vector.app.core.extensions.setOnImeDoneListener
import im.vector.app.core.extensions.showKeyboard
import im.vector.app.core.extensions.toReducedUrl
+import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.ensureTrailingSlash
+import im.vector.app.core.utils.openApplicationStore
+import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.core.utils.openUrlInExternalBrowser
import im.vector.app.databinding.FragmentFtueServerSelectionCombinedBinding
+import im.vector.app.features.onboarding.MasSupportRequiredException
import im.vector.app.features.onboarding.OnboardingAction
import im.vector.app.features.onboarding.OnboardingFlow
import im.vector.app.features.onboarding.OnboardingViewEvents
import im.vector.app.features.onboarding.OnboardingViewState
import im.vector.lib.strings.CommonStrings
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
+import javax.inject.Inject
@AndroidEntryPoint
class FtueAuthCombinedServerSelectionFragment :
AbstractFtueAuthFragment() {
+ @Inject lateinit var buildMeta: BuildMeta
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding {
return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false)
@@ -57,6 +68,22 @@ class FtueAuthCombinedServerSelectionFragment :
}
views.chooseServerGetInTouch.debouncedClicks { openUrlInExternalBrowser(requireContext(), getString(im.vector.app.config.R.string.ftue_ems_url)) }
views.chooseServerSubmit.debouncedClicks { updateServerUrl() }
+ (Config.sunsetConfig as? SunsetConfig.Enabled)?.let { config ->
+ views.chooseServerCardDownloadReplacementApp.debouncedClicks {
+ openApplicationStore(
+ activity = requireActivity(),
+ buildMeta = buildMeta,
+ appId = config.replacementApplicationId,
+ )
+ }
+ views.chooseServerCardDownloadReplacementApp.findViewById(R.id.view_download_replacement_app_learn_more)?.debouncedClicks {
+ openUrlInChromeCustomTab(
+ context = requireContext(),
+ session = null,
+ url = config.learnMoreLink,
+ )
+ }
+ }
views.chooseServerInput.clearErrorOnChange(viewLifecycleOwner)
}
@@ -89,10 +116,30 @@ class FtueAuthCombinedServerSelectionFragment :
}
override fun onError(throwable: Throwable) {
+ val isMasSupportRequiredException = throwable is MasSupportRequiredException
views.chooseServerInput.error = when {
throwable.isHomeserverUnavailable() -> getString(CommonStrings.login_error_homeserver_not_found)
+ isMasSupportRequiredException -> " "
else -> errorFormatter.toHumanReadable(throwable)
}
+ views.chooseServerCardErrorMas.isVisible = isMasSupportRequiredException
+ views.chooseServerCardDownloadReplacementApp.isVisible = isMasSupportRequiredException
+ if (isMasSupportRequiredException) {
+ views.chooseServerSubmit.isEnabled = false
+ }
+ val config = Config.sunsetConfig
+ if (throwable is MasSupportRequiredException && config is SunsetConfig.Enabled) {
+ views.chooseServerCardErrorMas.findViewById(R.id.view_card_error_title).text =
+ getString(CommonStrings.error_mas_not_supported_title, views.chooseServerInput.content())
+ views.chooseServerCardErrorMas.findViewById(R.id.view_card_error_subtitle).text =
+ getString(
+ CommonStrings.error_mas_not_supported_subtitle,
+ config.replacementApplicationName,
+ views.chooseServerInput.content(),
+ )
+ views.chooseServerCardDownloadReplacementApp.findViewById(R.id.view_download_replacement_app_title).text =
+ getString(CommonStrings.view_download_replacement_app_title, config.replacementApplicationName)
+ }
}
private fun String.toReducedUrlKeepingSchemaIfInsecure() = toReducedUrl(keepSchema = this.startsWith("http://"))
diff --git a/vector/src/main/res/drawable-mdpi/replacement_app_icon.png b/vector/src/main/res/drawable-mdpi/replacement_app_icon.png
new file mode 100644
index 00000000000..db4e68bbc22
Binary files /dev/null and b/vector/src/main/res/drawable-mdpi/replacement_app_icon.png differ
diff --git a/vector/src/main/res/drawable-xhdpi/replacement_app_icon.png b/vector/src/main/res/drawable-xhdpi/replacement_app_icon.png
new file mode 100644
index 00000000000..d8e0864606e
Binary files /dev/null and b/vector/src/main/res/drawable-xhdpi/replacement_app_icon.png differ
diff --git a/vector/src/main/res/drawable-xxhdpi/replacement_app_icon.png b/vector/src/main/res/drawable-xxhdpi/replacement_app_icon.png
new file mode 100644
index 00000000000..589de41ed37
Binary files /dev/null and b/vector/src/main/res/drawable-xxhdpi/replacement_app_icon.png differ
diff --git a/vector/src/main/res/drawable/card_background.xml b/vector/src/main/res/drawable/card_background.xml
new file mode 100644
index 00000000000..61cefc68ed4
--- /dev/null
+++ b/vector/src/main/res/drawable/card_background.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/drawable/card_background_error.xml b/vector/src/main/res/drawable/card_background_error.xml
new file mode 100644
index 00000000000..ee43a083397
--- /dev/null
+++ b/vector/src/main/res/drawable/card_background_error.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/drawable/ic_error.xml b/vector/src/main/res/drawable/ic_error.xml
new file mode 100644
index 00000000000..7ff2b529080
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_error.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/vector/src/main/res/layout/fragment_ftue_combined_login.xml b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
index a589ec6f5a2..b7a5fb459d6 100644
--- a/vector/src/main/res/layout/fragment_ftue_combined_login.xml
+++ b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
@@ -83,7 +83,8 @@
app:layout_constraintBottom_toTopOf="@id/selectedServerDescription"
app:layout_constraintEnd_toStartOf="@id/editServerButton"
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
- app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader" />
+ app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader"
+ tools:text="server.org" />
+ app:layout_constraintVertical_bias="0"
+ app:layout_constraintVertical_chainStyle="packed" />
+
+
+
+
+
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/chooseServerCardDownloadReplacementApp">
+ android:maxLines="1"
+ android:nextFocusForward="@id/createAccountPasswordInput" />
diff --git a/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml b/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml
index f1944e25ada..8430ff2dcdf 100644
--- a/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml
+++ b/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml
@@ -98,7 +98,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/ftue_auth_choose_server_entry_hint"
- app:layout_constraintBottom_toTopOf="@id/actionSpacing"
+ app:layout_constraintBottom_toTopOf="@id/chooseServerCardErrorMas"
app:layout_constraintEnd_toEndOf="@id/chooseServerGutterEnd"
app:layout_constraintStart_toStartOf="@id/chooseServerGutterStart"
app:layout_constraintTop_toBottomOf="@id/titleContentSpacing">
@@ -112,13 +112,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/chooseServerCardDownloadReplacementApp" />