Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:56:56 +01:00
parent 75dc487a7a
commit 39c29d175b
6317 changed files with 388324 additions and 2 deletions

7
app-ui-catalog/README.md Normal file
View file

@ -0,0 +1,7 @@
# Thunderbird UI Catalog
Uses [`:core:ui:compose:designsystem`](../core/ui/compose/designsystem/README.md)
This is a catalog of all the components in the Thunderbird design system.
It is a work in progress, and will be updated as the design system evolves.

View file

@ -0,0 +1,41 @@
plugins {
id(ThunderbirdPlugins.App.androidCompose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.kotlin.parcelize)
}
android {
namespace = "net.thunderbird.ui.catalog"
defaultConfig {
applicationId = "net.thunderbird.ui.catalog"
versionCode = 1
versionName = "1.0"
}
buildTypes {
// Preview build type to render compose without debug features.
// This gives a better idea of the real world drawing performance.
create("preview") {
initWith(getByName("debug"))
applicationIdSuffix = ".preview"
isDebuggable = false
matchingFallbacks += listOf("release")
}
}
}
dependencies {
implementation(projects.core.ui.compose.navigation)
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.ui.legacy.designsystem)
implementation(projects.core.ui.compose.theme2.thunderbird)
implementation(projects.core.ui.compose.theme2.k9mail)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.kotlinx.datetime)
}

21
app-ui-catalog/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,18 @@
package net.thunderbird.ui.catalog.ui.page.atom
import androidx.compose.runtime.Composable
import app.k9mail.core.ui.compose.common.annotation.PreviewDevicesWithBackground
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
import kotlinx.collections.immutable.persistentListOf
@Composable
@PreviewDevicesWithBackground
internal fun CatalogContentPreview() {
PreviewWithTheme {
CatalogAtomContent(
pages = persistentListOf(CatalogAtomPage.TYPOGRAPHY, CatalogAtomPage.COLOR),
initialPage = CatalogAtomPage.TYPOGRAPHY,
onEvent = {},
)
}
}

View file

@ -0,0 +1,20 @@
package net.thunderbird.ui.catalog.ui.page.atom.view
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.common.annotation.PreviewDevices
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
import app.k9mail.core.ui.compose.theme2.MainTheme
@Composable
@Preview(showBackground = true)
@PreviewDevices
internal fun ColorContentPreview() {
PreviewWithTheme {
ColorContent(
text = "Primary",
color = MainTheme.colors.primary,
textColor = MainTheme.colors.onPrimary,
)
}
}

View file

