Add Android app project

This commit is contained in:
Severiano Jaramillo 2024-04-28 19:22:04 -07:00
parent 32bc268594
commit f2e7b13775
20 changed files with 619 additions and 0 deletions

68
app/build.gradle.kts Normal file
View file

@ -0,0 +1,68 @@
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
}
android {
namespace = "net.agorise.kee"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
applicationId = "net.agorise.kee"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation(projects.shared.preferences)
implementation(projects.shared.stargate)
implementation(platform(libs.compose.bom))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.ktx)
implementation(libs.compose.ui)
implementation(libs.compose.ui.graphics)
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.material3)
implementation(libs.voyager.navigator)
implementation(libs.voyager.screenmodel)
debugImplementation(libs.compose.ui.tooling)
debugImplementation(libs.compose.ui.test.manifest)
}

21
app/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,24 @@
<?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:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true">
<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,20 @@
package net.agorise.kee
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import cafe.adriel.voyager.navigator.Navigator
import net.agorise.kee.ui.screen.home.HomeScreen
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Navigator(HomeScreen())
}
}
}

View file

@ -0,0 +1,29 @@
package net.agorise.kee.ui.component.nodestatus
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import net.agorise.kee.ui.theme.KeeTheme
@Composable
fun NodeStatus(blockCount: Int? = null) {
val text = if (blockCount == null) {
"Not connected"
} else {
"Block #: $blockCount"
}
Text(text, color = MaterialTheme.colorScheme.onBackground)
}
@Preview
@Composable
private fun NodeStatusLightPreview() = KeeTheme(useDarkTheme = false) {
NodeStatus()
}
@Preview
@Composable
private fun NodeStatusDarkPreview() = KeeTheme(useDarkTheme = true) {
NodeStatus()
}

View file

@ -0,0 +1,38 @@
package net.agorise.kee.ui.component.topappbar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import net.agorise.kee.ui.theme.KeeTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun KeeTopAppBar(
title: String,
) {
TopAppBar(
colors = topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary
),
title = {
Text(title)
}
)
}
@Preview
@Composable
private fun TopAppBarLightPreview() = KeeTheme(useDarkTheme = false) {
KeeTopAppBar("Kee Wallet")
}
@Preview
@Composable
private fun TopAppBarDarkPreview() = KeeTheme(useDarkTheme = true) {
KeeTopAppBar("Kee Wallet")
}

View file

@ -0,0 +1,63 @@
package net.agorise.kee.ui.screen.home
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import net.agorise.kee.ui.component.topappbar.KeeTopAppBar
import net.agorise.kee.ui.screen.importaccount.ImportAccountScreen
import net.agorise.kee.ui.theme.KeeTheme
import net.agorise.shared.preferences.KeePreferences
class HomeScreen : Screen {
@Composable
override fun Content() = KeeTheme {
HomeScreenContent()
}
}
@Composable
private fun HomeScreenContent() {
val navigator = LocalNavigator.current
// Navigate to Import Account screen immediately if there is no active account
if (KeePreferences.isAccountActive().not()) {
navigator?.replace(ImportAccountScreen())
}
Scaffold(
topBar = { KeeTopAppBar("Kee Wallet") }
) { innerPadding ->
Column(
modifier = Modifier.padding(innerPadding).padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("Welcome to Kee")
Button(
onClick = { navigator?.replace(ImportAccountScreen()) }
) {
Text("Import Account")
}
}
}
}
@Preview
@Composable
private fun HomeScreenContentLightPreview() = KeeTheme(useDarkTheme = false) {
HomeScreenContent()
}
@Preview
@Composable
private fun HomeScreenContentDarkPreview() = KeeTheme(useDarkTheme = true) {
HomeScreenContent()
}

View file

