Add basic WebSocket connectivity with Dero node.

- Created a new stargate module that will contain the code to connect with the Dero nodes. This stargate module will use the ktor library, which is a multiplatform networking library that works on both Android and iOS.
- Added a bassic connectivity with a Dero node. It just connects with the node, listens for messages, and prints them out to the standard output.
- Introduced the ScreenModel library and created a basic ImportAccountScreenModel. ScreenModel classes serve to host all the logic for their corresponding screens.
- Added the Internet permssion to the Android app, to be able to connect with the internet.
- Removed the Desktop target. After discussing with Ken, we decided that investing on a Desktop app does not make sense. We will focus on Android and iOS only.
This commit is contained in:
Severiano Jaramillo 2024-03-25 21:14:16 -07:00
parent 34c82ba8f6
commit 8b9727082a
11 changed files with 141 additions and 65 deletions

View file

@ -1,5 +1,3 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
plugins { plugins {
alias(libs.plugins.kotlinMultiplatform) alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication) alias(libs.plugins.androidApplication)
@ -15,8 +13,6 @@ kotlin {
} }
} }
jvm("desktop")
listOf( listOf(
iosX64(), iosX64(),
iosArm64(), iosArm64(),
@ -29,26 +25,39 @@ kotlin {
} }
sourceSets { sourceSets {
val desktopMain by getting val androidMain by getting {
dependencies {
androidMain.dependencies { implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.ui.tooling.preview) implementation(libs.androidx.activity.compose)
implementation(libs.androidx.activity.compose) }
} }
commonMain.dependencies {
implementation(projects.shared.preferences)
implementation(compose.components.resources) val commonMain by getting {
implementation(compose.components.uiToolingPreview) commonMain.dependencies {
implementation(compose.foundation) implementation(projects.shared.preferences)
implementation(compose.material3) implementation(projects.shared.stargate)
implementation(compose.runtime)
implementation(compose.ui) implementation(compose.components.resources)
implementation(libs.voyager.navigator) implementation(compose.components.uiToolingPreview)
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1") implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.runtime)
implementation(compose.ui)
implementation(libs.coroutines.core)
implementation(libs.voyager.navigator)
implementation(libs.voyager.screenmodel)
}
} }
desktopMain.dependencies {
implementation(compose.desktop.currentOs) val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {}
} }
} }
} }
@ -86,15 +95,3 @@ android {
debugImplementation(libs.compose.ui.tooling) debugImplementation(libs.compose.ui.tooling)
} }
} }
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "net.agorise.kee"
packageVersion = "1.0.0"
}
}
}

View file

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"

View file