@ -0,0 +1,221 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="1024dp"
android:height="1024dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M866.3,634H493.4C482.5,634 473.6,642.9 473.6,653.8V713.5C473.6,801.4 544.8,872.6 632.6,872.6H839C904.8,872.6 958.3,819.2 958.3,753.3V701.1C958.3,671.2 928.4,634 866.3,634Z"
android:fillColor="#008787"/>
<path
android:pathData="M439.5,260.2L439.6,260.2C466.3,167 582.5,121.5 700.9,121.5C782.7,121.5 856.2,147.4 906.5,188.5C875.3,190.1 845.7,196.2 818.5,206C859.2,221.1 894.2,244.4 920.1,273.2C902.9,270.2 885,268.7 866.8,268.7C864.8,268.7 862.8,268.7 860.9,268.7C907.9,337 935.5,419.7 935.5,508.9C935.5,742.8 745.9,932.4 512,932.4C281.7,932.4 88.5,739.5 88.5,508.9C88.5,472.4 93.3,434.8 102.7,399.5C105.2,392.1 108.6,385 113.2,382.4C118.9,379.2 124.1,388.9 125,392C131.2,415.3 139.5,437.9 149.8,459.5C148.9,411.1 169.6,367.1 198,329C217,303.6 234.5,280 242.6,212.1C243.2,207.6 247.5,204.3 251.9,205.7C313.5,225.9 346.5,328.6 341.4,414.4C375.4,419.3 375.3,383.7 375.3,383.7C364.4,350.2 371.6,288 439.4,260.2L439.5,260.2Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="396.6"
android:startY="288.4"
android:endX="832.5"
android:endY="817.9"
android:type="linear">
<item android:offset="0" android:color="#FF88CCFC"/>
<item android:offset="1" android:color="#FF590DF2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M921.6,401C931.9,633.9 740.5,834.8 507,834.8C288.4,834.8 109.3,665.8 93.1,451.4C90.2,471.2 88.7,491.4 88.5,512C90.2,741.5 282.9,932.4 512,932.4C745.9,932.4 935.5,742.8 935.5,508.9C935.5,471.6 930.7,435.5 921.6,401Z"
android:strokeAlpha="0.9"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="234.4"
android:centerY="452.7"
android:gradientRadius="358.1"
android:type="radial">
<item android:offset="0.5" android:color="#000B4186"/>
<item android:offset="1" android:color="#720B4186"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M502.2,291.3C497.6,283.2 476.5,271.3 467.3,269.2C502.2,157.6 679.7,123.3 788.4,143C833.6,151.3 889.9,175.9 907,188.7C856.7,147.6 783.2,121.7 701.4,121.7C583,121.7 466.7,167.2 440.1,260.4L440,260.4L439.9,260.4C372.1,288.2 364.9,350.5 375.8,383.9C386.2,344 436,295 502.2,291.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="587.8"
android:startY="332.8"
android:endX="442.3"
android:endY="231"
android:type="linear">
<item android:offset="0" android:color="#00D13AEF"/>
<item android:offset="1" android:color="#E5F9D286"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M624.7,214C529.6,232.7 498.5,238.8 466.7,269.1C502.4,174.4 593.7,155.2 702.5,198.4C672.5,204.6 646.9,209.6 624.7,214Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="307.2"
android:startY="473.7"
android:endX="616.4"
android:endY="164.5"
android:type="linear">
<item android:offset="0" android:color="#FF450FB0"/>
<item android:offset="0.8" android:color="#1E450FB0"/>
<item android:offset="1" android:color="#00450FB0"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M111.5,386.6C85.5,493 105.6,618 223.7,723C188.5,684.6 145.6,542.6 240.3,441.2C246.7,434.4 257.6,439.4 258,448.8C265.8,659.5 435.8,788.2 631.9,764.1C571.1,760.7 370.3,690.4 519.7,662.5C597.8,648 720.3,625.2 720.3,515.3C720.3,337.1 582.5,285 499,292.8C441.8,298.1 391,334.4 375.3,383.7C381.3,403.1 357.4,416.8 341.4,414.5C346.5,328.6 313.5,225.9 251.9,205.7C247.5,204.3 243.2,207.6 242.6,212.1C234.5,280 217,303.6 198,329C169.6,367.1 148.9,411.1 149.8,459.5C139.5,437.9 131.2,415.3 125,392C124.3,389.4 120.5,382.1 115.9,381.8C113.4,381.6 112.1,384 111.5,386.6Z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="352.3"
android:centerY="739.6"
android:gradientRadius="558.7"
android:type="radial">
<item android:offset="0" android:color="#FF650877"/>
<item android:offset="1" android:color="#00340B86"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M467.2,673.3C582.2,766.7 813.5,696.7 813.5,469.6C720.1,611.2 601.2,708.8 467.2,673.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="778.1"
android:startY="576.8"
android:endX="630.1"
android:endY="762.5"
android:type="linear">
<item android:offset="0" android:color="#00D647E2"/>
<item android:offset="1" android:color="#A3E7BB65"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M240.3,441.2C242.7,438.6 245.8,437.7 248.7,438.1C164,541.5 232.3,723.1 279.3,767.7C281.9,775.1 234.8,736.5 228.3,727.5C192.6,697.2 141.4,547.1 240.3,441.2Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="173.2"
android:startY="429.1"
android:endX="241.4"
android:endY="712.5"
android:type="linear">
<item android:offset="0.1" android:color="#FFF9BF86"/>
<item android:offset="1" android:color="#00D63AEF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M512,681.9C627,681.9 720.3,605.8 720.3,511.9C720.3,418.1 627,342 512,342C413.9,342 303.7,405.8 303.7,514.4C303.8,682.2 481,778.7 632.1,764.1C620.8,762.8 550,759 502.1,704.9C497.8,700 490.3,691.5 493.7,686C497.1,680.4 506.5,681.9 512,681.9Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="512"
android:startY="404.2"
android:endX="512"
android:endY="762.4"
android:type="linear">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="0.9" android:color="#FFBEE1FE"/>
<item android:offset="1" android:color="#FF96CEFD"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M541.2,257C563.5,249.9 561.6,227.9 561.6,227.9C561.6,227.9 550.4,214.8 528.3,222C507.7,228.8 504.4,243.6 504.4,243.6C504.4,243.6 515.7,265 541.2,257Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M512.5,440.5C465.2,423.4 438.7,420.8 391.5,440.5V582C438.7,567 465.3,566.7 512.5,582C564,573.1 589.7,572.9 629,582V440.5C586.8,424 560.8,426.3 512.5,440.5Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="510.3"
android:startY="426.7"
android:endX="510.3"
android:endY="582"
android:type="linear">
<item android:offset="0" android:color="#A3BCE0FD"/>
<item android:offset="1" android:color="#00D647E2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M445,561.3C454.5,561.3 463.8,562.3 472.9,564.4C481.9,566.6 490.9,569.8 499.8,574V454C491.5,449.1 482.7,445.5 473.3,443C464,440.6 454.5,439.4 445,439.4C437.7,439.4 430.4,440.1 423.2,441.5C416,442.9 409.1,445.1 402.4,447.9V568.6C409.5,566.1 416.5,564.3 423.5,563.1C430.5,561.9 437.7,561.3 445,561.3ZM524.2,574C533.1,569.8 542.1,566.6 551.1,564.4C560.2,562.3 569.5,561.3 579,561.3C586.3,561.3 593.5,561.9 600.5,563.1C607.5,564.3 614.5,566.1 621.6,568.6V447.9C614.9,445.1 608,442.9 600.8,441.5C593.6,440.1 586.3,439.4 579,439.4C569.5,439.4 560,440.6 550.7,443C541.3,445.5 532.5,449.1 524.2,454V574ZM512,610C502.3,602.3 491.7,596.3 480.3,592C469,587.8 457.2,585.6 445,585.6C436.5,585.6 428.1,586.7 419.9,589C411.7,591.2 403.8,594.4 396.3,598.4C392,600.7 387.9,600.6 383.9,598.1C380,595.7 378,592.1 378,587.5V440.6C378,438.4 378.6,436.2 379.7,434.2C380.8,432.2 382.5,430.6 384.7,429.6C394,424.8 403.8,421.1 413.9,418.7C424.1,416.2 434.4,415 445,415C456.8,415 468.3,416.5 479.6,419.6C490.8,422.6 501.6,427.2 512,433.3C522.4,427.2 533.2,422.6 544.4,419.6C555.7,416.5 567.2,415 579,415C589.6,415 599.9,416.2 610.1,418.7C620.2,421.1 630,424.8 639.3,429.6C641.5,430.6 643.2,432.2 644.3,434.2C645.4,436.2 646,438.4 646,440.6V587.5C646,592.1 644,595.7 640.1,598.1C636.1,600.6 632,600.7 627.7,598.4C620.2,594.4 612.3,591.2 604.1,589C595.9,586.7 587.5,585.6 579,585.6C566.8,585.6 555,587.8 543.7,592C532.3,596.3 521.7,602.3 512,610Z"
android:fillColor="#0768BA"
android:fillAlpha="0.3"/>
<path
android:pathData="M414.5,539.6H428.3L435.3,519.5H466.9L474.2,539.6H487.6L458.1,461H444.1L414.5,539.6ZM439.2,508.2L450.8,475.6H451.4L463,508.2H439.2ZM536.4,486.9V466.2C543.1,463.3 549.9,461.2 556.9,459.8C563.9,458.4 571.3,457.7 579,457.7C584.3,457.7 589.5,458.1 594.5,458.9C599.6,459.7 604.6,460.7 609.5,461.9V481.4C604.6,479.6 599.7,478.2 594.7,477.3C589.7,476.4 584.5,475.9 579,475.9C571.3,475.9 563.9,476.9 556.8,478.8C549.7,480.8 542.9,483.5 536.4,486.9ZM536.4,553.9V533.2C543.1,530.4 549.9,528.2 556.9,526.8C563.9,525.4 571.3,524.7 579,524.7C584.3,524.7 589.5,525.1 594.5,525.9C599.6,526.7 604.6,527.7 609.5,529V548.5C604.6,546.6 599.7,545.3 594.7,544.3C589.7,543.4 584.5,543 579,543C571.3,543 563.9,543.9 556.8,545.7C549.7,547.5 542.9,550.3 536.4,553.9ZM536.4,520.4V499.7C543.1,496.9 549.9,494.7 556.9,493.3C563.9,491.9 571.3,491.2 579,491.2C584.3,491.2 589.5,491.6 594.5,492.4C599.6,493.2 604.6,494.2 609.5,495.4V514.9C604.6,513.1 599.7,511.7 594.7,510.8C589.7,509.9 584.5,509.5 579,509.5C571.3,509.5 563.9,510.4 556.8,512.3C549.7,514.3 542.9,517 536.4,520.4ZM445,561.3C454.5,561.3 463.8,562.3 472.9,564.4C481.9,566.6 490.9,569.8 499.8,574V454C491.5,449.1 482.7,445.5 473.3,443C464,440.6 454.5,439.4 445,439.4C437.7,439.4 430.4,440.1 423.2,441.5C416,442.9 409.1,445.1 402.4,447.9V568.6C409.5,566.1 416.5,564.3 423.5,563.1C430.5,561.9 437.7,561.3 445,561.3ZM524.2,574C533.1,569.8 542.1,566.6 551.1,564.4C560.2,562.3 569.5,561.3 579,561.3C586.3,561.3 593.5,561.9 600.5,563.1C607.5,564.3 614.5,566.1 621.6,568.6V447.9C614.9,445.1 608,442.9 600.8,441.5C593.6,440.1 586.3,439.4 579,439.4C569.5,439.4 560,440.6 550.7,443C541.3,445.5 532.5,449.1 524.2,454V574ZM512,610C502.3,602.3 491.7,596.3 480.3,592C469,587.8 457.2,585.6 445,585.6C436.5,585.6 428.1,586.7 419.9,589C411.7,591.2 403.8,594.4 396.3,598.4C392,600.7 387.9,600.6 383.9,598.1C380,595.7 378,592.1 378,587.5V440.6C378,438.4 378.6,436.2 379.7,434.2C380.8,432.2 382.5,430.6 384.7,429.6C394,424.8 403.8,421.1 413.9,418.7C424.1,416.2 434.4,415 445,415C456.8,415 468.3,416.5 479.6,419.6C490.8,422.6 501.6,427.2 512,433.3C522.4,427.2 533.2,422.6 544.4,419.6C555.7,416.5 567.2,415 579,415C589.6,415 599.9,416.2 610.1,418.7C620.2,421.1 630,424.8 639.3,429.6C641.5,430.6 643.2,432.2 644.3,434.2C645.4,436.2 646,438.4 646,440.6V587.5C646,592.1 644,595.7 640.1,598.1C636.1,600.6 632,600.7 627.7,598.4C620.2,594.4 612.3,591.2 604.1,589C595.9,586.7 587.5,585.6 579,585.6C566.8,585.6 555,587.8 543.7,592C532.3,596.3 521.7,602.3 512,610Z"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:startX="472"
android:startY="430"
android:endX="555.5"
android:endY="629"
android:type="linear">
<item android:offset="0" android:color="#FF590DF2"/>
<item android:offset="0.5" android:color="#FF1373D9"/>
<item android:offset="1" android:color="#FFEF3ACC"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M435.3,517.5H433.8L433.4,518.8L426.8,537.6H417.4L445.5,463H456.7L484.7,537.6H475.6L468.8,518.8L468.3,517.5H466.9H435.3ZM437.3,507.6L436.4,510.2H439.2H463H465.8L464.9,507.6L453.3,475L452.8,473.6H451.4H450.8H449.4L448.9,475L437.3,507.6ZM499,575.9L501.8,577.2V574V454V452.9L500.8,452.3C492.3,447.3 483.3,443.6 473.8,441.1C464.3,438.6 454.7,437.4 445,437.4C437.6,437.4 430.2,438.1 422.8,439.5C415.5,441 408.4,443.2 401.6,446.1L400.4,446.6V447.9V568.6V571.4L403,570.5C410,568 417,566.2 423.9,565C430.8,563.8 437.8,563.3 445,563.3C454.4,563.3 463.5,564.3 472.4,566.4C481.3,568.5 490.1,571.6 499,575.9ZM522.2,574V577.2L525,575.9C533.9,571.6 542.7,568.5 551.6,566.4C560.5,564.3 569.6,563.3 579,563.3C586.2,563.3 593.2,563.8 600.1,565C607,566.2 614,568 621,570.5L623.6,571.4V568.6V447.9V446.6L622.4,446.1C615.6,443.2 608.5,441 601.2,439.5C593.8,438.1 586.4,437.4 579,437.4C569.3,437.4 559.7,438.6 550.2,441.1C540.7,443.6 531.7,447.3 523.2,452.3L522.2,452.9V454V574ZM385.5,431.4L385.6,431.4L385.6,431.4C394.8,426.6 404.4,423 414.4,420.6C424.4,418.2 434.6,417 445,417C456.6,417 467.9,418.5 479,421.5C490.1,424.5 500.8,429 511,435L512,435.6L513,435C523.2,429 533.9,424.5 545,421.5C556.1,418.5 567.4,417 579,417C589.4,417 599.6,418.2 609.6,420.6C619.6,423 629.2,426.6 638.4,431.4L638.4,431.4L638.5,431.4C640.3,432.3 641.7,433.5 642.6,435.2C643.5,436.9 644,438.7 644,440.6V587.5C644,591.4 642.4,594.3 639,596.4C635.7,598.5 632.3,598.6 628.7,596.7C621,592.5 613,589.3 604.6,587C596.3,584.8 587.7,583.6 579,583.6C566.6,583.6 554.6,585.8 543,590.2C531.9,594.3 521.6,600.1 512,607.5C502.4,600.1 492.1,594.3 481,590.2C469.4,585.8 457.4,583.6 445,583.6C436.3,583.6 427.7,584.8 419.4,587C411,589.3 403,592.5 395.3,596.7C391.7,598.6 388.3,598.5 385,596.4C381.6,594.3 380,591.4 380,587.5V440.6C380,438.7 380.5,436.9 381.4,435.2C382.3,433.5 383.7,432.3 385.5,431.4ZM607.5,463.5V478.6C603.4,477.2 599.2,476.1 595,475.3C589.9,474.4 584.6,473.9 579,473.9C571.1,473.9 563.5,474.9 556.2,476.9C550.1,478.6 544.1,480.8 538.4,483.6V467.5C544.6,465 550.9,463.1 557.3,461.7C564.2,460.4 571.4,459.7 579,459.7C584.2,459.7 589.2,460.1 594.2,460.9C598.7,461.6 603.1,462.5 607.5,463.5ZM607.5,530.5V545.6C603.4,544.2 599.2,543.1 595,542.4C589.9,541.4 584.6,541 579,541C571.1,541 563.5,541.9 556.3,543.8C550.1,545.4 544.1,547.6 538.4,550.6V534.6C544.6,532 550.9,530.1 557.3,528.8C564.2,527.4 571.4,526.7 579,526.7C584.2,526.7 589.2,527.1 594.2,527.9C598.7,528.6 603.1,529.5 607.5,530.5ZM607.5,497V512.1C603.4,510.7 599.2,509.6 595,508.9C589.9,507.9 584.6,507.5 579,507.5C571.1,507.5 563.5,508.4 556.2,510.4C550.1,512.1 544.1,514.3 538.4,517.2V501C544.6,498.5 550.9,496.6 557.3,495.3C564.2,493.9 571.4,493.2 579,493.2C584.2,493.2 589.2,493.6 594.2,494.4C598.7,495.1 603.1,496 607.5,497Z"
android:strokeAlpha="0.2"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#0768BA"/>
<path
android:pathData="M891,698.4C821.5,837.2 677.9,932.4 512.1,932.4C483.8,932.4 456,929.5 429,924V859.4C429,770.5 497.3,698.4 581.5,698.4H891Z"
android:strokeAlpha="0.2"
android:fillColor="#000000"
android:fillAlpha="0.2"/>
<path
android:pathData="M866.3,634H493.4C482.5,634 473.6,642.9 473.6,653.8V713.5C473.6,801.4 544.8,872.6 632.6,872.6H839C904.8,872.6 958.3,819.2 958.3,753.3V701.1C958.3,671.2 928.4,634 866.3,634Z"
android:strokeAlpha="0.9"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:startX="856.4"
android:startY="862.9"
android:endX="727.4"
android:endY="740.7"
android:type="linear">
<item android:offset="0" android:color="#7F054096"/>
<item android:offset="0.1" android:color="#700F3D9C"/>
<item android:offset="0.3" android:color="#3F2F35B1"/>
<item android:offset="0.5" android:color="#1C462FBF"/>
<item android:offset="0.7" android:color="#07542BC8"/>
<item android:offset="0.9" android:color="#00592ACB"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M938.4,720.9H595.6C514.5,720.9 448.7,786.7 448.7,867.8V939.7C448.7,950.7 457.6,959.6 468.6,959.6H811.4C892.5,959.6 958.3,893.8 958.3,812.7V701.1C958.3,712.1 949.4,720.9 938.4,720.9Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="504.7"
android:startY="742.8"
android:endX="970.6"
android:endY="940"
android:type="linear">
<item android:offset="0" android:color="#FF54FFBD"/>
<item android:offset="1" android:color="#FF00DDFF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M526.1,887H493.1V793.9H526.4C535.7,793.9 543.8,795.8 550.5,799.5C557.3,803.2 562.5,808.5 566.1,815.5C569.8,822.4 571.6,830.7 571.6,840.4C571.6,850.1 569.8,858.4 566.1,865.4C562.5,872.3 557.3,877.7 550.5,881.4C543.7,885.1 535.5,887 526.1,887ZM512.8,870.1H525.3C531.1,870.1 536,869.1 540,867C544,865 547,861.7 549,857.4C551,853 552,847.3 552,840.4C552,833.5 551,827.9 549,823.5C547,819.1 544,815.9 540,813.9C536,811.8 531.1,810.8 525.3,810.8H512.8V870.1Z"
android:fillColor="#000000"/>
<path
android:pathData="M586.2,887V793.9H648.9V810.1H605.9V832.3H645.7V848.5H605.9V870.8H649.1V887H586.2Z"
android:fillColor="#000000"/>
<path
android:pathData="M664.6,887V793.9H701.9C708.7,793.9 714.4,794.9 719,797C723.6,799 727,801.8 729.3,805.4C731.6,809 732.8,813.1 732.8,817.8C732.8,821.4 732,824.6 730.6,827.4C729.1,830.1 727.1,832.3 724.6,834.1C722.1,835.8 719.2,837 716,837.8V838.7C719.5,838.8 722.8,839.8 725.9,841.7C729,843.5 731.6,846.1 733.5,849.5C735.4,852.8 736.4,856.7 736.4,861.3C736.4,866.2 735.2,870.6 732.7,874.5C730.3,878.3 726.7,881.4 722,883.6C717.2,885.9 711.3,887 704.4,887H664.6ZM684.3,870.9H700.3C705.8,870.9 709.8,869.9 712.3,867.8C714.8,865.7 716.1,862.8 716.1,859.3C716.1,856.7 715.5,854.5 714.2,852.5C713,850.5 711.2,849 708.9,847.9C706.6,846.7 703.9,846.2 700.8,846.2H684.3V870.9ZM684.3,832.9H698.9C701.6,832.9 704,832.4 706,831.5C708.2,830.5 709.8,829.1 711,827.4C712.3,825.6 712.9,823.5 712.9,821C712.9,817.7 711.7,815 709.3,812.9C707,810.8 703.6,809.8 699.2,809.8H684.3V832.9Z"
android:fillColor="#000000"/>
<path
android:pathData="M806.7,793.9H826.4V854.4C826.4,861.2 824.8,867.1 821.5,872.2C818.3,877.3 813.8,881.2 808,884.1C802.2,886.9 795.5,888.3 787.8,888.3C780.1,888.3 773.3,886.9 767.5,884.1C761.7,881.2 757.2,877.3 754,872.2C750.8,867.1 749.2,861.2 749.2,854.4V793.9H768.9V852.7C768.9,856.2 769.7,859.4 771.2,862.1C772.8,864.9 775,867.1 777.9,868.6C780.7,870.2 784,871 787.8,871C791.6,871 794.9,870.2 797.8,868.6C800.6,867.1 802.8,864.9 804.4,862.1C805.9,859.4 806.7,856.2 806.7,852.7V793.9Z"
android:fillColor="#000000"/>
<path
android:pathData="M904.3,824C903.7,821.8 902.8,819.8 901.6,818.1C900.5,816.4 899.1,815 897.4,813.8C895.8,812.6 893.9,811.6 891.8,811C889.7,810.4 887.4,810 884.8,810C880.1,810 875.9,811.2 872.3,813.6C868.7,816 865.9,819.4 863.9,823.9C861.9,828.4 860.9,833.9 860.9,840.4C860.9,846.8 861.9,852.4 863.9,856.9C865.8,861.5 868.6,864.9 872.2,867.3C875.8,869.7 880.1,870.9 885,870.9C889.5,870.9 893.3,870.1 896.4,868.5C899.6,866.9 902,864.6 903.7,861.7C905.4,858.8 906.2,855.4 906.2,851.4L910.2,852H886.2V837.2H925.2V848.9C925.2,857.1 923.5,864.1 920,870C916.5,875.8 911.8,880.4 905.7,883.5C899.7,886.7 892.7,888.3 884.9,888.3C876.2,888.3 868.5,886.3 861.9,882.5C855.3,878.6 850.2,873.1 846.5,866C842.8,858.8 841,850.4 841,840.5C841,833 842,826.3 844.2,820.4C846.4,814.4 849.5,809.4 853.5,805.3C857.5,801.2 862.1,798 867.4,795.9C872.6,793.7 878.3,792.6 884.5,792.6C889.8,792.6 894.7,793.4 899.2,795C903.8,796.5 907.8,798.6 911.3,801.4C914.9,804.2 917.8,807.5 920,811.4C922.2,815.2 923.7,819.4 924.3,824H904.3Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,37 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1024dp"
android:height="1024dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M595.6,720.9H938.4C949.4,720.9 958.3,712 958.3,701.1V812.7C958.3,893.8 892.5,959.6 811.4,959.6H468.6C457.6,959.6 448.7,950.7 448.7,939.7V867.8C448.7,786.7 514.5,720.9 595.6,720.9ZM504.6,887H535.5C544.4,887 552,885.3 558.4,881.8C564.8,878.3 569.7,873.3 573.1,866.7C576.5,860.2 578.2,852.4 578.2,843.3C578.2,834.2 576.5,826.4 573.1,819.9C569.7,813.4 564.8,808.4 558.4,805C552.1,801.5 544.6,799.7 535.8,799.7H504.6V887ZM534.8,871.2H523V815.5H534.8C540.3,815.5 544.8,816.5 548.6,818.4C552.3,820.4 555.1,823.4 557,827.5C558.9,831.6 559.8,836.8 559.8,843.3C559.8,849.8 558.9,855.1 557,859.2C555.1,863.3 552.3,866.3 548.5,868.3C544.8,870.2 540.2,871.2 534.8,871.2ZM591.9,799.7V887H650.9V871.8H610.3V850.9H647.7V835.7H610.3V814.9H650.7V799.7H591.9ZM665.4,799.7V887H702.7C709.2,887 714.7,885.9 719.1,883.8C723.6,881.7 727,878.9 729.2,875.3C731.5,871.6 732.7,867.5 732.7,862.9C732.7,858.6 731.8,854.9 730,851.8C728.2,848.7 725.8,846.2 722.9,844.5C720,842.8 716.8,841.8 713.5,841.7V840.8C716.6,840.2 719.3,839 721.6,837.4C724,835.8 725.9,833.6 727.2,831.1C728.6,828.5 729.3,825.5 729.3,822.1C729.3,817.7 728.2,813.9 726,810.5C723.9,807.1 720.7,804.5 716.4,802.6C712.1,800.7 706.7,799.7 700.3,799.7H665.4ZM698.9,871.9H683.8V848.7H699.3C702.2,848.7 704.8,849.3 706.9,850.3C709.1,851.4 710.7,852.8 711.9,854.7C713.1,856.5 713.6,858.6 713.6,861C713.6,864.3 712.5,867 710.1,869C707.8,870.9 704,871.9 698.9,871.9ZM697.5,836.2H683.8V814.6H697.8C701.9,814.6 705.1,815.6 707.3,817.5C709.5,819.5 710.7,822 710.7,825.2C710.7,827.5 710.1,829.4 708.9,831.1C707.8,832.7 706.2,834 704.2,834.9C702.3,835.8 700,836.2 697.5,836.2ZM817.1,799.7H798.6V854.8C798.6,858.2 797.9,861.1 796.4,863.7C794.9,866.3 792.9,868.3 790.2,869.8C787.6,871.3 784.5,872 780.9,872C777.3,872 774.2,871.3 771.5,869.8C768.9,868.3 766.8,866.3 765.3,863.7C763.9,861.1 763.2,858.2 763.2,854.8V799.7H744.7V856.4C744.7,862.8 746.2,868.3 749.2,873.1C752.2,877.9 756.5,881.6 761.9,884.3C767.3,886.9 773.6,888.2 780.9,888.2C788.1,888.2 794.4,886.9 799.8,884.3C805.3,881.6 809.5,877.9 812.5,873.1C815.5,868.3 817.1,862.8 817.1,856.4V799.7ZM887.6,822.4C888.7,824 889.5,825.9 890.1,827.9H908.9C908.3,823.6 906.9,819.7 904.8,816.1C902.7,812.5 900,809.4 896.7,806.8C893.4,804.1 889.6,802.1 885.3,800.7C881.1,799.3 876.5,798.5 871.5,798.5C865.8,798.5 860.4,799.5 855.5,801.6C850.5,803.6 846.2,806.5 842.5,810.4C838.7,814.2 835.8,819 833.8,824.5C831.7,830.1 830.7,836.4 830.7,843.4C830.7,852.7 832.4,860.6 835.9,867.3C839.3,874 844.2,879.1 850.4,882.8C856.5,886.4 863.7,888.2 871.9,888.2C879.2,888.2 885.8,886.7 891.4,883.8C897.1,880.8 901.6,876.5 904.8,871.1C908.1,865.6 909.7,859 909.7,851.3V840.3H873.2V854.2H891.9C891.8,857.7 891,860.7 889.5,863.3C888,866 885.7,868.2 882.7,869.7C879.7,871.1 876.2,871.9 872,871.9C867.4,871.9 863.4,870.8 860,868.5C856.6,866.3 854,863.1 852.2,858.8C850.3,854.5 849.4,849.4 849.4,843.3C849.4,837.2 850.4,832.1 852.2,827.9C854.1,823.6 856.7,820.4 860.1,818.2C863.4,816 867.4,814.9 871.8,814.9C874.2,814.9 876.4,815.2 878.3,815.8C880.3,816.3 882.1,817.2 883.6,818.3C885.2,819.5 886.5,820.8 887.6,822.4Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M440,249.7L440.1,249.7C466.8,156.5 583,111 701.4,111C783.2,111 856.7,136.9 907,178C875.8,179.6 846.2,185.7 819,195.5C859.7,210.7 894.7,233.9 920.6,262.7C903.4,259.7 885.5,258.2 867.3,258.2C865.3,258.2 863.3,258.2 861.4,258.2C908.4,326.5 936,409.2 936,498.4C936,575 915.6,646.9 880,708.9H595.6C573.7,708.9 552.7,713.4 533.7,721.4C522.9,714.2 512.3,705.3 502.6,694.4C498.3,689.5 490.8,681 494.2,675.5C497.1,670.8 504.1,671.1 509.6,671.3L509.6,671.3C510.6,671.4 511.6,671.4 512.5,671.4C627.5,671.4 720.8,595.3 720.8,501.4C720.8,407.6 627.5,331.5 512.5,331.5C465.1,331.5 415,346.4 375.8,374C375.8,373.5 375.8,373.2 375.8,373.2C364.9,339.8 372.1,277.6 439.9,249.7L440,249.7ZM501.4,739.9C394.7,707.3 304.3,623.8 304.2,503.9C304.2,464.4 318.8,430.8 342.1,404C342.1,404 342.1,404 342,404C342,404 341.9,404 341.9,404C347,318.1 314,215.4 252.4,195.2C248,193.8 243.7,197.1 243.1,201.6C235,269.6 217.5,293.1 198.5,318.5C170.1,356.6 149.4,400.6 150.3,449C140,427.4 131.7,404.9 125.5,381.6C124.6,378.4 119.4,368.7 113.7,372C109.1,374.6 105.7,381.7 103.2,389.1C93.8,424.3 89,462 89,498.4C89,703.2 241.3,878.2 436.7,914.9V867.8C436.7,815.3 462.2,768.8 501.4,739.9ZM541.7,246.5C564,239.5 562.1,217.4 562.1,217.4C562.1,217.4 550.9,204.3 528.8,211.6C508.2,218.4 504.9,233.1 504.9,233.1C504.9,233.1 516.2,254.5 541.7,246.5Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M893.8,708.9C904.8,689.2 914.2,668.4 922,646.9C939.1,656.4 950.1,670 955.1,683.7C956.3,687 955.5,691.5 954,695C948.5,708 942.7,708.9 938.4,708.9H893.8ZM911.1,641.8C902.7,665.2 892.3,687.7 880,708.9L809,708.9V635.4L866.3,634C883.8,634 898.7,636.9 911.1,641.8ZM809,720.9V767H957.5C958,762.5 958.3,757.9 958.3,753.3V701.2C958.2,709.3 953.3,716.3 946.3,719.3C943.8,720.4 941.2,720.9 938.4,720.9L887,720.9L809,720.9Z"
android:strokeAlpha="0.9"
android:fillColor="#000000"
android:fillType="evenOdd"
android:fillAlpha="0.9"/>
<path
android:pathData="M595.6,720.9H938.4C949.4,720.9 958.3,712 958.3,701.1V812.7C958.3,893.8 892.5,959.6 811.4,959.6H468.6C457.6,959.6 448.7,950.7 448.7,939.7V867.8C448.7,786.7 514.5,720.9 595.6,720.9ZM504.6,887H535.5C544.4,887 552,885.3 558.4,881.8C564.8,878.3 569.7,873.3 573.1,866.7C576.5,860.2 578.2,852.4 578.2,843.3C578.2,834.2 576.5,826.4 573.1,819.9C569.7,813.4 564.8,808.4 558.4,805C552.1,801.5 544.6,799.7 535.8,799.7H504.6V887ZM534.8,871.2H523V815.5H534.8C540.3,815.5 544.8,816.5 548.6,818.4C552.3,820.4 555.1,823.4 557,827.5C558.9,831.6 559.8,836.8 559.8,843.3C559.8,849.8 558.9,855.1 557,859.2C555.1,863.3 552.3,866.3 548.5,868.3C544.8,870.2 540.2,871.2 534.8,871.2ZM591.9,799.7V887H650.9V871.8H610.3V850.9H647.7V835.7H610.3V814.9H650.7V799.7H591.9ZM665.4,799.7V887H702.7C709.2,887 714.7,885.9 719.1,883.8C723.6,881.7 727,878.9 729.2,875.3C731.5,871.6 732.7,867.5 732.7,862.9C732.7,858.6 731.8,854.9 730,851.8C728.2,848.7 725.8,846.2 722.9,844.5C720,842.8 716.8,841.8 713.5,841.7V840.8C716.6,840.2 719.3,839 721.6,837.4C724,835.8 725.9,833.6 727.2,831.1C728.6,828.5 729.3,825.5 729.3,822.1C729.3,817.7 728.2,813.9 726,810.5C723.9,807.1 720.7,804.5 716.4,802.6C712.1,800.7 706.7,799.7 700.3,799.7H665.4ZM698.9,871.9H683.8V848.7H699.3C702.2,848.7 704.8,849.3 706.9,850.3C709.1,851.4 710.7,852.8 711.9,854.7C713.1,856.5 713.6,858.6 713.6,861C713.6,864.3 712.5,867 710.1,869C707.8,870.9 704,871.9 698.9,871.9ZM697.5,836.2H683.8V814.6H697.8C701.9,814.6 705.1,815.6 707.3,817.5C709.5,819.5 710.7,822 710.7,825.2C710.7,827.5 710.1,829.4 708.9,831.1C707.8,832.7 706.2,834 704.2,834.9C702.3,835.8 700,836.2 697.5,836.2ZM817.1,799.7H798.6V854.8C798.6,858.2 797.9,861.1 796.4,863.7C794.9,866.3 792.9,868.3 790.2,869.8C787.6,871.3 784.5,872 780.9,872C777.3,872 774.2,871.3 771.5,869.8C768.9,868.3 766.8,866.3 765.3,863.7C763.9,861.1 763.2,858.2 763.2,854.8V799.7H744.7V856.4C744.7,862.8 746.2,868.3 749.2,873.1C752.2,877.9 756.5,881.6 761.9,884.3C767.3,886.9 773.6,888.2 780.9,888.2C788.1,888.2 794.4,886.9 799.8,884.3C805.3,881.6 809.5,877.9 812.5,873.1C815.5,868.3 817.1,862.8 817.1,856.4V799.7ZM887.6,822.4C888.7,824 889.5,825.9 890.1,827.9H908.9C908.3,823.6 906.9,819.7 904.8,816.1C902.7,812.5 900,809.4 896.7,806.8C893.4,804.1 889.6,802.1 885.3,800.7C881.1,799.3 876.5,798.5 871.5,798.5C865.8,798.5 860.4,799.5 855.5,801.6C850.5,803.6 846.2,806.5 842.5,810.4C838.7,814.2 835.8,819 833.8,824.5C831.7,830.1 830.7,836.4 830.7,843.4C830.7,852.7 832.4,860.6 835.9,867.3C839.3,874 844.2,879.1 850.4,882.8C856.5,886.4 863.7,888.2 871.9,888.2C879.2,888.2 885.8,886.7 891.4,883.8C897.1,880.8 901.6,876.5 904.8,871.1C908.1,865.6 909.7,859 909.7,851.3V840.3H873.2V854.2H891.9C891.8,857.7 891,860.7 889.5,863.3C888,866 885.7,868.2 882.7,869.7C879.7,871.1 876.2,871.9 872,871.9C867.4,871.9 863.4,870.8 860,868.5C856.6,866.3 854,863.1 852.2,858.8C850.3,854.5 849.4,849.4 849.4,843.3C849.4,837.2 850.4,832.1 852.2,827.9C854.1,823.6 856.7,820.4 860.1,818.2C863.4,816 867.4,814.9 871.8,814.9C874.2,814.9 876.4,815.2 878.3,815.8C880.3,816.3 882.1,817.2 883.6,818.3C885.2,819.5 886.5,820.8 887.6,822.4Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M440,249.7L440.1,249.7C466.8,156.5 583,111 701.4,111C783.2,111 856.7,136.9 907,178C875.8,179.6 846.2,185.7 819,195.5C859.7,210.7 894.7,233.9 920.6,262.7C903.4,259.7 885.5,258.2 867.3,258.2C865.3,258.2 863.3,258.2 861.4,258.2C908.4,326.5 936,409.2 936,498.4C936,575 915.6,646.9 880,708.9H595.6C573.7,708.9 552.7,713.4 533.7,721.4C522.9,714.2 512.3,705.3 502.6,694.4C498.3,689.5 490.8,681 494.2,675.5C497.1,670.8 504.1,671.1 509.6,671.3L509.6,671.3C510.6,671.4 511.6,671.4 512.5,671.4C627.5,671.4 720.8,595.3 720.8,501.4C720.8,407.6 627.5,331.5 512.5,331.5C465.1,331.5 415,346.4 375.8,374C375.8,373.5 375.8,373.2 375.8,373.2C364.9,339.8 372.1,277.6 439.9,249.7L440,249.7ZM501.4,739.9C394.7,707.3 304.3,623.8 304.2,503.9C304.2,464.4 318.8,430.8 342.1,404C342.1,404 342.1,404 342,404C342,404 341.9,404 341.9,404C347,318.1 314,215.4 252.4,195.2C248,193.8 243.7,197.1 243.1,201.6C235,269.6 217.5,293.1 198.5,318.5C170.1,356.6 149.4,400.6 150.3,449C140,427.4 131.7,404.9 125.5,381.6C124.6,378.4 119.4,368.7 113.7,372C109.1,374.6 105.7,381.7 103.2,389.1C93.8,424.3 89,462 89,498.4C89,703.2 241.3,878.2 436.7,914.9V867.8C436.7,815.3 462.2,768.8 501.4,739.9ZM541.7,246.5C564,239.5 562.1,217.4 562.1,217.4C562.1,217.4 550.9,204.3 528.8,211.6C508.2,218.4 504.9,233.1 504.9,233.1C504.9,233.1 516.2,254.5 541.7,246.5Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M893.8,708.9C904.8,689.2 914.2,668.4 922,646.9C939.1,656.4 950.1,670 955.1,683.7C956.3,687 955.5,691.5 954,695C948.5,708 942.7,708.9 938.4,708.9H893.8ZM911.1,641.8C902.7,665.2 892.3,687.7 880,708.9L809,708.9V635.4L866.3,634C883.8,634 898.7,636.9 911.1,641.8ZM809,720.9V767H957.5C958,762.5 958.3,757.9 958.3,753.3V701.2C958.2,709.3 953.3,716.3 946.3,719.3C943.8,720.4 941.2,720.9 938.4,720.9L887,720.9L809,720.9Z"
android:strokeAlpha="0.9"
android:fillColor="#000000"
android:fillType="evenOdd"
android:fillAlpha="0.9"/>
<path
android:pathData="M414.5,543.6H428.3L435.3,523.5H466.9L474.2,543.6H487.6L458.1,465H444.1L414.5,543.6ZM439.2,512.2L450.8,479.6H451.4L463,512.2H439.2ZM536.4,490.9V470.2C543.1,467.3 549.9,465.2 556.9,463.8C563.9,462.4 571.3,461.7 579,461.7C584.3,461.7 589.5,462.1 594.5,462.9C599.6,463.7 604.6,464.7 609.5,465.9V485.4C604.6,483.6 599.7,482.2 594.7,481.3C589.7,480.4 584.5,479.9 579,479.9C571.3,479.9 563.9,480.9 556.8,482.8C549.7,484.8 542.9,487.5 536.4,490.9ZM536.4,557.9V537.2C543.1,534.4 549.9,532.2 556.9,530.8C563.9,529.4 571.3,528.7 579,528.7C584.3,528.7 589.5,529.1 594.5,529.9C599.6,530.7 604.6,531.7 609.5,533V552.5C604.6,550.6 599.7,549.3 594.7,548.3C589.7,547.4 584.5,547 579,547C571.3,547 563.9,547.9 556.8,549.7C549.7,551.5 542.9,554.3 536.4,557.9ZM536.4,524.4V503.7C543.1,500.9 549.9,498.7 556.9,497.3C563.9,495.9 571.3,495.2 579,495.2C584.3,495.2 589.5,495.6 594.5,496.4C599.6,497.2 604.6,498.2 609.5,499.4V518.9C604.6,517.1 599.7,515.7 594.7,514.8C589.7,513.9 584.5,513.5 579,513.5C571.3,513.5 563.9,514.4 556.8,516.3C549.7,518.3 542.9,521 536.4,524.4ZM445,565.3C454.5,565.3 463.8,566.3 472.9,568.4C481.9,570.6 490.9,573.8 499.8,578V458C491.5,453.1 482.7,449.5 473.3,447C464,444.6 454.5,443.4 445,443.4C437.7,443.4 430.4,444.1 423.2,445.5C416,446.9 409.1,449.1 402.4,451.9V572.6C409.5,570.1 416.5,568.3 423.5,567.1C430.5,565.9 437.7,565.3 445,565.3ZM524.2,578C533.1,573.8 542.1,570.6 551.1,568.4C560.2,566.3 569.5,565.3 579,565.3C586.3,565.3 593.5,565.9 600.5,567.1C607.5,568.3 614.5,570.1 621.6,572.6V451.9C614.9,449.1 608,446.9 600.8,445.5C593.6,444.1 586.3,443.4 579,443.4C569.5,443.4 560,444.6 550.7,447C541.3,449.5 532.5,453.1 524.2,458V578ZM512,614C502.3,606.3 491.7,600.3 480.3,596C469,591.8 457.2,589.6 445,589.6C436.5,589.6 428.1,590.7 419.9,593C411.7,595.2 403.8,598.4 396.3,602.4C392,604.7 387.9,604.6 383.9,602.1C380,599.7 378,596.1 378,591.5V444.6C378,442.4 378.6,440.2 379.7,438.2C380.8,436.2 382.5,434.6 384.7,433.6C394,428.8 403.8,425.1 413.9,422.7C424.1,420.2 434.4,419 445,419C456.8,419 468.3,420.5 479.6,423.6C490.8,426.6 501.6,431.2 512,437.3C522.4,431.2 533.2,426.6 544.4,423.6C555.7,420.5 567.2,419 579,419C589.6,419 599.9,420.2 610.1,422.7C620.2,425.1 630,428.8 639.3,433.6C641.5,434.6 643.2,436.2 644.3,438.2C645.4,440.2 646,442.4 646,444.6V591.5C646,596.1 644,599.7 640.1,602.1C636.1,604.6 632,604.7 627.7,602.4C620.2,598.4 612.3,595.2 604.1,593C595.9,590.7 587.5,589.6 579,589.6C566.8,589.6 555,591.8 543.7,596C532.3,600.3 521.7,606.3 512,614Z"
android:fillColor="#000000"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".CatalogApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Thunderbird.Splashscreen"
>
<activity
android:name=".CatalogActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,21 @@
package net.thunderbird.ui.catalog
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import net.thunderbird.ui.catalog.ui.CatalogScreen
class CatalogActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
CatalogScreen()
}
}
}

View file

@ -0,0 +1,19 @@
package net.thunderbird.ui.catalog
import android.app.Application
import net.thunderbird.ui.catalog.di.catalogUiModule
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
class CatalogApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
allowOverride(false)
androidContext(this@CatalogApplication)
modules(catalogUiModule)
}
}
}