@ -0,0 +1,96 @@
package net.agorise.kee.ui.screen.importaccount
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import net.agorise.kee.R
import net.agorise.kee.ui.component.nodestatus.NodeStatus
import net.agorise.kee.ui.component.topappbar.KeeTopAppBar
import net.agorise.kee.ui.screen.home.HomeScreen
import net.agorise.kee.ui.theme.KeeTheme
private const val RECOVERY_WORDS_COUNT = 25
class ImportAccountScreen : Screen {
@Composable
override fun Content() = KeeTheme {
val screenModel = rememberScreenModel { ImportAccountScreenModel() }
val state by screenModel.state.collectAsState()
ImportAccountScreenContent(state)
}
}
@Composable
private fun ImportAccountScreenContent(state: ImportAccountScreenModel.State) {
val navigator = LocalNavigator.current
Scaffold(
topBar = { KeeTopAppBar("Import Account") }
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
.padding(top = 16.dp, start = 16.dp, end = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
var recoveryText by remember { mutableStateOf("") }
val recoveryWords = recoveryText.split(" ").filter { it.isNotBlank() }
Icon(
painter = painterResource(R.drawable.ic_kee_logo),
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(top = 16.dp).size(80.dp)
)
Text("Enter your recovery words below")
OutlinedTextField(
value = recoveryText,
onValueChange = { recoveryText = it },
modifier = Modifier.fillMaxWidth(),
minLines = 5,
maxLines = 5,
supportingText = {
Text(
text = "${recoveryWords.count()}/${RECOVERY_WORDS_COUNT} words",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.End,
)
}
)
Button(
enabled = recoveryWords.size == RECOVERY_WORDS_COUNT,
modifier = Modifier.padding(top = 16.dp),
onClick = { navigator?.replace(HomeScreen()) }
) {
Text("Import Account")
}
Spacer(modifier = Modifier.weight(1f))
NodeStatus(state.blockCount)
}
}
}
@Preview
@Composable
private fun ImportAccountScreenContentLightPreview() = KeeTheme(useDarkTheme = false) {
ImportAccountScreenContent(ImportAccountScreenModel.State())
}
@Preview
@Composable
private fun ImportAccountScreenContentDarkPreview() = KeeTheme(useDarkTheme = true) {
ImportAccountScreenContent(ImportAccountScreenModel.State(blockCount = 265482))
}

View file

@ -0,0 +1,39 @@
package net.agorise.kee.ui.screen.importaccount
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import net.agorise.shared.stargate.StargateBridge
class ImportAccountScreenModel : StateScreenModel<ImportAccountScreenModel.State>(State()) {
data class State(
val blockCount: Int? = null,
)
private val stargateBridge = StargateBridge()
init {
screenModelScope.launch(Dispatchers.IO) {
stargateBridge.start()
}
listenForBlockCount()
}
private fun listenForBlockCount() {
stargateBridge.blockCountChannel.receiveAsFlow().onEach { blockCount ->
mutableState.value = state.value.copy(blockCount = blockCount)
}.launchIn(screenModelScope)
}
override fun onDispose() {
super.onDispose()
stargateBridge.stop()
}
}

View file

@ -0,0 +1,68 @@
package net.agorise.kee.ui.theme
import androidx.compose.ui.graphics.Color
// Color used to generate the theme using the Material Theme Builder tool
// val seed = Color(0xFF7F6289)
val md_theme_light_primary = Color(0xFF7E4895)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFF8D8FF)
val md_theme_light_onPrimaryContainer = Color(0xFF320047)
val md_theme_light_secondary = Color(0xFF69596D)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFF1DCF4)
val md_theme_light_onSecondaryContainer = Color(0xFF231728)
val md_theme_light_tertiary = Color(0xFF815250)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFDAD8)
val md_theme_light_onTertiaryContainer = Color(0xFF331111)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1E1B1E)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1E1B1E)
val md_theme_light_surfaceVariant = Color(0xFFEBDFEA)
val md_theme_light_onSurfaceVariant = Color(0xFF4C444D)
val md_theme_light_outline = Color(0xFF7D747D)
val md_theme_light_inverseOnSurface = Color(0xFFF6EFF3)
val md_theme_light_inverseSurface = Color(0xFF332F33)
val md_theme_light_inversePrimary = Color(0xFFEBB2FF)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF7E4895)
val md_theme_light_outlineVariant = Color(0xFFCEC3CD)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFFEBB2FF)
val md_theme_dark_onPrimary = Color(0xFF4B1763)
val md_theme_dark_primaryContainer = Color(0xFF64307B)
val md_theme_dark_onPrimaryContainer = Color(0xFFF8D8FF)
val md_theme_dark_secondary = Color(0xFFD4C0D7)
val md_theme_dark_onSecondary = Color(0xFF392C3D)
val md_theme_dark_secondaryContainer = Color(0xFF504255)
val md_theme_dark_onSecondaryContainer = Color(0xFFF1DCF4)
val md_theme_dark_tertiary = Color(0xFFF5B7B5)
val md_theme_dark_onTertiary = Color(0xFF4C2524)
val md_theme_dark_tertiaryContainer = Color(0xFF663B3A)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFDAD8)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1E1B1E)
val md_theme_dark_onBackground = Color(0xFFE8E0E5)
val md_theme_dark_surface = Color(0xFF1E1B1E)
val md_theme_dark_onSurface = Color(0xFFE8E0E5)
val md_theme_dark_surfaceVariant = Color(0xFF4C444D)
val md_theme_dark_onSurfaceVariant = Color(0xFFCEC3CD)
val md_theme_dark_outline = Color(0xFF978E97)
val md_theme_dark_inverseOnSurface = Color(0xFF1E1B1E)
val md_theme_dark_inverseSurface = Color(0xFFE8E0E5)
val md_theme_dark_inversePrimary = Color(0xFF7E4895)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFEBB2FF)
val md_theme_dark_outlineVariant = Color(0xFF4C444D)
val md_theme_dark_scrim = Color(0xFF000000)

