Merge pull request #6795 from thundernest/add_onboarding_feature
Add onboarding feature
This commit is contained in:
commit
1a302a2dd3
21 changed files with 551 additions and 53 deletions
|
@ -10,7 +10,10 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.common.DevicePreviews
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveContent
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
|
||||
import app.k9mail.ui.catalog.items.buttonItems
|
||||
import app.k9mail.ui.catalog.items.colorItems
|
||||
import app.k9mail.ui.catalog.items.imageItems
|
||||
|
@ -30,39 +33,57 @@ fun CatalogContent(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(300.dp),
|
||||
contentPadding = contentPadding,
|
||||
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
modifier = modifier.padding(MainTheme.spacings.double),
|
||||
) {
|
||||
themeHeaderItem(text = "Thunderbird Catalog")
|
||||
themeSelectorItems(
|
||||
catalogTheme = catalogTheme,
|
||||
catalogThemeVariant = catalogThemeVariant,
|
||||
onThemeChange = onThemeChange,
|
||||
onThemeVariantChange = onThemeVariantChange,
|
||||
)
|
||||
ResponsiveContent {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(300.dp),
|
||||
contentPadding = contentPadding,
|
||||
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
modifier = modifier.padding(MainTheme.spacings.double),
|
||||
) {
|
||||
themeHeaderItem(text = "Thunderbird Catalog")
|
||||
themeSelectorItems(
|
||||
catalogTheme = catalogTheme,
|
||||
catalogThemeVariant = catalogThemeVariant,
|
||||
onThemeChange = onThemeChange,
|
||||
onThemeVariantChange = onThemeVariantChange,
|
||||
)
|
||||
|
||||
typographyItems()
|
||||
colorItems()
|
||||
buttonItems()
|
||||
selectionControlItems()
|
||||
textFieldItems()
|
||||
imageItems()
|
||||
typographyItems()
|
||||
colorItems()
|
||||
buttonItems()
|
||||
selectionControlItems()
|
||||
textFieldItems()
|
||||
imageItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
internal fun CatalogContentPreview() {
|
||||
CatalogContent(
|
||||
catalogTheme = CatalogTheme.K9,
|
||||
catalogThemeVariant = CatalogThemeVariant.LIGHT,
|
||||
onThemeChange = {},
|
||||
onThemeVariantChange = {},
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
internal fun CatalogContentK9ThemePreview() {
|
||||
K9Theme {
|
||||
CatalogContent(
|
||||
catalogTheme = CatalogTheme.K9,
|
||||
catalogThemeVariant = CatalogThemeVariant.LIGHT,
|
||||
onThemeChange = {},
|
||||
onThemeVariantChange = {},
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
internal fun CatalogContentThunderbirdThemePreview() {
|
||||
ThunderbirdTheme {
|
||||
CatalogContent(
|
||||
catalogTheme = CatalogTheme.THUNDERBIRD,
|
||||
catalogThemeVariant = CatalogThemeVariant.LIGHT,
|
||||
onThemeChange = {},
|
||||
onThemeVariantChange = {},
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -578,6 +578,8 @@ style:
|
|||
ignoreConstantDeclaration: true
|
||||
ignoreCompanionObjectPropertyDeclaration: true
|
||||
ignoreAnnotation: false
|
||||
ignoreAnnotated:
|
||||
- 'Preview'
|
||||
ignoreNamedArgument: true
|
||||
ignoreEnums: false
|
||||
ignoreRanges: false
|
||||
|
|
|
@ -12,11 +12,11 @@ enum class WindowSizeClass {
|
|||
;
|
||||
|
||||
companion object {
|
||||
private const val COMPACT_MAX_WIDTH = 600
|
||||
private const val COMPACT_MAX_HEIGHT = 480
|
||||
const val COMPACT_MAX_WIDTH = 600
|
||||
const val COMPACT_MAX_HEIGHT = 480
|
||||
|
||||
private const val MEDIUM_MAX_WIDTH = 840
|
||||
private const val MEDIUM_MAX_HEIGHT = 900
|
||||
const val MEDIUM_MAX_WIDTH = 840
|
||||
const val MEDIUM_MAX_HEIGHT = 900
|
||||
|
||||
fun fromWidth(width: Int): WindowSizeClass {
|
||||
return when {
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
|
||||
import androidx.compose.material.Surface as MaterialSurface
|
||||
|
@ -13,11 +14,13 @@ import androidx.compose.material.Surface as MaterialSurface
|
|||
fun Surface(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = MainTheme.colors.surface,
|
||||
elevation: Dp = MainTheme.elevations.default,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MaterialSurface(
|
||||
modifier = modifier,
|
||||
content = content,
|
||||
elevation = elevation,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.template
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
|
||||
/**
|
||||
* The [LazyColumnWithFooter] composable creates a [LazyColumn] with a footer.
|
||||
*
|
||||
* @param modifier The modifier to be applied to the layout.
|
||||
* @param verticalArrangement The vertical arrangement of the children.
|
||||
* @param horizontalAlignment The horizontal alignment of the children.
|
||||
* @param footer The footer to be displayed at the bottom of the [LazyColumn].
|
||||
* @param content The content of the [LazyColumn].
|
||||
*/
|
||||
@Composable
|
||||
fun LazyColumnWithFooter(
|
||||
modifier: Modifier = Modifier,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
footer: @Composable () -> Unit = {},
|
||||
content: LazyListScope.() -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
verticalArrangement = verticalArrangementWithFooter(verticalArrangement),
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
) {
|
||||
content()
|
||||
item { footer() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun verticalArrangementWithFooter(verticalArrangement: Arrangement.Vertical) = remember {
|
||||
object : Arrangement.Vertical {
|
||||
override fun Density.arrange(
|
||||
totalSize: Int,
|
||||
sizes: IntArray,
|
||||
outPositions: IntArray,
|
||||
) {
|
||||
val innerSizes = sizes.dropLast(1).toIntArray()
|
||||
val footerSize = sizes.last()
|
||||
val innerTotalSize = totalSize - footerSize
|
||||
|
||||
with(verticalArrangement) {
|
||||
arrange(
|
||||
totalSize = innerTotalSize,
|
||||
sizes = innerSizes,
|
||||
outPositions = outPositions,
|
||||
)
|
||||
}
|
||||
|
||||
outPositions[outPositions.lastIndex] = totalSize - footerSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
internal fun LazyColumnWithFooterPreview() {
|
||||
K9Theme {
|
||||
Surface {
|
||||
LazyColumnWithFooter(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(32.dp, Alignment.CenterVertically),
|
||||
footer = { Text(text = "Footer") },
|
||||
) {
|
||||
items(10) {
|
||||
Text(text = "Item $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package app.k9mail.core.ui.compose.designsystem.template
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.common.DevicePreviews
|
||||
import app.k9mail.core.ui.compose.common.window.WindowSizeClass
|
||||
import app.k9mail.core.ui.compose.common.window.getWindowSizeInfo
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
|
||||
/**
|
||||
* The [ResponsiveContent] composable automatically adapts its child content to different screen sizes and resolutions,
|
||||
* providing a responsive layout for a better user experience.
|
||||
*
|
||||
* It uses the [WindowSizeClass] (Compact, Medium, or Expanded) to make appropriate layout adjustments.
|
||||
*
|
||||
* @param modifier The modifier to be applied to the layout.
|
||||
* @param content The content to be displayed.
|
||||
*/
|
||||
@Composable
|
||||
fun ResponsiveContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val windowSizeClass = getWindowSizeInfo()
|
||||
|
||||
when (windowSizeClass.screenWidthSizeClass) {
|
||||
WindowSizeClass.Compact -> CompactContent(modifier = modifier, content = content)
|
||||
WindowSizeClass.Medium -> MediumContent(modifier = modifier, content = content)
|
||||
WindowSizeClass.Expanded -> ExpandedContent(modifier = modifier, content = content)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CompactContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MediumContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.requiredWidth(WindowSizeClass.COMPACT_MAX_WIDTH.dp),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExpandedContent(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
when (getWindowSizeInfo().screenHeightSizeClass) {
|
||||
WindowSizeClass.Compact -> MediumContent(modifier, content)
|
||||
WindowSizeClass.Medium -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.requiredWidth(WindowSizeClass.MEDIUM_MAX_WIDTH.dp),
|
||||
elevation = MainTheme.elevations.raised,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowSizeClass.Expanded -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(modifier),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.requiredWidth(WindowSizeClass.MEDIUM_MAX_WIDTH.dp)
|
||||
.requiredHeight(WindowSizeClass.MEDIUM_MAX_HEIGHT.dp),
|
||||
elevation = MainTheme.elevations.raised,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DevicePreviews
|
||||
internal fun ResponsiveContentPreview() {
|
||||
K9Theme {
|
||||
Surface {
|
||||
ResponsiveContent {
|
||||
Surface(
|
||||
color = MainTheme.colors.info,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,9 @@ import androidx.compose.ui.unit.dp
|
|||
|
||||
@Immutable
|
||||
data class Elevations(
|
||||
val card: Dp = 0.dp,
|
||||
val default: Dp = 0.dp,
|
||||
val raised: Dp = 2.dp,
|
||||
val card: Dp = 4.dp,
|
||||
)
|
||||
|
||||
internal val LocalElevations = staticCompositionLocalOf { Elevations() }
|
||||
|
|
|
@ -2,6 +2,9 @@ package app.k9mail.core.ui.compose.theme
|
|||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import app.k9mail.core.ui.compose.theme.color.MaterialColor
|
||||
import app.k9mail.core.ui.compose.theme.color.darkColors
|
||||
import app.k9mail.core.ui.compose.theme.color.lightColors
|
||||
|
||||
private val k9LightColorPalette = lightColors(
|
||||
primary = MaterialColor.gray_800,
|
||||
|
@ -12,7 +15,7 @@ private val k9LightColorPalette = lightColors(
|
|||
|
||||
private val k9DarkColorPalette = darkColors(
|
||||
primary = MaterialColor.gray_100,
|
||||
primaryVariant = MaterialColor.gray_50,
|
||||
primaryVariant = MaterialColor.gray_400,
|
||||
secondary = MaterialColor.pink_300,
|
||||
secondaryVariant = MaterialColor.pink_500,
|
||||
)
|
||||
|
@ -22,7 +25,7 @@ fun K9Theme(
|
|||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val images = Images(logo = R.drawable.core_ui_theme_logo_orange)
|
||||
val images = Images(logo = R.drawable.core_ui_theme_k9_logo)
|
||||
|
||||
MainTheme(
|
||||
lightColorPalette = k9LightColorPalette,
|
||||
|
|
|
@ -7,6 +7,9 @@ import androidx.compose.material.Typography
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import app.k9mail.core.ui.compose.theme.color.Colors
|
||||
import app.k9mail.core.ui.compose.theme.color.LocalColors
|
||||
import app.k9mail.core.ui.compose.theme.color.toMaterialColors
|
||||
|
||||
@Composable
|
||||
fun MainTheme(
|
||||
|
|
|
@ -2,21 +2,22 @@ package app.k9mail.core.ui.compose.theme
|
|||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import app.k9mail.core.ui.compose.theme.color.MaterialColor
|
||||
import app.k9mail.core.ui.compose.theme.color.darkColors
|
||||
import app.k9mail.core.ui.compose.theme.color.lightColors
|
||||
|
||||
private val thunderbirdLightColorPalette = lightColors(
|
||||
primary = MaterialColor.blue_600,
|
||||
primaryVariant = MaterialColor.light_blue_500,
|
||||
primary = MaterialColor.blue_800,
|
||||
primaryVariant = MaterialColor.light_blue_700,
|
||||
secondary = MaterialColor.pink_500,
|
||||
secondaryVariant = MaterialColor.pink_300,
|
||||
background = MaterialColor.gray_200,
|
||||
)
|
||||
|
||||
private val thunderbirdDarkColorPalette = darkColors(
|
||||
primary = MaterialColor.blue_100,
|
||||
primaryVariant = MaterialColor.blue_50,
|
||||
primary = MaterialColor.blue_200,
|
||||
primaryVariant = MaterialColor.blue_400,
|
||||
secondary = MaterialColor.pink_300,
|
||||
secondaryVariant = MaterialColor.pink_500,
|
||||
background = MaterialColor.gray_800,
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
@ -24,7 +25,7 @@ fun ThunderbirdTheme(
|
|||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val images = Images(logo = R.drawable.core_ui_theme_logo_teal)
|
||||
val images = Images(logo = R.drawable.core_ui_theme_thunderbird_logo)
|
||||
|
||||
MainTheme(
|
||||
lightColorPalette = thunderbirdLightColorPalette,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package app.k9mail.core.ui.compose.theme
|
||||
package app.k9mail.core.ui.compose.theme.color
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
|
@ -31,7 +31,7 @@ internal fun lightColors(
|
|||
primaryVariant: Color = MaterialColor.deep_purple_900,
|
||||
secondary: Color = MaterialColor.cyan_600,
|
||||
secondaryVariant: Color = MaterialColor.cyan_800,
|
||||
background: Color = MaterialColor.gray_200,
|
||||
background: Color = Color.White,
|
||||
surface: Color = Color.White,
|
||||
success: Color = MaterialColor.green_600,
|
||||
error: Color = MaterialColor.red_600,
|
||||
|
@ -66,9 +66,9 @@ internal fun darkColors(
|
|||
primary: Color = MaterialColor.deep_purple_200,
|
||||
primaryVariant: Color = MaterialColor.deep_purple_50,
|
||||
secondary: Color = MaterialColor.cyan_300,
|
||||
secondaryVariant: Color = MaterialColor.cyan_100,
|
||||
background: Color = MaterialColor.gray_800,
|
||||
surface: Color = MaterialColor.gray_900,
|
||||
secondaryVariant: Color = secondary,
|
||||
background: Color = MaterialColor.gray_950,
|
||||
surface: Color = MaterialColor.gray_950,
|
||||
success: Color = MaterialColor.green_300,
|
||||
error: Color = MaterialColor.red_300,
|
||||
warning: Color = MaterialColor.orange_300,
|
|
@ -1,6 +1,6 @@
|
|||
@file:Suppress("unused")
|
||||
|
||||
package app.k9mail.core.ui.compose.theme
|
||||
package app.k9mail.core.ui.compose.theme.color
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
|
@ -214,4 +214,5 @@ internal object MaterialColor {
|
|||
val gray_700 = Color(color = 0xFF616161)
|
||||
val gray_800 = Color(color = 0xFF424242)
|
||||
val gray_900 = Color(color = 0xFF212121)
|
||||
val gray_950 = Color(color = 0xFF121212)
|
||||
}
|
|
@ -64,7 +64,7 @@
|
|||
android:pathData="M171.99,116V48C171.99,41.37 166.62,36 159.99,36L31.99,36C25.37,36 19.99,41.37 19.99,48V116C19.99,122.62 25.37,128 31.99,128L159.99,128C166.62,128 171.99,122.62 171.99,116Z"
|
||||
android:strokeAlpha="0.2" />
|
||||
<path
|
||||
android:fillColor="#FFB74D"
|
||||
android:fillColor="#FF2C55"
|
||||
android:pathData="M172,116V48C172,41.37 166.63,36 160,36L32,36C25.37,36 20,41.37 20,48V116C20,122.63 25.37,128 32,128H160C166.63,128 172,122.63 172,116Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
@ -73,10 +73,10 @@
|
|||
android:strokeColor="#FBE9E7"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:fillColor="#FFB74D"
|
||||
android:fillColor="#FF2C55"
|
||||
android:pathData="M32,36C25.35,36 20,41.35 20,48V49C20,42.35 25.35,37 32,37H160C166.65,37 172,42.35 172,49V48C172,41.35 166.65,36 160,36H32Z" />
|
||||
<path
|
||||
android:fillColor="#F57C00"
|
||||
android:fillColor="#C2185B"
|
||||
android:pathData="M20,115V116C20,122.65 25.35,128 32,128H160C166.65,128 172,122.65 172,116V115C172,121.65 166.65,127 160,127H32C25.35,127 20,121.65 20,115Z" />
|
||||
<path
|
||||
android:fillAlpha="0.2"
|
|
@ -64,7 +64,7 @@
|
|||
android:pathData="M171.99,116V48C171.99,41.37 166.62,36 159.99,36L31.99,36C25.37,36 19.99,41.37 19.99,48V116C19.99,122.62 25.37,128 31.99,128L159.99,128C166.62,128 171.99,122.62 171.99,116Z"
|
||||
android:strokeAlpha="0.2" />
|
||||
<path
|
||||
android:fillColor="#4DB6AC"
|
||||
android:fillColor="#1E88E5"
|
||||
android:pathData="M172,116V48C172,41.37 166.63,36 160,36L32,36C25.37,36 20,41.37 20,48V116C20,122.63 25.37,128 32,128H160C166.63,128 172,122.63 172,116Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
|
@ -73,7 +73,7 @@
|
|||
android:strokeColor="#FBE9E7"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:fillColor="#4DB6AC"
|
||||
android:fillColor="#1E88E5"
|
||||
android:pathData="M32,36C25.35,36 20,41.35 20,48V49C20,42.35 25.35,37 32,37H160C166.65,37 172,42.35 172,49V48C172,41.35 166.65,36 160,36H32Z" />
|
||||
<path
|
||||
android:fillColor="#00796B"
|
12
feature/onboarding/build.gradle.kts
Normal file
12
feature/onboarding/build.gradle.kts
Normal file
|
@ -0,0 +1,12 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.androidCompose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "net.thunderbird.feature.onboarding"
|
||||
resourcePrefix = "onboarding_"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.ui.compose.designsystem)
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package net.thunderbird.feature.onboarding
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.common.DevicePreviews
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Background
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.button.Button
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonText
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBody1
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadline2
|
||||
import app.k9mail.core.ui.compose.designsystem.template.LazyColumnWithFooter
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveContent
|
||||
import app.k9mail.core.ui.compose.theme.K9Theme
|
||||
import app.k9mail.core.ui.compose.theme.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
|
||||
|
||||
@Composable
|
||||
internal fun OnboardingContent(
|
||||
onStartClick: () -> Unit,
|
||||
onImportClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ResponsiveContent {
|
||||
Background(
|
||||
modifier = modifier,
|
||||
) {
|
||||
LazyColumnWithFooter(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.padding(MainTheme.spacings.double),
|
||||
footer = {
|
||||
WelcomeFooter(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = MainTheme.spacings.triple),
|
||||
onStartClick = onStartClick,
|
||||
onImportClick = onImportClick,
|
||||
)
|
||||
},
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.sizes.large, Alignment.CenterVertically),
|
||||
) {
|
||||
item {
|
||||
WelcomeLogo(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
WelcomeTitle(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
WelcomeMessage(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WelcomeLogo(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.then(modifier),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(MainTheme.sizes.huge)
|
||||
.clip(shape = RoundedCornerShape(percent = 50))
|
||||
.background(color = MainTheme.colors.surface),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = MainTheme.images.logo),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(MainTheme.sizes.huge),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WelcomeTitle(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
TextHeadline2(
|
||||
text = stringResource(id = R.string.onboarding_welcome_title),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WelcomeMessage(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = MainTheme.spacings.quadruple, end = MainTheme.spacings.quadruple)
|
||||
.then(modifier),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
TextBody1(
|
||||
text = stringResource(id = R.string.onboarding_welcome_message),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WelcomeFooter(
|
||||
onStartClick: () -> Unit,
|
||||
onImportClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Button(
|
||||
text = stringResource(id = R.string.onboarding_welcome_start_button),
|
||||
onClick = onStartClick,
|
||||
)
|
||||
ButtonText(
|
||||
text = stringResource(id = R.string.onboarding_welcome_import_button),
|
||||
onClick = onImportClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DevicePreviews
|
||||
internal fun OnboardingContentK9Preview() {
|
||||
K9Theme {
|
||||
OnboardingContent(
|
||||
onStartClick = {},
|
||||
onImportClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DevicePreviews
|
||||
internal fun OnboardingContentThunderbirdPreview() {
|
||||
ThunderbirdTheme {
|
||||
OnboardingContent(
|
||||
onStartClick = {},
|
||||
onImportClick = {},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package net.thunderbird.feature.onboarding
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@Composable
|
||||
fun OnboardingScreen(
|
||||
onStartClick: () -> Unit,
|
||||
onImportClick: () -> Unit,
|
||||
) {
|
||||
OnboardingContent(
|
||||
onStartClick = onStartClick,
|
||||
onImportClick = onImportClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
internal fun OnboardingScreenPreview() {
|
||||
OnboardingScreen(
|
||||
onStartClick = {},
|
||||
onImportClick = {},
|
||||
)
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package net.thunderbird.feature.onboarding.navigation
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
import net.thunderbird.feature.onboarding.OnboardingScreen
|
||||
|
||||
const val NAVIGATION_ROUTE_ONBOARDING = "onboarding"
|
||||
|
||||
fun NavController.navigateToOnboarding(
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(NAVIGATION_ROUTE_ONBOARDING, navOptions)
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.onboardingScreen(
|
||||
onStartClick: () -> Unit,
|
||||
onImportClick: () -> Unit,
|
||||
) {
|
||||
composable(route = NAVIGATION_ROUTE_ONBOARDING) {
|
||||
OnboardingScreen(
|
||||
onStartClick = onStartClick,
|
||||
onImportClick = onImportClick,
|
||||
)
|
||||
}
|
||||
}
|
7
feature/onboarding/src/main/res/values/strings.xml
Normal file
7
feature/onboarding/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="onboarding_welcome_title">K-9 Mail</string>
|
||||
<string name="onboarding_welcome_message">Welcome to K-9 Mail, the Android email client designed for enhanced security, easy customization, and seamless management of all your email accounts.</string>
|
||||
<string name="onboarding_welcome_start_button">Start</string>
|
||||
<string name="onboarding_welcome_import_button">Import settings</string>
|
||||
</resources>
|
|
@ -83,6 +83,7 @@ androidx-compose-activity = "androidx.activity:activity-compose:1.6.1"
|
|||
androidx-compose-lifecycle-viewmodel = "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
|
||||
androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidxComposeMaterial" }
|
||||
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidxComposeMaterial" }
|
||||
androidx-compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" }
|
||||
androidx-test-core = "androidx.test:core:1.5.0"
|
||||
androidx-test-ext-junit-ktx = "androidx.test.ext:junit-ktx:1.1.5"
|
||||
androidx-test-espresso-core = "androidx.test.espresso:espresso-core:3.5.1"
|
||||
|
@ -154,6 +155,7 @@ shared-jvm-android-compose = [
|
|||
"androidx-compose-foundation",
|
||||
"androidx-compose-ui-tooling-preview",
|
||||
"androidx-compose-lifecycle-viewmodel",
|
||||
"androidx-compose-navigation",
|
||||
]
|
||||
shared-jvm-android-compose-debug = [
|
||||
"androidx-compose-ui-tooling",
|
||||
|
|
|
@ -41,6 +41,10 @@ include(
|
|||
":app:html-cleaner",
|
||||
)
|
||||
|
||||
include(
|
||||
":feature:onboarding",
|
||||
)
|
||||
|
||||
include(
|
||||
":core:common",
|
||||
":core:testing",
|
||||
|
|
Loading…
Reference in a new issue