View file

@ -0,0 +1,12 @@
package net.thunderbird.ui.catalog.di
import net.thunderbird.ui.catalog.ui.CatalogViewModel
import net.thunderbird.ui.catalog.ui.page.CatalogPageViewModel
import org.koin.core.module.Module
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
val catalogUiModule: Module = module {
viewModel { CatalogViewModel() }
viewModel { CatalogPageViewModel() }
}

View file

@ -0,0 +1,85 @@
package net.thunderbird.ui.catalog.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import app.k9mail.core.ui.compose.designsystem.organism.drawer.ModalNavigationDrawer
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import net.thunderbird.ui.catalog.ui.CatalogContract.State
import net.thunderbird.ui.catalog.ui.common.ThemeTopAppBar
import net.thunderbird.ui.catalog.ui.common.drawer.DrawerContent
import net.thunderbird.ui.catalog.ui.navigation.CatalogNavHost
import net.thunderbird.ui.catalog.ui.navigation.CatalogRoute
@Suppress("LongMethod")
@Composable
fun CatalogContent(
navController: NavHostController,
state: State,
onThemeChanged: () -> Unit,
onThemeVariantChanged: () -> Unit,
modifier: Modifier = Modifier,
) {
ModalNavigationDrawer(
drawerContent = { closeDrawer ->
DrawerContent(
closeDrawer = closeDrawer,
theme = state.theme,
themeVariant = state.themeVariant,
onThemeChanged = onThemeChanged,
onThemeVariantChanged = onThemeVariantChanged,
onNavigateToAtoms = {
navController.navigate(
route = CatalogRoute.Atom,
navOptions = NavOptions.Builder()
.setLaunchSingleTop(true)
.build(),
)
},
onNavigateToMolecules = {
navController.navigate(
route = CatalogRoute.Molecule,
navOptions = NavOptions.Builder()
.setLaunchSingleTop(true)
.build(),
)
},
onNavigateToOrganisms = {
navController.navigate(
route = CatalogRoute.Organism,
navOptions = NavOptions.Builder()
.setLaunchSingleTop(true)
.build(),
)
},
onNavigateToTemplates = {
navController.navigate(
route = CatalogRoute.Template,
navOptions = NavOptions.Builder()
.setLaunchSingleTop(true)
.build(),
)
},
)
},
) { openDrawer ->
Scaffold(
modifier = modifier,
topBar = {
ThemeTopAppBar(
onMenuClick = openDrawer,
theme = state.theme,
themeVariant = state.themeVariant,
onThemeClick = onThemeChanged,
onThemeVariantClick = onThemeVariantChanged,
)
},
) { paddingValues ->
CatalogNavHost(
navController = navController,
paddingValues = paddingValues,
)
}
}
}

View file

@ -0,0 +1,47 @@
package net.thunderbird.ui.catalog.ui
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
interface CatalogContract {
enum class Theme(
val displayName: String,
) {
THEME_2_K9("K-9 UI"),
THEME_2_THUNDERBIRD("Thunderbird UI"),
}
enum class ThemeVariant(
val displayName: String,
) {
LIGHT("Light"),
DARK("Dark"),
}
interface ViewModel : UnidirectionalViewModel<State, Event, Nothing>
data class State(
val theme: Theme = Theme.THEME_2_THUNDERBIRD,
val themeVariant: ThemeVariant = ThemeVariant.LIGHT,
)
sealed interface Event {
data object OnThemeChanged : Event
data object OnThemeVariantChanged : Event
}
}
fun CatalogContract.Theme.next(): CatalogContract.Theme {
val themes = CatalogContract.Theme.entries
val currentThemeIndex = themes.indexOf(this)
val nextThemeIndex = (currentThemeIndex + 1) % themes.size
return themes[nextThemeIndex]
}
fun CatalogContract.ThemeVariant.next(): CatalogContract.ThemeVariant {
val variants = CatalogContract.ThemeVariant.entries
val currentVariantIndex = variants.indexOf(this)
val nextVariantIndex = (currentVariantIndex + 1) % variants.size
return variants[nextVariantIndex]
}

View file

@ -0,0 +1,42 @@
package net.thunderbird.ui.catalog.ui
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.rememberNavController
import app.k9mail.core.ui.compose.common.mvi.observe
import app.k9mail.core.ui.compose.designsystem.atom.Surface
import net.thunderbird.ui.catalog.ui.CatalogContract.Event.OnThemeChanged
import net.thunderbird.ui.catalog.ui.CatalogContract.Event.OnThemeVariantChanged
import net.thunderbird.ui.catalog.ui.CatalogContract.ViewModel
import net.thunderbird.ui.catalog.ui.common.theme.ThemeSwitch
import org.koin.androidx.compose.koinViewModel
@Composable
fun CatalogScreen(
modifier: Modifier = Modifier,
viewModel: ViewModel = koinViewModel<CatalogViewModel>(),
) {
val (state, dispatch) = viewModel.observe(handleEffect = {})
val navController = rememberNavController()
ThemeSwitch(
theme = state.value.theme,
themeVariant = state.value.themeVariant,
) {
Surface(
modifier = Modifier
.fillMaxSize()
.safeDrawingPadding()
.then(modifier),
) {
CatalogContent(
navController = navController,
state = state.value,
onThemeChanged = { dispatch(OnThemeChanged) },
onThemeVariantChanged = { dispatch(OnThemeVariantChanged) },
)
}
}
}

View file

@ -0,0 +1,28 @@
package net.thunderbird.ui.catalog.ui
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import net.thunderbird.ui.catalog.ui.CatalogContract.Event
import net.thunderbird.ui.catalog.ui.CatalogContract.Event.OnThemeChanged
import net.thunderbird.ui.catalog.ui.CatalogContract.Event.OnThemeVariantChanged
import net.thunderbird.ui.catalog.ui.CatalogContract.State
import net.thunderbird.ui.catalog.ui.CatalogContract.ViewModel
class CatalogViewModel(
initialState: State = State(),
) : BaseViewModel<State, Event, Nothing>(initialState), ViewModel {
override fun event(event: Event) {
when (event) {
is OnThemeChanged -> {
updateState { it.copy(theme = it.theme.next()) }
}
is OnThemeVariantChanged -> {
updateState {
it.copy(
themeVariant = it.themeVariant.next(),
)
}
}
}
}
}

View file

@ -0,0 +1,43 @@
package net.thunderbird.ui.catalog.ui.common
import androidx.compose.material.icons.filled.DarkMode
import androidx.compose.material.icons.filled.LightMode
import androidx.compose.material.icons.filled.ShuffleOn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonIcon
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBarWithMenuButton
import net.thunderbird.ui.catalog.ui.CatalogContract.Theme
import net.thunderbird.ui.catalog.ui.CatalogContract.ThemeVariant
import androidx.compose.material.icons.Icons as MaterialIcons
@Composable
fun ThemeTopAppBar(
onMenuClick: () -> Unit,
theme: Theme,
themeVariant: ThemeVariant,
onThemeClick: () -> Unit,
onThemeVariantClick: () -> Unit,
modifier: Modifier = Modifier,
) {
TopAppBarWithMenuButton(
title = "${theme.displayName} Catalog",
onMenuClick = onMenuClick,
actions = {
ButtonIcon(
onClick = onThemeClick,
imageVector = MaterialIcons.Filled.ShuffleOn,
contentDescription = "${theme.displayName} Theme",
)
ButtonIcon(
onClick = onThemeVariantClick,
imageVector = when (themeVariant) {
ThemeVariant.LIGHT -> MaterialIcons.Filled.DarkMode
ThemeVariant.DARK -> MaterialIcons.Filled.LightMode
},
contentDescription = "${theme.displayName} Theme Variant",
)
},
modifier = modifier,
)
}

View file

@ -0,0 +1,106 @@
package net.thunderbird.ui.catalog.ui.common.drawer
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import app.k9mail.core.ui.compose.designsystem.organism.drawer.ModalDrawerSheet
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerDivider
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerHeadline
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem
import net.thunderbird.ui.catalog.ui.CatalogContract.Theme
import net.thunderbird.ui.catalog.ui.CatalogContract.ThemeVariant
import net.thunderbird.ui.catalog.ui.next
@Suppress("LongParameterList", "LongMethod")
@Composable
fun DrawerContent(
closeDrawer: () -> Unit,
theme: Theme,
themeVariant: ThemeVariant,
onThemeChanged: () -> Unit,
onThemeVariantChanged: () -> Unit,
onNavigateToAtoms: () -> Unit,
onNavigateToMolecules: () -> Unit,
onNavigateToOrganisms: () -> Unit,
onNavigateToTemplates: () -> Unit,
modifier: Modifier = Modifier,
) {
ModalDrawerSheet(
modifier = modifier,
) {
NavigationDrawerHeadline(
title = "Design system",
)
NavigationDrawerItem(
label = "Atoms",
selected = false,
onClick = {
closeDrawer()
onNavigateToAtoms()
},
)
NavigationDrawerItem(
label = "Molecules",
selected = false,
onClick = {
closeDrawer()
onNavigateToMolecules()
},
)
NavigationDrawerItem(
label = "Organisms",
selected = false,
onClick = {
closeDrawer()
onNavigateToOrganisms()
},
)
NavigationDrawerItem(
label = "Templates",
selected = false,
onClick = {
closeDrawer()
onNavigateToTemplates()
},
)
NavigationDrawerDivider()
NavigationDrawerHeadline(
title = "Theme",
)
NavigationDrawerItem(
label = buildAnnotatedString {
append("Change to ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(theme.next().displayName)
}
append(" theme")
},
selected = false,
onClick = {
closeDrawer()
onThemeChanged()
},
)
NavigationDrawerItem(
label = buildAnnotatedString {
append("Change to ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(themeVariant.next().displayName)
}
append(" theme variant")
},
selected = false,
onClick = {
closeDrawer()
onThemeVariantChanged()
},
)
}
}

View file

@ -0,0 +1,62 @@
package net.thunderbird.ui.catalog.ui.common.theme
import androidx.compose.runtime.Composable
import app.k9mail.core.ui.compose.theme2.k9mail.K9MailTheme2
import app.k9mail.core.ui.compose.theme2.thunderbird.ThunderbirdTheme2
import net.thunderbird.ui.catalog.ui.CatalogContract.Theme
import net.thunderbird.ui.catalog.ui.CatalogContract.ThemeVariant
@Composable
fun ThemeSwitch(
theme: Theme,
themeVariant: ThemeVariant,
content: @Composable () -> Unit,
) {
when (theme) {
Theme.THEME_2_K9 -> K9Theme2Switch(
themeVariant = themeVariant,
content = content,
)
Theme.THEME_2_THUNDERBIRD -> ThunderbirdTheme2Switch(
themeVariant = themeVariant,
content = content,
)
}
}
@Composable
private fun K9Theme2Switch(
themeVariant: ThemeVariant,
content: @Composable () -> Unit,
) {
when (themeVariant) {
ThemeVariant.LIGHT -> K9MailTheme2(
darkTheme = false,
content = content,
)
ThemeVariant.DARK -> K9MailTheme2(
darkTheme = true,
content = content,
)
}
}
@Composable
private fun ThunderbirdTheme2Switch(
themeVariant: ThemeVariant,
content: @Composable () -> Unit,
) {
when (themeVariant) {
ThemeVariant.LIGHT -> ThunderbirdTheme2(
darkTheme = false,
content = content,
)
ThemeVariant.DARK -> ThunderbirdTheme2(
darkTheme = true,
content = content,
)
}
}

View file

@ -0,0 +1,29 @@
package net.thunderbird.ui.catalog.ui.navigation
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
@Composable
fun CatalogNavHost(
navController: NavHostController,
modifier: Modifier = Modifier,
paddingValues: PaddingValues = PaddingValues(),
startDestination: CatalogRoute = CatalogRoute.Atom,
catalogNavigation: CatalogNavigation = DefaultCatalogNavigation(),
) {
NavHost(
navController = navController,
startDestination = startDestination,
modifier = modifier.padding(paddingValues),
) {
catalogNavigation.registerRoutes(
navGraphBuilder = this,
onBack = { navController.popBackStack() },
onFinish = { navController.popBackStack() },
)
}
}

View file

@ -0,0 +1,5 @@
package net.thunderbird.ui.catalog.ui.navigation
import app.k9mail.core.ui.compose.navigation.Navigation
interface CatalogNavigation : Navigation<CatalogRoute>

View file

@ -0,0 +1,47 @@
package net.thunderbird.ui.catalog.ui.navigation
import app.k9mail.core.ui.compose.navigation.Route
import kotlinx.serialization.Serializable
sealed interface CatalogRoute : Route {
@Serializable
data object Atom : CatalogRoute {
override val basePath: String = BASE_PATH
override fun route(): String = basePath
const val BASE_PATH = "$CATALOG_BASE_PATH/atom"
}
@Serializable
data object Molecule : CatalogRoute {
override val basePath: String = BASE_PATH
override fun route(): String = basePath
const val BASE_PATH = "$CATALOG_BASE_PATH/molecule"
}
@Serializable
data object Organism : CatalogRoute {
override val basePath: String = BASE_PATH
override fun route(): String = basePath
const val BASE_PATH = "$CATALOG_BASE_PATH/organism"
}
@Serializable
data object Template : CatalogRoute {
override val basePath: String = BASE_PATH
override fun route(): String = basePath
const val BASE_PATH = "$CATALOG_BASE_PATH/template"
}
companion object {
const val CATALOG_BASE_PATH = "app://catalog"
}
}

View file

@ -0,0 +1,43 @@
package net.thunderbird.ui.catalog.ui.navigation
import androidx.navigation.NavGraphBuilder
import app.k9mail.core.ui.compose.navigation.deepLinkComposable
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomScreen
import net.thunderbird.ui.catalog.ui.page.molecule.CatalogMoleculeScreen
import net.thunderbird.ui.catalog.ui.page.organism.CatalogOrganismScreen
import net.thunderbird.ui.catalog.ui.page.template.CatalogTemplateScreen
class DefaultCatalogNavigation : CatalogNavigation {
override fun registerRoutes(
navGraphBuilder: NavGraphBuilder,
onBack: () -> Unit,
onFinish: (CatalogRoute) -> Unit,
) {
with(navGraphBuilder) {
deepLinkComposable<CatalogRoute.Atom>(
basePath = CatalogRoute.Atom.BASE_PATH,
) { backStackEntry ->
CatalogAtomScreen()
}
deepLinkComposable<CatalogRoute.Molecule>(
basePath = CatalogRoute.Molecule.BASE_PATH,
) { backStackEntry ->
CatalogMoleculeScreen()
}
deepLinkComposable<CatalogRoute.Organism>(
basePath = CatalogRoute.Organism.BASE_PATH,
) { backStackEntry ->
CatalogOrganismScreen()
}
deepLinkComposable<CatalogRoute.Template>(
basePath = CatalogRoute.Template.BASE_PATH,
) { backStackEntry ->
CatalogTemplateScreen()
}
}
}
}

View file

@ -0,0 +1,24 @@
package net.thunderbird.ui.catalog.ui.page
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage
interface CatalogPageContract {
interface CatalogPage {
val displayName: String
val isFullScreen: Boolean
}
interface ViewModel : UnidirectionalViewModel<State, Event, Nothing>
data class State(
val page: CatalogPage = CatalogAtomPage.TYPOGRAPHY,
)
sealed interface Event {
data class OnPageChanged(
val page: CatalogPage,
) : Event
}
}

View file

@ -0,0 +1,23 @@
package net.thunderbird.ui.catalog.ui.page
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.Event
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.State
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.ViewModel
class CatalogPageViewModel(
initialState: State = State(),
) : BaseViewModel<State, Event, Nothing>(initialState), ViewModel {
override fun event(event: Event) {
when (event) {
is Event.OnPageChanged -> {
updateState {
it.copy(
page = event.page,
)
}
}
}
}
}

View file

@ -0,0 +1,51 @@
package net.thunderbird.ui.catalog.ui.page.atom
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.collections.immutable.ImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.BUTTON
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.CARD
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.COLOR
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.ICON
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.IMAGE
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.SELECTION_CONTROL
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.TEXT_FIELD
import net.thunderbird.ui.catalog.ui.page.atom.CatalogAtomPage.TYPOGRAPHY
import net.thunderbird.ui.catalog.ui.page.atom.items.buttonItems
import net.thunderbird.ui.catalog.ui.page.atom.items.cardItems
import net.thunderbird.ui.catalog.ui.page.atom.items.colorItems
import net.thunderbird.ui.catalog.ui.page.atom.items.iconItems
import net.thunderbird.ui.catalog.ui.page.atom.items.imageItems
import net.thunderbird.ui.catalog.ui.page.atom.items.selectionControlItems
import net.thunderbird.ui.catalog.ui.page.atom.items.textFieldItems
import net.thunderbird.ui.catalog.ui.page.atom.items.typographyItems
import net.thunderbird.ui.catalog.ui.page.common.PagedContent
@Composable
fun CatalogAtomContent(
pages: ImmutableList<CatalogAtomPage>,
initialPage: CatalogAtomPage,
onEvent: (CatalogPageContract.Event) -> Unit,
modifier: Modifier = Modifier,
) {
PagedContent(
pages = pages,
initialPage = initialPage,
modifier = modifier,
onRenderPage = {
when (it) {
TYPOGRAPHY -> typographyItems()
COLOR -> colorItems()
BUTTON -> buttonItems()
SELECTION_CONTROL -> selectionControlItems()
TEXT_FIELD -> textFieldItems()
ICON -> iconItems()
IMAGE -> imageItems()
CARD -> cardItems()
}
},
onRenderFullScreenPage = {},
onEvent = onEvent,
)
}

View file

@ -0,0 +1,27 @@
package net.thunderbird.ui.catalog.ui.page.atom
import kotlinx.collections.immutable.toImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.CatalogPage
enum class CatalogAtomPage(
override val displayName: String,
override val isFullScreen: Boolean = false,
) : CatalogPage {
TYPOGRAPHY("Typography"),
COLOR("Colors"),
BUTTON("Buttons"),
SELECTION_CONTROL("Selection Controls"),
TEXT_FIELD("TextFields"),
ICON("Icons"),
IMAGE("Images"),
CARD("Cards"),
;
override fun toString(): String {
return displayName
}
companion object {
fun all() = entries.toImmutableList()
}
}

View file

@ -0,0 +1,23 @@
package net.thunderbird.ui.catalog.ui.page.atom
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.common.mvi.observe
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.ViewModel
import net.thunderbird.ui.catalog.ui.page.CatalogPageViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
fun CatalogAtomScreen(
modifier: Modifier = Modifier,
viewModel: ViewModel = koinViewModel<CatalogPageViewModel>(),
) {
val (state, dispatch) = viewModel.observe { }
CatalogAtomContent(
pages = CatalogAtomPage.all(),
initialPage = state.value.page as? CatalogAtomPage ?: CatalogAtomPage.TYPOGRAPHY,
onEvent = { dispatch(it) },
modifier = modifier,
)
}

View file