View file

@ -0,0 +1,89 @@
package net.agorise.kee.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun KeeTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (!useDarkTheme) {
LightColors
} else {
DarkColors
}
MaterialTheme(
colorScheme = colors,
content = content
)
}

View file

@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="912"
android:viewportHeight="1053.1">
<group android:scaleX="0.49362832"
android:scaleY="0.57"
android:translateX="230.90547"
android:translateY="226.4165">
<path
android:pathData="M841.2,748.6v0.4l-385.2,222.4 -385.2,-222.4V304.2l385.2,-222.4 385.2,222.4v0.4l70.8,-40.9v-0.4L456,0 0,263.3v526.6l456,263.3 456,-263.3v-0.4l-70.8,-40.9z"
android:fillColor="@color/ic_launcher_foreground"/>
<path
android:pathData="M699.2,488H464.5a38.2,38.2 0,0 0,-13.4 2.5,140.8 140.8,0 1,0 0,72.3 38.2,38.2 0,0 0,13.4 2.5H553v46.9a38.6,38.6 0,0 0,77.2 0v-46.9h9.1v32.8a38.6,38.6 0,0 0,77.2 0v-37a38.6,38.6 0,0 0,-17.4 -73ZM315,599.3a72.7,72.7 0,1 1,72.7 -72.7A72.7,72.7 0,0 1,315 599.3Z"
android:fillColor="@color/ic_launcher_foreground"/>
</group>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="912dp"
android:height="1053.1dp"
android:viewportWidth="912"
android:viewportHeight="1053.1">
<path
android:pathData="M841.2,748.6v0.4l-385.2,222.4 -385.2,-222.4V304.2l385.2,-222.4 385.2,222.4v0.4l70.8,-40.9v-0.4L456,0 0,263.3v526.6l456,263.3 456,-263.3v-0.4l-70.8,-40.9z"
android:fillColor="#fff"/>
<path
android:pathData="M841.2,748.6v0.4l-385.2,222.4 -385.2,-222.4V304.2l385.2,-222.4 385.2,222.4v0.4l70.8,-40.9v-0.4L456,0 0,263.3v526.6l456,263.3 456,-263.3v-0.4l-70.8,-40.9z"
android:fillColor="#fff"/>
<path
android:pathData="M699.2,488H464.5a38.2,38.2 0,0 0,-13.4 2.5,140.8 140.8,0 1,0 0,72.3 38.2,38.2 0,0 0,13.4 2.5H553v46.9a38.6,38.6 0,0 0,77.2 0v-46.9h9.1v32.8a38.6,38.6 0,0 0,77.2 0v-37a38.6,38.6 0,0 0,-17.4 -73ZM315,599.3a72.7,72.7 0,1 1,72.7 -72.7A72.7,72.7 0,0 1,315 599.3Z"
android:fillColor="#fff"/>
</vector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<resources>
<!-- These colors are only used for the launcher icon -->
<color name="ic_launcher_foreground">#7E4895</color>
<color name="ic_launcher_background">#F8D8FF</color>
</resources>

View file

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

View file

@ -5,4 +5,5 @@ plugins {
alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.jetbrainsCompose) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
}

View file

@ -6,15 +6,24 @@ android-targetSdk = "34"
androidx-activityCompose = "1.8.2"
compose = "1.6.4"
compose-plugin = "1.6.1"
composeCompiler = "1.5.12"
composeBom = "2024.04.01"
coroutines = "1.8.0"
cryptography = "0.3.0"
kotlin = "1.9.23"
ktor = "2.3.9"
multiplatform-settings = "1.1.1"
voyager = "1.0.0"
coreKtx = "1.13.0"
[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activityCompose" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose" }
compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose" }
coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
@ -33,3 +42,4 @@ androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View file

@ -22,3 +22,4 @@ include(":composeApp")
include(":shared:crypto")
include(":shared:preferences")
include(":shared:stargate")
include(":app")