@ -7,6 +7,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import kee.composeapp.generated.resources.Res import kee.composeapp.generated.resources.Res
@ -22,9 +23,10 @@ import ui.theme.KeeTheme
private const val RECOVERY_WORDS_COUNT = 25 private const val RECOVERY_WORDS_COUNT = 25
class ImportAccountScreen : Screen { class ImportAccountScreen : Screen {
@Composable @Composable
override fun Content() = KeeTheme { override fun Content() = KeeTheme {
val screenModel = rememberScreenModel { ImportAccountScreenModel() }
ImportAccountScreenContent() ImportAccountScreenContent()
} }
} }
@ -33,7 +35,7 @@ class ImportAccountScreen : Screen {
@Composable @Composable
private fun ImportAccountScreenContent() { private fun ImportAccountScreenContent() {
val navigator = LocalNavigator.current val navigator = LocalNavigator.current
Scaffold( Scaffold(
topBar = { KeeTopAppBar("Import Account") } topBar = { KeeTopAppBar("Import Account") }
) { innerPadding -> ) { innerPadding ->

View file

@ -0,0 +1,23 @@
package net.agorise.kee.ui.screen.importaccount
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.launch
import net.agorise.shared.stargate.Bridge
class ImportAccountScreenModel : ScreenModel {
private val bridge = Bridge()
init {
screenModelScope.launch(Dispatchers.IO) {
bridge.start()
}
}
override fun onDispose() {
super.onDispose()
bridge.stop()
}
}

View file

@ -1,7 +0,0 @@
package net.agorise.kee
class JVMPlatform: Platform {
override val name: String = "Java ${System.getProperty("java.version")}"
}
actual fun getPlatform(): Platform = JVMPlatform()

View file

@ -1,20 +0,0 @@
package net.agorise.kee
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import cafe.adriel.voyager.navigator.Navigator
import net.agorise.kee.ui.screen.home.HomeScreen
fun main() = application {
val state = rememberWindowState(
size = DpSize(420.dp, 880.dp),
position = WindowPosition(300.dp, 300.dp)
)
Window(title = "Kee", onCloseRequest = ::exitApplication, state = state) {
Navigator(HomeScreen())
}
}

View file

@ -4,9 +4,11 @@ android-compileSdk = "34"
android-minSdk = "26" android-minSdk = "26"
android-targetSdk = "34" android-targetSdk = "34"
androidx-activityCompose = "1.8.2" androidx-activityCompose = "1.8.2"
compose = "1.6.2" compose = "1.6.4"
compose-plugin = "1.6.0" compose-plugin = "1.6.0"
coroutines = "1.8.0"
kotlin = "1.9.22" kotlin = "1.9.22"
ktor = "2.3.9"
multiplatform-settings = "1.1.1" multiplatform-settings = "1.1.1"
voyager = "1.0.0" voyager = "1.0.0"
@ -14,8 +16,13 @@ voyager = "1.0.0"
androidx-activity-compose = { group = "androidx.activity", name ="activity-compose", version.ref = "androidx-activityCompose" } androidx-activity-compose = { group = "androidx.activity", name ="activity-compose", version.ref = "androidx-activityCompose" }
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose" } 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" } compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" }
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }
ktor-client-websockets = { group = "io.ktor", name = "ktor-client-websockets", version.ref = "ktor" }
multiplatform-settings = { group="com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatform-settings" } multiplatform-settings = { group="com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatform-settings" }
voyager-navigator = { group = "cafe.adriel.voyager", name = "voyager-navigator", version.ref = "voyager" } voyager-navigator = { group = "cafe.adriel.voyager", name = "voyager-navigator", version.ref = "voyager" }
voyager-screenmodel = { group = "cafe.adriel.voyager", name = "voyager-screenmodel", version.ref = "voyager" }
[plugins] [plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" }

View file

@ -20,3 +20,4 @@ dependencyResolutionManagement {
include(":composeApp") include(":composeApp")
include(":shared:preferences") include(":shared:preferences")
include(":shared:stargate")

View file

@ -12,8 +12,6 @@ kotlin {
} }
} }
jvm()
iosX64() iosX64()
iosArm64() iosArm64()
iosSimulatorArm64() iosSimulatorArm64()

View file

@ -0,0 +1,40 @@
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
}
kotlin {
androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "11"
}
}
}
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
commonMain.dependencies {
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.websockets)
}
}
}
android {
namespace = "net.agorise.shared.stargate"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View file

@ -0,0 +1,33 @@
package net.agorise.shared.stargate
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.webSocket
import io.ktor.http.HttpMethod
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.coroutines.flow.receiveAsFlow
class Bridge {
private val client: HttpClient = HttpClient(CIO) { install(WebSockets) }
suspend fun start() {
client.webSocket(method = HttpMethod.Get, host = "node.derofoundation.org", port = 11012, path = "/ws") {
outputMessages()
}
}
fun stop() {
client.close()
}
private suspend fun DefaultClientWebSocketSession.outputMessages() {
incoming.receiveAsFlow().collect { message ->
(message as? Frame.Text)?.let { textFrame ->
println(textFrame.readText())
}
}
}
}