@ -0,0 +1,166 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonElevated
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonFilled
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonFilledTonal
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonIcon
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonOutlined
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonSegmentedSingleChoice
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonText
import app.k9mail.core.ui.compose.designsystem.atom.button.RadioButton
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
import kotlinx.collections.immutable.persistentListOf
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.wideItem
@Suppress("LongMethod")
fun LazyGridScope.buttonItems() {
sectionHeaderItem(text = "Button - Filled")
defaultItem {
ButtonFilled(
text = "Enabled",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
ButtonFilled(
text = "Disabled",
onClick = { },
enabled = false,
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - Filled Tonal")
defaultItem {
ButtonFilledTonal(
text = "Enabled",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
ButtonFilledTonal(
text = "Disabled",
onClick = { },
enabled = false,
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - Elevated")
defaultItem {
ButtonElevated(
text = "Enabled",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
ButtonElevated(
text = "Disabled",
onClick = { },
enabled = false,
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - Outlined")
defaultItem {
ButtonOutlined(
text = "Enabled",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
ButtonOutlined(
text = "Disabled",
onClick = { },
enabled = false,
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - Text")
defaultItem {
ButtonText(
text = "Enabled",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
ButtonText(
text = "Disabled",
onClick = { },
enabled = false,
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - Icon")
defaultItem {
ButtonIcon(
imageVector = Icons.Outlined.AccountCircle,
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
ButtonIcon(
imageVector = Icons.Outlined.AccountCircle,
onClick = { },
enabled = false,
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - RadioButton")
defaultItem {
RadioButton(
selected = false,
label = "Radio Button",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
defaultItem {
RadioButton(
selected = true,
label = "Selected Radio Button",
onClick = { },
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Button - Segmented Single Choice")
wideItem {
val options = persistentListOf(
"Option 1",
"Option 2",
"Option 3",
)
var selectedOption by remember { mutableStateOf(options[0]) }
ButtonSegmentedSingleChoice(
modifier = Modifier.padding(defaultItemPadding()),
onClick = {
selectedOption = it
},
options = options,
optionTitle = { it },
selectedOption = selectedOption,
)
}
}

View file

@ -0,0 +1,64 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.card.CardElevated
import app.k9mail.core.ui.compose.designsystem.atom.card.CardFilled
import app.k9mail.core.ui.compose.designsystem.atom.card.CardOutlined
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.wideItem
fun LazyGridScope.cardItems() {
sectionCardElevated()
sectionCardFilled()
sectionCardOutlined()
}
fun LazyGridScope.sectionCardElevated() {
sectionHeaderItem(text = "Card - Elevated")
wideItem {
CardElevated(
modifier = Modifier.padding(horizontal = MainTheme.spacings.triple),
) {
Box(
modifier = Modifier.padding(MainTheme.spacings.triple),
) {
TextBodyLarge(text = "Inside a CardElevated")
}
}
}
}
fun LazyGridScope.sectionCardFilled() {
sectionHeaderItem(text = "Card - Filled")
wideItem {
CardFilled(
modifier = Modifier.padding(horizontal = MainTheme.spacings.triple),
) {
Box(
modifier = Modifier.padding(MainTheme.spacings.triple),
) {
TextBodyLarge(text = "Inside a CardFilled")
}
}
}
}
fun LazyGridScope.sectionCardOutlined() {
sectionHeaderItem(text = "Card - Outlined")
wideItem {
CardOutlined(
modifier = Modifier.padding(horizontal = MainTheme.spacings.triple),
) {
Box(
modifier = Modifier.padding(MainTheme.spacings.triple),
) {
TextBodyLarge(text = "Inside a CardOutlined")
}
}
}
}

View file

@ -0,0 +1,326 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.lazy.grid.LazyGridScope
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.atom.view.ColorContent
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.wideItem
@Suppress("LongMethod")
fun LazyGridScope.colorItems() {
sectionHeaderItem(text = "Material 3 theme colors")
wideItem {
ColorContent(
text = "Primary",
color = MainTheme.colors.primary,
textColor = MainTheme.colors.onPrimary,
)
}
wideItem {
ColorContent(
text = "On Primary",
color = MainTheme.colors.onPrimary,
textColor = MainTheme.colors.primary,
)
}
wideItem {
ColorContent(
text = "Primary Container",
color = MainTheme.colors.primaryContainer,
textColor = MainTheme.colors.onPrimaryContainer,
)
}
wideItem {
ColorContent(
text = "On Primary Container",
color = MainTheme.colors.onPrimaryContainer,
textColor = MainTheme.colors.primaryContainer,
)
}
wideItem {
ColorContent(
text = "Secondary",
color = MainTheme.colors.secondary,
textColor = MainTheme.colors.onSecondary,
)
}
wideItem {
ColorContent(
text = "On Secondary",
color = MainTheme.colors.onSecondary,
textColor = MainTheme.colors.secondary,
)
}
wideItem {
ColorContent(
text = "Secondary Container",
color = MainTheme.colors.secondaryContainer,
textColor = MainTheme.colors.onSecondaryContainer,
)
}
wideItem {
ColorContent(
text = "On Secondary Container",
color = MainTheme.colors.onSecondaryContainer,
textColor = MainTheme.colors.secondaryContainer,
)
}
wideItem {
ColorContent(
text = "Tertiary",
color = MainTheme.colors.tertiary,
textColor = MainTheme.colors.onTertiary,
)
}
wideItem {
ColorContent(
text = "On Tertiary",
color = MainTheme.colors.onTertiary,
textColor = MainTheme.colors.tertiary,
)
}
wideItem {
ColorContent(
text = "Tertiary Container",
color = MainTheme.colors.tertiaryContainer,
textColor = MainTheme.colors.onTertiaryContainer,
)
}
wideItem {
ColorContent(
text = "On Tertiary Container",
color = MainTheme.colors.onTertiaryContainer,
textColor = MainTheme.colors.tertiaryContainer,
)
}
wideItem {
ColorContent(
text = "Error",
color = MainTheme.colors.error,
textColor = MainTheme.colors.onError,
)
}
wideItem {
ColorContent(
text = "On Error",
color = MainTheme.colors.onError,
textColor = MainTheme.colors.error,
)
}
wideItem {
ColorContent(
text = "Error Container",
color = MainTheme.colors.errorContainer,
textColor = MainTheme.colors.onErrorContainer,
)
}
wideItem {
ColorContent(
text = "On Error Container",
color = MainTheme.colors.onErrorContainer,
textColor = MainTheme.colors.errorContainer,
)
}
wideItem {
ColorContent(
text = "Surface",
color = MainTheme.colors.surface,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "On Surface",
color = MainTheme.colors.onSurface,
textColor = MainTheme.colors.surface,
)
}
wideItem {
ColorContent(
text = "On Surface Variant",
color = MainTheme.colors.onSurfaceVariant,
textColor = MainTheme.colors.surface,
)
}
wideItem {
ColorContent(
text = "Surface Container Lowest",
color = MainTheme.colors.surfaceContainerLowest,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "Surface Container Low",
color = MainTheme.colors.surfaceContainerLow,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "Surface Container",
color = MainTheme.colors.surfaceContainer,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "Surface Container High",
color = MainTheme.colors.surfaceContainerHigh,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "Surface Container Highest",
color = MainTheme.colors.surfaceContainerHighest,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "Inverse Surface",
color = MainTheme.colors.inverseSurface,
textColor = MainTheme.colors.inverseOnSurface,
)
}
wideItem {
ColorContent(
text = "Inverse On Surface",
color = MainTheme.colors.inverseOnSurface,
textColor = MainTheme.colors.inverseSurface,
)
}
wideItem {
ColorContent(
text = "Inverse Primary",
color = MainTheme.colors.inversePrimary,
textColor = MainTheme.colors.onPrimaryContainer,
)
}
wideItem {
ColorContent(
text = "Outline",
color = MainTheme.colors.outline,
textColor = MainTheme.colors.surface,
)
}
wideItem {
ColorContent(
text = "Outline Variant",
color = MainTheme.colors.outlineVariant,
textColor = MainTheme.colors.inverseSurface,
)
}
wideItem {
ColorContent(
text = "Surface Bright",
color = MainTheme.colors.surfaceBright,
textColor = MainTheme.colors.onSurface,
)
}
wideItem {
ColorContent(
text = "Surface Dim",
color = MainTheme.colors.surfaceDim,
textColor = MainTheme.colors.onSurface,
)
}
sectionHeaderItem(text = "Material 3 theme custom colors")
wideItem {
ColorContent(
text = "Info",
color = MainTheme.colors.info,
textColor = MainTheme.colors.onInfo,
)
}
wideItem {
ColorContent(
text = "On Info",
color = MainTheme.colors.onInfo,
textColor = MainTheme.colors.info,
)
}
wideItem {
ColorContent(
text = "Info Container",
color = MainTheme.colors.infoContainer,
textColor = MainTheme.colors.onInfoContainer,
)
}
wideItem {
ColorContent(
text = "On Info Container",
color = MainTheme.colors.onInfoContainer,
textColor = MainTheme.colors.infoContainer,
)
}
wideItem {
ColorContent(
text = "Success",
color = MainTheme.colors.success,
textColor = MainTheme.colors.onSuccess,
)
}
wideItem {
ColorContent(
text = "On Success",
color = MainTheme.colors.onSuccess,
textColor = MainTheme.colors.success,
)
}
wideItem {
ColorContent(
text = "Success Container",
color = MainTheme.colors.successContainer,
textColor = MainTheme.colors.onSuccessContainer,
)
}
wideItem {
ColorContent(
text = "On Success Container",
color = MainTheme.colors.onSuccessContainer,
textColor = MainTheme.colors.successContainer,
)
}
wideItem {
ColorContent(
text = "Warning",
color = MainTheme.colors.warning,
textColor = MainTheme.colors.onWarning,
)
}
wideItem {
ColorContent(
text = "On Warning",
color = MainTheme.colors.onWarning,
textColor = MainTheme.colors.warning,
)
}
wideItem {
ColorContent(
text = "Warning Container",
color = MainTheme.colors.warningContainer,
textColor = MainTheme.colors.onWarningContainer,
)
}
wideItem {
ColorContent(
text = "On Warning Container",
color = MainTheme.colors.onWarningContainer,
textColor = MainTheme.colors.warningContainer,
)
}
}

View file

@ -0,0 +1,138 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
import androidx.compose.material3.Icon as Material3Icon
import app.k9mail.core.ui.legacy.designsystem.atom.icon.Icons as LegacyIcons
fun LazyGridScope.iconItems() {
sectionHeaderItem(
text = "Compose Icons",
)
sectionSubtitleItem(text = "Sizes")
defaultItem {
IconItem(
name = "Small",
imageVector = Icons.Outlined.Info,
modifier = Modifier.size(MainTheme.sizes.iconSmall),
)
}
defaultItem {
IconItem(
name = "Default",
imageVector = Icons.Outlined.Info,
modifier = Modifier.size(MainTheme.sizes.icon),
)
}
defaultItem {
IconItem(
name = "Large",
imageVector = Icons.Outlined.Info,
modifier = Modifier.size(MainTheme.sizes.iconLarge),
)
}
sectionSubtitleItem(text = "Filled")
getIconsFor(Icons.Filled)
sectionSubtitleItem(text = "Outlined")
getIconsFor(Icons.Outlined)
sectionHeaderItem(
text = "Legacy Icons",
)
sectionSubtitleItem(text = "Filled")
getLegacyIconsFor(LegacyIcons.Filled)
sectionSubtitleItem(text = "Outlined")
getLegacyIconsFor(LegacyIcons.Outlined)
}
private inline fun <reified T> LazyGridScope.getIconsFor(icons: T) {
for (method in T::class.java.methods) {
if (exclusions.contains(method.name)) {
continue
} else if (method.name.startsWith("get")) {
defaultItem {
method.isAccessible = true
val imageVector = method.invoke(icons) as ImageVector
IconItem(
name = method.name.replaceFirst("get", ""),
imageVector = imageVector,
)
}
}
}
}
private inline fun <reified T> LazyGridScope.getLegacyIconsFor(icons: T) {
for (method in T::class.java.methods) {
if (exclusions.contains(method.name)) {
continue
} else if (method.name.startsWith("get")) {
defaultItem {
method.isAccessible = true
val drawableResId = method.invoke(icons) as Int
LegacyIconItem(
name = method.name.replaceFirst("get", ""),
drawableResId = drawableResId,
)
}
}
}
}
private val exclusions = listOf("getClass")
@Composable
private fun IconItem(
name: String,
imageVector: ImageVector,
modifier: Modifier = Modifier,
) {
Column(
modifier = Modifier
.padding(defaultItemPadding()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
Icon(
imageVector = imageVector,
modifier = modifier,
)
TextBodySmall(text = name)
}
}
@Composable
private fun LegacyIconItem(
name: String,
drawableResId: Int,
modifier: Modifier = Modifier,
) {
Column(
modifier = Modifier
.padding(defaultItemPadding())
.then(modifier),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
Material3Icon(
painter = painterResource(id = drawableResId),
contentDescription = null,
)
TextBodySmall(text = name)
}
}

View file

@ -0,0 +1,182 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
import app.k9mail.core.ui.compose.designsystem.atom.image.FixedScaleImage
import app.k9mail.core.ui.compose.designsystem.atom.image.RemoteImage
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.core.ui.compose.designsystem.atom.icon.filled.Star
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
fun LazyGridScope.imageItems() {
sectionHeaderItem(text = "Images")
defaultItem {
Image(
painter = painterResource(id = MainTheme.images.logo),
contentDescription = "logo",
modifier = Modifier.padding(defaultItemPadding()),
)
}
sectionHeaderItem(text = "Images with fixed scale")
fixedScaleImagesCropped()
fixedScaleImagesOverflow()
fixedScaleImagesAlignment()
sectionHeaderItem(text = "Remote images")
remoteImage(
url = "https://www.thunderbird.net/media/img/thunderbird/favicon-196.png",
description = "Weblink",
)
}
private fun LazyGridScope.fixedScaleImagesCropped() {
sectionSubtitleItem(text = "Images are cropped by parent container size")
fullSpanItem {
FixedScaleImageView(
description = "Small container",
width = 40.dp,
height = 40.dp,
)
}
fullSpanItem {
FixedScaleImageView(
description = "Small horizontal container",
width = 40.dp,
height = 200.dp,
)
}
fullSpanItem {
FixedScaleImageView(
description = "Small vertical container",
width = 200.dp,
height = 40.dp,
)
}
}
private fun LazyGridScope.fixedScaleImagesOverflow() {
sectionSubtitleItem(text = "Images overflow parent container size")
fullSpanItem {
FixedScaleImageView(
description = "Small container",
width = 40.dp,
height = 40.dp,
allowOverflow = true,
)
}
fullSpanItem {
FixedScaleImageView(
description = "Small horizontal container",
width = 40.dp,
height = 200.dp,
allowOverflow = true,
)
}
fullSpanItem {
FixedScaleImageView(
description = "Small vertical container",
width = 200.dp,
height = 40.dp,
allowOverflow = true,
)
}
}
private fun LazyGridScope.fixedScaleImagesAlignment() {
sectionSubtitleItem(text = "Images with different alignments")
fullSpanItem {
FixedScaleImageView(
description = "Center",
width = 200.dp,
height = 200.dp,
)
}
fullSpanItem {
FixedScaleImageView(
description = "Top center",
width = 200.dp,
height = 200.dp,
alignment = Alignment.TopCenter,
)
}
fullSpanItem {
FixedScaleImageView(
description = "Bottom center",
width = 200.dp,
height = 200.dp,
alignment = Alignment.BottomCenter,
)
}
}
@Composable
private fun FixedScaleImageView(
description: String,
width: Dp,
height: Dp,
alignment: Alignment = Alignment.Center,
allowOverflow: Boolean = false,
) {
Column(
modifier = Modifier.padding(defaultItemPadding()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box(
modifier = Modifier
.width(width)
.height(height)
.border(1.dp, MainTheme.colors.primary, MainTheme.shapes.small),
) {
FixedScaleImage(
id = MainTheme.images.logo,
alignment = alignment,
allowOverflow = allowOverflow,
)
}
TextBodySmall(text = description)
}
}
private fun LazyGridScope.remoteImage(
url: String,
description: String,
) {
fullSpanItem {
Column(
modifier = Modifier.padding(defaultItemPadding()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
RemoteImage(
url = url,
modifier = Modifier.size(MainTheme.sizes.large),
placeholder = {
Icon(
imageVector = Icons.Filled.Star,
)
},
)
TextBodySmall(text = description)
}
}
}

View file

@ -0,0 +1,109 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.Checkbox
import app.k9mail.core.ui.compose.designsystem.atom.RadioGroup
import app.k9mail.core.ui.compose.designsystem.atom.Switch
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlinx.collections.immutable.persistentListOf
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
@Suppress("LongMethod")
fun LazyGridScope.selectionControlItems() {
sectionHeaderItem(text = "Checkbox")
captionItem(caption = "Checked") {
Checkbox(checked = true, onCheckedChange = {})
}
captionItem(caption = "Unchecked") {
Checkbox(checked = false, onCheckedChange = {})
}
captionItem(caption = "Disabled Checked") {
Checkbox(checked = true, onCheckedChange = {}, enabled = false)
}
captionItem(caption = "Disabled") {
Checkbox(checked = false, onCheckedChange = {}, enabled = false)
}
sectionHeaderItem(text = "Switch")
captionItem(caption = "Checked") {
Switch(checked = true, onCheckedChange = {})
}
captionItem(caption = "Unchecked") {
Switch(checked = false, onCheckedChange = {})
}
captionItem(caption = "Disabled Checked") {
Switch(checked = true, onCheckedChange = {}, enabled = false)
}
captionItem(caption = "Disabled") {
Switch(checked = false, onCheckedChange = {}, enabled = false)
}
sectionHeaderItem(text = "Radio Group")
defaultItem {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(defaultItemPadding()),
) {
TextTitleMedium(text = "Selected")
RadioGroup(
onClick = {},
options = radioGroupChoice,
optionTitle = { it.second },
selectedOption = radioGroupChoice[0],
modifier = Modifier
.padding(MainTheme.spacings.default)
.fillMaxWidth(),
)
}
}
defaultItem {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(defaultItemPadding()),
) {
TextTitleMedium(text = "Unselected")
RadioGroup(
onClick = {},
options = radioGroupChoice,
optionTitle = { it.second },
modifier = Modifier
.padding(MainTheme.spacings.default)
.fillMaxWidth(),
)
}
}
}
private fun LazyGridScope.captionItem(
caption: String,
content: @Composable () -> Unit,
) {
defaultItem {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(defaultItemPadding()),
) {
content()
TextBodySmall(text = caption)
}
}
}
val radioGroupChoice = persistentListOf(
Pair("1", "Alpha"),
Pair("2", "Beta"),
Pair("3", "Gamma"),
Pair("4", "Delta"),
)

View file

@ -0,0 +1,276 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlined
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlinedEmailAddress
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlinedNumber
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlinedPassword
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlinedSelect
import app.k9mail.core.ui.compose.designsystem.molecule.input.CheckboxInput
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import net.thunderbird.ui.catalog.ui.page.common.helper.WithRememberedState
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
fun LazyGridScope.textFieldItems() {
sectionHeaderItem(text = "Text field - Outlined")
sectionSubtitleItem(text = "Default")
textFieldOutlinedItems()
sectionSubtitleItem(text = "Password")
passwordTextFieldOutlinedItems()
sectionSubtitleItem(text = "Email address")
emailTextFieldOutlinedItems()
sectionSubtitleItem(text = "Number")
numberTextFieldOutlinedItems()
sectionSubtitleItem(text = "Selection")
selectionTextFieldOutlinedItems()
}
@Stable
data class TextFieldState<T>(
val input: T,
val label: String = "Label",
val showLabel: Boolean = false,
val showTrailingIcon: Boolean = false,
val isDisabled: Boolean = false,
val isReadOnly: Boolean = false,
val isRequired: Boolean = false,
val hasError: Boolean = false,
val isSingleLine: Boolean = false,
)
@Suppress("LongMethod")
@Composable
fun <T> TextFieldDemo(
initialState: TextFieldState<T>,
modifier: Modifier = Modifier,
hasTrailingIcon: Boolean = false,
hasSingleLine: Boolean = false,
content: @Composable (state: MutableState<TextFieldState<T>>) -> Unit,
) {
WithRememberedState(input = initialState) { state ->
Column(
modifier = Modifier
.fillMaxWidth()
.padding(defaultItemPadding())
.then(modifier),
) {
key(state.value.showLabel, state.value.isRequired) {
content(state)
}
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
top = MainTheme.spacings.double,
start = MainTheme.spacings.default,
),
) {
TextTitleMedium(text = "Configuration:")
}
CheckboxInput(
text = "Show Label",
checked = state.value.showLabel,
onCheckedChange = { state.value = state.value.copy(showLabel = it) },
contentPadding = defaultPadding,
)
if (hasTrailingIcon) {
CheckboxInput(
text = "Show Trailing Icon",
checked = state.value.showTrailingIcon,
onCheckedChange = { state.value = state.value.copy(showTrailingIcon = it) },
contentPadding = defaultPadding,
)
}
CheckboxInput(
text = "Is required",
checked = state.value.isRequired,
onCheckedChange = { state.value = state.value.copy(isRequired = it) },
contentPadding = defaultPadding,
)
CheckboxInput(
text = "Is read-only",
checked = state.value.isReadOnly,
onCheckedChange = { state.value = state.value.copy(isReadOnly = it) },
contentPadding = defaultPadding,
)
CheckboxInput(
text = "Is disabled",
checked = state.value.isDisabled,
onCheckedChange = { state.value = state.value.copy(isDisabled = it) },
contentPadding = defaultPadding,
)
CheckboxInput(
text = "Has Error",
checked = state.value.hasError,
onCheckedChange = { state.value = state.value.copy(hasError = it) },
contentPadding = defaultPadding,
)
if (hasSingleLine) {
CheckboxInput(
text = "Single line",
checked = state.value.isSingleLine,
onCheckedChange = { state.value = state.value.copy(isSingleLine = it) },
contentPadding = defaultPadding,
)
}
}
}
}
}
private val defaultPadding = PaddingValues(0.dp)
private fun LazyGridScope.textFieldOutlinedItems() {
fullSpanItem {
TextFieldDemo(
hasTrailingIcon = true,
hasSingleLine = true,
initialState = TextFieldState(input = ""),
) { state ->
TextFieldOutlined(
value = state.value.input,
label = if (state.value.showLabel) state.value.label else null,
onValueChange = { state.value = state.value.copy(input = it) },
trailingIcon = {
if (state.value.showTrailingIcon) {
Icon(imageVector = Icons.Outlined.AccountCircle)
}
},
isEnabled = !state.value.isDisabled,
isReadOnly = state.value.isReadOnly,
isRequired = state.value.isRequired,
isSingleLine = state.value.isSingleLine,
hasError = state.value.hasError,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
private fun LazyGridScope.passwordTextFieldOutlinedItems() {
fullSpanItem {
TextFieldDemo(
initialState = TextFieldState(
input = "",
label = "Password",
),
) { state ->
TextFieldOutlinedPassword(
value = state.value.input,
label = if (state.value.showLabel) state.value.label else null,
onValueChange = { state.value = state.value.copy(input = it) },
isEnabled = !state.value.isDisabled,
isReadOnly = state.value.isReadOnly,
isRequired = state.value.isRequired,
hasError = state.value.hasError,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
private fun LazyGridScope.emailTextFieldOutlinedItems() {
fullSpanItem {
TextFieldDemo(
initialState = TextFieldState(
input = "",
label = "Email Address",
),
) { state ->
TextFieldOutlinedEmailAddress(
value = state.value.input,
label = if (state.value.showLabel) state.value.label else null,
onValueChange = { state.value = state.value.copy(input = it) },
isEnabled = !state.value.isDisabled,
isReadOnly = state.value.isReadOnly,
isRequired = state.value.isRequired,
hasError = state.value.hasError,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
private fun LazyGridScope.numberTextFieldOutlinedItems() {
fullSpanItem {
TextFieldDemo(
initialState = TextFieldState<Long?>(
input = 123L,
label = "Number",
),
) { state ->
TextFieldOutlinedNumber(
value = state.value.input,
label = if (state.value.showLabel) state.value.label else null,
onValueChange = { state.value = state.value.copy(input = it) },
isEnabled = !state.value.isDisabled,
isReadOnly = state.value.isReadOnly,
isRequired = state.value.isRequired,
hasError = state.value.hasError,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
private data class TextFieldSelectState(
val options: ImmutableList<String> = persistentListOf("Option 1", "Option 2", "Option 3"),
val selectedOption: String = options.first(),
)
private fun LazyGridScope.selectionTextFieldOutlinedItems() {
fullSpanItem {
TextFieldDemo(
initialState = TextFieldState(
input = TextFieldSelectState(),
label = "Select",
),
) { state ->
key(
state.value.input.selectedOption,
) {
TextFieldOutlinedSelect(
options = state.value.input.options,
label = if (state.value.showLabel) state.value.label else null,
onValueChange = {
state.value = state.value.copy(input = state.value.input.copy(selectedOption = it))
},
selectedOption = state.value.input.selectedOption,
isEnabled = !state.value.isDisabled,
isReadOnly = state.value.isReadOnly,
isRequired = state.value.isRequired,
hasError = state.value.hasError,
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}

View file

@ -0,0 +1,167 @@
package net.thunderbird.ui.catalog.ui.page.atom.items
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextDisplayLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextDisplayMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextDisplaySmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadlineLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadlineMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadlineSmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelSmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleSmall
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
fun LazyGridScope.typographyItems() {
sectionHeaderItem(text = "Text styles")
textItems()
sectionHeaderItem(text = "Text styles - Colored")
textItems(color = Color.Magenta)
sectionHeaderItem(text = "Text styles - Annotated")
textItems(isAnnotated = true)
}
@Suppress("LongMethod")
private fun LazyGridScope.textItems(
isAnnotated: Boolean = false,
color: Color = Color.Unspecified,
) {
fullSpanItem {
TextDisplayLarge(
text = annotatedString("DisplayLarge", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextDisplayMedium(
text = annotatedString("DisplayMedium", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextDisplaySmall(
text = annotatedString("DisplaySmall", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextHeadlineLarge(
text = annotatedString("HeadlineLarge", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextHeadlineMedium(
text = annotatedString("HeadlineMedium", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextHeadlineSmall(
text = annotatedString("HeadlineSmall", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextTitleLarge(
text = annotatedString("TitleLarge", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextTitleMedium(
text = annotatedString("TitleMedium", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextTitleSmall(
text = annotatedString("TitleSmall", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextBodyLarge(
text = annotatedString("BodyLarge", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextBodyMedium(
text = annotatedString("BodyMedium", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextBodySmall(
text = annotatedString("BodySmall", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextLabelLarge(
text = annotatedString("LabelLarge", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextLabelMedium(
text = annotatedString("LabelMedium", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
fullSpanItem {
TextLabelSmall(
text = annotatedString("LabelSmall", isAnnotated),
modifier = Modifier.padding(defaultItemPadding()),
color = color,
)
}
}
private fun annotatedString(
name: String,
isAnnotated: Boolean,
) = buildAnnotatedString {
append(name)
if (isAnnotated) {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append("Annotated")
}
}
}

View file

@ -0,0 +1,53 @@
package net.thunderbird.ui.catalog.ui.page.atom.view
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
@Composable
internal fun ColorContent(
text: String,
color: Color,
textColor: Color,
modifier: Modifier = Modifier,
) {
Surface(
color = color,
modifier = Modifier
.padding(defaultItemPadding())
.then(modifier),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(MainTheme.spacings.double),
) {
Column(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
TextBodyLarge(
text = text,
color = textColor,
)
TextBodySmall(
text = color.toHex(),
color = textColor,
)
}
}
}
}
private fun Color.toHex(): String {
return "#${Integer.toHexString(toArgb()).uppercase()}"
}

View file

@ -0,0 +1,93 @@
package net.thunderbird.ui.catalog.ui.page.common
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveContentWithSurface
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.launch
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.CatalogPage
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
@Composable
fun <T : CatalogPage> PagedContent(
pages: ImmutableList<T>,
initialPage: T,
onRenderPage: LazyGridScope.(T) -> Unit,
onEvent: (CatalogPageContract.Event) -> Unit,
modifier: Modifier = Modifier,
onRenderFullScreenPage: @Composable (T) -> Unit = {},
) {
val pagerState = rememberPagerState(
initialPage = pages.indexOf(initialPage),
initialPageOffsetFraction = 0f,
) {
pages.size
}
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(pagerState.settledPage) {
onEvent(CatalogPageContract.Event.OnPageChanged(pages[pagerState.settledPage]))
}
Column(
modifier = modifier,
) {
ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
) {
pages.forEachIndexed { index, title ->
Tab(
selected = pagerState.currentPage == index,
onClick = {
coroutineScope.launch {
pagerState.animateScrollToPage(index)
onEvent(CatalogPageContract.Event.OnPageChanged(pages[index]))
}
},
text = { Text(text = title.toString()) },
)
}
}
ResponsiveContentWithSurface {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxSize(),
) { pageIndex ->
if (pages[pageIndex].isFullScreen) {
onRenderFullScreenPage(pages[pageIndex])
} else {
LazyVerticalGrid(
columns = GridCells.Adaptive(MainTheme.sizes.larger),
modifier = Modifier
.fillMaxSize()
.imePadding(),
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
) {
onRenderPage(pages[pageIndex])
fullSpanItem { Spacer(modifier = Modifier.height(MainTheme.sizes.smaller)) }
}
}
}
}
}
}

View file

@ -0,0 +1,15 @@
package net.thunderbird.ui.catalog.ui.page.common.helper
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@Composable
internal fun <T> WithRememberedState(
input: T,
content: @Composable (state: MutableState<T>) -> Unit,
) {
val state = remember { mutableStateOf(input) }
content(state)
}

View file

@ -0,0 +1,14 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import java.util.UUID
fun LazyGridScope.defaultItem(content: @Composable LazyGridItemScope.() -> Unit) {
item(
key = UUID.randomUUID().toString(),
) {
content()
}
}

View file

@ -0,0 +1,10 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import app.k9mail.core.ui.compose.theme2.MainTheme
@Composable
fun defaultItemPadding(): PaddingValues = PaddingValues(
horizontal = MainTheme.spacings.double,
)

View file

@ -0,0 +1,16 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import java.util.UUID
fun LazyGridScope.fullSpanItem(content: @Composable LazyGridItemScope.() -> Unit) {
item(
key = UUID.randomUUID().toString(),
span = { GridItemSpan(maxLineSpan) },
) {
content()
}
}

View file

@ -0,0 +1,34 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.theme2.MainTheme
@Composable
fun ItemOutlinedView(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
Column(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
modifier = Modifier
.padding(defaultItemPadding())
.then(modifier),
) {
Box(
modifier = Modifier
.border(1.dp, Color.Gray)
.fillMaxWidth(),
) {
content()
}
}
}

View file

@ -0,0 +1,31 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleLarge
import app.k9mail.core.ui.compose.theme2.MainTheme
fun LazyGridScope.sectionHeaderItem(
text: String,
) {
fullSpanItem {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.double,
top = MainTheme.spacings.double,
end = MainTheme.spacings.double,
),
) {
TextTitleLarge(
text = text,
)
DividerHorizontal()
}
}
}

View file

@ -0,0 +1,30 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.theme2.MainTheme
fun LazyGridScope.sectionInfoItem(
text: String,
) {
fullSpanItem {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.double,
end = MainTheme.spacings.double,
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
TextBodySmall(
text = text,
)
}
}
}

View file

@ -0,0 +1,31 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.theme2.MainTheme
fun LazyGridScope.sectionSubtitleItem(
text: String,
) {
fullSpanItem {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.double,
top = MainTheme.spacings.default,
end = MainTheme.spacings.double,
),
) {
TextTitleMedium(
text = text,
)
DividerHorizontal()
}
}
}

View file

@ -0,0 +1,16 @@
package net.thunderbird.ui.catalog.ui.page.common.list
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import java.util.UUID
fun LazyGridScope.wideItem(content: @Composable LazyGridItemScope.() -> Unit) {
item(
key = UUID.randomUUID().toString(),
span = { GridItemSpan(2) },
) {
content()
}
}

View file

@ -0,0 +1,41 @@
package net.thunderbird.ui.catalog.ui.page.molecule
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.collections.immutable.ImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract
import net.thunderbird.ui.catalog.ui.page.common.PagedContent
import net.thunderbird.ui.catalog.ui.page.molecule.CatalogMoleculePage.INPUT
import net.thunderbird.ui.catalog.ui.page.molecule.CatalogMoleculePage.PULL_TO_REFRESH
import net.thunderbird.ui.catalog.ui.page.molecule.CatalogMoleculePage.STATE
import net.thunderbird.ui.catalog.ui.page.molecule.items.PullToRefresh
import net.thunderbird.ui.catalog.ui.page.molecule.items.inputItems
import net.thunderbird.ui.catalog.ui.page.molecule.items.stateItems
@Composable
fun CatalogMoleculeContent(
pages: ImmutableList<CatalogMoleculePage>,
initialPage: CatalogMoleculePage,
onEvent: (CatalogPageContract.Event) -> Unit,
modifier: Modifier = Modifier,
) {
PagedContent(
pages = pages,
initialPage = initialPage,
modifier = modifier,
onRenderPage = {
when (it) {
INPUT -> inputItems()
STATE -> stateItems()
else -> throw IllegalArgumentException("Unknown page: $it")
}
},
onRenderFullScreenPage = { page ->
when (page) {
PULL_TO_REFRESH -> PullToRefresh()
else -> throw IllegalArgumentException("Unknown page: $page")
}
},
onEvent = onEvent,
)
}

View file

@ -0,0 +1,22 @@
package net.thunderbird.ui.catalog.ui.page.molecule
import kotlinx.collections.immutable.toImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.CatalogPage
enum class CatalogMoleculePage(
override val displayName: String,
override val isFullScreen: Boolean = false,
) : CatalogPage {
INPUT("Inputs"),
STATE("States"),
PULL_TO_REFRESH("Pull to refresh", isFullScreen = true),
;
override fun toString(): String {
return displayName
}
companion object {
fun all() = entries.toImmutableList()
}
}

View file

@ -0,0 +1,23 @@
package net.thunderbird.ui.catalog.ui.page.molecule
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.common.mvi.observe
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.ViewModel
import net.thunderbird.ui.catalog.ui.page.CatalogPageViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
fun CatalogMoleculeScreen(
modifier: Modifier = Modifier,
viewModel: ViewModel = koinViewModel<CatalogPageViewModel>(),
) {
val (state, dispatch) = viewModel.observe { }
CatalogMoleculeContent(
pages = CatalogMoleculePage.all(),
initialPage = state.value.page as? CatalogMoleculePage ?: CatalogMoleculePage.INPUT,
onEvent = { dispatch(it) },
modifier = modifier,
)
}

View file

@ -0,0 +1,187 @@
package net.thunderbird.ui.catalog.ui.page.molecule.items
import androidx.compose.foundation.lazy.grid.LazyGridScope
import app.k9mail.core.ui.compose.designsystem.molecule.input.CheckboxInput
import app.k9mail.core.ui.compose.designsystem.molecule.input.EmailAddressInput
import app.k9mail.core.ui.compose.designsystem.molecule.input.NumberInput
import app.k9mail.core.ui.compose.designsystem.molecule.input.PasswordInput
import app.k9mail.core.ui.compose.designsystem.molecule.input.SelectInput
import app.k9mail.core.ui.compose.designsystem.molecule.input.SwitchInput
import app.k9mail.core.ui.compose.designsystem.molecule.input.TextInput
import kotlinx.collections.immutable.persistentListOf
import net.thunderbird.ui.catalog.ui.page.common.helper.WithRememberedState
import net.thunderbird.ui.catalog.ui.page.common.list.ItemOutlinedView
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
@Suppress("LongMethod")
fun LazyGridScope.inputItems() {
sectionHeaderItem(text = "TextInput")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = "") { state ->
TextInput(
text = state.value,
onTextChange = { state.value = it },
)
}
}
}
sectionSubtitleItem(text = "With error")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = "") { state ->
TextInput(
text = state.value,
onTextChange = { state.value = it },
errorMessage = "Invalid input",
)
}
}
}
sectionHeaderItem(text = "EmailAddressInput")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = "") { state ->
EmailAddressInput(
emailAddress = state.value,
onEmailAddressChange = { state.value = it },
)
}
}
}
sectionSubtitleItem(text = "With error")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = "wrong email address") { state ->
EmailAddressInput(
emailAddress = state.value,
onEmailAddressChange = { state.value = it },
errorMessage = "Invalid email address",
)
}
}
}
sectionHeaderItem(text = "NumberInput")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
WithRememberedState<Long?>(input = null) { state ->
NumberInput(
value = state.value,
onValueChange = { state.value = it },
)
}
}
}
sectionSubtitleItem(text = "With error")
fullSpanItem {
ItemOutlinedView {
WithRememberedState<Long?>(input = 123L) { state ->
NumberInput(
value = state.value,
onValueChange = { state.value = it },
errorMessage = "Invalid number",
)
}
}
}
sectionHeaderItem(text = "PasswordInput")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = "") { state ->
PasswordInput(
password = state.value,
onPasswordChange = { state.value = it },
)
}
}
}
sectionSubtitleItem(text = "With error")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = "wrong password") { state ->
PasswordInput(
password = state.value,
onPasswordChange = { state.value = it },
errorMessage = "Invalid password",
)
}
}
}
sectionHeaderItem(text = "SelectInput")
fullSpanItem {
val options = persistentListOf("Option 1", "Option 2", "Option 3")
ItemOutlinedView {
WithRememberedState(input = options.first()) { state ->
SelectInput(
options = options,
selectedOption = state.value,
onOptionChange = { state.value = it },
)
}
}
}
sectionHeaderItem(text = "CheckboxInput")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = false) { state ->
CheckboxInput(
text = "Check the box",
checked = state.value,
onCheckedChange = { state.value = it },
)
}
}
}
sectionSubtitleItem(text = "With error")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = false) { state ->
CheckboxInput(
text = "Check the box",
checked = state.value,
onCheckedChange = { state.value = it },
errorMessage = "Checkbox must be checked",
)
}
}
}
sectionHeaderItem(text = "SwitchInput")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = false) { state ->
SwitchInput(
text = "Switch the toggle",
checked = state.value,
onCheckedChange = { state.value = it },
)
}
}
}
sectionSubtitleItem(text = "With error")
fullSpanItem {
ItemOutlinedView {
WithRememberedState(input = false) { state ->
SwitchInput(
text = "Switch the toggle",
checked = state.value,
onCheckedChange = { state.value = it },
errorMessage = "Switch must be checked",
)
}
}
}
}

View file

@ -0,0 +1,47 @@
package net.thunderbird.ui.catalog.ui.page.molecule.items
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.molecule.PullToRefreshBox
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Suppress("MagicNumber")
@Composable
fun PullToRefresh(
modifier: Modifier = Modifier,
) {
val isRefreshing = remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
PullToRefreshBox(
isRefreshing = isRefreshing.value,
onRefresh = {
isRefreshing.value = true
coroutineScope.launch {
delay(2000)
isRefreshing.value = false
}
},
contentAlignment = Alignment.Center,
modifier = modifier.fillMaxWidth(),
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
) {
items(10) {
TextTitleMedium(text = "Item $it")
}
}
}
}

View file

@ -0,0 +1,166 @@
package net.thunderbird.ui.catalog.ui.page.molecule.items
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorState
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingState
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingView
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
import net.thunderbird.ui.catalog.ui.page.common.list.ItemOutlinedView
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionInfoItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
@Suppress("LongMethod")
fun LazyGridScope.stateItems() {
sectionHeaderItem(text = "ErrorView")
fullSpanItem {
ItemOutlinedView {
ErrorView(
title = "Error",
)
}
}
fullSpanItem {
ItemOutlinedView {
ErrorView(
title = "Error with message",
message = "Something went wrong",
)
}
}
fullSpanItem {
ItemOutlinedView {
ErrorView(
title = "Error with retry",
onRetry = {},
)
}
}
fullSpanItem {
ItemOutlinedView {
ErrorView(
title = "Error with retry and message",
message = "Something went wrong",
onRetry = {},
)
}
}
sectionHeaderItem(text = "LoadingView")
sectionSubtitleItem(text = "Default")
fullSpanItem {
ItemOutlinedView {
LoadingView()
}
}
sectionSubtitleItem(text = "With message")
fullSpanItem {
ItemOutlinedView {
LoadingView(
message = "Loading...",
)
}
}
sectionHeaderItem(text = "ContentLoadingView")
sectionInfoItem(text = "Click below to change state")
fullSpanItem {
Column {
ItemOutlinedView {
StatefulContentLoadingView()
}
}
}
sectionHeaderItem(text = "ContentLoadingErrorView")
sectionInfoItem(text = "Click below to change state")
fullSpanItem {
Column {
ItemOutlinedView {
StatefulContentLoadingErrorView()
}
}
}
}
@Composable
private fun StatefulContentLoadingView() {
val state = remember {
mutableStateOf<ContentLoadingState>(ContentLoadingState.Loading)
}
ContentLoadingView(
state = state.value,
modifier = Modifier
.clickable {
when (state.value) {
ContentLoadingState.Loading -> {
state.value = ContentLoadingState.Content
}
ContentLoadingState.Content -> {
state.value = ContentLoadingState.Loading
}
}
}
.height(200.dp)
.fillMaxSize(),
loading = {
TextTitleMedium(text = "Loading...")
},
content = {
TextTitleMedium(text = "Content")
},
)
}
@Composable
private fun StatefulContentLoadingErrorView() {
val state = remember {
mutableStateOf<ContentLoadingErrorState>(ContentLoadingErrorState.Loading)
}
ContentLoadingErrorView(
state = state.value,
modifier = Modifier
.clickable {
when (state.value) {
ContentLoadingErrorState.Loading -> {
state.value = ContentLoadingErrorState.Content
}
ContentLoadingErrorState.Content -> {
state.value = ContentLoadingErrorState.Error
}
ContentLoadingErrorState.Error -> {
state.value = ContentLoadingErrorState.Loading
}
}
}
.height(200.dp)
.fillMaxSize(),
error = {
TextTitleMedium(text = "Error")
},
loading = {
TextTitleMedium(text = "Loading...")
},
content = {
TextTitleMedium(text = "Content")
},
)
}

View file

@ -0,0 +1,47 @@
package net.thunderbird.ui.catalog.ui.page.organism
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.collections.immutable.ImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract
import net.thunderbird.ui.catalog.ui.page.common.PagedContent
import net.thunderbird.ui.catalog.ui.page.organism.CatalogOrganismPage.APP_BAR
import net.thunderbird.ui.catalog.ui.page.organism.CatalogOrganismPage.BANNER
import net.thunderbird.ui.catalog.ui.page.organism.CatalogOrganismPage.DIALOG
import net.thunderbird.ui.catalog.ui.page.organism.CatalogOrganismPage.MESSAGE_ITEM
import net.thunderbird.ui.catalog.ui.page.organism.CatalogOrganismPage.SNACKBAR
import net.thunderbird.ui.catalog.ui.page.organism.items.SnackbarItems
import net.thunderbird.ui.catalog.ui.page.organism.items.appBarItems
import net.thunderbird.ui.catalog.ui.page.organism.items.bannerItems
import net.thunderbird.ui.catalog.ui.page.organism.items.dialogItems
import net.thunderbird.ui.catalog.ui.page.organism.items.message.messageItems
@Composable
fun CatalogOrganismContent(
pages: ImmutableList<CatalogOrganismPage>,
initialPage: CatalogOrganismPage,
onEvent: (CatalogPageContract.Event) -> Unit,
modifier: Modifier = Modifier,
) {
PagedContent(
pages = pages,
initialPage = initialPage,
modifier = modifier,
onRenderPage = {
when (it) {
APP_BAR -> appBarItems()
DIALOG -> dialogItems()
BANNER -> bannerItems()
MESSAGE_ITEM -> messageItems()
SNACKBAR -> Unit
}
},
onRenderFullScreenPage = {
when (it) {
SNACKBAR -> SnackbarItems()
else -> Unit
}
},
onEvent = onEvent,
)
}

View file

@ -0,0 +1,24 @@
package net.thunderbird.ui.catalog.ui.page.organism
import kotlinx.collections.immutable.toImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.CatalogPage
enum class CatalogOrganismPage(
override val displayName: String,
override val isFullScreen: Boolean = false,
) : CatalogPage {
APP_BAR("App Bars"),
DIALOG("Dialogs"),
BANNER("Banners"),
SNACKBAR("Snackbars", isFullScreen = true),
MESSAGE_ITEM("Message Items"),
;
override fun toString(): String {
return displayName
}
companion object {
fun all() = entries.toImmutableList()
}
}

View file

@ -0,0 +1,23 @@
package net.thunderbird.ui.catalog.ui.page.organism
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.common.mvi.observe
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.ViewModel
import net.thunderbird.ui.catalog.ui.page.CatalogPageViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
fun CatalogOrganismScreen(
modifier: Modifier = Modifier,
viewModel: ViewModel = koinViewModel<CatalogPageViewModel>(),
) {
val (state, dispatch) = viewModel.observe { }
CatalogOrganismContent(
pages = CatalogOrganismPage.all(),
initialPage = state.value.page as? CatalogOrganismPage ?: CatalogOrganismPage.APP_BAR,
onEvent = { dispatch(it) },
modifier = modifier,
)
}

View file

@ -0,0 +1,145 @@
package net.thunderbird.ui.catalog.ui.page.organism.items
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonIcon
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
import app.k9mail.core.ui.compose.designsystem.organism.SubtitleTopAppBar
import app.k9mail.core.ui.compose.designsystem.organism.SubtitleTopAppBarWithBackButton
import app.k9mail.core.ui.compose.designsystem.organism.SubtitleTopAppBarWithMenuButton
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBar
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBarWithBackButton
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBarWithMenuButton
import net.thunderbird.ui.catalog.ui.page.common.list.ItemOutlinedView
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
fun LazyGridScope.appBarItems() {
topAppBarItems()
subtitleTopAppBarItems()
}
private fun LazyGridScope.topAppBarItems() {
sectionHeaderItem(text = "TopAppBar")
sectionSubtitleItem(text = "With menu icon")
fullSpanItem {
ItemOutlinedView {
TopAppBar(
title = "Title",
actions = {
ButtonIcon(
onClick = {},
imageVector = Icons.Outlined.Info,
)
ButtonIcon(
onClick = {},
imageVector = Icons.Outlined.Check,
)
ButtonIcon(
onClick = {},
imageVector = Icons.Outlined.Visibility,
)
},
)
}
}
sectionSubtitleItem(text = "With back menu icon")
fullSpanItem {
ItemOutlinedView {
TopAppBarWithMenuButton(
title = "Title",
onMenuClick = {},
)
}
}
sectionSubtitleItem(text = "With back icon")
fullSpanItem {
ItemOutlinedView {
TopAppBarWithBackButton(
title = "Title",
onBackClick = {},
)
}
}
}
@Suppress("LongMethod")
private fun LazyGridScope.subtitleTopAppBarItems() {
sectionHeaderItem(text = "SubtitleTopAppBar")
sectionSubtitleItem(text = "With menu icon")
fullSpanItem {
ItemOutlinedView {
SubtitleTopAppBar(
title = "Title",
subtitle = "Subtitle",
actions = {
DemoActionButton(
imageVector = Icons.Outlined.Info,
)
DemoActionButton(
imageVector = Icons.Outlined.Check,
)
DemoActionButton(
imageVector = Icons.Outlined.Visibility,
)
},
)
}
}
sectionSubtitleItem(text = "With long subtitle")
fullSpanItem {
ItemOutlinedView {
SubtitleTopAppBar(
title = "Title",
subtitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
"Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
actions = {
DemoActionButton(
imageVector = Icons.Outlined.Info,
)
DemoActionButton(
imageVector = Icons.Outlined.Check,
)
DemoActionButton(
imageVector = Icons.Outlined.Visibility,
)
},
)
}
}
sectionSubtitleItem(text = "With back menu icon")
fullSpanItem {
ItemOutlinedView {
SubtitleTopAppBarWithMenuButton(
title = "Title",
subtitle = "Subtitle",
onMenuClick = {},
)
}
}
sectionSubtitleItem(text = "With back icon")
fullSpanItem {
ItemOutlinedView {
SubtitleTopAppBarWithBackButton(
title = "Title",
subtitle = "Subtitle",
onBackClick = {},
)
}
}
}
/**
* Demo action button that does nothing on click.
*/
@Composable
private fun DemoActionButton(
imageVector: ImageVector,
) {
ButtonIcon(
onClick = {},
imageVector = imageVector,
)
}

View file

@ -0,0 +1,10 @@
package net.thunderbird.ui.catalog.ui.page.organism.items
import androidx.compose.foundation.lazy.grid.LazyGridScope
import net.thunderbird.ui.catalog.ui.page.organism.items.banners.bannerGlobal
import net.thunderbird.ui.catalog.ui.page.organism.items.banners.bannerInline
fun LazyGridScope.bannerItems() {
bannerGlobal()
bannerInline()
}

View file

@ -0,0 +1,10 @@
package net.thunderbird.ui.catalog.ui.page.organism.items
import androidx.compose.foundation.lazy.grid.LazyGridScope
import net.thunderbird.ui.catalog.ui.page.organism.items.dialogs.alertDialogs
import net.thunderbird.ui.catalog.ui.page.organism.items.dialogs.basicDialogs
fun LazyGridScope.dialogItems() {
basicDialogs()
alertDialogs()
}

View file

@ -0,0 +1,99 @@
package net.thunderbird.ui.catalog.ui.page.organism.items
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonText
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.organism.snackbar.SnackbarDuration
import app.k9mail.core.ui.compose.designsystem.organism.snackbar.SnackbarHost
import app.k9mail.core.ui.compose.designsystem.organism.snackbar.rememberSnackbarHostState
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlinx.coroutines.launch
@Composable
fun SnackbarItems(modifier: Modifier = Modifier) {
val snackbarHostState = rememberSnackbarHostState()
val coroutineScope = rememberCoroutineScope()
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
modifier = modifier,
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(it),
) {
SnackbarSubsection(title = "Without action") {
ButtonText(
text = "Show snackbar",
onClick = {
coroutineScope.launch {
snackbarHostState.showSnackbar(
message = "Snackbar message",
actionLabel = null,
)
}
},
)
}
SnackbarSubsection(title = "With action") {
ButtonText(
text = "Show snackbar",
onClick = {
coroutineScope.launch {
snackbarHostState.showSnackbar(
message = "Snackbar message",
actionLabel = "The action",
)
}
},
)
}
SnackbarDuration.entries.forEach { duration ->
SnackbarSubsection(title = "With ${duration.name} duration") {
ButtonText(
text = "Show snackbar",
onClick = {
coroutineScope.launch {
snackbarHostState.showSnackbar(
message = "Snackbar message with ${duration.name} of duration",
duration = duration,
)
}
},
)
}
}
Spacer(modifier = Modifier.weight(1f))
}
}
}
@Composable
private fun SnackbarSubsection(
title: String,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.double,
top = MainTheme.spacings.default,
end = MainTheme.spacings.double,
),
) {
TextTitleMedium(text = title)
DividerHorizontal()
content()
}
}

View file

@ -0,0 +1,74 @@
package net.thunderbird.ui.catalog.ui.page.organism.items.banners
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.molecule.notification.NotificationActionButton
import app.k9mail.core.ui.compose.designsystem.organism.banner.global.ErrorBannerGlobalNotificationCard
import app.k9mail.core.ui.compose.designsystem.organism.banner.global.InfoBannerGlobalNotificationCard
import app.k9mail.core.ui.compose.designsystem.organism.banner.global.SuccessBannerGlobalNotificationCard
import app.k9mail.core.ui.compose.designsystem.organism.banner.global.WarningBannerGlobalNotificationCard
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
fun LazyGridScope.bannerGlobal() {
sectionHeaderItem("Banner Global")
errorBannerGlobal()
infoBannerGlobal()
warningBannerGlobal()
successBannerGlobal()
}
fun LazyGridScope.errorBannerGlobal() {
sectionSubtitleItem("Error")
fullSpanItem {
ErrorBannerGlobalNotificationCard(
text = "Notification Text",
action = {
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}
fun LazyGridScope.infoBannerGlobal() {
sectionSubtitleItem("Information")
fullSpanItem {
InfoBannerGlobalNotificationCard(
text = "Notification Text",
action = {
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}
fun LazyGridScope.warningBannerGlobal() {
sectionSubtitleItem("Warning")
fullSpanItem {
WarningBannerGlobalNotificationCard(
text = "Notification Text",
action = {
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}
fun LazyGridScope.successBannerGlobal() {
sectionSubtitleItem("Success")
fullSpanItem {
SuccessBannerGlobalNotificationCard(
text = "Notification Text",
action = {
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}

View file

@ -0,0 +1,82 @@
package net.thunderbird.ui.catalog.ui.page.organism.items.banners
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.molecule.notification.NotificationActionButton
import app.k9mail.core.ui.compose.designsystem.organism.banner.inline.ErrorBannerInlineNotificationCard
import app.k9mail.core.ui.compose.designsystem.organism.banner.inline.InfoBannerInlineNotificationCard
import app.k9mail.core.ui.compose.designsystem.organism.banner.inline.SuccessBannerInlineNotificationCard
import app.k9mail.core.ui.compose.designsystem.organism.banner.inline.WarningBannerInlineNotificationCard
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
fun LazyGridScope.bannerInline() {
sectionHeaderItem("Banner Inline")
errorBannerInline()
infoBannerInline()
warningBannerInline()
successBannerInline()
}
fun LazyGridScope.errorBannerInline() {
sectionSubtitleItem("Error")
fullSpanItem {
ErrorBannerInlineNotificationCard(
title = "Notification title",
supportingText = "Supporting text",
actions = {
NotificationActionButton(text = "View support article", onClick = {}, isExternalLink = true)
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}
fun LazyGridScope.infoBannerInline() {
sectionSubtitleItem("Information")
fullSpanItem {
InfoBannerInlineNotificationCard(
title = "Notification title",
supportingText = "Supporting text",
actions = {
NotificationActionButton(text = "Action 2", onClick = {})
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}
fun LazyGridScope.warningBannerInline() {
sectionSubtitleItem("Warning")
fullSpanItem {
WarningBannerInlineNotificationCard(
title = "Notification title",
supportingText = "Supporting text",
actions = {
NotificationActionButton(text = "Action 2", onClick = {})
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}
fun LazyGridScope.successBannerInline() {
sectionSubtitleItem("Success")
fullSpanItem {
SuccessBannerInlineNotificationCard(
title = "Notification title",
supportingText = "Supporting text",
actions = {
NotificationActionButton(text = "Action 2", onClick = {})
NotificationActionButton(text = "Action 1", onClick = {})
},
modifier = Modifier.padding(MainTheme.spacings.double),
)
}
}

View file

@ -0,0 +1,103 @@
package net.thunderbird.ui.catalog.ui.page.organism.items.dialogs
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.outlined.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonFilled
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodySmall
import app.k9mail.core.ui.compose.designsystem.organism.AlertDialog
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
internal fun LazyGridScope.alertDialogs() {
sectionHeaderItem("Alert dialogs")
sectionSubtitleItem("Simple dialog")
alertDialogItem(
title = "Simple dialog",
text = "This is a simple dialog",
)
sectionSubtitleItem("Dialog with icon")
alertDialogItem(
icon = Icons.Outlined.Info,
title = "Dialog with icon",
text = "This is a dialog with icon",
)
sectionSubtitleItem("Dialog with cancel")
alertDialogItem(
icon = Icons.Outlined.AccountCircle,
title = "Dialog with cancel",
text = "This is a dialog with cancel",
hasCancel = true,
)
sectionSubtitleItem("Dialog with custom content")
alertDialogItem(
title = "Dialog with custom content",
text = "This is a dialog with custom content",
) {
Column(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
TextBodyLarge("Large body")
TextBodyMedium("Medium body")
TextBodySmall("Small body")
}
}
}
private fun LazyGridScope.alertDialogItem(
title: String,
text: String,
icon: ImageVector? = null,
hasCancel: Boolean = false,
content: @Composable (() -> Unit)? = null,
) = defaultItem {
var showDialog by remember { mutableStateOf(false) }
ButtonFilled(
text = "Show dialog",
onClick = { showDialog = true },
modifier = Modifier.padding(defaultItemPadding()),
)
if (showDialog) {
if (content != null) {
AlertDialog(
title = title,
confirmText = "Accept",
onConfirmClick = { showDialog = false },
dismissText = if (hasCancel) "Cancel" else null,
onDismissClick = { showDialog = false },
onDismissRequest = { showDialog = false },
) {
content()
}
} else {
AlertDialog(
icon = icon,
title = title,
text = text,
confirmText = "Accept",
onConfirmClick = { showDialog = false },
dismissText = if (hasCancel) "Cancel" else null,
onDismissClick = { showDialog = false },
onDismissRequest = { showDialog = false },
)
}
}
}

View file

@ -0,0 +1,230 @@
package net.thunderbird.ui.catalog.ui.page.organism.items.dialogs
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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonFilled
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonText
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextHeadlineSmall
import app.k9mail.core.ui.compose.designsystem.organism.BasicDialog
import app.k9mail.core.ui.compose.theme2.MainTheme
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItemPadding
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
internal fun LazyGridScope.basicDialogs() {
sectionHeaderItem("Basic dialogs")
basicDialogContentAndButtonItem()
basicDialogContentButtonHeadlineItem()
basicDialogContentButtonHeadlineSupportingTextItem()
basicDialogWithDividers()
basicDialogComplexImplementation()
}
private fun LazyGridScope.basicDialogContentAndButtonItem() {
basicDialogItem(
sectionSubtitle = "Basic Dialog with content and buttons",
) {
BasicDialog(
onDismissRequest = { dismiss() },
content = {
TextBodyLarge("Dialog content")
},
buttons = { ButtonText(text = "Dismiss", onClick = { dismiss() }) },
contentPadding = PaddingValues(horizontal = MainTheme.spacings.triple),
)
}
}
private fun LazyGridScope.basicDialogContentButtonHeadlineItem() {
basicDialogItem(
sectionSubtitle = "Basic Dialog with content, buttons and headline as text",
) {
BasicDialog(
headlineText = "Headline text",
onDismissRequest = { dismiss() },
content = {
TextBodyLarge("Dialog content")
},
buttons = { ButtonText(text = "Dismiss", onClick = { dismiss() }) },
contentPadding = PaddingValues(horizontal = MainTheme.spacings.triple),
)
}
}
private fun LazyGridScope.basicDialogContentButtonHeadlineSupportingTextItem() {
basicDialogItem(
sectionSubtitle = "Basic Dialog with content, buttons, headline and supporting text",
) {
BasicDialog(
headlineText = "Headline text",
supportingText = "This is a supporting text",
onDismissRequest = { dismiss() },
content = {
TextBodyLarge("Dialog content")
},
buttons = { ButtonText(text = "Dismiss", onClick = { dismiss() }) },
contentPadding = PaddingValues(horizontal = MainTheme.spacings.triple),
)
}
}
private fun LazyGridScope.basicDialogWithDividers() {
basicDialogItem(
sectionSubtitle = "Basic Dialog with dividers",
) {
BasicDialog(
onDismissRequest = { dismiss() },
headlineText = "Headline text",
supportingText = "This is a supporting text",
content = {
TextBodyLarge("Dialog content")
},
buttons = { ButtonText(text = "Dismiss", onClick = { dismiss() }) },
contentPadding = PaddingValues(all = MainTheme.spacings.triple),
showDividers = true,
dividerColor = MainTheme.colors.primary,
)
}
}
private fun LazyGridScope.basicDialogComplexImplementation() {
basicDialogItem(
sectionSubtitle = "Complex Basic dialog building",
) {
BasicDialog(
onDismissRequest = { dismiss() },
content = { ComplexBasicDialogContent() },
buttons = {
ButtonText(text = "Cancel", onClick = { dismiss() })
ButtonText(text = "Accept", onClick = { dismiss() })
},
headline = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
modifier = Modifier.fillMaxWidth(),
) {
Icon(imageVector = Icons.Default.Refresh, contentDescription = null)
TextHeadlineSmall(text = "Reset settings?")
}
},
supportingText = {
TextBodyMedium(
text = "This will reset your app preferences back to their default settings. " +
"The following accounts will also be signed out:",
color = MainTheme.colors.onSurfaceVariant,
)
},
showDividers = true,
)
}
}
@Composable
private fun ComplexBasicDialogContent(modifier: Modifier = Modifier) {
Column(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
modifier = modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.triple,
end = MainTheme.spacings.triple,
),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
) {
Box(
modifier = Modifier
.size(MainTheme.sizes.iconAvatar)
.background(color = MainTheme.colors.primary, shape = CircleShape),
)
Text(text = "Account 1")
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
) {
Box(
modifier = Modifier
.size(MainTheme.sizes.iconAvatar)
.background(color = MainTheme.colors.primary, shape = CircleShape),
)
Text(text = "Account 2")
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
) {
Box(
modifier = Modifier
.size(MainTheme.sizes.iconAvatar)
.background(color = MainTheme.colors.primary, shape = CircleShape),
)
Text(text = "Account 3")
}
}
}
private fun LazyGridScope.basicDialogItem(
sectionSubtitle: String,
dialog: @Composable BasicDialogItemScope.() -> Unit,
) {
sectionSubtitleItem(sectionSubtitle)
defaultItem {
val scope = remember { BasicDialogItemScope() }
ButtonFilled(
text = "Show dialog",
onClick = { scope.show() },
modifier = Modifier.padding(defaultItemPadding()),
)
if (scope.showDialog.value) {
scope.dialog()
}
}
}
private interface BasicDialogItemScope {
val showDialog: State<Boolean>
fun show()
fun dismiss()
companion object {
operator fun invoke(): BasicDialogItemScope = object : BasicDialogItemScope {
private val _showDialog = mutableStateOf(false)
override val showDialog: State<Boolean> = _showDialog
override fun show() {
_showDialog.value = true
}
override fun dismiss() {
_showDialog.value = false
}
}
}
}

View file

@ -0,0 +1,423 @@
package net.thunderbird.ui.catalog.ui.page.organism.items.message
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Slider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelSmall
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleSmall
import app.k9mail.core.ui.compose.designsystem.atom.textfield.TextFieldOutlined
import app.k9mail.core.ui.compose.designsystem.molecule.input.CheckboxInput
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlin.math.roundToInt
import kotlin.random.Random
import kotlin.time.Clock
import kotlin.time.ExperimentalTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import net.thunderbird.core.ui.compose.designsystem.organism.message.ActiveMessageItem
import net.thunderbird.core.ui.compose.designsystem.organism.message.JunkMessageItem
import net.thunderbird.core.ui.compose.designsystem.organism.message.NewMessageItem
import net.thunderbird.core.ui.compose.designsystem.organism.message.ReadMessageItem
import net.thunderbird.core.ui.compose.designsystem.organism.message.UnreadMessageItem
import net.thunderbird.ui.catalog.ui.page.common.list.fullSpanItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionSubtitleItem
@OptIn(ExperimentalMaterial3Api::class)
fun LazyGridScope.messageItems() {
sectionHeaderItem("Message Item")
sectionSubtitleItem("Configuration")
fullSpanItem {
var config by remember {
mutableStateOf(
value = MessageItemConfiguration(
sender = "Sender Name",
subject = "The subject",
preview = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eleifend, leo at " +
"elementum luctus, felis nisl placerat enim, quis aliquam erat nibh gravida eros. Nunc ac " +
"elit mauris. Vivamus tristique, nisi eget rutrum condimentum, dui neque bibendum tortor, " +
"id fringilla nisi sem eget velit. In euismod leo luctus, tristique ante et, vulputate " +
"metus. Integer volutpat pulvinar dictum. Suspendisse et orci quis diam convallis accumsan " +
"in non justo.",
hideSection = false,
hideAvatar = false,
swapSenderAndSubject = false,
randomizeAttachment = false,
maxPreviewLines = 2,
),
)
}
Column {
MessageItemConfiguration(
config = config,
onSenderChange = { config = config.copy(sender = it) },
onSubjectChange = { config = config.copy(subject = it) },
onPreviewChange = { config = config.copy(preview = it) },
onHideSectionChange = { config = config.copy(hideSection = it) },
onHideAvatarChange = { config = config.copy(hideAvatar = it) },
onSwapSenderAndSubjectChange = { config = config.copy(swapSenderAndSubject = it) },
onRandomizeAttachmentChange = { config = config.copy(randomizeAttachment = it) },
onMaxPreviewLines = { config = config.copy(maxPreviewLines = it) },
)
DividerHorizontal(modifier = Modifier.padding(MainTheme.spacings.default))
CatalogMessageItems(config = config)
}
}
}
private data class MessageItemConfiguration(
val sender: String,
val subject: String,
val preview: String,
val hideSection: Boolean,
val hideAvatar: Boolean,
val swapSenderAndSubject: Boolean,
val randomizeAttachment: Boolean,
val maxPreviewLines: Int,
)
@Composable
private fun MessageItemConfiguration(
config: MessageItemConfiguration,
modifier: Modifier = Modifier,
onSenderChange: (String) -> Unit = {},
onSubjectChange: (String) -> Unit = {},
onPreviewChange: (String) -> Unit = {},
onHideSectionChange: (Boolean) -> Unit = {},
onHideAvatarChange: (Boolean) -> Unit = {},
onSwapSenderAndSubjectChange: (Boolean) -> Unit = {},
onRandomizeAttachmentChange: (Boolean) -> Unit = {},
onMaxPreviewLines: (Int) -> Unit = {},
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.half),
) {
CheckboxInput(
text = "Hide sections",
onCheckedChange = onHideSectionChange,
checked = config.hideSection,
)
CheckboxInput(
text = "Hide avatar",
onCheckedChange = onHideAvatarChange,
checked = config.hideAvatar,
)
CheckboxInput(
text = "Swap sender and subject",
onCheckedChange = onSwapSenderAndSubjectChange,
checked = config.swapSenderAndSubject,
)
CheckboxInput(
text = "Randomize attachment",
onCheckedChange = onRandomizeAttachmentChange,
checked = config.randomizeAttachment,
)
TextFieldOutlined(
value = config.sender,
label = "Sender",
onValueChange = onSenderChange,
isSingleLine = true,
modifier = Modifier.fillMaxWidth().padding(horizontal = MainTheme.spacings.double),
)
TextFieldOutlined(
value = config.subject,
label = "Subject",
onValueChange = onSubjectChange,
isSingleLine = true,
modifier = Modifier.fillMaxWidth().padding(horizontal = MainTheme.spacings.double),
)
TextFieldOutlined(
value = config.preview,
label = "Preview",
onValueChange = onPreviewChange,
isSingleLine = false,
modifier = Modifier.fillMaxWidth().padding(horizontal = MainTheme.spacings.double),
)
Column(modifier = Modifier.padding(horizontal = MainTheme.spacings.double)) {
TextLabelSmall(text = "Preview lines: ${config.maxPreviewLines}")
Slider(
value = config.maxPreviewLines.toFloat(),
onValueChange = { onMaxPreviewLines(it.roundToInt()) },
valueRange = 1f..6f,
steps = 6,
)
}
}
}
@Composable
private fun CatalogMessageItems(config: MessageItemConfiguration, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
CatalogNewMessageItem(config = config)
CatalogUnreadMessageItem(config = config)
CatalogReadMessageItem(config = config)
CatalogActiveMessageItem(config = config)
CatalogJunkMessageItem(config = config)
}
}
@Composable
private fun ColumnScope.CatalogNewMessageItem(config: MessageItemConfiguration) {
if (!config.hideSection) {
Section(text = "New Message", modifier = Modifier.padding(vertical = MainTheme.spacings.double))
}
var selected by remember { mutableStateOf(false) }
var favourite by remember { mutableStateOf(false) }
NewMessageItem(
sender = config.sender,
subject = config.subject,
preview = config.preview,
receivedAt = @OptIn(ExperimentalTime::class) Clock.System.now()
.toLocalDateTime(TimeZone.currentSystemDefault()),
favourite = favourite,
avatar = {
if (!config.hideAvatar) {
Avatar(
sender = config.sender,
enabled = !selected,
onClick = { selected = true },
)
}
},
onClick = {
if (selected) {
selected = false
}
},
onFavouriteChange = { favourite = it },
modifier = Modifier.fillMaxWidth(),
selected = selected,
swapSenderWithSubject = config.swapSenderAndSubject,
hasAttachments = remember(config.randomizeAttachment) {
if (config.randomizeAttachment) Random.nextBoolean() else false
},
maxPreviewLines = config.maxPreviewLines,
)
}
@Composable
private fun ColumnScope.CatalogUnreadMessageItem(config: MessageItemConfiguration) {
if (!config.hideSection) {
Section(text = "Unread Message", modifier = Modifier.padding(vertical = MainTheme.spacings.double))
}
var selected by remember { mutableStateOf(false) }
var favourite by remember { mutableStateOf(false) }
UnreadMessageItem(
sender = config.sender,
subject = config.subject,
preview = config.preview,
receivedAt = @OptIn(ExperimentalTime::class) Clock.System.now()
.toLocalDateTime(TimeZone.currentSystemDefault()),
favourite = favourite,
avatar = {
if (!config.hideAvatar) {
Avatar(
sender = config.sender,
enabled = !selected,
onClick = { selected = true },
)
}
},
onClick = {
if (selected) {
selected = false
}
},
onFavouriteChange = { favourite = it },
modifier = Modifier.fillMaxWidth(),
selected = selected,
swapSenderWithSubject = config.swapSenderAndSubject,
hasAttachments = remember(config.randomizeAttachment) {
if (config.randomizeAttachment) Random.nextBoolean() else false
},
maxPreviewLines = config.maxPreviewLines,
)
}
@Composable
private fun ColumnScope.CatalogReadMessageItem(config: MessageItemConfiguration) {
if (!config.hideSection) {
Section(text = "Read Message", modifier = Modifier.padding(vertical = MainTheme.spacings.double))
}
var selected by remember { mutableStateOf(false) }
var favourite by remember { mutableStateOf(false) }
ReadMessageItem(
sender = config.sender,
subject = config.subject,
preview = config.preview,
receivedAt = @OptIn(ExperimentalTime::class) Clock.System.now()
.toLocalDateTime(TimeZone.currentSystemDefault()),
favourite = favourite,
avatar = {
if (!config.hideAvatar) {
Avatar(
sender = config.sender,
enabled = !selected,
onClick = { selected = true },
)
}
},
onClick = {
if (selected) {
selected = false
}
},
onFavouriteChange = { favourite = it },
modifier = Modifier.fillMaxWidth(),
selected = selected,
swapSenderWithSubject = config.swapSenderAndSubject,
hasAttachments = remember(config.randomizeAttachment) {
if (config.randomizeAttachment) Random.nextBoolean() else false
},
maxPreviewLines = config.maxPreviewLines,
)
}
@Composable
private fun ColumnScope.CatalogActiveMessageItem(config: MessageItemConfiguration) {
if (!config.hideSection) {
Section(text = "Active Message", modifier = Modifier.padding(vertical = MainTheme.spacings.double))
}
var selected by remember { mutableStateOf(false) }
var favourite by remember { mutableStateOf(false) }
ActiveMessageItem(
sender = config.sender,
subject = config.subject,
preview = config.preview,
receivedAt = @OptIn(ExperimentalTime::class) Clock.System.now()
.toLocalDateTime(TimeZone.currentSystemDefault()),
favourite = favourite,
avatar = {
if (!config.hideAvatar) {
Avatar(
sender = config.sender,
enabled = !selected,
onClick = { selected = true },
)
}
},
onClick = {
if (selected) {
selected = false
}
},
onFavouriteChange = { favourite = it },
modifier = Modifier.fillMaxWidth(),
selected = selected,
swapSenderWithSubject = config.swapSenderAndSubject,
hasAttachments = remember(config.randomizeAttachment) {
if (config.randomizeAttachment) Random.nextBoolean() else false
},
maxPreviewLines = config.maxPreviewLines,
)
}
@Composable
private fun ColumnScope.CatalogJunkMessageItem(config: MessageItemConfiguration) {
if (!config.hideSection) {
Section(text = "Junk Message", modifier = Modifier.padding(vertical = MainTheme.spacings.double))
}
var selected by remember { mutableStateOf(false) }
JunkMessageItem(
sender = config.sender,
subject = config.subject,
preview = config.preview,
receivedAt = @OptIn(ExperimentalTime::class) Clock.System.now()
.toLocalDateTime(TimeZone.currentSystemDefault()),
avatar = {
if (!config.hideAvatar) {
Avatar(
sender = config.sender,
enabled = !selected,
onClick = { selected = true },
)
}
},
onClick = {
if (selected) {
selected = false
}
},
modifier = Modifier.fillMaxWidth(),
selected = selected,
swapSenderWithSubject = config.swapSenderAndSubject,
hasAttachments = remember(config.randomizeAttachment) {
if (config.randomizeAttachment) Random.nextBoolean() else false
},
maxPreviewLines = config.maxPreviewLines,
)
}
@Composable
private fun Section(text: String, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.double,
top = MainTheme.spacings.default,
end = MainTheme.spacings.double,
),
) {
TextTitleMedium(
text = text,
)
DividerHorizontal()
}
}
@Composable
private fun Avatar(
sender: String,
enabled: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.size(MainTheme.sizes.iconAvatar)
.clip(CircleShape)
.clickable(enabled = enabled, onClick = onClick)
.padding(MainTheme.spacings.half)
.background(
color = MainTheme.colors.primaryContainer.copy(alpha = 0.15f),
shape = CircleShape,
)
.border(width = 1.dp, color = MainTheme.colors.primary, shape = CircleShape),
) {
val monogram = remember(sender) {
val parts = sender.split(" ")
buildString {
append(parts.first().first())
if (parts.size > 1) {
append(parts.last().first())
}
}
}
TextTitleSmall(text = monogram, modifier = Modifier.align(Alignment.Center))
}
}

View file

@ -0,0 +1,30 @@
package net.thunderbird.ui.catalog.ui.page.template
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import kotlinx.collections.immutable.ImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract
import net.thunderbird.ui.catalog.ui.page.common.PagedContent
import net.thunderbird.ui.catalog.ui.page.template.CatalogTemplatePage.LAYOUT
import net.thunderbird.ui.catalog.ui.page.template.items.layoutItems
@Composable
fun CatalogTemplateContent(
pages: ImmutableList<CatalogTemplatePage>,
initialPage: CatalogTemplatePage,
onEvent: (CatalogPageContract.Event) -> Unit,
modifier: Modifier = Modifier,
) {
PagedContent(
pages = pages,
initialPage = initialPage,
modifier = modifier,
onRenderPage = {
when (it) {
LAYOUT -> layoutItems()
}
},
onRenderFullScreenPage = {},
onEvent = onEvent,
)
}

View file

@ -0,0 +1,20 @@
package net.thunderbird.ui.catalog.ui.page.template
import kotlinx.collections.immutable.toImmutableList
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.CatalogPage
enum class CatalogTemplatePage(
override val displayName: String,
override val isFullScreen: Boolean = false,
) : CatalogPage {
LAYOUT("Layouts"),
;
override fun toString(): String {
return displayName
}
companion object {
fun all() = entries.toImmutableList()
}
}

View file

@ -0,0 +1,23 @@
package net.thunderbird.ui.catalog.ui.page.template
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.common.mvi.observe
import net.thunderbird.ui.catalog.ui.page.CatalogPageContract.ViewModel
import net.thunderbird.ui.catalog.ui.page.CatalogPageViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
fun CatalogTemplateScreen(
modifier: Modifier = Modifier,
viewModel: ViewModel = koinViewModel<CatalogPageViewModel>(),
) {
val (state, dispatch) = viewModel.observe { }
CatalogTemplateContent(
pages = CatalogTemplatePage.Companion.all(),
initialPage = state.value.page as? CatalogTemplatePage ?: CatalogTemplatePage.LAYOUT,
onEvent = { dispatch(it) },
modifier = modifier,
)
}

View file

@ -0,0 +1,146 @@
package net.thunderbird.ui.catalog.ui.page.template.items
import android.os.Parcelable
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.Surface
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
import app.k9mail.core.ui.compose.designsystem.template.ListDetailPane
import app.k9mail.core.ui.compose.designsystem.template.rememberListDetailNavigationController
import app.k9mail.core.ui.compose.theme2.MainTheme
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import net.thunderbird.ui.catalog.ui.page.common.list.defaultItem
import net.thunderbird.ui.catalog.ui.page.common.list.sectionHeaderItem
fun LazyGridScope.layoutItems() {
sectionHeaderItem(text = "ListDetailPane")
defaultItem {
ListDetailPaneItem()
}
}
@Composable
private fun ListDetailPaneItem() {
val navigationController = rememberListDetailNavigationController<ListItem>()
val coroutineScope = rememberCoroutineScope()
Column(
modifier = Modifier
.fillMaxWidth()
.height(MainTheme.sizes.huger)
.padding(MainTheme.spacings.double),
horizontalAlignment = Alignment.CenterHorizontally,
) {
ListDetailPane(
navigationController = navigationController,
listPane = {
Surface(
color = MainTheme.colors.primaryContainer,
) {
Column(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
) {
TextTitleMedium("List pane")
LazyColumn(
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
itemsIndexed(createItems()) { index, item ->
ListItem(
item = item,
onClick = {
coroutineScope.launch {
navigationController.value.navigateToDetail(item)
}
},
)
}
}
}
}
},
detailPane = { item ->
Surface(
color = MainTheme.colors.secondaryContainer,
) {
ListDetail(
item = item,
onClick = {
coroutineScope.launch {
navigationController.value.navigateBack()
}
},
)
}
},
)
}
}
@Composable
private fun ListItem(
item: ListItem,
onClick: () -> Unit,
) {
Column(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(MainTheme.spacings.default),
) {
TextBodyLarge(item.title)
}
}
@Composable
private fun ListDetail(
item: ListItem,
onClick: () -> Unit,
) {
Column(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(MainTheme.spacings.default),
) {
TextTitleMedium("Detail pane")
Spacer(modifier = Modifier.height(MainTheme.spacings.default))
TextBodyLarge(item.title)
}
}
@Parcelize
internal data class ListItem(
val id: String,
val title: String,
) : Parcelable
private fun createItems(): List<ListItem> {
return listOf(
ListItem(
id = "1",
title = "Item 1",
),
ListItem(
id = "2",
title = "Item 2",
),
ListItem(
id = "3",
title = "Item 3",
),
)
}

View file

@ -0,0 +1,164 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="1024dp"
android:height="1024dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M439.5,260.2L439.6,260.2C466.3,167 582.5,121.5 700.9,121.5C782.7,121.5 856.2,147.4 906.5,188.5C875.3,190.1 845.7,196.2 818.5,206C859.2,221.1 894.2,244.4 920.1,273.2C902.9,270.2 885,268.7 866.8,268.7C864.8,268.7 862.8,268.7 860.9,268.7C907.9,337 935.5,419.7 935.5,508.9C935.5,742.8 745.9,932.4 512,932.4C281.7,932.4 88.5,739.5 88.5,508.9C88.5,472.4 93.3,434.8 102.7,399.5C105.2,392.1 108.6,385 113.2,382.4C118.9,379.2 124.1,388.9 125,392C131.2,415.3 139.5,437.9 149.8,459.5C148.9,411.1 169.6,367.1 198,329C217,303.6 234.5,280 242.6,212.1C243.2,207.6 247.5,204.3 251.9,205.7C313.5,225.9 346.5,328.6 341.4,414.4C375.4,419.3 375.3,383.7 375.3,383.7C364.4,350.2 371.6,288 439.4,260.2L439.5,260.2Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="396.6"
android:startY="288.4"
android:endX="832.5"
android:endY="817.9"
android:type="linear">
<item android:offset="0" android:color="#FF88CCFC"/>
<item android:offset="1" android:color="#FF590DF2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M921.6,401C931.9,633.9 740.5,834.8 507,834.8C288.4,834.8 109.3,665.8 93.1,451.4C90.2,471.2 88.7,491.4 88.5,512C90.2,741.5 282.9,932.4 512,932.4C745.9,932.4 935.5,742.8 935.5,508.9C935.5,471.6 930.7,435.5 921.6,401Z"
android:strokeAlpha="0.9"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="234.4"
android:centerY="452.7"
android:gradientRadius="358.1"
android:type="radial">
<item android:offset="0.5" android:color="#000B4186"/>
<item android:offset="1" android:color="#720B4186"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M502.2,291.3C497.6,283.2 476.5,271.3 467.3,269.2C502.2,157.6 679.7,123.3 788.4,143C833.6,151.3 889.9,175.9 907,188.7C856.7,147.6 783.2,121.7 701.4,121.7C583,121.7 466.7,167.2 440.1,260.4L440,260.4L439.9,260.4C372.1,288.2 364.9,350.5 375.8,383.9C386.2,344 436,295 502.2,291.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="587.8"
android:startY="332.8"
android:endX="442.3"
android:endY="231"
android:type="linear">
<item android:offset="0" android:color="#00D13AEF"/>
<item android:offset="1" android:color="#E5F9D286"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M624.7,214C529.6,232.7 498.5,238.8 466.7,269.1C502.4,174.4 593.7,155.3 702.5,198.4C672.5,204.6 646.9,209.6 624.7,214Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="307.2"
android:startY="473.7"
android:endX="616.4"
android:endY="164.5"
android:type="linear">
<item android:offset="0" android:color="#FF450FB0"/>
<item android:offset="0.8" android:color="#1E4610B1"/>
<item android:offset="1" android:color="#00450FB0"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M111.5,386.6C85.5,493 105.6,618 223.7,723C188.5,684.6 145.6,542.6 240.3,441.2C246.7,434.4 257.6,439.4 258,448.8C265.8,659.5 435.8,788.2 631.9,764.1C571.1,760.7 370.3,690.4 519.7,662.5C597.8,648 720.3,625.2 720.3,515.3C720.3,337.1 582.5,285 499,292.8C441.8,298.1 391,334.4 375.3,383.7C381.3,403.1 357.4,416.8 341.4,414.5C346.5,328.6 313.5,225.9 251.9,205.7C247.5,204.3 243.2,207.6 242.6,212.1C234.5,280 217,303.6 198,329C169.6,367.1 148.9,411.1 149.8,459.5C139.5,437.9 131.2,415.3 125,392C124.3,389.4 120.5,382.1 115.9,381.8C113.4,381.6 112.1,384 111.5,386.6Z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="352.3"
android:centerY="739.6"
android:gradientRadius="558.7"
android:type="radial">
<item android:offset="0" android:color="#FF650877"/>
<item android:offset="1" android:color="#00340B86"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M467.2,673.3C582.2,766.7 813.5,696.7 813.5,469.6C720.1,611.2 601.2,708.8 467.2,673.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="778.1"
android:startY="576.8"
android:endX="630.1"
android:endY="762.5"
android:type="linear">
<item android:offset="0" android:color="#00D647E2"/>
<item android:offset="1" android:color="#A3E7BB65"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M240.3,441.2C242.7,438.6 245.8,437.7 248.7,438.1C164,541.5 232.3,723.1 279.3,767.7C281.9,775.1 234.8,736.5 228.3,727.5C192.6,697.2 141.4,547.1 240.3,441.2Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="173.2"
android:startY="429.1"
android:endX="241.4"
android:endY="712.5"
android:type="linear">
<item android:offset="0.1" android:color="#FFF9BF86"/>
<item android:offset="1" android:color="#00D63AEF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M512,681.9C627,681.9 720.3,605.8 720.3,511.9C720.3,418.1 627,342 512,342C413.9,342 303.7,405.8 303.7,514.4C303.8,682.2 481,778.7 632.1,764.1C620.8,762.8 550,759 502.1,704.9C497.8,700 490.3,691.5 493.7,686C497.1,680.4 506.5,681.9 512,681.9Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="512"
android:startY="404.2"
android:endX="512"
android:endY="762.4"
android:type="linear">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="0.9" android:color="#FFBEE1FE"/>
<item android:offset="1" android:color="#FF96CEFD"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M541.2,257C563.5,249.9 561.6,227.9 561.6,227.9C561.6,227.9 550.4,214.8 528.3,222C507.7,228.8 504.4,243.6 504.4,243.6C504.4,243.6 515.7,265 541.2,257Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M512.5,440.5C465.2,423.4 438.7,420.8 391.5,440.5V582C438.7,567 465.3,566.7 512.5,582C564,573.1 589.7,572.9 629,582V440.5C586.8,424 560.8,426.3 512.5,440.5Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="510.3"
android:startY="426.7"
android:endX="510.3"
android:endY="582"
android:type="linear">
<item android:offset="0" android:color="#A3BCE0FD"/>
<item android:offset="1" android:color="#00D647E2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M445,561.3C454.5,561.3 463.8,562.3 472.9,564.4C481.9,566.6 490.9,569.8 499.8,574V454C491.5,449.1 482.7,445.5 473.3,443C464,440.6 454.5,439.4 445,439.4C437.7,439.4 430.4,440.1 423.2,441.5C416,442.9 409.1,445.1 402.4,447.9V568.6C409.5,566.1 416.5,564.3 423.5,563.1C430.5,561.9 437.7,561.3 445,561.3ZM524.2,574C533.1,569.8 542.1,566.6 551.1,564.4C560.2,562.3 569.5,561.3 579,561.3C586.3,561.3 593.5,561.9 600.5,563.1C607.5,564.3 614.5,566.1 621.6,568.6V447.9C614.9,445.1 608,442.9 600.8,441.5C593.6,440.1 586.3,439.4 579,439.4C569.5,439.4 560,440.6 550.7,443C541.3,445.5 532.5,449.1 524.2,454V574ZM512,610C502.3,602.3 491.7,596.3 480.3,592C469,587.8 457.2,585.6 445,585.6C436.5,585.6 428.1,586.7 419.9,589C411.7,591.2 403.8,594.4 396.3,598.4C392,600.7 387.9,600.6 383.9,598.1C380,595.7 378,592.1 378,587.5V440.6C378,438.4 378.6,436.2 379.7,434.2C380.8,432.2 382.5,430.6 384.7,429.6C394,424.8 403.8,421.1 413.9,418.7C424.1,416.2 434.4,415 445,415C456.8,415 468.3,416.5 479.6,419.6C490.8,422.6 501.6,427.2 512,433.3C522.4,427.2 533.2,422.6 544.4,419.6C555.7,416.5 567.2,415 579,415C589.6,415 599.9,416.2 610.1,418.7C620.2,421.1 630,424.8 639.3,429.6C641.5,430.6 643.2,432.2 644.3,434.2C645.4,436.2 646,438.4 646,440.6V587.5C646,592.1 644,595.7 640.1,598.1C636.1,600.6 632,600.7 627.7,598.4C620.2,594.4 612.3,591.2 604.1,589C595.9,586.7 587.5,585.6 579,585.6C566.8,585.6 555,587.8 543.7,592C532.3,596.3 521.7,602.3 512,610Z"
android:fillColor="#0768BA"
android:fillAlpha="0.3"/>
<path
android:pathData="M414.5,539.6H428.3L435.3,519.5H466.9L474.2,539.6H487.6L458.1,461H444.1L414.5,539.6ZM439.2,508.2L450.8,475.6H451.4L463,508.2H439.2ZM536.4,486.9V466.2C543.1,463.3 549.9,461.2 556.9,459.8C563.9,458.4 571.3,457.7 579,457.7C584.3,457.7 589.5,458.1 594.5,458.9C599.6,459.7 604.6,460.7 609.5,461.9V481.4C604.6,479.6 599.7,478.2 594.7,477.3C589.7,476.4 584.5,475.9 579,475.9C571.3,475.9 563.9,476.9 556.8,478.8C549.7,480.8 542.9,483.5 536.4,486.9ZM536.4,553.9V533.2C543.1,530.4 549.9,528.2 556.9,526.8C563.9,525.4 571.3,524.7 579,524.7C584.3,524.7 589.5,525.1 594.5,525.9C599.6,526.7 604.6,527.7 609.5,529V548.5C604.6,546.6 599.7,545.3 594.7,544.3C589.7,543.4 584.5,543 579,543C571.3,543 563.9,543.9 556.8,545.7C549.7,547.5 542.9,550.3 536.4,553.9ZM536.4,520.4V499.7C543.1,496.9 549.9,494.7 556.9,493.3C563.9,491.9 571.3,491.2 579,491.2C584.3,491.2 589.5,491.6 594.5,492.4C599.6,493.2 604.6,494.2 609.5,495.4V514.9C604.6,513.1 599.7,511.7 594.7,510.8C589.7,509.9 584.5,509.5 579,509.5C571.3,509.5 563.9,510.4 556.8,512.3C549.7,514.3 542.9,517 536.4,520.4ZM445,561.3C454.5,561.3 463.8,562.3 472.9,564.4C481.9,566.6 490.9,569.8 499.8,574V454C491.5,449.1 482.7,445.5 473.3,443C464,440.6 454.5,439.4 445,439.4C437.7,439.4 430.4,440.1 423.2,441.5C416,442.9 409.1,445.1 402.4,447.9V568.6C409.5,566.1 416.5,564.3 423.5,563.1C430.5,561.9 437.7,561.3 445,561.3ZM524.2,574C533.1,569.8 542.1,566.6 551.1,564.4C560.2,562.3 569.5,561.3 579,561.3C586.3,561.3 593.5,561.9 600.5,563.1C607.5,564.3 614.5,566.1 621.6,568.6V447.9C614.9,445.1 608,442.9 600.8,441.5C593.6,440.1 586.3,439.4 579,439.4C569.5,439.4 560,440.6 550.7,443C541.3,445.5 532.5,449.1 524.2,454V574ZM512,610C502.3,602.3 491.7,596.3 480.3,592C469,587.8 457.2,585.6 445,585.6C436.5,585.6 428.1,586.7 419.9,589C411.7,591.2 403.8,594.4 396.3,598.4C392,600.7 387.9,600.6 383.9,598.1C380,595.7 378,592.1 378,587.5V440.6C378,438.4 378.6,436.2 379.7,434.2C380.8,432.2 382.5,430.6 384.7,429.6C394,424.8 403.8,421.1 413.9,418.7C424.1,416.2 434.4,415 445,415C456.8,415 468.3,416.5 479.6,419.6C490.8,422.6 501.6,427.2 512,433.3C522.4,427.2 533.2,422.6 544.4,419.6C555.7,416.5 567.2,415 579,415C589.6,415 599.9,416.2 610.1,418.7C620.2,421.1 630,424.8 639.3,429.6C641.5,430.6 643.2,432.2 644.3,434.2C645.4,436.2 646,438.4 646,440.6V587.5C646,592.1 644,595.7 640.1,598.1C636.1,600.6 632,600.7 627.7,598.4C620.2,594.4 612.3,591.2 604.1,589C595.9,586.7 587.5,585.6 579,585.6C566.8,585.6 555,587.8 543.7,592C532.3,596.3 521.7,602.3 512,610Z"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:startX="472"
android:startY="430"
android:endX="555.5"
android:endY="629"
android:type="linear">
<item android:offset="0" android:color="#FF590DF2"/>
<item android:offset="0.5" android:color="#FF1373D9"/>
<item android:offset="1" android:color="#FFEF3ACC"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M435.3,517.5H433.8L433.4,518.8L426.8,537.6H417.4L445.5,463H456.7L484.7,537.6H475.6L468.8,518.8L468.3,517.5H466.9H435.3ZM437.3,507.6L436.4,510.2H439.2H463H465.8L464.9,507.6L453.3,475L452.8,473.6H451.4H450.8H449.4L448.9,475L437.3,507.6ZM499,575.9L501.8,577.2V574V454V452.9L500.8,452.3C492.3,447.3 483.3,443.6 473.8,441.1C464.3,438.6 454.7,437.4 445,437.4C437.6,437.4 430.2,438.1 422.8,439.5C415.5,441 408.4,443.2 401.6,446.1L400.4,446.6V447.9V568.6V571.4L403,570.5C410,568 417,566.2 423.9,565C430.8,563.8 437.8,563.3 445,563.3C454.4,563.3 463.5,564.3 472.4,566.4C481.3,568.5 490.1,571.6 499,575.9ZM522.2,574V577.2L525,575.9C533.9,571.6 542.7,568.5 551.6,566.4C560.5,564.3 569.6,563.3 579,563.3C586.2,563.3 593.2,563.8 600.1,565C607,566.2 614,568 621,570.5L623.6,571.4V568.6V447.9V446.6L622.4,446.1C615.6,443.2 608.5,441 601.2,439.5C593.8,438.1 586.4,437.4 579,437.4C569.3,437.4 559.7,438.6 550.2,441.1C540.7,443.6 531.7,447.3 523.2,452.3L522.2,452.9V454V574ZM385.5,431.4L385.6,431.4L385.6,431.4C394.8,426.6 404.4,423 414.4,420.6C424.4,418.2 434.6,417 445,417C456.6,417 467.9,418.5 479,421.5C490.1,424.5 500.8,429 511,435L512,435.6L513,435C523.2,429 533.9,424.5 545,421.5C556.1,418.5 567.4,417 579,417C589.4,417 599.6,418.2 609.6,420.6C619.6,423 629.2,426.6 638.4,431.4L638.4,431.4L638.5,431.4C640.3,432.3 641.7,433.5 642.6,435.2C643.5,436.9 644,438.7 644,440.6V587.5C644,591.4 642.4,594.3 639,596.4C635.7,598.5 632.3,598.6 628.7,596.7C621,592.5 613,589.3 604.6,587C596.3,584.8 587.7,583.6 579,583.6C566.6,583.6 554.6,585.8 543,590.2C531.9,594.3 521.6,600.1 512,607.5C502.4,600.1 492.1,594.3 481,590.2C469.4,585.8 457.4,583.6 445,583.6C436.3,583.6 427.7,584.8 419.4,587C411,589.3 403,592.5 395.3,596.7C391.7,598.6 388.3,598.5 385,596.4C381.6,594.3 380,591.4 380,587.5V440.6C380,438.7 380.5,436.9 381.4,435.2C382.3,433.5 383.7,432.3 385.5,431.4ZM607.5,463.5V478.6C603.4,477.2 599.2,476.1 595,475.3C589.9,474.4 584.6,473.9 579,473.9C571.1,473.9 563.5,474.9 556.2,476.9C550.1,478.6 544.1,480.8 538.4,483.6V467.5C544.6,465 550.9,463.1 557.3,461.7C564.2,460.4 571.4,459.7 579,459.7C584.2,459.7 589.2,460.1 594.2,460.9C598.7,461.6 603.1,462.5 607.5,463.5ZM607.5,530.5V545.6C603.4,544.2 599.2,543.1 595,542.4C589.9,541.4 584.6,541 579,541C571.1,541 563.5,541.9 556.3,543.8C550.1,545.4 544.1,547.6 538.4,550.6V534.6C544.6,532 550.9,530.1 557.3,528.8C564.2,527.4 571.4,526.7 579,526.7C584.2,526.7 589.2,527.1 594.2,527.9C598.7,528.6 603.1,529.5 607.5,530.5ZM607.5,497V512.1C603.4,510.7 599.2,509.6 595,508.9C589.9,507.9 584.6,507.5 579,507.5C571.1,507.5 563.5,508.4 556.2,510.4C550.1,512.1 544.1,514.3 538.4,517.2V501C544.6,498.5 550.9,496.6 557.3,495.3C564.2,493.9 571.4,493.2 579,493.2C584.2,493.2 589.2,493.6 594.2,494.4C598.7,495.1 603.1,496 607.5,497Z"
android:strokeAlpha="0.2"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#0768BA"/>
</vector>

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1024dp"
android:height="1024dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M440.1,249.7L440,249.7L439.9,249.7C372.1,277.6 364.9,339.8 375.8,373.2C375.8,373.2 375.8,373.5 375.8,374C415,346.4 465.1,331.5 512.5,331.5C627.5,331.5 720.8,407.6 720.8,501.4C720.8,595.3 627.5,671.4 512.5,671.4C511.6,671.4 510.6,671.4 509.6,671.3L509.6,671.3C504.1,671.1 497.1,670.8 494.2,675.5C490.8,681 498.3,689.5 502.6,694.4C547.6,745.3 612.8,751.7 630,753.4C631.1,753.5 632,753.6 632.6,753.6C481.5,768.3 304.3,671.7 304.2,503.9C304.2,464.4 318.8,430.8 342.1,404C342,404 342,404 341.9,404C347,318.1 314,215.4 252.4,195.2C248,193.8 243.7,197.1 243.1,201.6C235,269.6 217.5,293.1 198.5,318.5C170.1,356.6 149.4,400.6 150.3,449C140,427.4 131.7,404.9 125.5,381.6C124.6,378.4 119.4,368.7 113.7,372C109.1,374.6 105.7,381.7 103.2,389.1C93.8,424.3 89,462 89,498.4C89,729.1 282.2,921.9 512.5,921.9C746.4,921.9 936,732.3 936,498.4C936,409.2 908.4,326.5 861.4,258.2C863.3,258.2 865.3,258.2 867.3,258.2C885.5,258.2 903.4,259.7 920.6,262.7C894.7,233.9 859.7,210.7 819,195.5C846.2,185.7 875.8,179.6 907,178C856.7,136.9 783.2,111 701.4,111C583,111 466.8,156.5 440.1,249.7ZM562.1,217.4C562.1,217.4 564,239.5 541.7,246.5C516.2,254.5 504.9,233.1 504.9,233.1C504.9,233.1 508.2,218.4 528.8,211.6C550.9,204.3 562.1,217.4 562.1,217.4Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
<path
android:pathData="M414.5,543.6H428.3L435.3,523.5H466.9L474.2,543.6H487.6L458.1,465H444.1L414.5,543.6ZM439.2,512.2L450.8,479.6H451.4L463,512.2H439.2ZM536.4,490.9V470.2C543.1,467.3 549.9,465.2 556.9,463.8C563.9,462.4 571.3,461.7 579,461.7C584.3,461.7 589.5,462.1 594.5,462.9C599.6,463.7 604.6,464.7 609.5,465.9V485.4C604.6,483.6 599.7,482.2 594.7,481.3C589.7,480.4 584.5,479.9 579,479.9C571.3,479.9 563.9,480.9 556.8,482.8C549.7,484.8 542.9,487.5 536.4,490.9ZM536.4,557.9V537.2C543.1,534.4 549.9,532.2 556.9,530.8C563.9,529.4 571.3,528.7 579,528.7C584.3,528.7 589.5,529.1 594.5,529.9C599.6,530.7 604.6,531.7 609.5,533V552.5C604.6,550.6 599.7,549.3 594.7,548.3C589.7,547.4 584.5,547 579,547C571.3,547 563.9,547.9 556.8,549.7C549.7,551.5 542.9,554.3 536.4,557.9ZM536.4,524.4V503.7C543.1,500.9 549.9,498.7 556.9,497.3C563.9,495.9 571.3,495.2 579,495.2C584.3,495.2 589.5,495.6 594.5,496.4C599.6,497.2 604.6,498.2 609.5,499.4V518.9C604.6,517.1 599.7,515.7 594.7,514.8C589.7,513.9 584.5,513.5 579,513.5C571.3,513.5 563.9,514.4 556.8,516.3C549.7,518.3 542.9,521 536.4,524.4ZM445,565.3C454.5,565.3 463.8,566.3 472.9,568.4C481.9,570.6 490.9,573.8 499.8,578V458C491.5,453.1 482.7,449.5 473.3,447C464,444.6 454.5,443.4 445,443.4C437.7,443.4 430.4,444.1 423.2,445.5C416,446.9 409.1,449.1 402.4,451.9V572.6C409.5,570.1 416.5,568.3 423.5,567.1C430.5,565.9 437.7,565.3 445,565.3ZM524.2,578C533.1,573.8 542.1,570.6 551.1,568.4C560.2,566.3 569.5,565.3 579,565.3C586.3,565.3 593.5,565.9 600.5,567.1C607.5,568.3 614.5,570.1 621.6,572.6V451.9C614.9,449.1 608,446.9 600.8,445.5C593.6,444.1 586.3,443.4 579,443.4C569.5,443.4 560,444.6 550.7,447C541.3,449.5 532.5,453.1 524.2,458V578ZM512,614C502.3,606.3 491.7,600.3 480.3,596C469,591.8 457.2,589.6 445,589.6C436.5,589.6 428.1,590.7 419.9,593C411.7,595.2 403.8,598.4 396.3,602.4C392,604.7 387.9,604.6 383.9,602.1C380,599.7 378,596.1 378,591.5V444.6C378,442.4 378.6,440.2 379.7,438.2C380.8,436.2 382.5,434.6 384.7,433.6C394,428.8 403.8,425.1 413.9,422.7C424.1,420.2 434.4,419 445,419C456.8,419 468.3,420.5 479.6,423.6C490.8,426.6 501.6,431.2 512,437.3C522.4,431.2 533.2,426.6 544.4,423.6C555.7,420.5 567.2,419 579,419C589.6,419 599.9,420.2 610.1,422.7C620.2,425.1 630,428.8 639.3,433.6C641.5,434.6 643.2,436.2 644.3,438.2C645.4,440.2 646,442.4 646,444.6V591.5C646,596.1 644,599.7 640.1,602.1C636.1,604.6 632,604.7 627.7,602.4C620.2,598.4 612.3,595.2 604.1,593C595.9,590.7 587.5,589.6 579,589.6C566.8,589.6 555,591.8 543.7,596C532.3,600.3 521.7,606.3 512,614Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,164 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="1024dp"
android:height="1024dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M439.5,260.2L439.6,260.2C466.3,167 582.5,121.5 700.9,121.5C782.7,121.5 856.2,147.4 906.5,188.5C875.3,190.1 845.7,196.2 818.5,206C859.2,221.1 894.2,244.4 920.1,273.2C902.9,270.2 885,268.7 866.8,268.7C864.8,268.7 862.8,268.7 860.9,268.7C907.9,337 935.5,419.7 935.5,508.9C935.5,742.8 745.9,932.4 512,932.4C281.7,932.4 88.5,739.5 88.5,508.9C88.5,472.4 93.3,434.8 102.7,399.5C105.2,392.1 108.6,385 113.2,382.4C118.9,379.2 124.1,388.9 125,392C131.2,415.3 139.5,437.9 149.8,459.5C148.9,411.1 169.6,367.1 198,329C217,303.6 234.5,280 242.6,212.1C243.2,207.6 247.5,204.3 251.9,205.7C313.5,225.9 346.5,328.6 341.4,414.4C375.4,419.3 375.3,383.7 375.3,383.7C364.4,350.2 371.6,288 439.4,260.2L439.5,260.2Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="396.6"
android:startY="288.4"
android:endX="832.5"
android:endY="817.9"
android:type="linear">
<item android:offset="0" android:color="#FF88CCFC"/>
<item android:offset="1" android:color="#FF590DF2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M921.6,401C931.9,633.9 740.5,834.8 507,834.8C288.4,834.8 109.3,665.8 93.1,451.4C90.2,471.2 88.7,491.4 88.5,512C90.2,741.5 282.9,932.4 512,932.4C745.9,932.4 935.5,742.8 935.5,508.9C935.5,471.6 930.7,435.5 921.6,401Z"
android:strokeAlpha="0.9"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="234.4"
android:centerY="452.7"
android:gradientRadius="358.1"
android:type="radial">
<item android:offset="0.5" android:color="#000B4186"/>
<item android:offset="1" android:color="#720B4186"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M502.2,291.3C497.6,283.2 476.5,271.3 467.3,269.2C502.2,157.6 679.7,123.3 788.4,143C833.6,151.3 889.9,175.9 907,188.7C856.7,147.6 783.2,121.7 701.4,121.7C583,121.7 466.7,167.2 440.1,260.4L440,260.4L439.9,260.4C372.1,288.2 364.9,350.5 375.8,383.9C386.2,344 436,295 502.2,291.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="587.8"
android:startY="332.8"
android:endX="442.3"
android:endY="231"
android:type="linear">
<item android:offset="0" android:color="#00D13AEF"/>
<item android:offset="1" android:color="#E5F9D286"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M624.7,214C529.6,232.7 498.5,238.8 466.7,269.1C502.4,174.4 593.7,155.3 702.5,198.4C672.5,204.6 646.9,209.6 624.7,214Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="307.2"
android:startY="473.7"
android:endX="616.4"
android:endY="164.5"
android:type="linear">
<item android:offset="0" android:color="#FF450FB0"/>
<item android:offset="0.8" android:color="#1E4610B1"/>
<item android:offset="1" android:color="#00450FB0"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M111.5,386.6C85.5,493 105.6,618 223.7,723C188.5,684.6 145.6,542.6 240.3,441.2C246.7,434.4 257.6,439.4 258,448.8C265.8,659.5 435.8,788.2 631.9,764.1C571.1,760.7 370.3,690.4 519.7,662.5C597.8,648 720.3,625.2 720.3,515.3C720.3,337.1 582.5,285 499,292.8C441.8,298.1 391,334.4 375.3,383.7C381.3,403.1 357.4,416.8 341.4,414.5C346.5,328.6 313.5,225.9 251.9,205.7C247.5,204.3 243.2,207.6 242.6,212.1C234.5,280 217,303.6 198,329C169.6,367.1 148.9,411.1 149.8,459.5C139.5,437.9 131.2,415.3 125,392C124.3,389.4 120.5,382.1 115.9,381.8C113.4,381.6 112.1,384 111.5,386.6Z">
<aapt:attr name="android:fillColor">
<gradient
android:centerX="352.3"
android:centerY="739.6"
android:gradientRadius="558.7"
android:type="radial">
<item android:offset="0" android:color="#FF650877"/>
<item android:offset="1" android:color="#00340B86"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M467.2,673.3C582.2,766.7 813.5,696.7 813.5,469.6C720.1,611.2 601.2,708.8 467.2,673.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="778.1"
android:startY="576.8"
android:endX="630.1"
android:endY="762.5"
android:type="linear">
<item android:offset="0" android:color="#00D647E2"/>
<item android:offset="1" android:color="#A3E7BB65"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M240.3,441.2C242.7,438.6 245.8,437.7 248.7,438.1C164,541.5 232.3,723.1 279.3,767.7C281.9,775.1 234.8,736.5 228.3,727.5C192.6,697.2 141.4,547.1 240.3,441.2Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="173.2"
android:startY="429.1"
android:endX="241.4"
android:endY="712.5"
android:type="linear">
<item android:offset="0.1" android:color="#FFF9BF86"/>
<item android:offset="1" android:color="#00D63AEF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M512,681.9C627,681.9 720.3,605.8 720.3,511.9C720.3,418.1 627,342 512,342C413.9,342 303.7,405.8 303.7,514.4C303.8,682.2 481,778.7 632.1,764.1C620.8,762.8 550,759 502.1,704.9C497.8,700 490.3,691.5 493.7,686C497.1,680.4 506.5,681.9 512,681.9Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="512"
android:startY="404.2"
android:endX="512"
android:endY="762.4"
android:type="linear">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="0.9" android:color="#FFBEE1FE"/>
<item android:offset="1" android:color="#FF96CEFD"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M541.2,257C563.5,249.9 561.6,227.9 561.6,227.9C561.6,227.9 550.4,214.8 528.3,222C507.7,228.8 504.4,243.6 504.4,243.6C504.4,243.6 515.7,265 541.2,257Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M512.5,440.5C465.2,423.4 438.7,420.8 391.5,440.5V582C438.7,567 465.3,566.7 512.5,582C564,573.1 589.7,572.9 629,582V440.5C586.8,424 560.8,426.3 512.5,440.5Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="510.3"
android:startY="426.7"
android:endX="510.3"
android:endY="582"
android:type="linear">
<item android:offset="0" android:color="#A3BCE0FD"/>
<item android:offset="1" android:color="#00D647E2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M445,561.3C454.5,561.3 463.8,562.3 472.9,564.4C481.9,566.6 490.9,569.8 499.8,574V454C491.5,449.1 482.7,445.5 473.3,443C464,440.6 454.5,439.4 445,439.4C437.7,439.4 430.4,440.1 423.2,441.5C416,442.9 409.1,445.1 402.4,447.9V568.6C409.5,566.1 416.5,564.3 423.5,563.1C430.5,561.9 437.7,561.3 445,561.3ZM524.2,574C533.1,569.8 542.1,566.6 551.1,564.4C560.2,562.3 569.5,561.3 579,561.3C586.3,561.3 593.5,561.9 600.5,563.1C607.5,564.3 614.5,566.1 621.6,568.6V447.9C614.9,445.1 608,442.9 600.8,441.5C593.6,440.1 586.3,439.4 579,439.4C569.5,439.4 560,440.6 550.7,443C541.3,445.5 532.5,449.1 524.2,454V574ZM512,610C502.3,602.3 491.7,596.3 480.3,592C469,587.8 457.2,585.6 445,585.6C436.5,585.6 428.1,586.7 419.9,589C411.7,591.2 403.8,594.4 396.3,598.4C392,600.7 387.9,600.6 383.9,598.1C380,595.7 378,592.1 378,587.5V440.6C378,438.4 378.6,436.2 379.7,434.2C380.8,432.2 382.5,430.6 384.7,429.6C394,424.8 403.8,421.1 413.9,418.7C424.1,416.2 434.4,415 445,415C456.8,415 468.3,416.5 479.6,419.6C490.8,422.6 501.6,427.2 512,433.3C522.4,427.2 533.2,422.6 544.4,419.6C555.7,416.5 567.2,415 579,415C589.6,415 599.9,416.2 610.1,418.7C620.2,421.1 630,424.8 639.3,429.6C641.5,430.6 643.2,432.2 644.3,434.2C645.4,436.2 646,438.4 646,440.6V587.5C646,592.1 644,595.7 640.1,598.1C636.1,600.6 632,600.7 627.7,598.4C620.2,594.4 612.3,591.2 604.1,589C595.9,586.7 587.5,585.6 579,585.6C566.8,585.6 555,587.8 543.7,592C532.3,596.3 521.7,602.3 512,610Z"
android:fillColor="#0768BA"
android:fillAlpha="0.3"/>
<path
android:pathData="M414.5,539.6H428.3L435.3,519.5H466.9L474.2,539.6H487.6L458.1,461H444.1L414.5,539.6ZM439.2,508.2L450.8,475.6H451.4L463,508.2H439.2ZM536.4,486.9V466.2C543.1,463.3 549.9,461.2 556.9,459.8C563.9,458.4 571.3,457.7 579,457.7C584.3,457.7 589.5,458.1 594.5,458.9C599.6,459.7 604.6,460.7 609.5,461.9V481.4C604.6,479.6 599.7,478.2 594.7,477.3C589.7,476.4 584.5,475.9 579,475.9C571.3,475.9 563.9,476.9 556.8,478.8C549.7,480.8 542.9,483.5 536.4,486.9ZM536.4,553.9V533.2C543.1,530.4 549.9,528.2 556.9,526.8C563.9,525.4 571.3,524.7 579,524.7C584.3,524.7 589.5,525.1 594.5,525.9C599.6,526.7 604.6,527.7 609.5,529V548.5C604.6,546.6 599.7,545.3 594.7,544.3C589.7,543.4 584.5,543 579,543C571.3,543 563.9,543.9 556.8,545.7C549.7,547.5 542.9,550.3 536.4,553.9ZM536.4,520.4V499.7C543.1,496.9 549.9,494.7 556.9,493.3C563.9,491.9 571.3,491.2 579,491.2C584.3,491.2 589.5,491.6 594.5,492.4C599.6,493.2 604.6,494.2 609.5,495.4V514.9C604.6,513.1 599.7,511.7 594.7,510.8C589.7,509.9 584.5,509.5 579,509.5C571.3,509.5 563.9,510.4 556.8,512.3C549.7,514.3 542.9,517 536.4,520.4ZM445,561.3C454.5,561.3 463.8,562.3 472.9,564.4C481.9,566.6 490.9,569.8 499.8,574V454C491.5,449.1 482.7,445.5 473.3,443C464,440.6 454.5,439.4 445,439.4C437.7,439.4 430.4,440.1 423.2,441.5C416,442.9 409.1,445.1 402.4,447.9V568.6C409.5,566.1 416.5,564.3 423.5,563.1C430.5,561.9 437.7,561.3 445,561.3ZM524.2,574C533.1,569.8 542.1,566.6 551.1,564.4C560.2,562.3 569.5,561.3 579,561.3C586.3,561.3 593.5,561.9 600.5,563.1C607.5,564.3 614.5,566.1 621.6,568.6V447.9C614.9,445.1 608,442.9 600.8,441.5C593.6,440.1 586.3,439.4 579,439.4C569.5,439.4 560,440.6 550.7,443C541.3,445.5 532.5,449.1 524.2,454V574ZM512,610C502.3,602.3 491.7,596.3 480.3,592C469,587.8 457.2,585.6 445,585.6C436.5,585.6 428.1,586.7 419.9,589C411.7,591.2 403.8,594.4 396.3,598.4C392,600.7 387.9,600.6 383.9,598.1C380,595.7 378,592.1 378,587.5V440.6C378,438.4 378.6,436.2 379.7,434.2C380.8,432.2 382.5,430.6 384.7,429.6C394,424.8 403.8,421.1 413.9,418.7C424.1,416.2 434.4,415 445,415C456.8,415 468.3,416.5 479.6,419.6C490.8,422.6 501.6,427.2 512,433.3C522.4,427.2 533.2,422.6 544.4,419.6C555.7,416.5 567.2,415 579,415C589.6,415 599.9,416.2 610.1,418.7C620.2,421.1 630,424.8 639.3,429.6C641.5,430.6 643.2,432.2 644.3,434.2C645.4,436.2 646,438.4 646,440.6V587.5C646,592.1 644,595.7 640.1,598.1C636.1,600.6 632,600.7 627.7,598.4C620.2,594.4 612.3,591.2 604.1,589C595.9,586.7 587.5,585.6 579,585.6C566.8,585.6 555,587.8 543.7,592C532.3,596.3 521.7,602.3 512,610Z"
android:fillAlpha="0.9">
<aapt:attr name="android:fillColor">
<gradient
android:startX="472"
android:startY="430"
android:endX="555.5"
android:endY="629"
android:type="linear">
<item android:offset="0" android:color="#FF590DF2"/>
<item android:offset="0.5" android:color="#FF1373D9"/>
<item android:offset="1" android:color="#FFEF3ACC"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M435.3,517.5H433.8L433.4,518.8L426.8,537.6H417.4L445.5,463H456.7L484.7,537.6H475.6L468.8,518.8L468.3,517.5H466.9H435.3ZM437.3,507.6L436.4,510.2H439.2H463H465.8L464.9,507.6L453.3,475L452.8,473.6H451.4H450.8H449.4L448.9,475L437.3,507.6ZM499,575.9L501.8,577.2V574V454V452.9L500.8,452.3C492.3,447.3 483.3,443.6 473.8,441.1C464.3,438.6 454.7,437.4 445,437.4C437.6,437.4 430.2,438.1 422.8,439.5C415.5,441 408.4,443.2 401.6,446.1L400.4,446.6V447.9V568.6V571.4L403,570.5C410,568 417,566.2 423.9,565C430.8,563.8 437.8,563.3 445,563.3C454.4,563.3 463.5,564.3 472.4,566.4C481.3,568.5 490.1,571.6 499,575.9ZM522.2,574V577.2L525,575.9C533.9,571.6 542.7,568.5 551.6,566.4C560.5,564.3 569.6,563.3 579,563.3C586.2,563.3 593.2,563.8 600.1,565C607,566.2 614,568 621,570.5L623.6,571.4V568.6V447.9V446.6L622.4,446.1C615.6,443.2 608.5,441 601.2,439.5C593.8,438.1 586.4,437.4 579,437.4C569.3,437.4 559.7,438.6 550.2,441.1C540.7,443.6 531.7,447.3 523.2,452.3L522.2,452.9V454V574ZM385.5,431.4L385.6,431.4L385.6,431.4C394.8,426.6 404.4,423 414.4,420.6C424.4,418.2 434.6,417 445,417C456.6,417 467.9,418.5 479,421.5C490.1,424.5 500.8,429 511,435L512,435.6L513,435C523.2,429 533.9,424.5 545,421.5C556.1,418.5 567.4,417 579,417C589.4,417 599.6,418.2 609.6,420.6C619.6,423 629.2,426.6 638.4,431.4L638.4,431.4L638.5,431.4C640.3,432.3 641.7,433.5 642.6,435.2C643.5,436.9 644,438.7 644,440.6V587.5C644,591.4 642.4,594.3 639,596.4C635.7,598.5 632.3,598.6 628.7,596.7C621,592.5 613,589.3 604.6,587C596.3,584.8 587.7,583.6 579,583.6C566.6,583.6 554.6,585.8 543,590.2C531.9,594.3 521.6,600.1 512,607.5C502.4,600.1 492.1,594.3 481,590.2C469.4,585.8 457.4,583.6 445,583.6C436.3,583.6 427.7,584.8 419.4,587C411,589.3 403,592.5 395.3,596.7C391.7,598.6 388.3,598.5 385,596.4C381.6,594.3 380,591.4 380,587.5V440.6C380,438.7 380.5,436.9 381.4,435.2C382.3,433.5 383.7,432.3 385.5,431.4ZM607.5,463.5V478.6C603.4,477.2 599.2,476.1 595,475.3C589.9,474.4 584.6,473.9 579,473.9C571.1,473.9 563.5,474.9 556.2,476.9C550.1,478.6 544.1,480.8 538.4,483.6V467.5C544.6,465 550.9,463.1 557.3,461.7C564.2,460.4 571.4,459.7 579,459.7C584.2,459.7 589.2,460.1 594.2,460.9C598.7,461.6 603.1,462.5 607.5,463.5ZM607.5,530.5V545.6C603.4,544.2 599.2,543.1 595,542.4C589.9,541.4 584.6,541 579,541C571.1,541 563.5,541.9 556.3,543.8C550.1,545.4 544.1,547.6 538.4,550.6V534.6C544.6,532 550.9,530.1 557.3,528.8C564.2,527.4 571.4,526.7 579,526.7C584.2,526.7 589.2,527.1 594.2,527.9C598.7,528.6 603.1,529.5 607.5,530.5ZM607.5,497V512.1C603.4,510.7 599.2,509.6 595,508.9C589.9,507.9 584.6,507.5 579,507.5C571.1,507.5 563.5,508.4 556.2,510.4C550.1,512.1 544.1,514.3 538.4,517.2V501C544.6,498.5 550.9,496.6 557.3,495.3C564.2,493.9 571.4,493.2 579,493.2C584.2,493.2 589.2,493.6 594.2,494.4C598.7,495.1 603.1,496 607.5,497Z"
android:strokeAlpha="0.2"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#0768BA"/>
</vector>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon
xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/launcher_icon_background" />
<foreground>
<inset
android:drawable="@drawable/ic_app_logo"
android:inset="22%"
/>
</foreground>
<monochrome>
<inset
android:drawable="@drawable/ic_app_logo_monochrome"
android:inset="22%"
/>
</monochrome>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="launcher_icon_background">#F0F8FF</color>
</resources>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Material orange 300 -->
<color name="ic_launcher_logo_main">#FFB74D</color>
<!-- Material orange 700 -->
<color name="ic_launcher_logo_shadow">#F57C00</color>
<!-- Material orange 50 -->
<color name="ic_launcher_background">#FFF3E0</color>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Thunderbird Catalog</string>
</resources>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Thunderbird" parent="Theme.Material3.DayNight.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Thunderbird.Splashscreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_startup_logo</item>
<item name="windowSplashScreenAnimationDuration">1000</item>
<item name="postSplashScreenTheme">@style/Theme.Thunderbird</item>
</style>
</resources>