1
0
mirror of https://github.com/vector-im/element-android.git synced 2025-07-31 07:04:23 +03:00

MAS require Element X

This commit is contained in:
Benoit Marty
2025-01-02 17:33:09 +01:00
committed by Benoit Marty
parent 037958faa7
commit d2c77a36ec
20 changed files with 379 additions and 10 deletions

View File

@ -3641,4 +3641,10 @@
<string name="pill_message_in_room">Message in %s</string> <string name="pill_message_in_room">Message in %s</string>
<string name="pill_message_in_unknown_room">Message in room</string> <string name="pill_message_in_unknown_room">Message in room</string>
<string name="pill_message_unknown_room_or_space">Room/Space</string> <string name="pill_message_unknown_room_or_space">Room/Space</string>
<string name="error_mas_not_supported_title">You can no longer create an account with %1$s using this app</string>
<string name="error_mas_not_supported_subtitle">Download %1$s to use %2$s for your account or choose a different homeserver.</string>
<string name="view_download_replacement_app_title">Download %1$s</string>
<string name="view_download_replacement_app_subtitle">Faster, more secure, and packed with powerful collaboration tools.</string>
</resources> </resources>

View File

@ -158,4 +158,25 @@
<color name="vctr_rich_text_editor_menu_button_background_light">#EEF8F4</color> <color name="vctr_rich_text_editor_menu_button_background_light">#EEF8F4</color>
<color name="vctr_rich_text_editor_menu_button_background_dark">#1D292A</color> <color name="vctr_rich_text_editor_menu_button_background_dark">#1D292A</color>
<!-- Other colors -->
<attr name="vctr_bg_critical_subtle" format="color" />
<color name="vctr_bg_critical_subtle_light">#FFF7F6</color>
<color name="vctr_bg_critical_subtle_dark">#3E0000</color>
<attr name="vctr_border_critical_subtle" format="color" />
<color name="vctr_border_critical_subtle_light">#FFC5BC</color>
<color name="vctr_border_critical_subtle_dark">#710000</color>
<attr name= "vctr_icon_critical_primary" format="color" />
<color name="vctr_icon_critical_primary_light">#D51928</color>
<color name="vctr_icon_critical_primary_dark">#FD3E3C</color>
<attr name= "vctr_text_critical_primary" format="color" />
<color name="vctr_text_critical_primary_light">#D51928</color>
<color name="vctr_text_critical_primary_dark">#FD3E3C</color>
<attr name= "vctr_text_primary" format="color" />
<color name="vctr_text_primary_light">#1B1D22</color>
<color name="vctr_text_primary_dark">#EBEEF2</color>
</resources> </resources>

View File

@ -55,6 +55,13 @@
<item name="vctr_list_separator_on_surface">?vctr_system</item> <item name="vctr_list_separator_on_surface">?vctr_system</item>
<item name="vctr_unread_background">?vctr_notice_secondary</item> <item name="vctr_unread_background">?vctr_notice_secondary</item>
<!-- Other colors -->
<item name="vctr_bg_critical_subtle" >@color/vctr_bg_critical_subtle_dark</item>
<item name="vctr_border_critical_subtle" >@color/vctr_border_critical_subtle_dark</item>
<item name= "vctr_icon_critical_primary" >@color/vctr_icon_critical_primary_dark</item>
<item name= "vctr_text_critical_primary" >@color/vctr_text_critical_primary_dark</item>
<item name= "vctr_text_primary" >@color/vctr_text_primary_dark</item>
<!-- Material color --> <!-- Material color -->
<item name="colorPrimary">@color/element_accent_dark</item> <item name="colorPrimary">@color/element_accent_dark</item>
<item name="colorPrimaryVariant">@color/element_accent_dark</item> <item name="colorPrimaryVariant">@color/element_accent_dark</item>

View File

