Merge branch 'develop' into feature/aris/threads

This commit is contained in:
ariskotsomitopoulos 2022-01-07 16:36:48 +02:00
commit 50e51cbe29
32 changed files with 840 additions and 22 deletions

View File

@ -14,6 +14,7 @@ jobs:
- name: Run code quality check suite
run: ./tools/check/check_code_quality.sh
# ktlint for all the modules
ktlint:
name: Kotlin Linter
runs-on: ubuntu-latest
@ -23,12 +24,55 @@ jobs:
run: |
./gradlew ktlintCheck --continue
- name: Upload reports
if: always()
uses: actions/upload-artifact@v2
with:
name: ktlinting-report
path: vector/build/reports/ktlint/*.*
path: |
*/build/reports/ktlint/ktlint*/ktlint*.txt
- name: Handle Results
if: always()
id: get-comment-body
run: |
results="$(cat */*/build/reports/ktlint/ktlint*/ktlint*.txt */build/reports/ktlint/ktlint*/ktlint*.txt | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g")"
if [ -z "$results" ]; then
body="👍 ✅ 👍"
else
body="👎 ❌ 👎 \`Failed${results}\`"
body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}"
body="$( echo $body | sed 's/\/home\/runner\/work\/element-android\/element-android\//\`<br\/>\`/g')"
body="$( echo $body | sed 's/\/src\/main\/java\// 🔸 /g')"
body="$( echo $body | sed 's/im\/vector\/app\///g')"
body="$( echo $body | sed 's/im\/vector\/lib\/attachmentviewer\///g')"
body="$( echo $body | sed 's/im\/vector\/lib\/multipicker\///g')"
body="$( echo $body | sed 's/im\/vector\/lib\///g')"
body="$( echo $body | sed 's/org\/matrix\/android\/sdk\///g')"
body="$( echo $body | sed 's/\/src\/androidTest\/java\// 🔸 /g')"
fi
echo "::set-output name=body::$body"
- name: Find Comment
if: always()
uses: peter-evans/find-comment@v1
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Ktlint Results
- name: Publish ktlint results to PR
if: always()
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
### Ktlint Results
# Lint for main module and all the other modules
${{ steps.get-comment-body.outputs.body }}
edit-mode: replace
# Lint for main module
android-lint:
name: Android Linter
runs-on: ubuntu-latest
@ -74,7 +118,6 @@ jobs:
run: ./gradlew clean lint${{ matrix.target }}Release --stacktrace
- name: Upload ${{ matrix.target }} linting report
uses: actions/upload-artifact@v2
if: always()
with:
name: release-lint-report-${{ matrix.target }}
path: |

1
changelog.d/4864.misc Normal file
View File

@ -0,0 +1 @@
Fix github actions ktlint reports and publish results on PR as comment

View File

@ -50,6 +50,17 @@ It's also possible for any icon to go to the main component by right-clicking on
- open the created vector drawable
- optionally update the color(s) to "#FF0000" (red) to ensure that the drawable is correctly tinted at runtime.
### Images
Android 4.3 (18+) fully supports the WebP image format which can often provide smaller image sizes without drastically impacting image quality (depending on the output encoding quality).
When importing non vector images, WebP is the preferred format.
Images can be converted to the WebP within Android Studio by
- right clicking the image file within the project file explorer
- select `Convert to WebP`
https://developer.android.com/studio/write/convert-webp
## Figma links
Figma links can be included in the layout, for future reference, but it is also OK to add a paragraph below here, to centralize the information

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="#3372C7DA"
android:startColor="#33BBE7CF" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="#33B972DA"
android:startColor="#3372C7DA" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="#330DBD8B"
android:startColor="#33B972DA" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:endColor="#33BBE7CF"
android:startColor="#330DBD8B" />
</shape>

View File

@ -2,4 +2,8 @@
<resources>
<!-- Navigation Drawer -->
<dimen name="navigation_drawer_max_width">400dp</dimen>
<!-- Onboarding -->
<item name="ftue_auth_gutter_start_percent" format="float" type="dimen">0.25</item>
<item name="ftue_auth_gutter_end_percent" format="float" type="dimen">0.75</item>
</resources>

View File