@ -55,6 +55,13 @@
<item name="vctr_list_separator_on_surface">?vctr_system</item> <item name="vctr_list_separator_on_surface">?vctr_system</item>
<item name="vctr_unread_background">?vctr_notice_secondary</item> <item name="vctr_unread_background">?vctr_notice_secondary</item>
<!-- Other colors -->
<item name="vctr_bg_critical_subtle" >@color/vctr_bg_critical_subtle_light</item>
<item name="vctr_border_critical_subtle" >@color/vctr_border_critical_subtle_light</item>
<item name= "vctr_icon_critical_primary" >@color/vctr_icon_critical_primary_light</item>
<item name= "vctr_text_critical_primary" >@color/vctr_text_critical_primary_light</item>
<item name= "vctr_text_primary" >@color/vctr_text_primary_light</item>
<!-- Material color --> <!-- Material color -->
<item name="colorPrimary">@color/element_accent_light</item> <item name="colorPrimary">@color/element_accent_light</item>
<item name="colorPrimaryVariant">@color/element_accent_light</item> <item name="colorPrimaryVariant">@color/element_accent_light</item>

View File

@ -97,4 +97,16 @@ object Config {
val ER_DEBUG_ANALYTICS_CONFIG = DEBUG_ANALYTICS_CONFIG.copy(sentryEnvironment = "element-r") val ER_DEBUG_ANALYTICS_CONFIG = DEBUG_ANALYTICS_CONFIG.copy(sentryEnvironment = "element-r")
val SHOW_UNVERIFIED_SESSIONS_ALERT_AFTER_MILLIS = 7.days.inWholeMilliseconds // 1 Week 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(
// TODO: update this URL.
learnMoreLink = "https://element.io/",
replacementApplicationName = "Element X",
replacementApplicationId = "io.element.android.x",
)
} }

View File

@ -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
}

View File

@ -32,6 +32,7 @@ import androidx.core.app.ShareCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.lib.strings.CommonStrings 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 { try {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appId"))) activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appId")))
} catch (activityNotFoundException: ActivityNotFoundException) { } 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")))
}
} }
} }

View File

@ -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")

View File

@ -12,6 +12,8 @@ import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject 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.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
@ -761,7 +763,13 @@ class OnboardingViewModel @AssistedInject constructor(
} }
OnboardingFlow.SignUp -> { OnboardingFlow.SignUp -> {
updateSignMode(SignMode.SignUp) updateSignMode(SignMode.SignUp)
internalRegisterAction(RegisterAction.StartRegistration) 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)
}
} }
OnboardingFlow.SignInSignUp, OnboardingFlow.SignInSignUp,
null -> { null -> {
@ -775,9 +783,17 @@ class OnboardingViewModel @AssistedInject constructor(
private suspend fun onHomeServerEdited(config: HomeServerConnectionConfig, serverTypeOverride: ServerType?, authResult: StartAuthenticationResult) { private suspend fun onHomeServerEdited(config: HomeServerConnectionConfig, serverTypeOverride: ServerType?, authResult: StartAuthenticationResult) {
when (awaitState().onboardingFlow) { when (awaitState().onboardingFlow) {
OnboardingFlow.SignUp -> internalRegisterAction(RegisterAction.StartRegistration) { OnboardingFlow.SignUp -> {
updateServerSelection(config, serverTypeOverride, authResult) if (authResult.selectedHomeserver.hasOidcCompatibilityFlow && Config.sunsetConfig is SunsetConfig.Enabled) {
_viewEvents.post(OnboardingViewEvents.OnHomeserverEdited) // 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 -> { OnboardingFlow.SignIn -> {
updateServerSelection(config, serverTypeOverride, authResult) updateServerSelection(config, serverTypeOverride, authResult)

View File

@ -11,7 +11,12 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint 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.associateContentStateWith
import im.vector.app.core.extensions.clearErrorOnChange import im.vector.app.core.extensions.clearErrorOnChange
import im.vector.app.core.extensions.content 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.setOnImeDoneListener
import im.vector.app.core.extensions.showKeyboard import im.vector.app.core.extensions.showKeyboard
import im.vector.app.core.extensions.toReducedUrl 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.ensureProtocol
import im.vector.app.core.utils.ensureTrailingSlash 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.core.utils.openUrlInExternalBrowser
import im.vector.app.databinding.FragmentFtueServerSelectionCombinedBinding 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.OnboardingAction
import im.vector.app.features.onboarding.OnboardingFlow import im.vector.app.features.onboarding.OnboardingFlow
import im.vector.app.features.onboarding.OnboardingViewEvents import im.vector.app.features.onboarding.OnboardingViewEvents
import im.vector.app.features.onboarding.OnboardingViewState import im.vector.app.features.onboarding.OnboardingViewState
import im.vector.lib.strings.CommonStrings import im.vector.lib.strings.CommonStrings
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class FtueAuthCombinedServerSelectionFragment : class FtueAuthCombinedServerSelectionFragment :
AbstractFtueAuthFragment<FragmentFtueServerSelectionCombinedBinding>() { AbstractFtueAuthFragment<FragmentFtueServerSelectionCombinedBinding>() {
@Inject lateinit var buildMeta: BuildMeta
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding {
return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false) 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.chooseServerGetInTouch.debouncedClicks { openUrlInExternalBrowser(requireContext(), getString(im.vector.app.config.R.string.ftue_ems_url)) }
views.chooseServerSubmit.debouncedClicks { updateServerUrl() } 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<View>(R.id.view_download_replacement_app_learn_more)?.debouncedClicks {
openUrlInChromeCustomTab(
context = requireContext(),
session = null,
url = config.learnMoreLink,
)
}
}
views.chooseServerInput.clearErrorOnChange(viewLifecycleOwner) views.chooseServerInput.clearErrorOnChange(viewLifecycleOwner)
} }
@ -89,10 +116,30 @@ class FtueAuthCombinedServerSelectionFragment :
} }
override fun onError(throwable: Throwable) { override fun onError(throwable: Throwable) {
val isMasSupportRequiredException = throwable is MasSupportRequiredException
views.chooseServerInput.error = when { views.chooseServerInput.error = when {
throwable.isHomeserverUnavailable() -> getString(CommonStrings.login_error_homeserver_not_found) throwable.isHomeserverUnavailable() -> getString(CommonStrings.login_error_homeserver_not_found)
isMasSupportRequiredException -> " "
else -> errorFormatter.toHumanReadable(throwable) 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<TextView>(R.id.view_card_error_title).text =
getString(CommonStrings.error_mas_not_supported_title, views.chooseServerInput.content())
views.chooseServerCardErrorMas.findViewById<TextView>(R.id.view_card_error_subtitle).text =
getString(
CommonStrings.error_mas_not_supported_subtitle,
config.replacementApplicationName,
views.chooseServerInput.content(),
)
views.chooseServerCardDownloadReplacementApp.findViewById<TextView>(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://")) private fun String.toReducedUrlKeepingSchemaIfInsecure() = toReducedUrl(keepSchema = this.startsWith("http://"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="?android:colorBackground" />
<stroke
android:width="1dp"
android:color="?vctr_content_quinary" />
<corners android:radius="8dp" />
</shape>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="?vctr_bg_critical_subtle" />
<stroke
android:width="1dp"
android:color="?vctr_border_critical_subtle" />
<corners android:radius="8dp" />
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M10,14.167C10.236,14.167 10.434,14.087 10.594,13.927C10.753,13.767 10.833,13.569 10.833,13.333C10.833,13.097 10.753,12.899 10.594,12.74C10.434,12.58 10.236,12.5 10,12.5C9.764,12.5 9.566,12.58 9.406,12.74C9.246,12.899 9.167,13.097 9.167,13.333C9.167,13.569 9.246,13.767 9.406,13.927C9.566,14.087 9.764,14.167 10,14.167ZM10,10.833C10.236,10.833 10.434,10.753 10.594,10.594C10.753,10.434 10.833,10.236 10.833,10V6.667C10.833,6.431 10.753,6.233 10.594,6.073C10.434,5.913 10.236,5.833 10,5.833C9.764,5.833 9.566,5.913 9.406,6.073C9.246,6.233 9.167,6.431 9.167,6.667V10C9.167,10.236 9.246,10.434 9.406,10.594C9.566,10.753 9.764,10.833 10,10.833ZM10,18.333C8.847,18.333 7.764,18.115 6.75,17.677C5.736,17.24 4.854,16.646 4.104,15.896C3.354,15.146 2.76,14.264 2.323,13.25C1.885,12.236 1.667,11.153 1.667,10C1.667,8.847 1.885,7.764 2.323,6.75C2.76,5.736 3.354,4.854 4.104,4.104C4.854,3.354 5.736,2.76 6.75,2.323C7.764,1.885 8.847,1.667 10,1.667C11.153,1.667 12.236,1.885 13.25,2.323C14.264,2.76 15.146,3.354 15.896,4.104C16.646,4.854 17.24,5.736 17.677,6.75C18.115,7.764 18.333,8.847 18.333,10C18.333,11.153 18.115,12.236 17.677,13.25C17.24,14.264 16.646,15.146 15.896,15.896C15.146,16.646 14.264,17.24 13.25,17.677C12.236,18.115 11.153,18.333 10,18.333Z"
android:fillColor="#D51928"/>
</vector>

View File

@ -83,7 +83,8 @@
app:layout_constraintBottom_toTopOf="@id/selectedServerDescription" app:layout_constraintBottom_toTopOf="@id/selectedServerDescription"
app:layout_constraintEnd_toStartOf="@id/editServerButton" app:layout_constraintEnd_toStartOf="@id/editServerButton"
app:layout_constraintStart_toStartOf="@id/loginGutterStart" app:layout_constraintStart_toStartOf="@id/loginGutterStart"
app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader" /> app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader"
tools:text="server.org" />
<TextView <TextView
android:id="@+id/selectedServerDescription" android:id="@+id/selectedServerDescription"

View File

@ -98,7 +98,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/ftue_auth_choose_server_entry_hint" 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_constraintEnd_toEndOf="@id/chooseServerGutterEnd"
app:layout_constraintStart_toStartOf="@id/chooseServerGutterStart" app:layout_constraintStart_toStartOf="@id/chooseServerGutterStart"
app:layout_constraintTop_toBottomOf="@id/titleContentSpacing"> app:layout_constraintTop_toBottomOf="@id/titleContentSpacing">
@ -112,13 +112,51 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<FrameLayout
android:id="@+id/chooseServerCardErrorMas"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/chooseServerCardDownloadReplacementApp"
app:layout_constraintEnd_toEndOf="@id/chooseServerGutterEnd"
app:layout_constraintStart_toStartOf="@id/chooseServerGutterStart"
app:layout_constraintTop_toBottomOf="@+id/chooseServerInput"
tools:visibility="visible">
<include
layout="@layout/view_error_registration_with_mas"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
<FrameLayout
android:id="@+id/chooseServerCardDownloadReplacementApp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/actionSpacing"
app:layout_constraintEnd_toEndOf="@id/chooseServerGutterEnd"
app:layout_constraintStart_toStartOf="@id/chooseServerGutterStart"
app:layout_constraintTop_toBottomOf="@+id/chooseServerCardErrorMas"
tools:visibility="visible">
<include
layout="@layout/view_download_replacement_app"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
<Space <Space
android:id="@+id/actionSpacing" android:id="@+id/actionSpacing"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/chooseServerSubmit" app:layout_constraintBottom_toTopOf="@id/chooseServerSubmit"
app:layout_constraintHeight_percent="0.03" app:layout_constraintHeight_percent="0.03"
app:layout_constraintTop_toBottomOf="@id/chooseServerInput" /> app:layout_constraintTop_toBottomOf="@+id/chooseServerCardDownloadReplacementApp" />
<Button <Button
android:id="@+id/chooseServerSubmit" android:id="@+id/chooseServerSubmit"

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="@drawable/card_background"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/replacement_app_icon" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="13dp"
android:orientation="vertical">
<TextView
android:id="@+id/view_download_replacement_app_title"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:textColorPrimary"
tools:text="@string/view_download_replacement_app_title" />
<TextView
android:id="@+id/view_download_replacement_app_subtitle"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/view_download_replacement_app_subtitle"
android:textColor="?android:textColorSecondary" />
<TextView
android:id="@+id/view_download_replacement_app_learn_more"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:paddingStart="0dp"
android:paddingTop="4dp"
android:paddingEnd="16dp"
android:paddingBottom="4dp"
android:text="@string/action_learn_more"
android:textColor="?android:textColorPrimary"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="@drawable/card_background_error"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_error"
app:tint="?vctr_icon_critical_primary" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/view_card_error_title"
style="@style/Widget.Vector.TextView.Body.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?vctr_text_critical_primary"
tools:text="@string/error_mas_not_supported_title" />
<TextView
android:id="@+id/view_card_error_subtitle"
style="@style/Widget.Vector.TextView.Caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="?vctr_text_primary"
tools:text="@string/error_mas_not_supported_subtitle" />
</LinearLayout>
</LinearLayout>