@ -51,4 +51,8 @@
<dimen name="composer_min_height">56dp</dimen>
<dimen name="composer_attachment_size">52dp</dimen>
<dimen name="composer_attachment_margin">1dp</dimen>
<!-- Onboarding -->
<item name="ftue_auth_gutter_start_percent" format="float" type="dimen">0.05</item>
<item name="ftue_auth_gutter_end_percent" format="float" type="dimen">0.95</item>
</resources>

View File

@ -208,4 +208,4 @@ public final class LiveDataTestObserver<T> implements Observer<T> {
liveData.observeForever(observer);
return observer;
}
}
}

View File

@ -16,8 +16,11 @@
package im.vector.app.features.debug.features
import androidx.datastore.preferences.core.Preferences
import im.vector.app.features.DefaultVectorFeatures
import im.vector.app.features.VectorFeatures
import javax.inject.Inject
import kotlin.reflect.KFunction1
class DebugFeaturesStateFactory @Inject constructor(
private val debugFeatures: DebugVectorFeatures,
@ -31,18 +34,23 @@ class DebugFeaturesStateFactory @Inject constructor(
featureOverride = debugFeatures.onboardingVariant(),
featureDefault = defaultFeatures.onboardingVariant()
),
Feature.BooleanFeature(
createBooleanFeature(
label = "FTUE Splash - I already have an account",
featureOverride = debugFeatures.isAlreadyHaveAccountSplashEnabled().takeIf {
debugFeatures.hasOverride(DebugFeatureKeys.alreadyHaveAnAccount)
},
featureDefault = defaultFeatures.isAlreadyHaveAccountSplashEnabled(),
factory = VectorFeatures::isAlreadyHaveAccountSplashEnabled,
key = DebugFeatureKeys.alreadyHaveAnAccount
)
))
}
private fun createBooleanFeature(key: Preferences.Key<Boolean>, label: String, factory: KFunction1<VectorFeatures, Boolean>): Feature {
return Feature.BooleanFeature(
label = label,
featureOverride = factory.invoke(debugFeatures).takeIf { debugFeatures.hasOverride(key) },
featureDefault = factory.invoke(defaultFeatures),
key = key
)
}
private inline fun <reified T : Enum<T>> createEnumFeature(label: String, featureOverride: T, featureDefault: T): Feature {
return Feature.EnumFeature(
label = label,

View File

@ -43,9 +43,11 @@ class DebugVectorFeatures(
return readPreferences().getEnum<VectorFeatures.OnboardingVariant>() ?: vectorFeatures.onboardingVariant()
}
override fun isAlreadyHaveAccountSplashEnabled(): Boolean = readPreferences()[DebugFeatureKeys.alreadyHaveAnAccount]
override fun isAlreadyHaveAccountSplashEnabled(): Boolean = read(DebugFeatureKeys.alreadyHaveAnAccount)
?: vectorFeatures.isAlreadyHaveAccountSplashEnabled()
override fun isSplashCarouselEnabled(): Boolean = read(DebugFeatureKeys.splashCarousel) ?: vectorFeatures.isSplashCarouselEnabled()
fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
if (value == null) {
it.remove(key)
@ -66,6 +68,8 @@ class DebugVectorFeatures(
}
}
private fun read(key: Preferences.Key<Boolean>): Boolean? = readPreferences()[key]
private fun readPreferences() = runBlocking { dataStore.data.first() }
private fun updatePreferences(block: (MutablePreferences) -> Unit) = runBlocking {
@ -92,6 +96,6 @@ 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")
}

View File

@ -103,6 +103,7 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordMailConfi
import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordSuccessFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthServerSelectionFragment
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.FtueAuthWaitForEmailFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthWebFragment
@ -444,6 +445,11 @@ interface FragmentModule {
@FragmentKey(FtueAuthSplashFragment::class)
fun bindFtueAuthSplashFragment(fragment: FtueAuthSplashFragment): Fragment
@Binds
@IntoMap
@FragmentKey(FtueAuthSplashCarouselFragment::class)
fun bindFtueAuthSplashCarouselFragment(fragment: FtueAuthSplashCarouselFragment): Fragment
@Binds
@IntoMap
@FragmentKey(FtueAuthWaitForEmailFragment::class)

View File

@ -24,19 +24,17 @@ interface VectorFeatures {
fun isAlreadyHaveAccountSplashEnabled(): Boolean
fun isSplashCarouselEnabled(): Boolean
enum class OnboardingVariant {
LEGACY,
LOGIN_2,
FTUE_AUTH
}
enum class NotificationSettingsVersion {
V1,
V2
}
}
class DefaultVectorFeatures : VectorFeatures {
override fun onboardingVariant(): VectorFeatures.OnboardingVariant = BuildConfig.ONBOARDING_VARIANT
override fun isAlreadyHaveAccountSplashEnabled() = true
override fun isSplashCarouselEnabled() = false
}

View File

@ -36,7 +36,8 @@ class OnboardingVariantFactory @Inject constructor(
views = views,
onboardingViewModel = onboardingViewModel.value,
activity = activity,
supportFragmentManager = activity.supportFragmentManager
supportFragmentManager = activity.supportFragmentManager,
vectorFeatures = vectorFeatures
)
VectorFeatures.OnboardingVariant.LOGIN_2 -> Login2Variant(
views = views,

View File

@ -0,0 +1,105 @@
/*
* 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.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.databinding.FragmentFtueSplashCarouselBinding
import im.vector.app.features.VectorFeatures
import im.vector.app.features.onboarding.OnboardingAction
import im.vector.app.features.onboarding.OnboardingFlow
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.failure.Failure
import java.net.UnknownHostException
import javax.inject.Inject
class FtueAuthSplashCarouselFragment @Inject constructor(
private val vectorPreferences: VectorPreferences,
private val vectorFeatures: VectorFeatures,
private val carouselController: SplashCarouselController
) : AbstractFtueAuthFragment<FragmentFtueSplashCarouselBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueSplashCarouselBinding {
return FragmentFtueSplashCarouselBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupViews()
}
private fun setupViews() {
views.splashCarousel.adapter = carouselController.adapter
TabLayoutMediator(views.carouselIndicator, views.splashCarousel) { _, _ -> }.attach()
carouselController.setData(SplashCarouselState())
views.loginSplashSubmit.debouncedClicks { getStarted() }
views.loginSplashAlreadyHaveAccount.apply {
isVisible = vectorFeatures.isAlreadyHaveAccountSplashEnabled()
debouncedClicks { alreadyHaveAnAccount() }
}
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) {
views.loginSplashVersion.isVisible = true
@SuppressLint("SetTextI18n")
views.loginSplashVersion.text = "Version : ${BuildConfig.VERSION_NAME}#${BuildConfig.BUILD_NUMBER}\n" +
"Branch: ${BuildConfig.GIT_BRANCH_NAME}"
views.loginSplashVersion.debouncedClicks { navigator.openDebug(requireContext()) }
}
}
private fun getStarted() {
val getStartedFlow = if (vectorFeatures.isAlreadyHaveAccountSplashEnabled()) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp
viewModel.handle(OnboardingAction.OnGetStarted(resetLoginConfig = false, onboardingFlow = getStartedFlow))
}
private fun alreadyHaveAnAccount() {
viewModel.handle(OnboardingAction.OnIAlreadyHaveAnAccount(resetLoginConfig = false, onboardingFlow = OnboardingFlow.SignIn))
}
override fun resetViewModel() {
// Nothing to do
}
override fun onError(throwable: Throwable) {
if (throwable is Failure.NetworkConnection &&
throwable.ioException is UnknownHostException) {
// Invalid homeserver from URL config
val url = viewModel.getInitialHomeServerUrl().orEmpty()
MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.dialog_title_error)
.setMessage(getString(R.string.login_error_homeserver_from_url_not_found, url))
.setPositiveButton(R.string.login_error_homeserver_from_url_not_found_enter_manual) { _, _ ->
val flow = withState(viewModel) { it.onboardingFlow } ?: OnboardingFlow.SignInSignUp
viewModel.handle(OnboardingAction.OnGetStarted(resetLoginConfig = true, flow))
}
.setNegativeButton(R.string.action_cancel, null)
.show()
} else {
super.onError(throwable)
}
}
}

View File

@ -15,7 +15,6 @@
*/
package im.vector.app.features.onboarding.ftueauth
import android.content.Intent
import android.view.View
import android.view.ViewGroup
@ -34,6 +33,7 @@ import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityLoginBinding
import im.vector.app.features.VectorFeatures
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.login.LoginMode
@ -61,7 +61,8 @@ class FtueAuthVariant(
private val views: ActivityLoginBinding,
private val onboardingViewModel: OnboardingViewModel,
private val activity: VectorBaseActivity<ActivityLoginBinding>,
private val supportFragmentManager: FragmentManager
private val supportFragmentManager: FragmentManager,
private val vectorFeatures: VectorFeatures
) : OnboardingVariant {
private val enterAnim = R.anim.enter_fade_in
@ -108,7 +109,11 @@ class FtueAuthVariant(
}
private fun addFirstFragment() {
activity.addFragment(views.loginFragmentContainer, FtueAuthSplashFragment::class.java)
val splashFragment = when (vectorFeatures.isSplashCarouselEnabled()) {
true -> FtueAuthSplashCarouselFragment::class.java
else -> FtueAuthSplashFragment::class.java
}
activity.addFragment(views.loginFragmentContainer, splashFragment)
}
private fun handleOnboardingViewEvents(viewEvents: OnboardingViewEvents) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 New Vector Ltd
* Copyright 2019 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.

View File

@ -0,0 +1,31 @@
/*
* 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 com.airbnb.epoxy.TypedEpoxyController
import javax.inject.Inject
class SplashCarouselController @Inject constructor() : TypedEpoxyController<SplashCarouselState>() {
override fun buildModels(data: SplashCarouselState) {
data.items.forEachIndexed { index, item ->
splashCarouselItem {
id(index)
item(item)
}
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.widget.ImageView
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
@EpoxyModelClass(layout = R.layout.item_splash_carousel)
abstract class SplashCarouselItem : VectorEpoxyModel<SplashCarouselItem.Holder>() {
@EpoxyAttribute
lateinit var item: SplashCarouselState.Item
override fun bind(holder: Holder) {
super.bind(holder)
holder.view.setBackgroundResource(item.pageBackground)
holder.image.setImageResource(item.image)
holder.title.setText(item.title)
holder.body.setText(item.body)
}
class Holder : VectorEpoxyHolder() {
val image by bind<ImageView>(R.id.carousel_item_image)
val title by bind<TextView>(R.id.carousel_item_title)
val body by bind<TextView>(R.id.carousel_item_body)
}
}

View File

@ -0,0 +1,57 @@
/*
* 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 androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import im.vector.app.R
data class SplashCarouselState(
val items: List<Item> = listOf(
Item(
R.string.ftue_auth_carousel_1_title,
R.string.ftue_auth_carousel_1_body,
R.drawable.onboarding_carousel_conversations,
R.drawable.bg_carousel_page_1
),
Item(
R.string.ftue_auth_carousel_2_title,
R.string.ftue_auth_carousel_2_body,
R.drawable.onboarding_carousel_ems,
R.drawable.bg_carousel_page_2
),
Item(
R.string.ftue_auth_carousel_3_title,
R.string.ftue_auth_carousel_3_body,
R.drawable.onboarding_carousel_connect,
R.drawable.bg_carousel_page_3
),
Item(
R.string.ftue_auth_carousel_4_title,
R.string.ftue_auth_carousel_4_body,
R.drawable.onboarding_carousel_universal,
R.drawable.bg_carousel_page_4
)
)
) {
data class Item(
@StringRes val title: Int,
@StringRes val body: Int,
@DrawableRes val image: Int,
@DrawableRes val pageBackground: Int
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="?vctr_content_quaternary" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="?colorAccent" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/indicator_onboarding_carousel_selected" android:state_selected="true" />
<item android:drawable="@drawable/indicator_onboarding_carousel_inactive" />
</selector>

View File

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
android:background="?android:colorBackground"
android:paddingStart="36dp"
android:paddingTop="@dimen/layout_vertical_margin"
android:paddingEnd="36dp"
android:paddingBottom="@dimen/layout_vertical_margin">
<!-- Strategy: 5 Spaces are used to spread the remaining space, using weight -->
<Space
android:id="@+id/loginSplashSpace1"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/loginSplashLogoContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintVertical_weight="4" />
<LinearLayout
android:id="@+id/loginSplashLogoContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@id/loginSplashSpace2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSpace1">
<ImageView
android:id="@+id/loginSplashLogo"
android:layout_width="64dp"
android:layout_height="64dp"
android:importantForAccessibility="no"
android:src="@drawable/element_logo_green"
android:transitionName="loginLogoTransition" />
<ImageView
android:id="@+id/logoType"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="8dp"
android:contentDescription="@string/app_name"
android:src="@drawable/element_logotype"
app:tint="?colorSecondary"
tools:ignore="MissingPrefix" />
</LinearLayout>
<Space
android:id="@+id/loginSplashSpace2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/loginSplashTitle"
app:layout_constraintTop_toBottomOf="@id/loginSplashLogoContainer"
app:layout_constraintVertical_weight="1" />
<TextView
android:id="@+id/loginSplashTitle"
style="@style/Widget.Vector.TextView.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/login_splash_title"
android:textColor="?vctr_content_primary"
android:transitionName="loginTitleTransition"
app:layout_constraintBottom_toTopOf="@id/loginSplashSpace3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSpace2" />
<Space
android:id="@+id/loginSplashSpace3"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/loginSplashContent"
app:layout_constraintTop_toBottomOf="@id/loginSplashTitle"
app:layout_constraintVertical_weight="2" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/loginSplashContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/loginSplashSpace4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSpace3">
<ImageView
android:id="@+id/loginSplashPicto1"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_login_splash_message_circle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/loginSplashText1"
app:tint="?vctr_content_secondary"
tools:ignore="MissingPrefix" />
<TextView
android:id="@+id/loginSplashText1"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="start"
android:text="@string/login_splash_text1"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toTopOf="@id/loginSplashText2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/loginSplashPicto1"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/loginSplashPicto2"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:src="@drawable/ic_login_splash_lock"
app:layout_constraintStart_toStartOf="@id/loginSplashPicto1"
app:layout_constraintTop_toTopOf="@id/loginSplashText2"
app:tint="?vctr_content_secondary"
tools:ignore="MissingPrefix" />
<TextView
android:id="@+id/loginSplashText2"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="start"
android:text="@string/login_splash_text2"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toTopOf="@id/loginSplashText3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/loginSplashText1"
app:layout_constraintTop_toBottomOf="@id/loginSplashText1" />
<ImageView
android:id="@+id/loginSplashPicto3"
android:layout_width="32dp"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:src="@drawable/ic_login_splash_sliders"
app:layout_constraintStart_toStartOf="@id/loginSplashPicto1"
app:layout_constraintTop_toTopOf="@id/loginSplashText3"
app:tint="?vctr_content_secondary"
tools:ignore="MissingPrefix" />
<TextView
android:id="@+id/loginSplashText3"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="start"
android:text="@string/login_splash_text3"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/loginSplashText1"
app:layout_constraintTop_toBottomOf="@id/loginSplashText2" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Space
android:id="@+id/loginSplashSpace4"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/loginSplashSubmit"
app:layout_constraintTop_toBottomOf="@id/loginSplashContent"
app:layout_constraintVertical_weight="2" />
<Button
android:id="@+id/loginSplashSubmit"
style="@style/Widget.Vector.Button.Login"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/login_splash_submit"
android:textAllCaps="true"
android:transitionName="loginSubmitTransition"
app:layout_constraintBottom_toTopOf="@id/loginSplashSpace5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSpace4" />
<TextView
android:id="@+id/loginSplashAlreadyHaveAccount"
style="@style/Widget.Vector.Button.Text.Login"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/login_splash_already_have_account"
android:textAllCaps="true"
android:transitionName="loginSubmitTransition"
app:layout_constraintBottom_toTopOf="@id/loginSplashSpace5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSubmit" />
<Space
android:id="@+id/loginSplashSpace5"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashSubmit"
app:layout_constraintVertical_weight="4" />
<TextView
android:id="@+id/loginSplashVersion"
style="@style/Widget.Vector.TextView.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?vctr_content_secondary"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="@string/settings_version"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
android:background="@null">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/splashCarousel"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/splashCarouselFloatingSection"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.65" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/carouselIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
app:layout_constraintBottom_toTopOf="@id/loginSplashButtonsSpace"
app:layout_constraintTop_toBottomOf="@id/splashCarouselFloatingSection"
app:layout_constraintVertical_chainStyle="spread"
app:tabBackground="@drawable/indicator_onboarding_carousel_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabPaddingEnd="8dp"
app:tabPaddingStart="8dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/splashCarouselGutterStart"
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/splashCarouselGutterEnd"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
<Space
android:id="@+id/loginSplashButtonsSpace"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/loginSplashSubmit"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.01"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/carouselIndicator" />
<Button
android:id="@+id/loginSplashSubmit"
style="@style/Widget.Vector.Button.Login"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/login_splash_submit"
android:textAllCaps="true"
android:transitionName="loginSubmitTransition"
app:layout_constraintBottom_toTopOf="@id/loginSplashAlreadyHaveAccount"
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
app:layout_constraintTop_toBottomOf="@id/loginSplashButtonsSpace" />
<TextView
android:id="@+id/loginSplashAlreadyHaveAccount"
style="@style/Widget.Vector.Button.Text.Login"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/login_splash_already_have_account"
android:textAllCaps="true"
android:transitionName="loginSubmitTransition"
app:layout_constraintBottom_toTopOf="@id/loginSplashBottomSpace"
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
app:layout_constraintTop_toBottomOf="@id/loginSplashSubmit" />
<Space
android:id="@+id/loginSplashBottomSpace"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.05"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashAlreadyHaveAccount" />
<TextView
android:id="@+id/loginSplashVersion"
style="@style/Widget.Vector.TextView.Micro"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
android:textColor="?vctr_content_secondary"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_debug_icon"
app:drawableTint="?colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
tools:text="@string/settings_version"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/splashCarouselGutterStart"
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/splashCarouselGutterEnd"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
<Space
android:id="@+id/carousel_item_top_space"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/carousel_item_image_container"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/carousel_item_image_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:layout_constraintBottom_toTopOf="@id/carousel_item_image_bottom_space"
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
app:layout_constraintTop_toBottomOf="@id/carousel_item_top_space">
<ImageView
android:id="@+id/carousel_item_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:contentDescription="@null" />
</FrameLayout>
<Space
android:id="@+id/carousel_item_image_bottom_space"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.05"
app:layout_constraintBottom_toTopOf="@id/carousel_item_title"
app:layout_constraintHeight_percent="0.05"
app:layout_constraintTop_toBottomOf="@id/carousel_item_image_container" />
<TextView
android:id="@+id/carousel_item_title"
style="@style/Widget.Vector.TextView.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="@id/carousel_item_body"
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
app:layout_constraintTop_toBottomOf="@id/carousel_item_image_bottom_space"
tools:text="Login version" />
<TextView
android:id="@+id/carousel_item_body"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toTopOf="@id/splashCarouselFloatingSection"
app:layout_constraintEnd_toEndOf="@id/splashCarouselGutterEnd"
app:layout_constraintStart_toStartOf="@id/splashCarouselGutterStart"
app:layout_constraintTop_toBottomOf="@id/carousel_item_title"
tools:text="Login version" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/splashCarouselFloatingSection"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.65" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2536,6 +2536,16 @@
<string name="direct_room_join_rules_invite_by_you">You made this invite only.</string>
<string name="timeline_unread_messages">Unread messages</string>
<!-- Onboarding -->
<string name="ftue_auth_carousel_1_title">Own your conversions.</string>
<string name="ftue_auth_carousel_1_body">End-to-end encrypted messaging for secure and independent communication, connected via Matrix.</string>
<string name="ftue_auth_carousel_2_title">You\'re in control.</string>
<string name="ftue_auth_carousel_2_body">Element lets you choose where you messages are stored, keeping you in control of your data.</string>
<string name="ftue_auth_carousel_3_title">Connect with anyone.</string>
<string name="ftue_auth_carousel_3_body">Element works with all Matrix-based apps and can even bridge into proprietary messengers.</string>
<string name="ftue_auth_carousel_4_title">Cut the slack from teams.</string>
<string name="ftue_auth_carousel_4_body">As universal as email, Element is a completely new type of collaboration.</string>
<string name="login_splash_title">It\'s your conversation. Own it.</string>
<string name="login_splash_text1">Chat with people directly or in groups</string>
<string name="login_splash_text2">Keep conversations private with encryption</string>