diff --git a/FloconAndroid/.vscode/settings.json b/FloconAndroid/.vscode/settings.json index c5f3f6b9c..e0f15db2e 100644 --- a/FloconAndroid/.vscode/settings.json +++ b/FloconAndroid/.vscode/settings.json @@ -1,3 +1,3 @@ { - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "automatic" } \ No newline at end of file diff --git a/FloconAndroid/AGENT.md b/FloconAndroid/AGENT.md new file mode 100644 index 000000000..de224d978 --- /dev/null +++ b/FloconAndroid/AGENT.md @@ -0,0 +1,52 @@ +# Flocon Project Overview + +Flocon is a modular, plugin-based framework built with **Kotlin Multiplatform (KMP)**. It provides a standardized way to integrate common cross-cutting concerns like networking, database access, datastores, and deep linking into applications. + +## 🚀 Key Features + +- **Modular Architecture**: Separate modules for different functionalities (network, database, etc.). +- **Plugin System**: Easily extensible with "no-op" variants for testing and modularity. +- **KMP Support**: Targets Android, iOS, JVM, and WasmJs. +- **Modern Tech Stack**: Uses Room 3, Ktor, OkHttp, gRPC, and Compose Multiplatform. + +## 🛠 Technical Stack + +- **Kotlin**: 2.1.0 +- **Build System**: Gradle with Version Catalog (`libs.versions.toml`). +- **Dependency Injection**: Manual / Constructor injection (based on current exploration). +- **Asynchronous Programming**: Kotlin Coroutines & Flow. +- **Networking**: Ktor 3.x, OkHttp 4.x, gRPC 1.70.x. +- **Database**: Room 2.x & Room 3.0.0-alpha01. +- **UI**: Compose Multiplatform 1.9.0. + +## 📂 Module Structure + +- `:flocon`: Core library providing the plugin registration and context management. +- `:database`: + - `:database:core`: Abstractions for database providers. + - `:database:room` / `:database:room3`: Room-based implementations. +- `:network`: + - `:network:core`: Core networking abstractions. + - `:network:okhttp-interceptor` / `:network:ktor-interceptor`: Client-specific interceptors. +- `:grpc`: + - `:grpc-interceptor`: Interceptors for gRPC calls. +- `:datastores`: Modules for Typed DataStore integration. +- `:deeplinks`: Abstractions and implementations for handling deep links. + +## 🧩 Core Concepts + +### Plugins +Flocon operates on a plugin-based architecture. Modules typically provide a "Core" or implementation module and a "No-Op" module. The No-Op modules allow the app to compile and run without the actual implementation, which is useful for specialized builds or testing. + +### Interceptors +For networking and gRPC, Flocon uses an interceptor-based approach to hook into the communication pipeline of standard libraries (OkHttp, Ktor, gRPC). + +## 📖 Development Guidelines + +- **Multiplatform First**: Always consider KMP compatibility when adding new features or modules. +- **Modularity**: Keep modules focused and avoid circular dependencies. +- **Naming Conventions**: Follow the `io.github.openflocon.flocon` package naming structure. +- **Version Catalog**: All dependency versions must be managed in `gradle/libs.versions.toml`. + +--- +*Created by Antigravity AI to assist in project understanding.* diff --git a/FloconAndroid/okhttp-interceptor-no-op/build.gradle.kts b/FloconAndroid/analytics-no-op/build.gradle.kts similarity index 74% rename from FloconAndroid/okhttp-interceptor-no-op/build.gradle.kts rename to FloconAndroid/analytics-no-op/build.gradle.kts index 897fd4fc9..1d040d352 100644 --- a/FloconAndroid/okhttp-interceptor-no-op/build.gradle.kts +++ b/FloconAndroid/analytics-no-op/build.gradle.kts @@ -1,13 +1,36 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { + alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" + alias(libs.plugins.vanniktech.maven.publish) +} + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "11" + } + } + } + + jvm() + + iosX64() + iosArm64() + iosSimulatorArm64() + + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + } + } + } } android { - namespace = "io.github.openflocon.flocon.okhttp" + namespace = "io.github.openflocon.flocon.analytics.noop" compileSdk = 36 defaultConfig { @@ -26,24 +49,12 @@ android { ) } } - compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } -dependencies { - implementation(platform(libs.okhttp.bom)) - implementation(libs.okhttp3.okhttp) -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - mavenPublishing { publishToMavenCentral(automaticRelease = true) @@ -55,12 +66,12 @@ mavenPublishing { coordinates( groupId = project.property("floconGroupId") as String, - artifactId = "flocon-okhttp-interceptor-no-op", + artifactId = "flocon-analytics-no-op", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) pom { - name = "Flocon OkHttp Interceptor" + name = "Flocon Analytics No-Op" description = project.property("floconDescription") as String inceptionYear = "2025" url = "https://github.com/openflocon/Flocon" @@ -84,4 +95,4 @@ mavenPublishing { developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" } } -} \ No newline at end of file +} diff --git a/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsNoOp.kt b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsNoOp.kt new file mode 100644 index 000000000..bd8ff2d81 --- /dev/null +++ b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsNoOp.kt @@ -0,0 +1,49 @@ +package io.github.openflocon.flocon.plugins.analytics + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsItem + +class FloconAnalyticsConfig : FloconPluginConfig + +interface FloconAnalyticsPlugin : FloconPlugin { + fun registerAnalytics(analyticsItems: List) +} + +object FloconAnalytics : FloconPluginFactory { + override val name: String = "Analytics" + override val pluginId: String = Protocol.ToDevice.Analytics.Plugin + override fun createConfig(context: FloconContext) = FloconAnalyticsConfig() + override fun install( + pluginConfig: FloconAnalyticsConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconAnalyticsPlugin { + return FloconAnalyticsNoOpImpl() + } +} + +internal class FloconAnalyticsNoOpImpl : FloconPlugin, FloconAnalyticsPlugin { + override val key: String + get() = Protocol.ToDevice.Analytics.Plugin + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } + + override fun registerAnalytics(analyticsItems: List) { + // no op + } +} diff --git a/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/builder/AnalyticsBuilder.kt b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/builder/AnalyticsBuilder.kt new file mode 100644 index 000000000..2ae74f610 --- /dev/null +++ b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/builder/AnalyticsBuilder.kt @@ -0,0 +1,17 @@ +package io.github.openflocon.flocon.plugins.analytics.builder + +import io.github.openflocon.flocon.plugins.analytics.FloconAnalyticsPlugin +import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsEvent + +class AnalyticsBuilder( + val analyticsTableId: String, + private val analyticsPlugin: FloconAnalyticsPlugin?, +) { + fun logEvents(vararg events: AnalyticsEvent) { + // no-op + } + + fun logEvents(events: List) { + // no-op + } +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsEvent.kt b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsEvent.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsEvent.kt rename to FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsEvent.kt index 051c7c277..b0de88ffa 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsEvent.kt +++ b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsEvent.kt @@ -8,4 +8,4 @@ data class AnalyticsEvent( eventName: String, vararg properties: AnalyticsPropertiesConfig, ) : this(eventName, properties.toList()) -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/TableItem.kt b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsItem.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/TableItem.kt rename to FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsItem.kt index f11507629..55c7285cd 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/TableItem.kt +++ b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsItem.kt @@ -6,4 +6,4 @@ data class AnalyticsItem( val eventName: String, val createdAt: Long, val properties: List, -) \ No newline at end of file +) diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsPropertiesConfig.kt b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsPropertiesConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsPropertiesConfig.kt rename to FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsPropertiesConfig.kt index 4d1af07f7..f36d72382 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsPropertiesConfig.kt +++ b/FloconAndroid/analytics-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/model/AnalyticsPropertiesConfig.kt @@ -8,4 +8,4 @@ data class AnalyticsPropertiesConfig( infix fun String.analyticsProperty(value: String) = AnalyticsPropertiesConfig( name = this, value = value, -) \ No newline at end of file +) diff --git a/FloconAndroid/analytics/build.gradle.kts b/FloconAndroid/analytics/build.gradle.kts new file mode 100644 index 000000000..dbdb7375e --- /dev/null +++ b/FloconAndroid/analytics/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.analytics" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-analytics", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/FloconAnalyticsPlugin.kt b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/FloconAnalyticsPlugin.kt new file mode 100644 index 000000000..dd06edf60 --- /dev/null +++ b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/FloconAnalyticsPlugin.kt @@ -0,0 +1,72 @@ +package io.github.openflocon.flocon.analytics + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.core.encode +import io.github.openflocon.flocon.analytics.model.AnalyticsItem + +class FloconAnalyticsConfig : FloconPluginConfig + +interface FloconAnalyticsPlugin : FloconPlugin { + fun registerAnalytics(analyticsItems: List) +} + +object FloconAnalytics : FloconPluginFactory { + override val name: String = "Analytics" + override val pluginId: String = Protocol.ToDevice.Analytics.Plugin + override fun createConfig(context: FloconContext) = FloconAnalyticsConfig() + override fun install( + pluginConfig: FloconAnalyticsConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconAnalyticsPlugin { + return FloconAnalyticsPluginImpl( + sender = floconConfig.client as FloconMessageSender, + encoder = encoder + ) + } +} + +internal class FloconAnalyticsPluginImpl( + private val sender: FloconMessageSender, + private val encoder: FloconEncoder +) : FloconPlugin, FloconAnalyticsPlugin { + override val key: String + get() = Protocol.ToDevice.Analytics.Plugin + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } + + override fun registerAnalytics(analyticsItems: List) { + sendAnalytics(analyticsItems) + } + + private fun sendAnalytics(analyticsItems: List) { + analyticsItems.takeIf { it.isNotEmpty() }?.forEach { toSend -> + try { + sender.send( + plugin = Protocol.FromDevice.Analytics.Plugin, + method = Protocol.FromDevice.Analytics.Method.AddItems, + body = encoder.encode(toSend) + ) + } catch (t: Throwable) { + FloconLogger.logError("error on sendAnalytics", t) + } + } + } +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/builder/AnalyticsBuilder.kt b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/builder/AnalyticsBuilder.kt similarity index 74% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/builder/AnalyticsBuilder.kt rename to FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/builder/AnalyticsBuilder.kt index 6b78860c3..4b2387e1d 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/builder/AnalyticsBuilder.kt +++ b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/builder/AnalyticsBuilder.kt @@ -1,10 +1,10 @@ @file:OptIn(ExperimentalUuidApi::class) -package io.github.openflocon.flocon.plugins.analytics.builder +package io.github.openflocon.flocon.analytics.builder -import io.github.openflocon.flocon.plugins.analytics.FloconAnalyticsPlugin -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsEvent -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsItem +import io.github.openflocon.flocon.analytics.FloconAnalyticsPlugin +import io.github.openflocon.flocon.analytics.model.AnalyticsEvent +import io.github.openflocon.flocon.analytics.model.AnalyticsItem import io.github.openflocon.flocon.utils.currentTimeMillis import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid @@ -29,4 +29,4 @@ class AnalyticsBuilder( } analyticsPlugin?.registerAnalytics(analyticsItems) } -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/mapper/AnalyticsItemsMapper.kt b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/mapper/AnalyticsItemsMapper.kt similarity index 59% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/mapper/AnalyticsItemsMapper.kt rename to FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/mapper/AnalyticsItemsMapper.kt index f93b42598..cb312861a 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/mapper/AnalyticsItemsMapper.kt +++ b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/mapper/AnalyticsItemsMapper.kt @@ -1,17 +1,7 @@ -package io.github.openflocon.flocon.plugins.analytics.mapper +package io.github.openflocon.flocon.analytics.mapper -import io.github.openflocon.flocon.core.FloconEncoder -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsItem +import io.github.openflocon.flocon.analytics.model.AnalyticsItem import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -internal fun analyticsItemsToJson(item: AnalyticsItem): String { - return FloconEncoder.json.encodeToString( - listOf( - item.toSerializable() - ) - ) -} @Serializable internal class AnalyticsItemSerializable( @@ -28,7 +18,7 @@ internal class AnalyticsPropertySerializable( val value: String, ) -internal fun AnalyticsItem.toSerializable(): AnalyticsItemSerializable { +internal fun AnalyticsItem.toRemote(): AnalyticsItemSerializable { return AnalyticsItemSerializable( id = id, analyticsTableId = analyticsTableId, @@ -41,4 +31,4 @@ internal fun AnalyticsItem.toSerializable(): AnalyticsItemSerializable { ) } ) -} \ No newline at end of file +} diff --git a/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsEvent.kt b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsEvent.kt new file mode 100644 index 000000000..0de6c0798 --- /dev/null +++ b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsEvent.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.analytics.model + +data class AnalyticsEvent( + val eventName: String, + val properties: List, +) { + constructor( + eventName: String, + vararg properties: AnalyticsPropertiesConfig, + ) : this(eventName, properties.toList()) +} diff --git a/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsItem.kt b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsItem.kt new file mode 100644 index 000000000..ba8c85753 --- /dev/null +++ b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsItem.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.analytics.model + +data class AnalyticsItem( + val id: String, + val analyticsTableId: String, + val eventName: String, + val createdAt: Long, + val properties: List, +) diff --git a/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsPropertiesConfig.kt b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsPropertiesConfig.kt new file mode 100644 index 000000000..e73421fbb --- /dev/null +++ b/FloconAndroid/analytics/src/commonMain/kotlin/io/github/openflocon/flocon/analytics/model/AnalyticsPropertiesConfig.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.analytics.model + +data class AnalyticsPropertiesConfig( + val name: String, + val value: String, +) + +infix fun String.analyticsProperty(value: String) = AnalyticsPropertiesConfig( + name = this, + value = value, +) diff --git a/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/AndroidConfig.kt b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/AndroidConfig.kt new file mode 100644 index 000000000..e25d4d737 --- /dev/null +++ b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/AndroidConfig.kt @@ -0,0 +1,22 @@ +package io.github.openflocon.buildlogic + +import com.android.build.gradle.LibraryExtension +import org.gradle.api.JavaVersion +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +internal fun Project.configureAndroidLibrary() { + extensions.configure { + compileSdk = 36 + + defaultConfig { + minSdk = 23 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + } +} diff --git a/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconAndroidLibraryConventionPlugin.kt b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconAndroidLibraryConventionPlugin.kt new file mode 100644 index 000000000..af1fb9a27 --- /dev/null +++ b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconAndroidLibraryConventionPlugin.kt @@ -0,0 +1,26 @@ +package io.github.openflocon.buildlogic + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +class FloconAndroidLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + apply("org.jetbrains.kotlin.android") + } + + configureAndroidLibrary() + + tasks.withType().configureEach { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + } + } +} diff --git a/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconKotlinMultiplatformConventionPlugin.kt b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconKotlinMultiplatformConventionPlugin.kt new file mode 100644 index 000000000..f60e19246 --- /dev/null +++ b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconKotlinMultiplatformConventionPlugin.kt @@ -0,0 +1,38 @@ +package io.github.openflocon.buildlogic + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +class FloconKotlinMultiplatformConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.jetbrains.kotlin.multiplatform") + apply("com.android.library") + } + + configureAndroidLibrary() + + extensions.configure { + androidTarget { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + jvm() + + iosX64() + iosArm64() + iosSimulatorArm64() + + compilerOptions { + freeCompilerArgs.add("-XXLanguage:+ExpectRefinement") + } + } + } + } +} diff --git a/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconPublishConventionPlugin.kt b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconPublishConventionPlugin.kt new file mode 100644 index 000000000..df47b1eaf --- /dev/null +++ b/FloconAndroid/build-logic/convention/bin/main/io/github/openflocon/buildlogic/FloconPublishConventionPlugin.kt @@ -0,0 +1,52 @@ +package io.github.openflocon.buildlogic + +import com.vanniktech.maven.publish.MavenPublishBaseExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class FloconPublishConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.vanniktech.maven.publish") + } + + extensions.configure { + publishToMavenCentral(automaticRelease = true) + + if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { + // Skip signing + } else { + signAllPublications() + } + + pom { + name.set(project.name) + description.set(project.findProperty("floconDescription") as? String) + inceptionYear.set("2025") + url.set("https://github.com/openflocon/Flocon") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + developers { + developer { + id.set("openflocon") + name.set("Open Flocon") + url.set("https://github.com/openflocon") + } + } + scm { + url.set("https://github.com/openflocon/Flocon") + connection.set("scm:git:git://github.com/openflocon/Flocon.git") + developerConnection.set("scm:git:ssh://git@github.com/openflocon/Flocon.git") + } + } + } + } + } +} diff --git a/FloconAndroid/build-logic/convention/build.gradle.kts b/FloconAndroid/build-logic/convention/build.gradle.kts new file mode 100644 index 000000000..e9f9e4aec --- /dev/null +++ b/FloconAndroid/build-logic/convention/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + `kotlin-dsl` +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +group = "io.github.openflocon.buildlogic" + +dependencies { + implementation(libs.android.gradlePlugin) + implementation(libs.kotlin.gradlePlugin) + implementation(libs.vanniktech.mavenPublish.gradlePlugin) +} + +gradlePlugin { + plugins { + register("floconAndroidLibrary") { + id = "flocon.android.library" + implementationClass = "io.github.openflocon.buildlogic.FloconAndroidLibraryConventionPlugin" + } + register("floconKotlinMultiplatform") { + id = "flocon.kotlin.multiplatform" + implementationClass = "io.github.openflocon.buildlogic.FloconKotlinMultiplatformConventionPlugin" + } + register("floconPublish") { + id = "flocon.publish" + implementationClass = "io.github.openflocon.buildlogic.FloconPublishConventionPlugin" + } + } +} diff --git a/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/AndroidConfig.kt b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/AndroidConfig.kt new file mode 100644 index 000000000..e25d4d737 --- /dev/null +++ b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/AndroidConfig.kt @@ -0,0 +1,22 @@ +package io.github.openflocon.buildlogic + +import com.android.build.gradle.LibraryExtension +import org.gradle.api.JavaVersion +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +internal fun Project.configureAndroidLibrary() { + extensions.configure { + compileSdk = 36 + + defaultConfig { + minSdk = 23 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + } +} diff --git a/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconAndroidLibraryConventionPlugin.kt b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconAndroidLibraryConventionPlugin.kt new file mode 100644 index 000000000..af1fb9a27 --- /dev/null +++ b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconAndroidLibraryConventionPlugin.kt @@ -0,0 +1,26 @@ +package io.github.openflocon.buildlogic + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +class FloconAndroidLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + apply("org.jetbrains.kotlin.android") + } + + configureAndroidLibrary() + + tasks.withType().configureEach { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + } + } +} diff --git a/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconKotlinMultiplatformConventionPlugin.kt b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconKotlinMultiplatformConventionPlugin.kt new file mode 100644 index 000000000..f60e19246 --- /dev/null +++ b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconKotlinMultiplatformConventionPlugin.kt @@ -0,0 +1,38 @@ +package io.github.openflocon.buildlogic + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +class FloconKotlinMultiplatformConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.jetbrains.kotlin.multiplatform") + apply("com.android.library") + } + + configureAndroidLibrary() + + extensions.configure { + androidTarget { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + jvm() + + iosX64() + iosArm64() + iosSimulatorArm64() + + compilerOptions { + freeCompilerArgs.add("-XXLanguage:+ExpectRefinement") + } + } + } + } +} diff --git a/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconPublishConventionPlugin.kt b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconPublishConventionPlugin.kt new file mode 100644 index 000000000..df47b1eaf --- /dev/null +++ b/FloconAndroid/build-logic/convention/src/main/kotlin/io/github/openflocon/buildlogic/FloconPublishConventionPlugin.kt @@ -0,0 +1,52 @@ +package io.github.openflocon.buildlogic + +import com.vanniktech.maven.publish.MavenPublishBaseExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class FloconPublishConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.vanniktech.maven.publish") + } + + extensions.configure { + publishToMavenCentral(automaticRelease = true) + + if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { + // Skip signing + } else { + signAllPublications() + } + + pom { + name.set(project.name) + description.set(project.findProperty("floconDescription") as? String) + inceptionYear.set("2025") + url.set("https://github.com/openflocon/Flocon") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + developers { + developer { + id.set("openflocon") + name.set("Open Flocon") + url.set("https://github.com/openflocon") + } + } + scm { + url.set("https://github.com/openflocon/Flocon") + connection.set("scm:git:git://github.com/openflocon/Flocon.git") + developerConnection.set("scm:git:ssh://git@github.com/openflocon/Flocon.git") + } + } + } + } + } +} diff --git a/FloconAndroid/build-logic/gradle.properties b/FloconAndroid/build-logic/gradle.properties new file mode 100644 index 000000000..b29124d09 --- /dev/null +++ b/FloconAndroid/build-logic/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +kotlin.daemon.jvmargs=-Xmx1024m diff --git a/FloconAndroid/build-logic/settings.gradle.kts b/FloconAndroid/build-logic/settings.gradle.kts new file mode 100644 index 000000000..f26e6d9f9 --- /dev/null +++ b/FloconAndroid/build-logic/settings.gradle.kts @@ -0,0 +1,15 @@ +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "build-logic" + +include(":convention") diff --git a/FloconAndroid/build.gradle.kts b/FloconAndroid/build.gradle.kts index 5d6f07e57..8cc104597 100644 --- a/FloconAndroid/build.gradle.kts +++ b/FloconAndroid/build.gradle.kts @@ -7,5 +7,5 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.vanniktech.maven.publish) apply false - id("com.google.protobuf") version "0.9.5" apply false + alias(libs.plugins.protobuf) apply false } \ No newline at end of file diff --git a/FloconAndroid/crashreporter-no-op/build.gradle.kts b/FloconAndroid/crashreporter-no-op/build.gradle.kts new file mode 100644 index 000000000..f6431621f --- /dev/null +++ b/FloconAndroid/crashreporter-no-op/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.crashreporter.noop" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-crashreporter-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/crashreporter-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterNoOp.kt b/FloconAndroid/crashreporter-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterNoOp.kt new file mode 100644 index 000000000..b3ce8e05e --- /dev/null +++ b/FloconAndroid/crashreporter-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterNoOp.kt @@ -0,0 +1,50 @@ +package io.github.openflocon.flocon.crashreporter + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol + +class FloconCrashReporterConfig : FloconPluginConfig { + var catchFatalErrors: Boolean = true +} + +interface FloconCrashReporterPlugin : FloconPlugin { + fun setupCrashHandler() +} + +object FloconCrashReporter : FloconPluginFactory { + override val name: String = "CrashReporter" + override val pluginId: String = Protocol.ToDevice.Analytics.Plugin // Same as real impl + + override fun createConfig(context: FloconContext) = FloconCrashReporterConfig() + + override fun install( + pluginConfig: FloconCrashReporterConfig, + floconConfig: FloconConfig, + encoder: io.github.openflocon.flocon.core.FloconEncoder + ): FloconCrashReporterPlugin { + return FloconCrashReporterNoOpImpl() + } +} + +internal class FloconCrashReporterNoOpImpl : FloconPlugin, FloconCrashReporterPlugin { + override val key: String = "CRASH_REPORTER" + + override fun setupCrashHandler() { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } +} diff --git a/FloconAndroid/crashreporter/build.gradle.kts b/FloconAndroid/crashreporter/build.gradle.kts new file mode 100644 index 000000000..c8921cc6b --- /dev/null +++ b/FloconAndroid/crashreporter/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.crashreporter" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-crashreporter", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.android.kt b/FloconAndroid/crashreporter/src/androidMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.android.kt similarity index 82% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.android.kt rename to FloconAndroid/crashreporter/src/androidMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.android.kt index 1c6b0d62e..c916d416e 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.android.kt +++ b/FloconAndroid/crashreporter/src/androidMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.android.kt @@ -1,11 +1,11 @@ -package io.github.openflocon.flocon.plugins.crashreporter +package io.github.openflocon.flocon.crashreporter import android.content.Context import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.crashreporter.model.CrashReportDataModel -import io.github.openflocon.flocon.plugins.crashreporter.model.crashReportFromJson -import io.github.openflocon.flocon.plugins.crashreporter.model.toJson +import io.github.openflocon.flocon.crashreporter.model.CrashReportDataModel +import io.github.openflocon.flocon.crashreporter.model.crashReportFromJson +import io.github.openflocon.flocon.crashreporter.model.toJson import java.io.File internal class FloconCrashReporterDataSourceAndroid( @@ -55,5 +55,5 @@ internal class FloconCrashReporterDataSourceAndroid( } internal actual fun buildFloconCrashReporterDataSource(context: FloconContext): FloconCrashReporterDataSource { - return FloconCrashReporterDataSourceAndroid(context.appContext) + return FloconCrashReporterDataSourceAndroid(context.context) } diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/UncaughtExceptionHandler.android.kt b/FloconAndroid/crashreporter/src/androidMain/kotlin/io/github/openflocon/flocon/crashreporter/UncaughtExceptionHandler.kt similarity index 88% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/UncaughtExceptionHandler.android.kt rename to FloconAndroid/crashreporter/src/androidMain/kotlin/io/github/openflocon/flocon/crashreporter/UncaughtExceptionHandler.kt index 45c238648..7b15f98c5 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/UncaughtExceptionHandler.android.kt +++ b/FloconAndroid/crashreporter/src/androidMain/kotlin/io/github/openflocon/flocon/crashreporter/UncaughtExceptionHandler.kt @@ -1,6 +1,5 @@ -package io.github.openflocon.flocon.plugins.crashreporter +package io.github.openflocon.flocon.crashreporter -import android.os.Build import io.github.openflocon.flocon.FloconContext internal actual fun setupUncaughtExceptionHandler( diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.kt b/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.kt similarity index 76% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.kt rename to FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.kt index 1134b5554..09594503a 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.kt +++ b/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.kt @@ -1,7 +1,7 @@ -package io.github.openflocon.flocon.plugins.crashreporter +package io.github.openflocon.flocon.crashreporter import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.plugins.crashreporter.model.CrashReportDataModel +import io.github.openflocon.flocon.crashreporter.model.CrashReportDataModel internal interface FloconCrashReporterDataSource { fun saveCrash(crash: CrashReportDataModel) diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterPlugin.kt b/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterPlugin.kt similarity index 51% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterPlugin.kt rename to FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterPlugin.kt index 072d35521..c3d33a938 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterPlugin.kt +++ b/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterPlugin.kt @@ -1,36 +1,70 @@ -package io.github.openflocon.flocon.plugins.crashreporter +package io.github.openflocon.flocon.crashreporter +import io.github.openflocon.flocon.FloconConfig import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.crashreporter.model.CrashReportDataModel -import io.github.openflocon.flocon.plugins.crashreporter.model.crashReportsListToJson +import io.github.openflocon.flocon.crashreporter.model.CrashReportDataModel import io.github.openflocon.flocon.utils.currentTimeMillis -import io.github.openflocon.flocondesktop.BuildConfig import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid +class FloconCrashReporterConfig : FloconPluginConfig { + var catchFatalErrors: Boolean = true +} + +interface FloconCrashReporterPlugin : FloconPlugin { + fun setupCrashHandler() +} + +object FloconCrashReporter : FloconPluginFactory { + override val name: String = "CrashReporter" + override val pluginId: String = + Protocol.ToDevice.Analytics.Plugin // Crash reporter is usually write-only but we can set an ID + + override fun createConfig(context: FloconContext) = FloconCrashReporterConfig() + + override fun install( + pluginConfig: FloconCrashReporterConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconCrashReporterPlugin { + val client = floconConfig.client as FloconMessageSender + return FloconCrashReporterPluginImpl( + context = floconConfig.context, + sender = client, + coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), + ) + } +} + internal class FloconCrashReporterPluginImpl( private val context: FloconContext, private var sender: FloconMessageSender, private val coroutineScope: CoroutineScope, ) : FloconPlugin, FloconCrashReporterPlugin { + override val key: String = "CRASH_REPORTER" private val dataSource = buildFloconCrashReporterDataSource(context) - fun setupCrashHandler() { + override fun setupCrashHandler() { setupUncaughtExceptionHandler(context) { throwable -> val crash = createCrashReport(throwable) dataSource.saveCrash(crash) } } - override fun onConnectedToServer() { + override suspend fun onConnectedToServer() { // Send all pending crashes coroutineScope.launch { try { @@ -46,17 +80,20 @@ internal class FloconCrashReporterPluginImpl( } } - override fun onMessageReceived(messageFromServer: FloconMessageFromServer) { + override suspend fun onMessageReceived( + method: String, + body: String, + ) { // No messages from desktop for crashes (write-only plugin) } private fun sendCrashes(crashes: List) { try { - sender.send( - plugin = Protocol.FromDevice.CrashReporter.Plugin, - method = Protocol.FromDevice.CrashReporter.Method.ReportCrash, - body = crashReportsListToJson(crashes), - ) +// sender.send( +// plugin = Protocol.FromDevice.CrashReporter.Plugin, +// method = Protocol.FromDevice.CrashReporter.Method.ReportCrash, +// body = crashReportsListToJson(crashes), +// ) } catch (t: Throwable) { FloconLogger.logError("Crash report sending error", t) } diff --git a/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/model/CrashReportDataModel.kt b/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/model/CrashReportDataModel.kt new file mode 100644 index 000000000..be0d36ac1 --- /dev/null +++ b/FloconAndroid/crashreporter/src/commonMain/kotlin/io/github/openflocon/flocon/crashreporter/model/CrashReportDataModel.kt @@ -0,0 +1,22 @@ +package io.github.openflocon.flocon.crashreporter.model + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.encodeToString + +@Serializable +data class CrashReportDataModel( + val crashId: String, + val timestamp: Long, + val exceptionType: String, + val exceptionMessage: String, + val stackTrace: String, +) + +fun CrashReportDataModel.toJson(): String { + return Json.encodeToString(this) +} + +fun crashReportFromJson(json: String): CrashReportDataModel { + return Json.decodeFromString(json) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/CrashReporterDataSource.kt b/FloconAndroid/crashreporter/src/iosMain/kotlin/io/github/openflocon/flocon/crashreporter/CrashReporterDataSource.kt similarity index 75% rename from FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/CrashReporterDataSource.kt rename to FloconAndroid/crashreporter/src/iosMain/kotlin/io/github/openflocon/flocon/crashreporter/CrashReporterDataSource.kt index 4d80478b4..f2350c2f1 100644 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/CrashReporterDataSource.kt +++ b/FloconAndroid/crashreporter/src/iosMain/kotlin/io/github/openflocon/flocon/crashreporter/CrashReporterDataSource.kt @@ -1,8 +1,8 @@ -package io.github.openflocon.flocon.plugins.crashreporter +package io.github.openflocon.flocon.crashreporter -import io.github.openflocon.flocon.plugins.crashreporter.model.CrashReportDataModel +import io.github.openflocon.flocon.crashreporter.model.CrashReportDataModel -internal actual fun buildFloconCrashReporterDataSource(context: io.github.openflocon.flocon.FloconContext): io.github.openflocon.flocon.plugins.crashreporter.FloconCrashReporterDataSource { +internal actual fun buildFloconCrashReporterDataSource(context: io.github.openflocon.flocon.FloconContext): io.github.openflocon.flocon.crashreporter.FloconCrashReporterDataSource { return object : FloconCrashReporterDataSource { override fun saveCrash(crash: CrashReportDataModel) { // no op diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.jvm.kt b/FloconAndroid/crashreporter/src/jvmMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.jvm.kt similarity index 89% rename from FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.jvm.kt rename to FloconAndroid/crashreporter/src/jvmMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.jvm.kt index 7b62ac334..b81678f37 100644 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterDataSource.jvm.kt +++ b/FloconAndroid/crashreporter/src/jvmMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.jvm.kt @@ -1,10 +1,10 @@ -package io.github.openflocon.flocon.plugins.crashreporter +package io.github.openflocon.flocon.crashreporter import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.crashreporter.model.CrashReportDataModel -import io.github.openflocon.flocon.plugins.crashreporter.model.crashReportFromJson -import io.github.openflocon.flocon.plugins.crashreporter.model.toJson +import io.github.openflocon.flocon.crashreporter.model.CrashReportDataModel +import io.github.openflocon.flocon.crashreporter.model.crashReportFromJson +import io.github.openflocon.flocon.crashreporter.model.toJson import java.io.File internal class FloconCrashReporterDataSourceJvm : FloconCrashReporterDataSource { diff --git a/FloconAndroid/crashreporter/src/wasmJsMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.wasmJs.kt b/FloconAndroid/crashreporter/src/wasmJsMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.wasmJs.kt new file mode 100644 index 000000000..199337f59 --- /dev/null +++ b/FloconAndroid/crashreporter/src/wasmJsMain/kotlin/io/github/openflocon/flocon/crashreporter/FloconCrashReporterDataSource.wasmJs.kt @@ -0,0 +1,16 @@ +package io.github.openflocon.flocon.crashreporter + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.crashreporter.model.CrashReportDataModel + +internal actual fun buildFloconCrashReporterDataSource(context: FloconContext): FloconCrashReporterDataSource = object : FloconCrashReporterDataSource { + override fun saveCrash(crash: CrashReportDataModel) {} + override fun loadPendingCrashes(): List = emptyList() + override fun deleteCrash(crashId: String) {} +} + +internal actual fun setupUncaughtExceptionHandler( + context: FloconContext, + onCrash: (Throwable) -> Unit +) { +} diff --git a/FloconAndroid/database/core-no-op/build.gradle.kts b/FloconAndroid/database/core-no-op/build.gradle.kts new file mode 100644 index 000000000..69d424509 --- /dev/null +++ b/FloconAndroid/database/core-no-op/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + wasmJs { + moduleName = "flocon_database_core_no_op" + browser() + binaries.executable() + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + implementation(libs.kotlinx.coroutines.core) + } + } + + 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) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.database.core.noop" +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-database-core-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/database/core/build.gradle.kts b/FloconAndroid/database/core/build.gradle.kts new file mode 100644 index 000000000..37da966f6 --- /dev/null +++ b/FloconAndroid/database/core/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") + alias(libs.plugins.kotlin.serialization) +} + +kotlin { + wasmJs { + moduleName = "flocon_database_core" + browser() + binaries.executable() + } + + sourceSets { + val commonMain by getting { + dependencies { + api(projects.flocon) + + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + + 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 { + implementation(libs.androidx.sqlite.bundled) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.database.core" +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-database-core", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/database/core/src/androidMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.android.kt b/FloconAndroid/database/core/src/androidMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.android.kt new file mode 100644 index 000000000..a0157ddcc --- /dev/null +++ b/FloconAndroid/database/core/src/androidMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.android.kt @@ -0,0 +1,100 @@ +package io.github.openflocon.flocon.database.core.model + +import android.database.Cursor +import android.database.sqlite.SQLiteDatabase +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse +import java.util.Locale + +actual fun openDbAndExecuteQuery( + path: String, + query: String +): DatabaseExecuteSqlResponse { + var database: SQLiteDatabase? = null + return try { + database = SQLiteDatabase.openDatabase( + path, + null, + SQLiteDatabase.OPEN_READWRITE + ) + executeSQLInternal(database, query) + } catch (t: Throwable) { + DatabaseExecuteSqlResponse.Error( + message = t.message ?: "error on executeSQL", + originalSql = query, + ) + } finally { + database?.close() + } +} + +private fun executeSQLInternal( + database: SQLiteDatabase, + query: String +): DatabaseExecuteSqlResponse { + val firstWord = query.trim().let { + val idx = it.indexOf(' ') + if (idx >= 0) it.substring(0, idx) else it + }.uppercase(Locale.getDefault()) + + return when (firstWord) { + "SELECT", "PRAGMA", "EXPLAIN" -> executeSelect(database, query) + "INSERT" -> executeInsert(database, query) + "UPDATE", "DELETE" -> executeUpdateDelete(database, query) + else -> executeRawQuery(database, query) + } +} + +private fun executeSelect( + database: SQLiteDatabase, + query: String, +): DatabaseExecuteSqlResponse { + val cursor: Cursor = database.rawQuery(query, null) + return try { + val columnNames = cursor.columnNames.toList() + val rows = mutableListOf>() + while (cursor.moveToNext()) { + val values = mutableListOf() + for (i in 0 until cursor.columnCount) { + values.add( + when (cursor.getType(i)) { + Cursor.FIELD_TYPE_NULL -> null + Cursor.FIELD_TYPE_INTEGER -> cursor.getLong(i).toString() + Cursor.FIELD_TYPE_FLOAT -> cursor.getDouble(i).toString() + Cursor.FIELD_TYPE_BLOB -> cursor.getBlob(i).toString() + else -> cursor.getString(i) + } + ) + } + rows.add(values) + } + DatabaseExecuteSqlResponse.Select(columns = columnNames, values = rows) + } finally { + cursor.close() + } +} + +private fun executeInsert( + database: SQLiteDatabase, + query: String, +): DatabaseExecuteSqlResponse { + val statement = database.compileStatement(query) + val insertedId: Long = statement.executeInsert() + return DatabaseExecuteSqlResponse.Insert(insertedId) +} + +private fun executeUpdateDelete( + database: SQLiteDatabase, + query: String, +): DatabaseExecuteSqlResponse { + val statement = database.compileStatement(query) + val count: Int = statement.executeUpdateDelete() + return DatabaseExecuteSqlResponse.UpdateDelete(count) +} + +private fun executeRawQuery( + database: SQLiteDatabase, + query: String, +): DatabaseExecuteSqlResponse { + database.execSQL(query) + return DatabaseExecuteSqlResponse.RawSuccess +} \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabase.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabase.kt new file mode 100644 index 000000000..7e5e49832 --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabase.kt @@ -0,0 +1,37 @@ +package io.github.openflocon.flocon.database.core + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconEncoding +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.dsl.FloconMarker + +object FloconDatabase : FloconPluginFactory { + override val name: String = "Database" + override val pluginId: String = Protocol.ToDevice.Database.Plugin + + @FloconMarker + override fun createEncoding(): FloconEncoding = FloconDatabaseEncoding() + + override fun createConfig(context: FloconContext): FloconDatabaseConfig = + FloconDatabaseConfig(context) + + @OptIn(FloconMarker::class) + override fun install( + pluginConfig: FloconDatabaseConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconDatabasePlugin { + return FloconDatabasePluginImpl( + sender = floconConfig.client as FloconMessageSender, + scope = floconConfig.scope, + providers = pluginConfig.providers, + encoder = encoder + ) + .also { FloconDatabasePluginImpl.plugin = it } + } + +} diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabaseConfig.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabaseConfig.kt new file mode 100644 index 000000000..2da07ec7f --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabaseConfig.kt @@ -0,0 +1,15 @@ +package io.github.openflocon.flocon.database.core + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.database.core.datasource.FloconDatabaseProvider +import io.github.openflocon.flocon.dsl.FloconMarker + +class FloconDatabaseConfig( + val context: FloconContext +) : FloconPluginConfig { + + @FloconMarker + val providers: MutableList = mutableListOf() + +} diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabaseEncoding.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabaseEncoding.kt new file mode 100644 index 000000000..2a1b5cf6b --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabaseEncoding.kt @@ -0,0 +1,27 @@ +package io.github.openflocon.flocon.database.core + +import io.github.openflocon.flocon.FloconEncoding +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteResponse +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse +import io.github.openflocon.flocon.dsl.FloconMarker +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass + +@FloconMarker +internal class FloconDatabaseEncoding : FloconEncoding { + override val serializersModule: SerializersModule + get() = SerializersModule { + polymorphic(DatabaseExecuteResponse::class) { + polymorphic(DatabaseExecuteSqlResponse::class) { + subclass(DatabaseExecuteSqlResponse.Insert::class) + subclass(DatabaseExecuteSqlResponse.UpdateDelete::class) + subclass(DatabaseExecuteSqlResponse.Select::class) + subclass(DatabaseExecuteSqlResponse.RawSuccess::class) + subclass(DatabaseExecuteSqlResponse.Error::class) + + defaultDeserializer { DatabaseExecuteSqlResponse.Error.serializer() } + } + } + } +} \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt new file mode 100644 index 000000000..704879c5b --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.kt @@ -0,0 +1,23 @@ +package io.github.openflocon.flocon.database.core + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.database.core.datasource.FloconDatabaseProvider +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.error.pluginNotInitialized + +interface FloconDatabasePlugin : FloconPlugin { + + @FloconMarker + val providers: List + + fun register(floconDatabaseModel: FloconDatabaseModel) + + fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) + +} + +@OptIn(FloconMarker::class) +val Flocon.Companion.databasePlugin: FloconDatabasePlugin + get() = FloconDatabasePluginImpl.plugin ?: pluginNotInitialized("Database") \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt new file mode 100644 index 000000000..73c205bfe --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePluginImpl.kt @@ -0,0 +1,123 @@ +package io.github.openflocon.flocon.database.core + +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.core.decode +import io.github.openflocon.flocon.core.encode +import io.github.openflocon.flocon.database.core.datasource.FloconDatabaseProvider +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse +import io.github.openflocon.flocon.database.core.model.fromdevice.sql.DatabaseQueryLogModel +import io.github.openflocon.flocon.database.core.model.fromdevice.sql.DeviceDataBaseDataModel +import io.github.openflocon.flocon.database.core.model.fromdevice.sql.QueryResultDataModel +import io.github.openflocon.flocon.database.core.model.todevice.DatabaseQueryMessage +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.utils.currentTimeMillis +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.updateAndGet +import kotlinx.coroutines.launch + +internal class FloconDatabasePluginImpl( + private var sender: FloconMessageSender, + private val scope: CoroutineScope, + @FloconMarker + override val providers: List, + private val encoder: FloconEncoder +) : FloconPlugin, FloconDatabasePlugin { + + override val key: String = Protocol.FromDevice.Database.Plugin + + private val registeredDatabases = MutableStateFlow>(emptyList()) + + override suspend fun onMessageReceived( + method: String, + body: String + ) { + println("Database: $method & $body") + when (method) { + Protocol.ToDevice.Database.Method.GetDatabases -> sendAllDatabases(sender) + + Protocol.ToDevice.Database.Method.Query -> { + val queryMessage = encoder.decode(body) ?: return + val databaseModel = registeredDatabases.value + .find { it.id == queryMessage.database } + + val result = databaseModel?.executeQuery(query = queryMessage.query) + ?: DatabaseExecuteSqlResponse.Error( + message = "Database not found", + originalSql = queryMessage.query, + ) + + try { + sender.send( + plugin = Protocol.FromDevice.Database.Plugin, + method = Protocol.FromDevice.Database.Method.Query, + body = encoder.encode( + QueryResultDataModel( + requestId = queryMessage.requestId, + result = encoder.encode(result) + ) + ) + ) + } catch (t: Throwable) { + FloconLogger.logError("Database parsing error", t) + } + } + } + } + + override suspend fun onConnectedToServer() { + sendAllDatabases(sender) + } + + @OptIn(FloconMarker::class) + private fun sendAllDatabases(sender: FloconMessageSender) { + val databases = providers.flatMap { it.getAllDataBases(emptyList()) } + val all = registeredDatabases.updateAndGet { it + databases } + .map { DeviceDataBaseDataModel(id = it.id, name = it.displayName) } + + try { + sender.send( + plugin = Protocol.FromDevice.Database.Plugin, + method = Protocol.FromDevice.Database.Method.GetDatabases, + body = encoder.encode(all), + ) + } catch (t: Throwable) { + FloconLogger.logError("Database parsing error", t) + } + } + + override fun register(floconDatabaseModel: FloconDatabaseModel) { + registeredDatabases.update { it + floconDatabaseModel } + } + + override fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) { + scope.launch { + try { + sender.send( + plugin = Protocol.FromDevice.Database.Plugin, + method = Protocol.FromDevice.Database.Method.LogQuery, + body = encoder.encode( + DatabaseQueryLogModel( + dbName = dbName, + sqlQuery = sqlQuery, + bindArgs = bindArgs.map { it.toString() }, + timestamp = currentTimeMillis() + ) + ) + ) + } catch (t: Throwable) { + FloconLogger.logError("Database logging error", t) + } + } + } + + companion object { + var plugin: FloconDatabasePlugin? = null + } +} \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/datasource/FloconDatabaseProvider.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/datasource/FloconDatabaseProvider.kt new file mode 100644 index 000000000..047665840 --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/datasource/FloconDatabaseProvider.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.flocon.database.core.datasource + +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +interface FloconDatabaseProvider { + + @FloconMarker + fun getAllDataBases( + registeredDatabases: List + ): List + +} \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.kt new file mode 100644 index 000000000..c6978e3f8 --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.kt @@ -0,0 +1,29 @@ +package io.github.openflocon.flocon.database.core.model + +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteResponse +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse + +interface FloconDatabaseModel { + val id: String + val displayName: String + + suspend fun executeQuery(query: String): DatabaseExecuteResponse + +} + +data class FloconFileDatabaseModel( + override val id: String, + override val displayName: String, + val absolutePath: String +) : FloconDatabaseModel { + + override suspend fun executeQuery(query: String): DatabaseExecuteResponse { + return openDbAndExecuteQuery(absolutePath, query) + } + +} + +expect fun openDbAndExecuteQuery( + path: String, + query: String +): DatabaseExecuteSqlResponse \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/DatabaseExecuteResponse.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/DatabaseExecuteResponse.kt new file mode 100644 index 000000000..03ffb0d12 --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/DatabaseExecuteResponse.kt @@ -0,0 +1,11 @@ +@file:OptIn(ExperimentalSerializationApi::class) + +package io.github.openflocon.flocon.database.core.model.fromdevice + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonClassDiscriminator + +@Serializable +@JsonClassDiscriminator("type") +sealed interface DatabaseExecuteResponse \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/DatabaseExecuteSqlResponse.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/DatabaseExecuteSqlResponse.kt new file mode 100644 index 000000000..2987b5aed --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/DatabaseExecuteSqlResponse.kt @@ -0,0 +1,43 @@ +package io.github.openflocon.flocon.database.core.model.fromdevice + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +sealed interface DatabaseExecuteSqlResponse : DatabaseExecuteResponse { + + @Serializable + @SerialName("SELECT") + // Case for successful SELECT queries + class Select( + val columns: List, + val values: List> + ) : DatabaseExecuteSqlResponse + + // Case for successful INSERT queries + @Serializable + @SerialName("INSERT") + class Insert( + val insertedId: Long + ) : DatabaseExecuteSqlResponse + + // Case for successful UPDATE or DELETE queries + @Serializable + @SerialName("UPDATE_DELETE") + class UpdateDelete( + val affectedCount: Int + ) : DatabaseExecuteSqlResponse + + // Case for successful "raw" queries (CREATE TABLE, DROP TABLE, etc.) + @Serializable + @SerialName("RAW_SUCCESS") + object RawSuccess : DatabaseExecuteSqlResponse + + // Case for an SQL execution error + @Serializable + @SerialName("ERROR") + class Error( + val message: String = "", // Detailed error message + val originalSql: String = "", // SQL query that caused the error (optional) + ) : DatabaseExecuteSqlResponse +} diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt new file mode 100644 index 000000000..456b10fe4 --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DatabaseQueryLogModel.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.database.core.model.fromdevice.sql + +import kotlinx.serialization.Serializable + +@Serializable +data class DatabaseQueryLogModel( + val dbName: String, + val sqlQuery: String, + val bindArgs: List?, + val timestamp: Long, +) diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DeviceDataBaseDataModel.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DeviceDataBaseDataModel.kt new file mode 100644 index 000000000..74891fb6c --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/DeviceDataBaseDataModel.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.database.core.model.fromdevice.sql + +import kotlinx.serialization.Serializable + +@Serializable +data class DeviceDataBaseDataModel( + val id: String, + val name: String +) diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/QueryResultReceivedDataModel.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/QueryResultReceivedDataModel.kt new file mode 100644 index 000000000..e916b787c --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/fromdevice/sql/QueryResultReceivedDataModel.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.database.core.model.fromdevice.sql + +import kotlinx.serialization.Serializable + +@Serializable +internal data class QueryResultDataModel( + val requestId: String, + val result: String +) \ No newline at end of file diff --git a/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/todevice/DatabaseQueryMessage.kt b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/todevice/DatabaseQueryMessage.kt new file mode 100644 index 000000000..c0451d778 --- /dev/null +++ b/FloconAndroid/database/core/src/commonMain/kotlin/io/github/openflocon/flocon/database/core/model/todevice/DatabaseQueryMessage.kt @@ -0,0 +1,10 @@ +package io.github.openflocon.flocon.database.core.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +data class DatabaseQueryMessage( + val query: String, + val requestId: String, + val database: String, +) diff --git a/FloconAndroid/database/core/src/iosMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.ios.kt b/FloconAndroid/database/core/src/iosMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.ios.kt new file mode 100644 index 000000000..3ec4ab462 --- /dev/null +++ b/FloconAndroid/database/core/src/iosMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.ios.kt @@ -0,0 +1,3 @@ +package io.github.openflocon.flocon.database.core + +// iOS implementation is in model/FloconDatabaseModel.ios.kt diff --git a/FloconAndroid/database/core/src/iosMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.ios.kt b/FloconAndroid/database/core/src/iosMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.ios.kt new file mode 100644 index 000000000..7b8ddc3d6 --- /dev/null +++ b/FloconAndroid/database/core/src/iosMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.ios.kt @@ -0,0 +1,75 @@ +package io.github.openflocon.flocon.database.core.model + +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.driver.NativeSQLiteDriver +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse + +actual fun openDbAndExecuteQuery( + path: String, + query: String +): DatabaseExecuteSqlResponse { + val driver = NativeSQLiteDriver() + val connection = driver.open(fileName = path) + return try { + val firstWord = query.trim().let { + val idx = it.indexOf(' ') + if (idx >= 0) it.substring(0, idx) else it + }.uppercase() + + when (firstWord) { + "SELECT", "PRAGMA", "EXPLAIN" -> executeSelect(connection, query) + "INSERT" -> executeInsert(connection, query) + "UPDATE", "DELETE" -> executeUpdateDelete(connection, query) + else -> executeRawQuery(connection, query) + } + } catch (t: Throwable) { + DatabaseExecuteSqlResponse.Error( + message = t.message ?: "Error executing SQL", + originalSql = query + ) + } finally { + connection.close() + } +} + +// --- SQL execution helpers --- + +private fun executeSelect(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { + return connection.prepare(query).use { statement -> + val columnCount = statement.getColumnCount() + val columns = (0 until columnCount).map { statement.getColumnName(it) } + val rows = mutableListOf>() + + while (statement.step()) { + val row = (0 until columnCount).map { idx -> + if (statement.isNull(idx)) null else statement.getText(idx) + } + rows.add(row) + } + + DatabaseExecuteSqlResponse.Select(columns, rows) + } +} + +private fun executeInsert(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { + connection.prepare(query).use { it.step() } + + val id = connection.prepare("SELECT last_insert_rowid()").use { stmt -> + if (stmt.step()) stmt.getLong(0) else -1L + } + return DatabaseExecuteSqlResponse.Insert(id) +} + +private fun executeUpdateDelete(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { + connection.prepare(query).use { it.step() } + + val count = connection.prepare("SELECT changes()").use { stmt -> + if (stmt.step()) stmt.getLong(0).toInt() else 0 + } + return DatabaseExecuteSqlResponse.UpdateDelete(affectedCount = count) +} + +private fun executeRawQuery(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { + connection.prepare(query).use { it.step() } + return DatabaseExecuteSqlResponse.RawSuccess +} \ No newline at end of file diff --git a/FloconAndroid/database/core/src/jvmMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.jvm.kt b/FloconAndroid/database/core/src/jvmMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.jvm.kt new file mode 100644 index 000000000..fde7260cf --- /dev/null +++ b/FloconAndroid/database/core/src/jvmMain/kotlin/io/github/openflocon/flocon/database/core/FloconDatabasePlugin.jvm.kt @@ -0,0 +1,89 @@ +package io.github.openflocon.flocon.database.core.model + +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse +import java.sql.Connection +import java.sql.DriverManager +import java.sql.ResultSet + +actual fun openDbAndExecuteQuery( + path: String, + query: String +): DatabaseExecuteSqlResponse { + var connection: Connection? = null + return try { + Class.forName("org.sqlite.JDBC") + connection = DriverManager.getConnection("jdbc:sqlite:$path") + executeSQLInternal(connection, query) + } catch (t: Throwable) { + DatabaseExecuteSqlResponse.Error( + message = t.message ?: "error on executeSQL", + originalSql = query, + ) + } finally { + connection?.close() + } +} + +private fun executeSQLInternal( + connection: Connection, + query: String +): DatabaseExecuteSqlResponse { + val firstWord = query.trim().let { + val idx = it.indexOf(' ') + if (idx >= 0) it.substring(0, idx) else it + }.uppercase() + + return when (firstWord) { + "SELECT", "PRAGMA", "EXPLAIN" -> executeSelect(connection, query) + "INSERT" -> executeInsert(connection, query) + "UPDATE", "DELETE" -> executeUpdateDelete(connection, query) + else -> executeRawQuery(connection, query) + } +} + +private fun executeSelect( + connection: Connection, + query: String, +): DatabaseExecuteSqlResponse { + val statement = connection.createStatement() + val rs: ResultSet = statement.executeQuery(query) + val meta = rs.metaData + val columnCount = meta.columnCount + val columns = (1..columnCount).map { meta.getColumnName(it) } + val rows = mutableListOf>() + while (rs.next()) { + val row = (1..columnCount).map { i -> + rs.getString(i) + } + rows.add(row) + } + return DatabaseExecuteSqlResponse.Select(columns = columns, values = rows) +} + +private fun executeInsert( + connection: Connection, + query: String, +): DatabaseExecuteSqlResponse { + val statement = connection.createStatement() + statement.executeUpdate(query) + val keys: ResultSet = statement.generatedKeys + val insertedId = if (keys.next()) keys.getLong(1) else -1L + return DatabaseExecuteSqlResponse.Insert(insertedId) +} + +private fun executeUpdateDelete( + connection: Connection, + query: String, +): DatabaseExecuteSqlResponse { + val statement = connection.createStatement() + val count = statement.executeUpdate(query) + return DatabaseExecuteSqlResponse.UpdateDelete(count) +} + +private fun executeRawQuery( + connection: Connection, + query: String, +): DatabaseExecuteSqlResponse { + connection.createStatement().execute(query) + return DatabaseExecuteSqlResponse.RawSuccess +} \ No newline at end of file diff --git a/FloconAndroid/database/core/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.wasmJs.kt b/FloconAndroid/database/core/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.wasmJs.kt new file mode 100644 index 000000000..4888f7e96 --- /dev/null +++ b/FloconAndroid/database/core/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/core/model/FloconDatabaseModel.wasmJs.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.flocon.database.core.model + +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse + +actual fun openDbAndExecuteQuery( + path: String, + query: String +): DatabaseExecuteSqlResponse { + return DatabaseExecuteSqlResponse.Error( + message = "SQLite is not supported on WasmJS yet", + originalSql = query + ) +} diff --git a/FloconAndroid/database/room-no-op/build.gradle.kts b/FloconAndroid/database/room-no-op/build.gradle.kts new file mode 100644 index 000000000..22b4f41d1 --- /dev/null +++ b/FloconAndroid/database/room-no-op/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.database.coreNoOp) + } + } + + val androidMain by getting { + dependencies { + } + } + + val jvmMain by getting { + dependencies { + } + } + + 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) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.database.room.noop" +} + + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-database-room-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/floconRegisterDatabase.kt b/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/floconRegisterDatabase.kt new file mode 100644 index 000000000..1aefa1497 --- /dev/null +++ b/FloconAndroid/database/room-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/floconRegisterDatabase.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.database.room + +fun floconRegisterDatabase(displayName: String, database: Any) { + // no op +} + +fun floconRegisterDatabase(displayName: String, openHelper: Any, dummy: Boolean = true) { + // no op +} diff --git a/FloconAndroid/database/room/build.gradle.kts b/FloconAndroid/database/room/build.gradle.kts new file mode 100644 index 000000000..86da03840 --- /dev/null +++ b/FloconAndroid/database/room/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + api(projects.database.core) + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.sqlite.wrapper) + implementation(libs.androidx.sqlite.bundled) + } + } + + val androidMain by getting { + dependencies { + implementation(libs.kotlinx.coroutines.android) + } + } + + 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) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.database.room" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-database-room", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.android.kt b/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.android.kt new file mode 100644 index 000000000..2626606a4 --- /dev/null +++ b/FloconAndroid/database/room/src/androidMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.android.kt @@ -0,0 +1,77 @@ +package io.github.openflocon.flocon.database.room + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.database.core.model.FloconFileDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker +import java.io.File + +@OptIn(markerClass = [FloconMarker::class]) +internal actual class FloconRoomDatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoomDatabaseProvider { + + actual override fun register() { + val databases = getAllDataBases(emptyList()) + databases.forEach { Flocon.databasePlugin.register(it) } + } + + @FloconMarker + actual override fun getAllDataBases(registeredDatabases: List): List { + val databasesDir = context.context.getDatabasePath("dummy_db") + .parentFile + ?: return emptyList() + + val foundDatabases = mutableListOf() + // Start the recursive search from the base databases directory + scanDirectoryForDatabases( + directory = databasesDir, + depth = 0, + foundDatabases = foundDatabases + ) + + return foundDatabases + } + + private fun scanDirectoryForDatabases( + directory: File, + depth: Int, + foundDatabases: MutableList + ) { + if (depth >= MAX_DEPTH) { + return + } + directory.listFiles()?.forEach { file -> + if (file.isDirectory) { + // If it's a directory, recursively call this function + scanDirectoryForDatabases( + directory = file, + depth = depth + 1, + foundDatabases = foundDatabases, + ) + } else { + // If it's a file, check if it's a database file + if (file.isFile && + !file.name.endsWith("-wal") && // Write-Ahead Log + !file.name.endsWith("-shm") && // Shared-Memory + !file.name.endsWith("-journal") // Older journaling mode + ) { + foundDatabases.add( + FloconFileDatabaseModel( + id = file.absolutePath, // Use absolute path for unique ID + displayName = file.name, + absolutePath = file.absolutePath + ) + ) + } + } + } + } + + companion object { + private const val MAX_DEPTH = 7 + } +} \ No newline at end of file diff --git a/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseConfig.kt b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseConfig.kt new file mode 100644 index 000000000..f34d1d51d --- /dev/null +++ b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseConfig.kt @@ -0,0 +1,25 @@ +package io.github.openflocon.flocon.database.room + +import io.github.openflocon.flocon.database.core.FloconDatabaseConfig +import io.github.openflocon.flocon.dsl.FloconMarker + +class FloconRoomDatabaseConfig internal constructor() { + internal val paths: MutableList = mutableListOf() + + fun path(path: String) { + paths.add(path) + } + +} + +@OptIn(FloconMarker::class) +fun FloconDatabaseConfig.room(block: FloconRoomDatabaseConfig.() -> Unit = {}) { + val config = FloconRoomDatabaseConfig().apply(block) + + providers.add( + FloconRoomDatabaseProviderImpl( + context = context, + paths = config.paths + ) + ) +} \ No newline at end of file diff --git a/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt new file mode 100644 index 000000000..ce1ad90c5 --- /dev/null +++ b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseModel.kt @@ -0,0 +1,133 @@ +package io.github.openflocon.flocon.database.room + +import androidx.room.RoomDatabase +import androidx.room.Transactor +import androidx.room.execSQL +import androidx.room.useReaderConnection +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteResponse +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse + +fun floconRegisterDatabase(displayName: String, database: RoomDatabase) { + Flocon.databasePlugin.register( + FloconRoomDatabaseModel( + id = displayName, + displayName = displayName, + database = database + ) + ) +} + +fun floconLogDatabaseQuery(databaseName: String, sqlQuery: String, bindArgs: List) { + Flocon.databasePlugin.logQuery( + dbName = databaseName, + sqlQuery = sqlQuery, + bindArgs = bindArgs, + ) +} + +internal data class FloconRoomDatabaseModel( + override val id: String, + override val displayName: String, + val database: RoomDatabase +) : FloconDatabaseModel { + + override suspend fun executeQuery(query: String): DatabaseExecuteResponse { + return try { + database.useReaderConnection { connection -> + val firstWordUpperCase = getFirstWord(query).uppercase() + + when (firstWordUpperCase) { + "SELECT", + "PRAGMA", + "EXPLAIN" -> executeSelect( + connection = connection, + query = query + ) + + "INSERT" -> executeInsert( + connection = connection, + query = query + ) + + "UPDATE", + "DELETE" -> executeUpdateDelete( + connection = connection, + query = query + ) + + else -> executeRawQuery( + connection = connection, + query = query + ) + } + } + } catch (t: Throwable) { + DatabaseExecuteSqlResponse.Error( + message = t.message ?: "error on executeSQL", + originalSql = query, + ) + } + } +} + +private suspend fun executeSelect( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + return connection.usePrepared(query) { statement -> + val columnNames = mutableListOf() + val columnCount = statement.getColumnCount() + for (i in 0 until columnCount) { + columnNames.add(statement.getColumnName(i)) + } + + val rows = mutableListOf>() + while (statement.step()) { + val values = mutableListOf() + for (i in 0 until columnCount) { + values.add(if (statement.isNull(i)) null else statement.getText(i)) + } + rows.add(values) + } + + DatabaseExecuteSqlResponse.Select( + columns = columnNames, + values = rows + ) + } +} + +private suspend fun executeInsert( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + connection.execSQL(query) + val id = connection.usePrepared("SELECT last_insert_rowid()") { it.step(); it.getLong(0) } + return DatabaseExecuteSqlResponse.Insert(id) +} + +private suspend fun executeUpdateDelete( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + connection.execSQL(query) + val count = connection.usePrepared("SELECT changes()") { it.step(); it.getLong(0).toInt() } + return DatabaseExecuteSqlResponse.UpdateDelete(count) +} + +private suspend fun executeRawQuery( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + connection.execSQL(query) + return DatabaseExecuteSqlResponse.RawSuccess +} + +private fun getFirstWord(s: String): String { + val trimmed = s.trim() + val firstSpace = trimmed.indexOf(' ') + return if (firstSpace >= 0) trimmed.substring(0, firstSpace) else trimmed +} \ No newline at end of file diff --git a/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.kt b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.kt new file mode 100644 index 000000000..112a4ee42 --- /dev/null +++ b/FloconAndroid/database/room/src/commonMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.kt @@ -0,0 +1,29 @@ +package io.github.openflocon.flocon.database.room + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.datasource.FloconDatabaseProvider +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +interface FloconRoomDatabaseProvider : FloconDatabaseProvider { + + fun register() + +} + +@OptIn(FloconMarker::class) +internal expect class FloconRoomDatabaseProviderImpl( + context: FloconContext, + paths: List +) : FloconRoomDatabaseProvider { + override fun register() + override fun getAllDataBases(registeredDatabases: List): List +} + +@OptIn(FloconMarker::class) +val Flocon.Companion.databaseRoom: FloconRoomDatabaseProvider + get() = databasePlugin.providers + .firstNotNullOfOrNull { it as? FloconRoomDatabaseProvider } + ?: error("Room database provider not initialized") \ No newline at end of file diff --git a/FloconAndroid/database/room/src/main/java/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt b/FloconAndroid/database/room/src/main/java/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt new file mode 100644 index 000000000..5c12df0bf --- /dev/null +++ b/FloconAndroid/database/room/src/main/java/io/github/openflocon/flocon/database/room/extensions/RoomBuilderExt.kt @@ -0,0 +1,25 @@ +package io.github.openflocon.flocon.database.room.extensions + +import androidx.room.RoomDatabase +import androidx.room.RoomDatabase.QueryCallback +import io.github.openflocon.flocon.database.room.floconLogDatabaseQuery +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +inline fun RoomDatabase.Builder.floconLogs( + name: String? = T::class.simpleName, + executor: Executor = Executors.newSingleThreadExecutor(), + queryCallback: QueryCallback? = null +): RoomDatabase.Builder { + return setQueryCallback( + queryCallback = { sqlQuery, bindArgs -> + floconLogDatabaseQuery( + databaseName = name ?: "database", + sqlQuery = sqlQuery, + bindArgs = bindArgs + ) + queryCallback?.onQuery(sqlQuery, bindArgs) + }, + executor = executor + ) +} \ No newline at end of file diff --git a/FloconAndroid/ktor-interceptor-no-op/build.gradle.kts b/FloconAndroid/database/room3-no-op/build.gradle.kts similarity index 86% rename from FloconAndroid/ktor-interceptor-no-op/build.gradle.kts rename to FloconAndroid/database/room3-no-op/build.gradle.kts index 3f2d23cd9..a251c1dcf 100644 --- a/FloconAndroid/ktor-interceptor-no-op/build.gradle.kts +++ b/FloconAndroid/database/room3-no-op/build.gradle.kts @@ -19,11 +19,18 @@ kotlin { iosArm64() iosSimulatorArm64() + wasmJs { + moduleName = "flocon_database_room3_no_op" + browser() + binaries.executable() + } + sourceSets { val commonMain by getting { dependencies { - implementation(project(":flocon-base")) + implementation(project(":network:core-no-op")) implementation(libs.ktor.client.core) + implementation(project(":database:core-no-op")) } } @@ -40,6 +47,7 @@ kotlin { val iosX64Main by getting val iosArm64Main by getting val iosSimulatorArm64Main by getting + val wasmJsMain by getting val iosMain by creating { dependsOn(commonMain) iosX64Main.dependsOn(this) @@ -50,14 +58,11 @@ kotlin { } android { - namespace = "io.github.openflocon.flocon.ktor" + namespace = "io.github.openflocon.flocon.database.room3.noop" compileSdk = 36 defaultConfig { minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") } buildTypes { @@ -69,14 +74,12 @@ android { ) } } - compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } - mavenPublishing { publishToMavenCentral(automaticRelease = true) @@ -88,13 +91,12 @@ mavenPublishing { coordinates( groupId = project.property("floconGroupId") as String, - artifactId = "flocon-ktor-interceptor-no-op", + artifactId = "flocon-database-room3-no-op", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - pom { - name = "Flocon Ktor Interceptor No Op" + name = "Flocon Room 3 Implementation No-Op" description = project.property("floconDescription") as String inceptionYear = "2025" url = "https://github.com/openflocon/Flocon" @@ -118,4 +120,4 @@ mavenPublishing { developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" } } -} \ No newline at end of file +} diff --git a/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/floconRegisterDatabase.kt b/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/floconRegisterDatabase.kt new file mode 100644 index 000000000..e5f4b0a89 --- /dev/null +++ b/FloconAndroid/database/room3-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/floconRegisterDatabase.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.database.room3 + +fun floconRegisterDatabase(displayName: String, database: Any) { + // no op +} + +fun floconRegisterDatabase(displayName: String, openHelper: Any, dummy: Boolean = true) { + // no op +} diff --git a/FloconAndroid/okhttp-interceptor/build.gradle.kts b/FloconAndroid/database/room3/build.gradle.kts similarity index 54% rename from FloconAndroid/okhttp-interceptor/build.gradle.kts rename to FloconAndroid/database/room3/build.gradle.kts index 118ccbcbd..5e26920c7 100644 --- a/FloconAndroid/okhttp-interceptor/build.gradle.kts +++ b/FloconAndroid/database/room3/build.gradle.kts @@ -1,60 +1,74 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { + alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" + alias(libs.plugins.vanniktech.maven.publish) + alias(libs.plugins.androidx.room) + alias(libs.plugins.ksp) } -android { - namespace = "io.github.openflocon.flocon.okhttp" - compileSdk = 36 +kotlin { + androidTarget { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + jvm() - defaultConfig { - minSdk = 23 + iosArm64() + iosSimulatorArm64() - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") + wasmJs { + moduleName = "flocon_database_room3" + browser() + binaries.executable() } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.androidx.room3.runtime) + implementation(libs.androidx.sqlite) + implementation(project(":database:core")) + } } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + val wasmJsMain by getting { + dependsOn(commonMain) + } } } -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } +room { + schemaDirectory("$projectDir/schemas") } dependencies { + // KSP for Room + add("kspCommonMainMetadata", libs.androidx.room3.compiler) + add("kspAndroid", libs.androidx.room3.compiler) + add("kspJvm", libs.androidx.room3.compiler) + add("kspIosArm64", libs.androidx.room3.compiler) + add("kspIosSimulatorArm64", libs.androidx.room3.compiler) +} - implementation(project(":flocon-base")) - - implementation(platform(libs.kotlinx.coroutines.bom)) - implementation(libs.jetbrains.kotlinx.coroutines.core) - implementation(libs.jetbrains.kotlinx.coroutines.android) - - implementation(platform(libs.okhttp.bom)) - implementation(libs.okhttp3.okhttp) - implementation(libs.brotli.dec) +android { + namespace = "io.github.openflocon.flocon.database.room3" + compileSdk = 36 - testImplementation(libs.junit) + defaultConfig { + minSdk = 23 + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } } - mavenPublishing { publishToMavenCentral(automaticRelease = true) @@ -66,12 +80,12 @@ mavenPublishing { coordinates( groupId = project.property("floconGroupId") as String, - artifactId = "flocon-okhttp-interceptor", + artifactId = "flocon-database-room3", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) pom { - name = "Flocon OkHttp Interceptor" + name = "Flocon Room 3 Implementation" description = project.property("floconDescription") as String inceptionYear = "2025" url = "https://github.com/openflocon/Flocon" @@ -95,4 +109,4 @@ mavenPublishing { developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" } } -} \ No newline at end of file +} diff --git a/FloconAndroid/database/room3/src/androidMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.android.kt b/FloconAndroid/database/room3/src/androidMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.android.kt new file mode 100644 index 000000000..4dcc4d16b --- /dev/null +++ b/FloconAndroid/database/room3/src/androidMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.android.kt @@ -0,0 +1,77 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.database.core.model.FloconFileDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker +import java.io.File + +@OptIn(markerClass = [FloconMarker::class]) +internal actual class FloconRoom3DatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoom3DatabaseProvider { + + actual override fun register() { + val databases = getAllDataBases(emptyList()) + databases.forEach { Flocon.databasePlugin.register(it) } + } + + @FloconMarker + actual override fun getAllDataBases(registeredDatabases: List): List { + val databasesDir = context.context.getDatabasePath("dummy_db") + .parentFile + ?: return emptyList() + + val foundDatabases = mutableListOf() + // Start the recursive search from the base databases directory + scanDirectoryForDatabases( + directory = databasesDir, + depth = 0, + foundDatabases = foundDatabases + ) + + return foundDatabases + } + + private fun scanDirectoryForDatabases( + directory: File, + depth: Int, + foundDatabases: MutableList + ) { + if (depth >= MAX_DEPTH) { + return + } + directory.listFiles()?.forEach { file -> + if (file.isDirectory) { + // If it's a directory, recursively call this function + scanDirectoryForDatabases( + directory = file, + depth = depth + 1, + foundDatabases = foundDatabases, + ) + } else { + // If it's a file, check if it's a database file + if (file.isFile && + !file.name.endsWith("-wal") && // Write-Ahead Log + !file.name.endsWith("-shm") && // Shared-Memory + !file.name.endsWith("-journal") // Older journaling mode + ) { + foundDatabases.add( + FloconFileDatabaseModel( + id = file.absolutePath, // Use absolute path for unique ID + displayName = file.name, + absolutePath = file.absolutePath + ) + ) + } + } + } + } + + companion object { + private const val MAX_DEPTH = 7 + } +} \ No newline at end of file diff --git a/FloconAndroid/database/room3/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.kt b/FloconAndroid/database/room3/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.kt new file mode 100644 index 000000000..0c5fa278b --- /dev/null +++ b/FloconAndroid/database/room3/src/commonMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.kt @@ -0,0 +1,29 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.datasource.FloconDatabaseProvider +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +interface FloconRoom3DatabaseProvider : FloconDatabaseProvider { + + fun register() + +} + +@OptIn(FloconMarker::class) +internal expect class FloconRoom3DatabaseProviderImpl( + context: FloconContext, + paths: List +) : FloconRoom3DatabaseProvider { + override fun register() + override fun getAllDataBases(registeredDatabases: List): List +} + +@OptIn(FloconMarker::class) +val Flocon.Companion.databaseRoom3: FloconRoom3DatabaseProvider + get() = databasePlugin.providers + .firstNotNullOfOrNull { it as? FloconRoom3DatabaseProvider } + ?: error("Room3 database provider not initialized") \ No newline at end of file diff --git a/FloconAndroid/database/room3/src/iosMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.ios.kt b/FloconAndroid/database/room3/src/iosMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.ios.kt new file mode 100644 index 000000000..4d0537c54 --- /dev/null +++ b/FloconAndroid/database/room3/src/iosMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.ios.kt @@ -0,0 +1,24 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +@OptIn(markerClass = [FloconMarker::class]) +internal actual class FloconRoom3DatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoom3DatabaseProvider { + + actual override fun register() { + val databases = getAllDataBases(emptyList()) + databases.forEach { Flocon.databasePlugin.register(it) } + } + + @FloconMarker + actual override fun getAllDataBases(registeredDatabases: List): List { + return emptyList() + } +} diff --git a/FloconAndroid/database/room3/src/jvmMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.jvm.kt b/FloconAndroid/database/room3/src/jvmMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.jvm.kt new file mode 100644 index 000000000..4d0537c54 --- /dev/null +++ b/FloconAndroid/database/room3/src/jvmMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.jvm.kt @@ -0,0 +1,24 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +@OptIn(markerClass = [FloconMarker::class]) +internal actual class FloconRoom3DatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoom3DatabaseProvider { + + actual override fun register() { + val databases = getAllDataBases(emptyList()) + databases.forEach { Flocon.databasePlugin.register(it) } + } + + @FloconMarker + actual override fun getAllDataBases(registeredDatabases: List): List { + return emptyList() + } +} diff --git a/FloconAndroid/database/room3/src/main/java/io/github/openflocon/flocon/database/room3/extensions/Room3BuilderExt.kt b/FloconAndroid/database/room3/src/main/java/io/github/openflocon/flocon/database/room3/extensions/Room3BuilderExt.kt new file mode 100644 index 000000000..2b0f27d8f --- /dev/null +++ b/FloconAndroid/database/room3/src/main/java/io/github/openflocon/flocon/database/room3/extensions/Room3BuilderExt.kt @@ -0,0 +1,15 @@ +package io.github.openflocon.flocon.database.room3.extensions + +import androidx.room3.RoomDatabase +import java.util.concurrent.Executor +import java.util.concurrent.Executors + +inline fun RoomDatabase.Builder.floconLogs( + name: String? = T::class.simpleName, + executor: Executor = Executors.newSingleThreadExecutor(), + // QueryCallback is removed or altered in Room 3. + queryCallback: Any? = null +): RoomDatabase.Builder { + // TODO: Room 3 removed or changed QueryCallback. Logging must be rewritten for Room 3. + return this +} \ No newline at end of file diff --git a/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseConfig.kt b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseConfig.kt new file mode 100644 index 000000000..fd361d636 --- /dev/null +++ b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseConfig.kt @@ -0,0 +1,25 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.database.core.FloconDatabaseConfig +import io.github.openflocon.flocon.dsl.FloconMarker + +class FloconRoom3DatabaseConfig internal constructor() { + internal val paths: MutableList = mutableListOf() + + fun path(path: String) { + paths.add(path) + } + +} + +@OptIn(FloconMarker::class) +fun FloconDatabaseConfig.room(block: FloconRoom3DatabaseConfig.() -> Unit = {}) { + val config = FloconRoom3DatabaseConfig().apply(block) + + providers.add( + FloconRoom3DatabaseProviderImpl( + context = context, + paths = config.paths + ) + ) +} \ No newline at end of file diff --git a/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt new file mode 100644 index 000000000..b811ac716 --- /dev/null +++ b/FloconAndroid/database/room3/src/roomMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseModel.kt @@ -0,0 +1,133 @@ +package io.github.openflocon.flocon.database.room3 + +import androidx.room3.RoomDatabase +import androidx.room3.Transactor +import androidx.room3.executeSQL +import androidx.room3.useReaderConnection +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.database.core.databasePlugin +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteResponse +import io.github.openflocon.flocon.database.core.model.fromdevice.DatabaseExecuteSqlResponse + +fun floconRegisterDatabase(displayName: String, database: RoomDatabase) { + Flocon.databasePlugin.register( + FloconRoom3DatabaseModel( + id = displayName, + displayName = displayName, + database = database + ) + ) +} + +fun floconLogDatabaseQuery(databaseName: String, sqlQuery: String, bindArgs: List) { + Flocon.databasePlugin.logQuery( + dbName = databaseName, + sqlQuery = sqlQuery, + bindArgs = bindArgs, + ) +} + +internal data class FloconRoom3DatabaseModel( + override val id: String, + override val displayName: String, + val database: RoomDatabase +) : FloconDatabaseModel { + + override suspend fun executeQuery(query: String): DatabaseExecuteResponse { + return try { + database.useReaderConnection { connection -> + val firstWordUpperCase = getFirstWord(query).uppercase() + + when (firstWordUpperCase) { + "SELECT", + "PRAGMA", + "EXPLAIN" -> executeSelect( + connection = connection, + query = query + ) + + "INSERT" -> executeInsert( + connection = connection, + query = query + ) + + "UPDATE", + "DELETE" -> executeUpdateDelete( + connection = connection, + query = query + ) + + else -> executeRawQuery( + connection = connection, + query = query + ) + } + } + } catch (t: Throwable) { + DatabaseExecuteSqlResponse.Error( + message = t.message ?: "error on executeSQL", + originalSql = query, + ) + } + } +} + +private suspend fun executeSelect( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + return connection.usePrepared(query) { statement -> + val columnNames = mutableListOf() + val columnCount = statement.getColumnCount() + for (i in 0 until columnCount) { + columnNames.add(statement.getColumnName(i)) + } + + val rows = mutableListOf>() + while (statement.step()) { + val values = mutableListOf() + for (i in 0 until columnCount) { + values.add(if (statement.isNull(i)) null else statement.getText(i)) + } + rows.add(values) + } + + DatabaseExecuteSqlResponse.Select( + columns = columnNames, + values = rows + ) + } +} + +private suspend fun executeInsert( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + connection.executeSQL(query) + val id = connection.usePrepared("SELECT last_insert_rowid()") { it.step(); it.getLong(0) } + return DatabaseExecuteSqlResponse.Insert(id) +} + +private suspend fun executeUpdateDelete( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + connection.executeSQL(query) + val count = connection.usePrepared("SELECT changes()") { it.step(); it.getLong(0).toInt() } + return DatabaseExecuteSqlResponse.UpdateDelete(count) +} + +private suspend fun executeRawQuery( + connection: Transactor, + query: String, +): DatabaseExecuteSqlResponse { + connection.executeSQL(query) + return DatabaseExecuteSqlResponse.RawSuccess +} + +private fun getFirstWord(s: String): String { + val trimmed = s.trim() + val firstSpace = trimmed.indexOf(' ') + return if (firstSpace >= 0) trimmed.substring(0, firstSpace) else trimmed +} \ No newline at end of file diff --git a/FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.wasmJs.kt b/FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.wasmJs.kt new file mode 100644 index 000000000..ffadd900b --- /dev/null +++ b/FloconAndroid/database/room3/src/wasmJsMain/kotlin/io/github/openflocon/flocon/database/room3/FloconRoom3DatabaseProviderImpl.wasmJs.kt @@ -0,0 +1,20 @@ +package io.github.openflocon.flocon.database.room3 + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +@OptIn(markerClass = [FloconMarker::class]) +internal actual class FloconRoom3DatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoom3DatabaseProvider { + + actual override fun register() { + } + + @FloconMarker + actual override fun getAllDataBases(registeredDatabases: List): List { + return emptyList() + } +} diff --git a/FloconAndroid/datastores-no-op/build.gradle.kts b/FloconAndroid/datastores-no-op/build.gradle.kts index 859f94eac..e65ef303d 100644 --- a/FloconAndroid/datastores-no-op/build.gradle.kts +++ b/FloconAndroid/datastores-no-op/build.gradle.kts @@ -1,93 +1,56 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" + id("flocon.kotlin.multiplatform") + id("flocon.publish") } -android { - namespace = "io.github.openflocon.flocon.datastores" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") +kotlin { + wasmJs { + moduleName = "flocon_datastores_no_op" + browser() + binaries.executable() } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + implementation(projects.sharedprefs) + implementation(libs.kotlinx.coroutines.core) + } } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + 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) + } } } -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } +android { + namespace = "io.github.openflocon.flocon.datastores" } + dependencies { - implementation(project(":flocon-base")) + implementation(projects.flocon) implementation(platform(libs.kotlinx.coroutines.bom)) - implementation(libs.jetbrains.kotlinx.coroutines.core) + implementation(libs.kotlinx.coroutines.core) implementation(libs.androidx.datastore.preferences) } mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } - coordinates( groupId = project.property("floconGroupId") as String, artifactId = "flocon-datastores-no-op", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - - pom { - name = "Flocon Datastores Integration No Op" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } -} \ No newline at end of file +} diff --git a/FloconAndroid/datastores-no-op/src/main/AndroidManifest.xml b/FloconAndroid/datastores-no-op/src/androidMain/AndroidManifest.xml similarity index 100% rename from FloconAndroid/datastores-no-op/src/main/AndroidManifest.xml rename to FloconAndroid/datastores-no-op/src/androidMain/AndroidManifest.xml diff --git a/FloconAndroid/datastores-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt b/FloconAndroid/datastores-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt new file mode 100644 index 000000000..52d23e6b7 --- /dev/null +++ b/FloconAndroid/datastores-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt @@ -0,0 +1,31 @@ +package io.github.openflocon.flocon.preferences.datastores.model + +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreference +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreferenceValue + +interface FloconDatastoreMapper { + fun fromDatastore(datastoreValue: String) : String + fun toDatastore(valueForDatastore: String) : String +} + +class FloconDatastorePreference( + override val name: String, + @Suppress("UNUSED_PARAMETER") dataStore: Any? = null, +) : io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreference { + + override suspend fun set( + columnName: String, + value: io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreferenceValue + ) { + // no op + } + + override suspend fun columns(): List { + return emptyList() // no op + } + + override suspend fun get(columnName: String): io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreferenceValue? { + return null // no op + } + +} \ No newline at end of file diff --git a/FloconAndroid/datastores-no-op/src/main/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt b/FloconAndroid/datastores-no-op/src/main/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt deleted file mode 100644 index 885a2fdf3..000000000 --- a/FloconAndroid/datastores-no-op/src/main/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.openflocon.flocon.preferences.datastores.model - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.doublePreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.floatPreferencesKey -import androidx.datastore.preferences.core.intPreferencesKey -import androidx.datastore.preferences.core.longPreferencesKey -import androidx.datastore.preferences.core.stringPreferencesKey -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreferenceValue -import kotlinx.coroutines.flow.first - -interface FloconDatastoreMapper { - fun fromDatastore(datastoreValue: String) : String - fun toDatastore(valueForDatastore: String) : String -} - -class FloconDatastorePreference( - override val name: String, - val dataStore: DataStore, -) : FloconPreference { - - override suspend fun set( - columnName: String, - value: FloconPreferenceValue - ) { - // no op - } - - override suspend fun columns(): List { - return emptyList() // no op - } - - override suspend fun get(columnName: String): FloconPreferenceValue? { - return null // no op - } - -} \ No newline at end of file diff --git a/FloconAndroid/datastores/build.gradle.kts b/FloconAndroid/datastores/build.gradle.kts index 5f784f502..afcdc2b4c 100644 --- a/FloconAndroid/datastores/build.gradle.kts +++ b/FloconAndroid/datastores/build.gradle.kts @@ -1,94 +1,47 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" + id("flocon.kotlin.multiplatform") + id("flocon.publish") } -android { - namespace = "io.github.openflocon.flocon.datastores" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") +kotlin { + wasmJs { + moduleName = "flocon_datastores" + browser() + binaries.executable() } - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + implementation(projects.sharedprefs) + implementation(libs.kotlinx.coroutines.core) + } } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + val iosX64Main by getting + val iosArm64Main by getting + val iosSimulatorArm64Main by getting + val iosMain by creating { + dependsOn(commonMain) + dependencies { + implementation(libs.androidx.datastore.preferences) + } + iosX64Main.dependsOn(this) + iosArm64Main.dependsOn(this) + iosSimulatorArm64Main.dependsOn(this) + } } } -dependencies { - - implementation(project(":flocon-base")) - - implementation(platform(libs.kotlinx.coroutines.bom)) - implementation(libs.jetbrains.kotlinx.coroutines.core) - implementation(libs.jetbrains.kotlinx.coroutines.android) - - implementation(libs.androidx.datastore.preferences) +android { + namespace = "io.github.openflocon.flocon.datastores" } - mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } - coordinates( groupId = project.property("floconGroupId") as String, artifactId = "flocon-datastores", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - - pom { - name = "Flocon Datastores Integration" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } -} \ No newline at end of file +} diff --git a/FloconAndroid/datastores/src/main/AndroidManifest.xml b/FloconAndroid/datastores/src/androidMain/AndroidManifest.xml similarity index 100% rename from FloconAndroid/datastores/src/main/AndroidManifest.xml rename to FloconAndroid/datastores/src/androidMain/AndroidManifest.xml diff --git a/FloconAndroid/datastores/src/commonMain/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt b/FloconAndroid/datastores/src/commonMain/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt new file mode 100644 index 000000000..12f2ce12e --- /dev/null +++ b/FloconAndroid/datastores/src/commonMain/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt @@ -0,0 +1,90 @@ +package io.github.openflocon.flocon.preferences.datastores.model + +//import androidx.datastore.core.DataStore +//import androidx.datastore.preferences.core.Preferences +//import androidx.datastore.preferences.core.booleanPreferencesKey +//import androidx.datastore.preferences.core.doublePreferencesKey +//import androidx.datastore.preferences.core.edit +//import androidx.datastore.preferences.core.floatPreferencesKey +//import androidx.datastore.preferences.core.intPreferencesKey +//import androidx.datastore.preferences.core.longPreferencesKey +//import androidx.datastore.preferences.core.stringPreferencesKey +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreference +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreferenceValue +import kotlinx.coroutines.flow.first + +interface FloconDatastoreMapper { + fun fromDatastore(datastoreValue: String) : String + fun toDatastore(valueForDatastore: String) : String +} + +//class FloconDatastorePreference( +// override val name: String, +// private val dataStore: DataStore, +// private val mapper: FloconDatastoreMapper? = null // if we encrypted the datastore +//) : FloconPreference { +// +// override suspend fun set( +// columnName: String, +// FloconPreferenceValue +// ) { +// try { +// val data = dataStore.data.first().asMap() +// val key = data.keys.find { it.name == columnName } ?: return +// +// dataStore.edit { +// try { +// when (it[key]) { +// is String -> it[stringPreferencesKey(columnName)] = mapper?.let { +// it.toDatastore(value.stringValue!!) +// } ?: value.stringValue!! +// is Int -> it[intPreferencesKey(columnName)] = value.intValue!! +// is Boolean -> it[booleanPreferencesKey(columnName)] = value.booleanValue!! +// is Float -> it[floatPreferencesKey(columnName)] = value.floatValue!! +// is Long -> it[longPreferencesKey(columnName)] = value.longValue!! +// is Double -> it[doublePreferencesKey(columnName)] = +// value.floatValue!!.toDouble() +// } +// } catch (t: Throwable) { +// FloconLogger.logError("cannot update datastore preference", t) +// } +// } +// } catch (t: Throwable) { +// FloconLogger.logError(t.message ?: "cannot edit datastore preference columns", t) +// } +// } +// +// override suspend fun columns(): List { +// return try { +// dataStore.data.first().asMap().map { it.key.name } +// } catch (t: Throwable) { +// FloconLogger.logError(t.message ?: "cannot get datastore preference columns", t) +// emptyList() +// } +// } +// +// override suspend fun get(columnName: String): io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreferenceValue? { +// return try { +// val data = dataStore.data.first().asMap() +// val key = data.keys.find { it.name == columnName } ?: return null +// val value = data[key] ?: return null +// +// return when (value) { +// is String -> FloconPreferenceValue( +// stringValue = mapper?.fromDatastore(value) ?: value +// ) +// is Int -> FloconPreferenceValue(intValue = value) +// is Float -> FloconPreferenceValue(floatValue = value) +// is Double -> FloconPreferenceValue(floatValue = value.toFloat()) +// is Boolean -> FloconPreferenceValue(booleanValue = value) +// is Long -> FloconPreferenceValue(longValue = value) +// else -> null +// } +// } catch (t: Throwable) { +// FloconLogger.logError(t.message ?: "cannot get datastore preference value", t) +// null +// } +// } +// +//} \ No newline at end of file diff --git a/FloconAndroid/datastores/src/main/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt b/FloconAndroid/datastores/src/main/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt deleted file mode 100644 index a6b77fa6c..000000000 --- a/FloconAndroid/datastores/src/main/kotlin/io/github/openflocon/flocon/preferences/datastores/model/FloconDatastorePreference.kt +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.openflocon.flocon.preferences.datastores.model - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.doublePreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.floatPreferencesKey -import androidx.datastore.preferences.core.intPreferencesKey -import androidx.datastore.preferences.core.longPreferencesKey -import androidx.datastore.preferences.core.stringPreferencesKey -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreferenceValue -import kotlinx.coroutines.flow.first - -interface FloconDatastoreMapper { - fun fromDatastore(datastoreValue: String) : String - fun toDatastore(valueForDatastore: String) : String -} - -class FloconDatastorePreference( - override val name: String, - private val dataStore: DataStore, - private val mapper: FloconDatastoreMapper? = null // if we encrypted the datastore -) : FloconPreference { - - override suspend fun set( - columnName: String, - value: FloconPreferenceValue - ) { - try { - val data = dataStore.data.first().asMap() - val key = data.keys.find { it.name == columnName } ?: return - - dataStore.edit { - try { - when (it[key]) { - is String -> it[stringPreferencesKey(columnName)] = mapper?.let { - it.toDatastore(value.stringValue!!) - } ?: value.stringValue!! - is Int -> it[intPreferencesKey(columnName)] = value.intValue!! - is Boolean -> it[booleanPreferencesKey(columnName)] = value.booleanValue!! - is Float -> it[floatPreferencesKey(columnName)] = value.floatValue!! - is Long -> it[longPreferencesKey(columnName)] = value.longValue!! - is Double -> it[doublePreferencesKey(columnName)] = - value.floatValue!!.toDouble() - } - } catch (t: Throwable) { - FloconLogger.logError("cannot update datastore preference", t) - } - } - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "cannot edit datastore preference columns", t) - } - } - - override suspend fun columns(): List { - return try { - dataStore.data.first().asMap().map { it.key.name } - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "cannot get datastore preference columns", t) - emptyList() - } - } - - override suspend fun get(columnName: String): FloconPreferenceValue? { - return try { - val data = dataStore.data.first().asMap() - val key = data.keys.find { it.name == columnName } ?: return null - val value = data[key] ?: return null - - return when (value) { - is String -> FloconPreferenceValue(stringValue = mapper?.fromDatastore(value) ?: value) - is Int -> FloconPreferenceValue(intValue = value) - is Float -> FloconPreferenceValue(floatValue = value) - is Double -> FloconPreferenceValue(floatValue = value.toFloat()) - is Boolean -> FloconPreferenceValue(booleanValue = value) - is Long -> FloconPreferenceValue(longValue = value) - else -> null - } - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "cannot get datastore preference value", t) - null - } - } - -} \ No newline at end of file diff --git a/FloconAndroid/deeplinks-no-op/build.gradle.kts b/FloconAndroid/deeplinks-no-op/build.gradle.kts new file mode 100644 index 000000000..4ab3d4ec4 --- /dev/null +++ b/FloconAndroid/deeplinks-no-op/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + wasmJs { + moduleName = "flocon_deeplinks_no_op" + binaries.executable() + browser() + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + implementation(libs.kotlinx.coroutines.core) + } + } + + 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) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.deeplinks.noop" +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-deeplinks-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/deeplinks-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksNoOp.kt b/FloconAndroid/deeplinks-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksNoOp.kt new file mode 100644 index 000000000..daf00d0ed --- /dev/null +++ b/FloconAndroid/deeplinks-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksNoOp.kt @@ -0,0 +1,62 @@ +package io.github.openflocon.flocon.deeplinks + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.core.FloconEncoder + +class DeeplinkModel + +class DeeplinkVariable + +class DeeplinkLinkBuilder internal constructor(private val link: String) { + var label: String? = null + var description: String? = null + + infix fun String.withAutoComplete(suggestions: List) {} + infix fun String.withVariable(variableName: String) {} +} + +class DeeplinkVariableBuilder internal constructor(private val name: String) { + var description: String? = null + fun autoComplete(suggestions: List) {} +} + +abstract class FloconDeeplinksConfig : FloconPluginConfig { + abstract fun variable(name: String, block: DeeplinkVariableBuilder.() -> Unit = {}) + abstract fun deeplink(link: String, block: DeeplinkLinkBuilder.() -> Unit = {}) + internal abstract fun deeplinks(): List + internal abstract fun variables(): List +} + +internal class FloconDeeplinksConfigImpl : FloconDeeplinksConfig() { + override fun variable(name: String, block: DeeplinkVariableBuilder.() -> Unit) {} + override fun deeplink(link: String, block: DeeplinkLinkBuilder.() -> Unit) {} + override fun deeplinks(): List = emptyList() + override fun variables(): List = emptyList() +} + +interface FloconDeeplinksPlugin : FloconPlugin + +object FloconDeeplinks : FloconPluginFactory { + override val name: String = "Deeplinks" + override val pluginId: String = "FloconDeeplinks" + + override fun createConfig(context: FloconContext): FloconDeeplinksConfig = FloconDeeplinksConfigImpl() + + override fun install( + pluginConfig: FloconDeeplinksConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconDeeplinksPlugin { + return FloconDeeplinksPluginNoOp + } +} + +private object FloconDeeplinksPluginNoOp : FloconDeeplinksPlugin { + override val key: String = "DEEP_LINK" + override suspend fun onMessageReceived(method: String, body: String) {} + override suspend fun onConnectedToServer() {} +} diff --git a/FloconAndroid/deeplinks/build.gradle.kts b/FloconAndroid/deeplinks/build.gradle.kts new file mode 100644 index 000000000..b29a68d10 --- /dev/null +++ b/FloconAndroid/deeplinks/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") + alias(libs.plugins.kotlin.serialization) +} + +kotlin { + wasmJs { + moduleName = "flocon_deeplinks" + binaries.executable() + browser() + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + + 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) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.deeplinks" +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-deeplinks", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinkEncoding.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinkEncoding.kt new file mode 100644 index 000000000..47c95538c --- /dev/null +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinkEncoding.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.flocon.deeplinks + +import io.github.openflocon.flocon.FloconEncoding +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.deeplinks.model.DeeplinkParameterRemote +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass + +@FloconMarker +internal class FloconDeeplinkEncoding : FloconEncoding { + override val serializersModule: SerializersModule = SerializersModule { + polymorphic(DeeplinkParameterRemote::class) { + subclass(DeeplinkParameterRemote.AutoComplete::class) + subclass(DeeplinkParameterRemote.Variable::class) + } + } +} \ No newline at end of file diff --git a/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinks.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinks.kt new file mode 100644 index 000000000..16ba46096 --- /dev/null +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinks.kt @@ -0,0 +1,79 @@ +package io.github.openflocon.flocon.deeplinks + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconEncoding +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.core.encode +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.deeplinks.model.DeeplinkModel + +object FloconDeeplinks : FloconPluginFactory { + override val name: String = "Deeplinks" + override val pluginId: String = FloconDeeplinks::class.simpleName!! + override fun createConfig(context: FloconContext): FloconDeeplinksConfig = + FloconDeeplinksConfigImpl() + + @FloconMarker + override fun createEncoding(): FloconEncoding = FloconDeeplinkEncoding() + + @OptIn(FloconMarker::class) + override fun install( + pluginConfig: FloconDeeplinksConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconDeeplinksPlugin { + val plugin = FloconDeeplinksPluginImpl( + deeplinks = pluginConfig.deeplinks(), + variables = pluginConfig.variables(), + sender = floconConfig.client as FloconMessageSender, + encoder = encoder + ) + + return plugin + } +} + +internal class FloconDeeplinksPluginImpl( + private val deeplinks: List, + private val variables: List, + private val sender: FloconMessageSender, + private val encoder: FloconEncoder +) : FloconPlugin, FloconDeeplinksPlugin { + override val key: String = "DEEP_LINK" + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + registerDeeplinks( + deeplinks = deeplinks, + variables = variables + ) + } + + fun registerDeeplinks( + deeplinks: List, + variables: List + ) { + try { + sender.send( + plugin = Protocol.FromDevice.Deeplink.Plugin, + method = Protocol.FromDevice.Deeplink.Method.GetDeeplinks, + body = encoder.encode(createRemote(deeplinks = deeplinks, variables = variables)) + ) + } catch (t: Throwable) { + t.printStackTrace() + FloconLogger.logError("deeplink mapping error", t) + } + } +} diff --git a/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinksConfig.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinksConfig.kt new file mode 100644 index 000000000..2188911d6 --- /dev/null +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinksConfig.kt @@ -0,0 +1,37 @@ +package io.github.openflocon.flocon.deeplinks + +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.deeplinks.model.DeeplinkModel + +abstract class FloconDeeplinksConfig : FloconPluginConfig { + abstract fun variable(name: String, block: DeeplinkVariableBuilder.() -> Unit = {}) + + abstract fun deeplink(link: String, block: DeeplinkLinkBuilder.() -> Unit = {}) + + internal abstract fun deeplinks(): List + internal abstract fun variables(): List +} + +internal class FloconDeeplinksConfigImpl internal constructor() : FloconDeeplinksConfig() { + + private val variables = mutableListOf() + private val deeplinks = mutableListOf() + + override fun variable(name: String, block: DeeplinkVariableBuilder.() -> Unit) { + val variable = DeeplinkVariableBuilder(name).apply(block) + .build() + + variables.add(variable) + } + + override fun deeplink(link: String, block: DeeplinkLinkBuilder.() -> Unit) { + val deeplink = DeeplinkLinkBuilder(link).apply(block) + .build() + + deeplinks.add(deeplink) + } + + override fun deeplinks(): List = deeplinks.toList() + override fun variables(): List = variables.toList() + +} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksPlugin.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinksPlugin.kt similarity index 51% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksPlugin.kt rename to FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinksPlugin.kt index e1bfbad78..935b1a319 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksPlugin.kt +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/FloconDeeplinksPlugin.kt @@ -1,7 +1,7 @@ -package io.github.openflocon.flocon.plugins.deeplinks +package io.github.openflocon.flocon.deeplinks -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkModel +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.deeplinks.model.DeeplinkModel class DeeplinkLinkBuilder internal constructor( private val link: String @@ -13,14 +13,14 @@ class DeeplinkLinkBuilder internal constructor( infix fun String.withAutoComplete(suggestions: List) { parameters[this] = DeeplinkModel.Parameter.AutoComplete( - paramName = this, - suggestions.distinct() + name = this, + autoComplete = suggestions.distinct() ) } infix fun String.withVariable(variableName: String) { parameters[this] = DeeplinkModel.Parameter.Variable( - paramName = this, + name = this, variableName = variableName ) } @@ -69,44 +69,4 @@ data class DeeplinkVariable( } -class DeeplinkBuilder { - private val variables = mutableListOf() - private val deeplinks = mutableListOf() - - fun variable(name: String, block: DeeplinkVariableBuilder.() -> Unit = {}) { - val variable = DeeplinkVariableBuilder(name).apply(block) - .build() - - variables.add(variable) - } - - fun deeplink(link: String, block: DeeplinkLinkBuilder.() -> Unit = {}) { - val deeplink = DeeplinkLinkBuilder(link).apply(block) - .build() - - deeplinks.add(deeplink) - } - - internal fun deeplinks(): List = deeplinks.toList() - internal fun variables(): List = variables.toList() -} - -fun FloconApp.deeplinks(deeplinksBlock: DeeplinkBuilder.() -> Unit) { - this.client?.deeplinksPlugin?.let { - val builder = DeeplinkBuilder().apply(deeplinksBlock) - - it.registerDeeplinks( - deeplinks = builder.deeplinks(), - variables = builder.variables() - ) - } -} - -interface FloconDeeplinksPlugin { - - fun registerDeeplinks( - deeplinks: List, - variables: List - ) - -} \ No newline at end of file +interface FloconDeeplinksPlugin : FloconPlugin \ No newline at end of file diff --git a/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/Mapping.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/Mapping.kt new file mode 100644 index 000000000..4fbe5c66d --- /dev/null +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/Mapping.kt @@ -0,0 +1,47 @@ +package io.github.openflocon.flocon.deeplinks + +import io.github.openflocon.flocon.deeplinks.model.DeeplinkModel +import io.github.openflocon.flocon.deeplinks.model.DeeplinkParameterRemote +import io.github.openflocon.flocon.deeplinks.model.DeeplinkRemote +import io.github.openflocon.flocon.deeplinks.model.DeeplinkVariableRemote +import io.github.openflocon.flocon.deeplinks.model.DeeplinksRemote + +internal fun createRemote( + deeplinks: List, + variables: List +) = DeeplinksRemote( + deeplinks = deeplinks.map(DeeplinkModel::toRemote), + variables = variables.map(DeeplinkVariable::toRemote) +) + +internal fun DeeplinkModel.toRemote(): DeeplinkRemote = DeeplinkRemote( + label = label, + link = link, + description = description, + parameters = parameters.map { it.toRemote() } +) + +internal fun DeeplinkModel.Parameter.toRemote(): DeeplinkParameterRemote = when (this) { + is DeeplinkModel.Parameter.AutoComplete -> DeeplinkParameterRemote.AutoComplete( + name = name, + autoComplete = autoComplete + ) + + is DeeplinkModel.Parameter.Variable -> DeeplinkParameterRemote.Variable( + name = name, + variableName = variableName + ) +} + +internal fun DeeplinkVariable.toRemote() = DeeplinkVariableRemote( + name = name, + mode = mode.toRemote() +) + +internal fun DeeplinkVariable.Mode.toRemote() = when (this) { + is DeeplinkVariable.Mode.AutoComplete -> DeeplinkVariableRemote.Mode.AutoComplete( + suggestions = suggestions + ) + + DeeplinkVariable.Mode.Input -> DeeplinkVariableRemote.Mode.Input +} \ No newline at end of file diff --git a/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/model/DeeplinkModel.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/model/DeeplinkModel.kt new file mode 100644 index 000000000..aa3d60d59 --- /dev/null +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/model/DeeplinkModel.kt @@ -0,0 +1,25 @@ +package io.github.openflocon.flocon.deeplinks.model + +@ConsistentCopyVisibility +data class DeeplinkModel internal constructor( + val link: String, + val label: String? = null, + val description: String? = null, + val parameters: List +) { + sealed interface Parameter { + val name: String + + @ConsistentCopyVisibility + data class AutoComplete internal constructor( + override val name: String, + val autoComplete: List + ) : Parameter + + @ConsistentCopyVisibility + data class Variable internal constructor( + override val name: String, + val variableName: String + ) : Parameter + } +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/model/DeeplinksRemote.kt b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/model/DeeplinksRemote.kt similarity index 77% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/model/DeeplinksRemote.kt rename to FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/model/DeeplinksRemote.kt index e6e9015cc..9e5be525e 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/model/DeeplinksRemote.kt +++ b/FloconAndroid/deeplinks/src/commonMain/kotlin/io/github/openflocon/flocon/deeplinks/model/DeeplinksRemote.kt @@ -1,38 +1,21 @@ -@file:OptIn(ExperimentalSerializationApi::class) +package io.github.openflocon.flocon.deeplinks.model -package io.github.openflocon.flocon.plugins.deeplinks.model - -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonClassDiscriminator -@Serializable -@JsonClassDiscriminator("type") -internal sealed interface DeeplinkParameterRemote { - val name: String - - @Serializable - @SerialName("auto_complete") - data class AutoComplete( - override val name: String, - val autoComplete: List - ) : DeeplinkParameterRemote - - @Serializable - @SerialName("variable") - data class Variable( - override val name: String, - val variableName: String - ) : DeeplinkParameterRemote -} - @Serializable internal class DeeplinkRemote( val label: String? = null, val link: String, val description: String? = null, - val parameters: List + val parameters: List, +) + +@Serializable +internal class DeeplinksRemote( + val deeplinks: List, + val variables: List ) @Serializable @@ -43,23 +26,32 @@ internal data class DeeplinkVariableRemote( ) { @Serializable - @JsonClassDiscriminator("type") + @JsonClassDiscriminator("mode") sealed interface Mode { - - @Serializable @SerialName("input") - data object Input : Mode + object Input : Mode - @Serializable @SerialName("auto_complete") data class AutoComplete(val suggestions: List) : Mode - } } -@Serializable -internal class DeeplinksRemote( - val deeplinks: List, - val variables: List -) +@JsonClassDiscriminator("type") +internal sealed interface DeeplinkParameterRemote { + val name: String + + @Serializable + @SerialName("auto_complete") + data class AutoComplete( + override val name: String, + val autoComplete: List + ) : DeeplinkParameterRemote + + @Serializable + @SerialName("variable") + data class Variable( + override val name: String, + val variableName: String + ) : DeeplinkParameterRemote +} \ No newline at end of file diff --git a/FloconAndroid/device-no-op/build.gradle.kts b/FloconAndroid/device-no-op/build.gradle.kts new file mode 100644 index 000000000..44390215f --- /dev/null +++ b/FloconAndroid/device-no-op/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.device.noop" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-device-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/device-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePlugin.kt b/FloconAndroid/device-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePlugin.kt new file mode 100644 index 000000000..6e7bcd408 --- /dev/null +++ b/FloconAndroid/device-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePlugin.kt @@ -0,0 +1,47 @@ +package io.github.openflocon.flocon.plugins.device + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder + +class FloconDeviceConfig : FloconPluginConfig + +interface FloconDevicePlugin : FloconPlugin { + fun registerWithSerial(serial: String) +} + +object FloconDevice : FloconPluginFactory { + override val name: String = "Device" + override val pluginId: String = Protocol.ToDevice.Device.Plugin + override fun createConfig(context: FloconContext) = FloconDeviceConfig() + override fun install( + pluginConfig: FloconDeviceConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconDevicePlugin { + return FloconDevicePluginNoOpImpl() + } +} + +internal class FloconDevicePluginNoOpImpl : FloconPlugin, FloconDevicePlugin { + override val key: String = "DEVICE" + + override fun registerWithSerial(serial: String) { + // no op + } + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } +} diff --git a/FloconAndroid/device/build.gradle.kts b/FloconAndroid/device/build.gradle.kts new file mode 100644 index 000000000..1bbf9f756 --- /dev/null +++ b/FloconAndroid/device/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.device" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-device", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.android.kt b/FloconAndroid/device/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.android.kt similarity index 59% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.android.kt rename to FloconAndroid/device/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.android.kt index 14ce93b10..d7c3cc2ce 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.android.kt +++ b/FloconAndroid/device/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.android.kt @@ -1,8 +1,6 @@ package io.github.openflocon.flocon.plugins.device -import com.jakewharton.processphoenix.ProcessPhoenix import io.github.openflocon.flocon.FloconContext internal actual fun restartApp(context: FloconContext) { - ProcessPhoenix.triggerRebirth(context.appContext) } \ No newline at end of file diff --git a/FloconAndroid/device/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.android.kt b/FloconAndroid/device/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.android.kt new file mode 100644 index 000000000..09e6ec220 --- /dev/null +++ b/FloconAndroid/device/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.android.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.plugins.device + +import io.github.openflocon.flocon.FloconContext + +actual fun getAppIconBase64(context: FloconContext): String? { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt b/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt new file mode 100644 index 000000000..1d7ef5463 --- /dev/null +++ b/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt @@ -0,0 +1,80 @@ +package io.github.openflocon.flocon.plugins.device + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender + +class FloconDeviceConfig : FloconPluginConfig + +interface FloconDevicePlugin : FloconPlugin { + fun registerWithSerial(serial: String) +} + +object FloconDevice : FloconPluginFactory { + override val name: String = "Device" + override val pluginId: String = Protocol.ToDevice.Device.Plugin + override fun createConfig(context: FloconContext) = FloconDeviceConfig() + override fun install( + pluginConfig: FloconDeviceConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconDevicePlugin { + return FloconDevicePluginImpl( + sender = floconConfig.client as FloconMessageSender, + context = floconConfig.context + ) + } +} + +internal expect fun restartApp(context: FloconContext) + +internal class FloconDevicePluginImpl( + private var sender: FloconMessageSender, + private val context: FloconContext, +) : FloconPlugin, FloconDevicePlugin { + override val key: String = "DEVICE" + + override fun registerWithSerial(serial: String) { + try { +// sender.send( +// plugin = Protocol.FromDevice.Device.Plugin, +// method = Protocol.FromDevice.Device.Method.RegisterDevice, +// body = RegisterDeviceDataModel(serial).toJson().toString(), +// ) + } catch (t: Throwable) { + FloconLogger.logError("Device parsing error", t) + } + } + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + when (method) { + Protocol.ToDevice.Device.Method.GetAppIcon -> { + val icon = getAppIconBase64(context) + if (icon != null) { +// sender.send( +// plugin = Protocol.FromDevice.Device.Plugin, +// method = Protocol.FromDevice.Device.Method.AppIcon, +// body = icon, +// ) + } + } + + Protocol.ToDevice.Device.Method.RestartApp -> { + restartApp(context) + } + } + } + + override suspend fun onConnectedToServer() { + // no op + } +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt b/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt similarity index 100% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt rename to FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt diff --git a/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/model/fromdevice/RegisterDeviceDataModel.kt b/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/model/fromdevice/RegisterDeviceDataModel.kt new file mode 100644 index 000000000..e34d9e75b --- /dev/null +++ b/FloconAndroid/device/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/model/fromdevice/RegisterDeviceDataModel.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.plugins.device.model.fromdevice + +import kotlinx.serialization.Serializable + +@Serializable +internal class RegisterDeviceDataModel( + val serial: String +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.ios.kt b/FloconAndroid/device/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.ios.kt similarity index 100% rename from FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.ios.kt rename to FloconAndroid/device/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.ios.kt diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.ios.kt b/FloconAndroid/device/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.ios.kt similarity index 100% rename from FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.ios.kt rename to FloconAndroid/device/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.ios.kt diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.jvm.kt b/FloconAndroid/device/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.jvm.kt similarity index 100% rename from FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.jvm.kt rename to FloconAndroid/device/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.jvm.kt diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.jvm.kt b/FloconAndroid/device/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.jvm.kt similarity index 100% rename from FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.jvm.kt rename to FloconAndroid/device/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.jvm.kt diff --git a/FloconAndroid/device/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.wasmJs.kt b/FloconAndroid/device/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.wasmJs.kt new file mode 100644 index 000000000..93191f721 --- /dev/null +++ b/FloconAndroid/device/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.wasmJs.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon.plugins.device + +import io.github.openflocon.flocon.FloconContext + +internal actual fun restartApp(context: FloconContext) { +} diff --git a/FloconAndroid/device/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.wasmJs.kt b/FloconAndroid/device/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.wasmJs.kt new file mode 100644 index 000000000..cb477e7a2 --- /dev/null +++ b/FloconAndroid/device/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.wasmJs.kt @@ -0,0 +1,5 @@ +package io.github.openflocon.flocon.plugins.device + +import io.github.openflocon.flocon.FloconContext + +actual fun getAppIconBase64(context: FloconContext): String? = null diff --git a/FloconAndroid/files-no-op/build.gradle.kts b/FloconAndroid/files-no-op/build.gradle.kts new file mode 100644 index 000000000..558379a24 --- /dev/null +++ b/FloconAndroid/files-no-op/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.files.noop" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-files-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/files-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt b/FloconAndroid/files-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt new file mode 100644 index 000000000..7fb37ec01 --- /dev/null +++ b/FloconAndroid/files-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt @@ -0,0 +1,43 @@ +package io.github.openflocon.flocon.plugins.files + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder + +class FloconFilesConfig : FloconPluginConfig { + val roots = mutableListOf() +} + +interface FloconFilesPlugin : FloconPlugin + +object FloconFiles : FloconPluginFactory { + override val name: String = "Files" + override val pluginId: String = Protocol.ToDevice.Files.Plugin + override fun createConfig(context: FloconContext) = FloconFilesConfig() + override fun install( + pluginConfig: FloconFilesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconFilesPlugin { + return FloconFilesPluginNoOpImpl() + } +} + +internal class FloconFilesPluginNoOpImpl : FloconPlugin, FloconFilesPlugin { + override val key: String = "FILES" + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } +} diff --git a/FloconAndroid/files/build.gradle.kts b/FloconAndroid/files/build.gradle.kts new file mode 100644 index 000000000..1686a517e --- /dev/null +++ b/FloconAndroid/files/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.files" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-files", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/files/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.android.kt b/FloconAndroid/files/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.android.kt new file mode 100644 index 000000000..f6e708b5c --- /dev/null +++ b/FloconAndroid/files/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.android.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.plugins.files + +import io.github.openflocon.flocon.FloconContext + +internal actual fun fileDataSource(context: FloconContext): FileDataSource { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.android.kt b/FloconAndroid/files/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/files/FloconFilesPlugin.android.kt similarity index 86% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.android.kt rename to FloconAndroid/files/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/files/FloconFilesPlugin.android.kt index 840f97db4..435aa072d 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.android.kt +++ b/FloconAndroid/files/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/files/FloconFilesPlugin.android.kt @@ -1,22 +1,26 @@ -package io.github.openflocon.flocon.plugins.files +package io.github.openflocon.flocon.pluginsold.files import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconFile import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.plugins.files.FileDataSource import io.github.openflocon.flocon.plugins.files.model.fromdevice.FileDataModel import java.io.File internal class FileDataSourceAndroid( private val context: FloconContext, ) : FileDataSource { + + @FloconMarker override fun getFile( path: String, isConstantPath: Boolean ): FloconFile? { val file = if (isConstantPath) { when (path) { - "caches" -> context.appContext.cacheDir - "files" -> context.appContext.filesDir + "caches" -> context.context.cacheDir + "files" -> context.context.filesDir else -> File(path) } } else { @@ -25,6 +29,7 @@ internal class FileDataSourceAndroid( return file.takeIf { it.exists() }?.let { FloconFile(it) } } + @FloconMarker override fun getFolderContent( path: String, isConstantPath: Boolean, @@ -85,6 +90,7 @@ internal class FileDataSourceAndroid( } } + @FloconMarker override fun deleteFolderContent(folder: FloconFile) { deleteFolderContent(folder.file) } @@ -98,4 +104,4 @@ internal class FileDataSourceAndroid( } } -internal actual fun fileDataSource(context: FloconContext) : FileDataSource = FileDataSourceAndroid(context) \ No newline at end of file +//internal actual fun fileDataSource(context: FloconContext) : FileDataSource = FileDataSourceAndroid(context) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt similarity index 57% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt rename to FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt index 6be68c2d8..9910d3dfd 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt @@ -1,16 +1,19 @@ package io.github.openflocon.flocon.plugins.files +import io.github.openflocon.flocon.FloconConfig import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconFile import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder import io.github.openflocon.flocon.core.FloconFileSender import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconFileInfo -import io.github.openflocon.flocon.model.FloconMessageFromServer +import io.github.openflocon.flocon.core.decode +import io.github.openflocon.flocon.dsl.FloconMarker import io.github.openflocon.flocon.plugins.files.model.fromdevice.FileDataModel -import io.github.openflocon.flocon.plugins.files.model.fromdevice.FilesResultDataModel import io.github.openflocon.flocon.plugins.files.model.todevice.ToDeviceDeleteFileMessage import io.github.openflocon.flocon.plugins.files.model.todevice.ToDeviceDeleteFilesMessage import io.github.openflocon.flocon.plugins.files.model.todevice.ToDeviceDeleteFolderContentMessage @@ -19,31 +22,70 @@ import io.github.openflocon.flocon.plugins.files.model.todevice.ToDeviceGetFiles import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +class FloconFilesConfig : FloconPluginConfig { + val roots = mutableListOf() +} + +interface FloconFilesPlugin : FloconPlugin + +object FloconFiles : FloconPluginFactory { + override val name: String = "Files" + override val pluginId: String = Protocol.ToDevice.Files.Plugin + override fun createConfig(context: FloconContext) = FloconFilesConfig() + override fun install( + pluginConfig: FloconFilesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconFilesPlugin { + val client = floconConfig.client + return FloconFilesPluginImpl( + context = floconConfig.context, + floconFileSender = client as FloconFileSender, + sender = client as FloconMessageSender, + encoder = encoder + ) + } +} + internal interface FileDataSource { + + @FloconMarker fun getFile(path: String, isConstantPath: Boolean): FloconFile? - fun getFolderContent(path: String, isConstantPath: Boolean, withFoldersSize: Boolean): List + + fun getFolderContent( + path: String, + isConstantPath: Boolean, + withFoldersSize: Boolean + ): List + fun deleteFile(path: String) fun deleteFiles(path: List) + + @FloconMarker fun deleteFolderContent(folder: FloconFile) } -internal expect fun fileDataSource(context: FloconContext) : FileDataSource +internal expect fun fileDataSource(context: FloconContext): FileDataSource internal class FloconFilesPluginImpl( private val context: FloconContext, private val floconFileSender: FloconFileSender, private val sender: FloconMessageSender, + private val encoder: FloconEncoder ) : FloconPlugin, FloconFilesPlugin { + override val key: String = "FILES" private val fileDataSource = fileDataSource(context) private val withFoldersSize = MutableStateFlow(false) - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, + @FloconMarker + override suspend fun onMessageReceived( + method: String, + body: String, ) { - when (messageFromServer.method) { + when (method) { Protocol.ToDevice.Files.Method.ListFiles -> { - val listFilesMessage = ToDeviceGetFilesMessage.fromJson(message = messageFromServer.body) ?: return + val listFilesMessage = encoder.decode(body) ?: return withFoldersSize.update { listFilesMessage.withFoldersSize } @@ -55,22 +97,22 @@ internal class FloconFilesPluginImpl( } Protocol.ToDevice.Files.Method.GetFile -> { - val getFileMessage = ToDeviceGetFileMessage.fromJson(message = messageFromServer.body) ?: return - - fileDataSource.getFile(path = getFileMessage.path, isConstantPath = false)?.let { file -> - floconFileSender.send( - file = file, - infos = FloconFileInfo( - requestId = getFileMessage.requestId, - path = getFileMessage.path, - ) - ) - } + val getFileMessage = encoder.decode(body) ?: return + + fileDataSource.getFile(path = getFileMessage.path, isConstantPath = false) + ?.let { file -> +// floconFileSender.send( +// file = file, +// infos = FloconFileInfo( +// requestId = getFileMessage.requestId, +// path = getFileMessage.path, +// ) +// ) + } } Protocol.ToDevice.Files.Method.DeleteFile -> { - val deleteFilesMessage = - ToDeviceDeleteFileMessage.fromJson(message = messageFromServer.body) ?: return + val deleteFilesMessage = encoder.decode(body) ?: return fileDataSource.deleteFile( path = deleteFilesMessage.filePath, @@ -84,8 +126,7 @@ internal class FloconFilesPluginImpl( } Protocol.ToDevice.Files.Method.DeleteFiles -> { - val deleteFilesMessage = - ToDeviceDeleteFilesMessage.fromJson(message = messageFromServer.body) ?: return + val deleteFilesMessage = encoder.decode(body) ?: return fileDataSource.deleteFiles( path = deleteFilesMessage.filePaths, @@ -99,9 +140,7 @@ internal class FloconFilesPluginImpl( } Protocol.ToDevice.Files.Method.DeleteFolderContent -> { - val deleteFolderContentMessage = - ToDeviceDeleteFolderContentMessage.fromJson(message = messageFromServer.body) - ?: return + val deleteFolderContentMessage = encoder.decode(body) ?: return fileDataSource.getFile( path = deleteFolderContentMessage.path, @@ -126,27 +165,27 @@ internal class FloconFilesPluginImpl( isConstantPath: Boolean, requestId: String, ) { - val files = fileDataSource.getFolderContent( + fileDataSource.getFolderContent( path = path, isConstantPath = isConstantPath, withFoldersSize = withFoldersSize.value, ) try { - sender.send( - plugin = Protocol.FromDevice.Files.Plugin, - method = Protocol.FromDevice.Files.Method.ListFiles, - body = FilesResultDataModel( - requestId = requestId, - files = files, - ).toJson(), - ) +// sender.send( +// plugin = Protocol.FromDevice.Files.Plugin, +// method = Protocol.FromDevice.Files.Method.ListFiles, +// body = FilesResultDataModel( +// requestId = requestId, +// files = files, +// ).toJson(), +// ) } catch (t: Throwable) { FloconLogger.logError("File parsing error", t) } } - override fun onConnectedToServer() { + override suspend fun onConnectedToServer() { // no op } } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FileDataModel.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FileDataModel.kt similarity index 100% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FileDataModel.kt rename to FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FileDataModel.kt diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FilesResultDataModel.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FilesResultDataModel.kt similarity index 54% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FilesResultDataModel.kt rename to FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FilesResultDataModel.kt index 1d4400323..768ac68de 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FilesResultDataModel.kt +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/fromdevice/FilesResultDataModel.kt @@ -1,15 +1,9 @@ package io.github.openflocon.flocon.plugins.files.model.fromdevice -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString @Serializable internal data class FilesResultDataModel( val requestId: String, val files: List, -) { - fun toJson(): String { - return FloconEncoder.json.encodeToString(this) - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFileMessage.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFileMessage.kt new file mode 100644 index 000000000..9332cca48 --- /dev/null +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFileMessage.kt @@ -0,0 +1,12 @@ +package io.github.openflocon.flocon.plugins.files.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceDeleteFileMessage( + val requestId: String, + val parentPath: String, + val filePath: String, + val isConstantParentPath: Boolean, // ex: context.files / context.caches +) + diff --git a/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFilesMessage.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFilesMessage.kt new file mode 100644 index 000000000..757dddda7 --- /dev/null +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFilesMessage.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.plugins.files.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceDeleteFilesMessage( + val requestId: String, + val parentPath: String, + val filePaths: List, + val isConstantParentPath: Boolean, // ex: context.files / context.caches +) \ No newline at end of file diff --git a/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFolderContentMessage.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFolderContentMessage.kt new file mode 100644 index 000000000..677eab215 --- /dev/null +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFolderContentMessage.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.plugins.files.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceDeleteFolderContentMessage( + val requestId: String, + val path: String, + val isConstantPath: Boolean, // ex: context.files / context.caches +) + diff --git a/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFileMessage.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFileMessage.kt new file mode 100644 index 000000000..6c4ae0260 --- /dev/null +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFileMessage.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.plugins.files.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceGetFileMessage( + val requestId: String, + val path: String, +) \ No newline at end of file diff --git a/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFilesMessage.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFilesMessage.kt new file mode 100644 index 000000000..97774eb4a --- /dev/null +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFilesMessage.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.flocon.plugins.files.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceGetFilesMessage( + val requestId: String, + val path: String, + val isConstantPath: Boolean, // ex: context.files / context.caches + val withFoldersSize: Boolean = false, +) + + diff --git a/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/files/FloconFilesPlugin.kt b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/files/FloconFilesPlugin.kt new file mode 100644 index 000000000..82f56b8f3 --- /dev/null +++ b/FloconAndroid/files/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/files/FloconFilesPlugin.kt @@ -0,0 +1,37 @@ +package io.github.openflocon.flocon.pluginsold.files + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.core.FloconEncoder + +class FloconFilesConfig : FloconPluginConfig { + val roots = mutableListOf() +} + +/** + * Flocon Files Plugin. + * Used to inspect and download files from the device. + */ +object FloconFiles : FloconPluginFactory { + override fun createConfig(context: FloconContext): FloconFilesConfig { + TODO("Not yet implemented") + } + + override fun install( + pluginConfig: FloconFilesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconFilesPlugin { + TODO("Not yet implemented") + } + + override val name: String + get() = TODO("Not yet implemented") + override val pluginId: String + get() = TODO("Not yet implemented") +} + +interface FloconFilesPlugin : FloconPlugin \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.ios.kt b/FloconAndroid/files/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.ios.kt similarity index 90% rename from FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.ios.kt rename to FloconAndroid/files/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.ios.kt index ffa1dbc9a..1c7d1ef58 100644 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.ios.kt +++ b/FloconAndroid/files/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.ios.kt @@ -2,6 +2,7 @@ package io.github.openflocon.flocon.plugins.files import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconFile +import io.github.openflocon.flocon.dsl.FloconMarker import io.github.openflocon.flocon.plugins.files.model.fromdevice.FileDataModel internal actual fun fileDataSource(context: FloconContext): FileDataSource { @@ -9,6 +10,7 @@ internal actual fun fileDataSource(context: FloconContext): FileDataSource { } // TODO +@io.github.openflocon.flocon.dsl.FloconMarker internal class FileDataSourceIOs : FileDataSource { override fun getFile( path: String, diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.jvm.kt b/FloconAndroid/files/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.jvm.kt similarity index 100% rename from FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.jvm.kt rename to FloconAndroid/files/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.jvm.kt diff --git a/FloconAndroid/files/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.wasmJs.kt b/FloconAndroid/files/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.wasmJs.kt new file mode 100644 index 000000000..41a60203b --- /dev/null +++ b/FloconAndroid/files/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.wasmJs.kt @@ -0,0 +1,22 @@ +package io.github.openflocon.flocon.plugins.files + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconFile +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.plugins.files.model.fromdevice.FileDataModel + +internal actual fun fileDataSource(context: FloconContext): FileDataSource = FloconFilesDataSourceWasmJs() + +private class FloconFilesDataSourceWasmJs : FileDataSource { + @FloconMarker + override fun getFolderContent(path: String, isConstantPath: Boolean, withFoldersSize: Boolean): List = emptyList() + + @FloconMarker + override fun getFile(path: String, isConstantPath: Boolean): FloconFile? = null + + override fun deleteFile(path: String) {} + override fun deleteFiles(path: List) {} + + @FloconMarker + override fun deleteFolderContent(folder: FloconFile) {} +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/FloconApp.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/FloconApp.kt deleted file mode 100644 index 2add1f3ba..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/FloconApp.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.openflocon.flocon - -import io.github.openflocon.flocon.plugins.analytics.FloconAnalyticsPlugin -import io.github.openflocon.flocon.plugins.crashreporter.FloconCrashReporterPlugin -import io.github.openflocon.flocon.plugins.dashboard.FloconDashboardPlugin -import io.github.openflocon.flocon.plugins.database.FloconDatabasePlugin -import io.github.openflocon.flocon.plugins.deeplinks.FloconDeeplinksPlugin -import io.github.openflocon.flocon.plugins.device.FloconDevicePlugin -import io.github.openflocon.flocon.plugins.network.FloconNetworkPlugin -import io.github.openflocon.flocon.plugins.sharedprefs.FloconPreferencesPlugin -import io.github.openflocon.flocon.plugins.tables.FloconTablePlugin -import kotlinx.coroutines.flow.StateFlow - -abstract class FloconApp { - - companion object { - var instance: FloconApp? = null - private set - } - - interface Client { - - @Throws(Throwable::class) - suspend fun connect(onClosed: () -> Unit) - suspend fun disconnect() - - val databasePlugin: FloconDatabasePlugin - val dashboardPlugin: FloconDashboardPlugin - val tablePlugin: FloconTablePlugin - val deeplinksPlugin: FloconDeeplinksPlugin - val analyticsPlugin: FloconAnalyticsPlugin - val networkPlugin: FloconNetworkPlugin - val devicePlugin: FloconDevicePlugin - val preferencesPlugin: FloconPreferencesPlugin - val crashReporterPlugin: FloconCrashReporterPlugin - } - - open val client: Client? = null - - abstract val isInitialized : StateFlow - - protected fun initializeFlocon() { - instance = this - } - -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsPlugin.kt deleted file mode 100644 index ffc29c3ff..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsPlugin.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.openflocon.flocon.plugins.analytics - -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.analytics.builder.AnalyticsBuilder -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsItem - -fun floconAnalytics(analyticsName: String) : AnalyticsBuilder { - return AnalyticsBuilder( - analyticsTableId = analyticsName, - analyticsPlugin = FloconApp.instance?.client?.analyticsPlugin, - ) -} - -fun FloconApp.analytics(analyticsName: String): AnalyticsBuilder { - return AnalyticsBuilder( - analyticsTableId = analyticsName, - analyticsPlugin = this.client?.analyticsPlugin, - ) -} - -interface FloconAnalyticsPlugin { - fun registerAnalytics(analyticsItems: List) -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterPlugin.kt deleted file mode 100644 index 1c0f5c435..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/FloconCrashReporterPlugin.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.github.openflocon.flocon.plugins.crashreporter - -interface FloconCrashReporterPlugin \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt deleted file mode 100644 index 20ac34fb7..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.openflocon.flocon.plugins.dashboard - -import io.github.openflocon.flocon.plugins.dashboard.model.DashboardConfig - -interface FloconDashboardPlugin { - fun registerDashboard(dashboardConfig: DashboardConfig) -} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt deleted file mode 100644 index a300972b0..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.openflocon.flocon.plugins.database - -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.database.model.FloconDatabaseModel -import io.github.openflocon.flocon.plugins.database.model.FloconFileDatabaseModel - -fun floconRegisterDatabase(database: FloconDatabaseModel) { - FloconApp.instance?.client?.databasePlugin?.register( - database - ) -} - -fun floconRegisterDatabase(displayName: String, absolutePath: String) { - floconRegisterDatabase( - FloconFileDatabaseModel( - displayName = displayName, - absolutePath = absolutePath, - ) - ) -} - -fun floconLogDatabaseQuery(dbName: String, sqlQuery: String, bindArgs: List) { - FloconApp.instance?.client?.databasePlugin?.logQuery( - dbName = dbName, - sqlQuery = sqlQuery, - bindArgs = bindArgs, - ) -} - -interface FloconDatabasePlugin { - fun register(floconDatabaseModel: FloconDatabaseModel) - fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/FloconDatabaseModel.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/FloconDatabaseModel.kt deleted file mode 100644 index b75ab65f6..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/FloconDatabaseModel.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.openflocon.flocon.plugins.database.model - -interface FloconDatabaseModel { - val displayName: String -} - -data class FloconFileDatabaseModel( - override val displayName: String, - val absolutePath: String -) : FloconDatabaseModel \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/model/DeeplinkModel.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/model/DeeplinkModel.kt deleted file mode 100644 index 29533742c..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/model/DeeplinkModel.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.openflocon.flocon.plugins.deeplinks.model - -data class DeeplinkModel( - val link: String, - val label: String? = null, - val description: String? = null, - val parameters: List, -) { - - sealed interface Parameter { - val paramName: String - - data class AutoComplete( - override val paramName: String, - val autoComplete: List - ) : Parameter - - data class Variable( - override val paramName: String, - val variableName: String - ) : Parameter - - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePlugin.kt deleted file mode 100644 index 37beaf2ae..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePlugin.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.openflocon.flocon.plugins.device - -interface FloconDevicePlugin { - fun registerWithSerial(serial: String) -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt deleted file mode 100644 index 34f3cf447..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/FloconFilesPlugin.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.github.openflocon.flocon.plugins.files - -interface FloconFilesPlugin \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPlugin.kt deleted file mode 100644 index 76daf51fe..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPlugin.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.openflocon.flocon.plugins.network - -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketEvent -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketMockListener -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse - -fun floconLogWebSocketEvent(event: FloconWebSocketEvent) { - FloconApp.instance?.client?.networkPlugin?.logWebSocket(event) -} - -interface FloconNetworkPlugin { - val mocks: Collection - val badQualityConfig: BadQualityConfig? - - fun logRequest(request: FloconNetworkCallRequest) - fun logResponse(response: FloconNetworkCallResponse) - - fun logWebSocket( - event: FloconWebSocketEvent, - ) - - fun registerWebSocketMockListener(id: String, listener: FloconWebSocketMockListener) -} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt deleted file mode 100644 index e3893af2d..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs - -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference - -fun floconRegisterPreference(preference: FloconPreference) { - FloconApp.instance?.client?.preferencesPlugin?.register(preference) -} - -interface FloconPreferencesPlugin { - fun register(preference: FloconPreference) -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesPlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesPlugin.kt deleted file mode 100644 index 188adf89e..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesPlugin.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.openflocon.flocon.plugins.tables - -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.tables.builder.TableBuilder -import io.github.openflocon.flocon.plugins.tables.model.TableItem - -fun floconTable(tableName: String): TableBuilder { - return TableBuilder( - tableName = tableName, - tablePlugin = FloconApp.instance?.client?.tablePlugin, - ) -} - -fun FloconApp.table(tableName: String): TableBuilder { - return TableBuilder( - tableName = tableName, - tablePlugin = this.client?.tablePlugin, - ) -} - -interface FloconTablePlugin { - fun registerTable(tableItem: TableItem) -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/builder/TableBuilder.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/builder/TableBuilder.kt deleted file mode 100644 index 8e7f22a26..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/builder/TableBuilder.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:OptIn(ExperimentalUuidApi::class) - -package io.github.openflocon.flocon.plugins.tables.builder - -import io.github.openflocon.flocon.plugins.tables.FloconTablePlugin -import io.github.openflocon.flocon.plugins.tables.model.TableColumnConfig -import io.github.openflocon.flocon.plugins.tables.model.TableItem -import io.github.openflocon.flocon.utils.currentTimeMillis -import kotlin.uuid.ExperimentalUuidApi -import kotlin.uuid.Uuid - -class TableBuilder( - val tableName: String, - private val tablePlugin: FloconTablePlugin?, -) { - fun log(vararg columns: TableColumnConfig) { - val dashboardConfig = TableItem( - id = Uuid.random().toString(), - name = tableName, - columns = columns.toList(), - createdAt = currentTimeMillis(), - ) - tablePlugin?.registerTable(dashboardConfig) - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableColumnConfig.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableColumnConfig.kt deleted file mode 100644 index bfb85182d..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableColumnConfig.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.openflocon.flocon.plugins.tables.model - -data class TableColumnConfig( - val columnName: String, - val value: String, -) - -infix fun String.toParam(value: String) = TableColumnConfig( - columnName = this, - value = value, -) \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt deleted file mode 100644 index f4635bd90..000000000 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.openflocon.flocon.plugins.tables.model - -data class TableItem( - val id: String, - val name: String, - val createdAt: Long, - val columns: List, -) \ No newline at end of file diff --git a/FloconAndroid/flocon-no-op/build.gradle.kts b/FloconAndroid/flocon-no-op/build.gradle.kts index 963d4a6d6..445d245c4 100644 --- a/FloconAndroid/flocon-no-op/build.gradle.kts +++ b/FloconAndroid/flocon-no-op/build.gradle.kts @@ -1,39 +1,20 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - alias(libs.plugins.kotlin.multiplatform) - alias(libs.plugins.android.library) - alias(libs.plugins.vanniktech.maven.publish) + id("flocon.kotlin.multiplatform") + id("flocon.publish") } kotlin { - androidTarget { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } + wasmJs { + moduleName = "flocon_no_op" + binaries.executable() + browser() } - - jvm() - - iosX64() - iosArm64() - iosSimulatorArm64() sourceSets { val commonMain by getting { dependencies { - implementation(libs.jetbrains.kotlinx.coroutines.core.fixed) - api(project(":flocon-base")) - } - } - - val androidMain by getting { - dependencies { - } - } - - val jvmMain by getting { - dependencies { + api(projects.flocon) + implementation(libs.kotlinx.coroutines.core) } } @@ -51,68 +32,13 @@ kotlin { android { namespace = "io.github.openflocon.flocon" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } } -mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } +mavenPublishing { coordinates( groupId = project.property("floconGroupId") as String, artifactId = "flocon-no-op", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - - pom { - name = "Flocon No Op" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.android.kt b/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.android.kt index 7e6344375..9961ece8d 100644 --- a/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.android.kt +++ b/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.android.kt @@ -12,7 +12,7 @@ actual object Flocon : FloconApp() { // This is a no-op implementation @Suppress("UNUSED_PARAMETER") fun initialize(context: Context) { - initializeFlocon() + // no-op } } diff --git a/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPreference.kt b/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPreference.kt deleted file mode 100644 index d5be3322c..000000000 --- a/FloconAndroid/flocon-no-op/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPreference.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs - -import android.content.SharedPreferences -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreferenceValue - -data class FloconSharedPreference( - override val name: String, - val sharedPreferences: SharedPreferences, -) : FloconPreference { - - override suspend fun set( - columnName: String, - value: FloconPreferenceValue - ) { - // no op - } - - override suspend fun columns(): List { - return emptyList() // no op - } - - override suspend fun get(columnName: String): FloconPreferenceValue? { - return null // no op - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon-no-op/src/wasmJsMain/kotlin/io/github/openflocon/flocon/Flocon.wasmJs.kt b/FloconAndroid/flocon-no-op/src/wasmJsMain/kotlin/io/github/openflocon/flocon/Flocon.wasmJs.kt new file mode 100644 index 000000000..88207316f --- /dev/null +++ b/FloconAndroid/flocon-no-op/src/wasmJsMain/kotlin/io/github/openflocon/flocon/Flocon.wasmJs.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +actual object Flocon : FloconApp { + override val isInitialized: StateFlow = MutableStateFlow(false) +} diff --git a/FloconAndroid/flocon/build.gradle.kts b/FloconAndroid/flocon/build.gradle.kts index 337f2b1e9..960db4acb 100644 --- a/FloconAndroid/flocon/build.gradle.kts +++ b/FloconAndroid/flocon/build.gradle.kts @@ -1,46 +1,33 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - alias(libs.plugins.kotlin.multiplatform) - alias(libs.plugins.android.library) + id("flocon.kotlin.multiplatform") alias(libs.plugins.kotlin.serialization) - alias(libs.plugins.vanniktech.maven.publish) + id("flocon.publish") alias(libs.plugins.buildconfig) } kotlin { - androidTarget { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } + wasmJs { + moduleName = "flocon" + binaries.executable() + browser() } - - jvm() - - iosX64() - iosArm64() - iosSimulatorArm64() sourceSets { val commonMain by getting { dependencies { - implementation(libs.jetbrains.kotlinx.coroutines.core.fixed) + implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.serialization.json) - api(project(":flocon-base")) } } - + val androidMain by getting { dependencies { implementation(libs.kotlinx.coroutines.android) implementation(libs.jakewharton.process.phoenix) implementation("com.squareup.okhttp3:okhttp:4.12.0") - - implementation(libs.androidx.sqlite) - implementation(libs.androidx.sqlite.framework) } } - + val jvmMain by getting { dependencies { implementation(libs.ktor.client.core) @@ -68,10 +55,9 @@ kotlin { implementation(libs.ktor.client.logging) implementation(libs.ktor.serialization.kotlinx.json) - implementation(libs.androidx.sqlite.bundled) - // to store the device id implementation("com.russhwolf:multiplatform-settings:1.3.0") + implementation(libs.androidx.sqlite.bundled) } } } @@ -85,71 +71,15 @@ buildConfig { android { namespace = "io.github.openflocon.flocon" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") sourceSets["main"].res.srcDirs("src/androidMain/res") } mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } - coordinates( groupId = project.property("floconGroupId") as String, artifactId = "flocon", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - - pom { - name = "Flocon" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.kt index 5490c40e4..e951880c6 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/Flocon.kt @@ -1,25 +1,15 @@ package io.github.openflocon.flocon import android.content.Context -import io.github.openflocon.flocon.client.FloconClientImpl -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.plugins.analytics.FloconAnalyticsPlugin -import io.github.openflocon.flocon.plugins.dashboard.FloconDashboardPlugin -import io.github.openflocon.flocon.plugins.deeplinks.FloconDeeplinksPlugin -import io.github.openflocon.flocon.plugins.tables.FloconTablePlugin -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -object Flocon : FloconCore() { - fun initialize(context: Context) { - super.initializeFlocon( - FloconContext(appContext = context) - ) - } -} \ No newline at end of file +//object Flocon : FloconCore() { +// +// fun initialize(context: Context, block: FloconConfiguration.() -> Unit = {}) { +// val configuration = FloconConfiguration().apply(block) +// super.initializeFlocon( +// context = FloconContext(context = context), +// configuration = configuration +// ) +// } +// +//} diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconBroadcastReceiver.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconBroadcastReceiver.kt index 8949202b1..4b70f8283 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconBroadcastReceiver.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconBroadcastReceiver.kt @@ -3,7 +3,6 @@ package io.github.openflocon.flocon import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.util.Log internal class FloconBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -13,7 +12,7 @@ internal class FloconBroadcastReceiver : BroadcastReceiver() { if (serial != null) { FloconLogger.log("serial : $serial") - Flocon.client?.devicePlugin?.registerWithSerial(serial) + //Flocon.client?.devicePlugin?.registerWithSerial(serial) } } } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconContext.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconContext.android.kt new file mode 100644 index 000000000..4d7ce3ef7 --- /dev/null +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconContext.android.kt @@ -0,0 +1,5 @@ +package io.github.openflocon.flocon + +import android.content.Context + +actual class FloconContext(val context: Context) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconCore.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconCore.android.kt index fe2393a4b..59b7f7117 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconCore.android.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconCore.android.kt @@ -1,19 +1,18 @@ package io.github.openflocon.flocon -import android.content.Context import android.util.Log import android.widget.Toast -actual class FloconContext(val appContext: Context) - internal actual fun displayClearTextError(context: FloconContext) { Toast.makeText( - context.appContext, - "Cannot start Flocon : ClearText Issue, see Logcat", - Toast.LENGTH_LONG - ).show() + /* context = */ context.context, + /* text = */ "Cannot start Flocon : ClearText Issue, see Logcat", + /* duration = */ Toast.LENGTH_LONG + ) + .show() Log.e( - "Flocon", + /* tag = */ "Flocon", + /* msg = */ "Flocon uses ClearText communication to the server, it seems you already have a network-security-config setup on your project, please ensure you allowed cleartext communication on your debug app https://github.com/openflocon/Flocon?tab=readme-ov-file#-why-flocon-cant-see-your-device-calls-and-how-to-fix-it-" ) } \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/androidMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt similarity index 97% rename from FloconAndroid/flocon-base/src/androidMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt rename to FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt index aa34a812e..68fc4f94a 100644 --- a/FloconAndroid/flocon-base/src/androidMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt @@ -5,17 +5,16 @@ import android.util.Log actual object FloconLogger { actual var enabled = false private const val TAG = "FloconLogger" - + actual fun logError(text: String, throwable: Throwable?) { if(enabled) { Log.e(TAG, text, throwable) } } - + actual fun log(text: String) { if(enabled) { Log.d(TAG, text) } } -} - +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/ServerHost.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/ServerHost.android.kt index 45b760333..87275d6d9 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/ServerHost.android.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/ServerHost.android.kt @@ -3,6 +3,6 @@ package io.github.openflocon.flocon import io.github.openflocon.flocon.utils.NetUtils internal actual fun getServerHost(floconContext: FloconContext): String { - val appContext = floconContext.appContext + val appContext = floconContext.context return NetUtils.getServerHost(context = appContext) } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/core/AppInfos.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/core/AppInfos.android.kt index 45bfbe9bf..7405f5d02 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/core/AppInfos.android.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/core/AppInfos.android.kt @@ -18,9 +18,10 @@ private fun deviceName(): String { } internal actual fun getAppInfos(floconContext: FloconContext): AppInfos { - val appContext = floconContext.appContext + val appContext = floconContext.context + return AppInfos( - deviceId = deviceId(floconContext.appContext), + deviceId = deviceId(appContext), deviceName = deviceName(), appName = AppUtils.getAppName(appContext), appPackageName = AppUtils.getAppPackageName(appContext), diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.android.kt deleted file mode 100644 index 08023c639..000000000 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.android.kt +++ /dev/null @@ -1,276 +0,0 @@ -package io.github.openflocon.flocon.plugins.database - -import android.content.Context -import android.database.Cursor -import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.sqlite.db.SupportSQLiteOpenHelper -import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.plugins.database.model.FloconDatabaseModel -import io.github.openflocon.flocon.plugins.database.model.FloconFileDatabaseModel -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DatabaseExecuteSqlResponse -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DeviceDataBaseDataModel -import java.io.File -import java.util.Locale - -internal actual fun buildFloconDatabaseDataSource(context: FloconContext): FloconDatabaseDataSource { - return FloconDatabaseDataSourceAndroid(context.appContext) -} - -internal class FloconDatabaseDataSourceAndroid(private val context: Context) : - FloconDatabaseDataSource { - - private val MAX_DEPTH = 7 - - override fun executeSQL( - registeredDatabases: List, - databaseName: String, - query: String - ): DatabaseExecuteSqlResponse { - val databaseModel = registeredDatabases.find { it.displayName == databaseName } - return when(databaseModel) { - is FloconSqliteDatabaseModel -> { - executeSQL( - database = databaseModel.database, - query = query, - ) - } - else -> openDbAndExecuteQuery( - databaseName = databaseName, - query = query, - ) - } - } - - private fun openDbAndExecuteQuery( - databaseName: String, - query: String - ): DatabaseExecuteSqlResponse { - var helper: SupportSQLiteOpenHelper? = null - return try { - val path = context.getDatabasePath(databaseName) - val version = getDatabaseVersion(path = path.absolutePath) - helper = FrameworkSQLiteOpenHelperFactory().create( - SupportSQLiteOpenHelper.Configuration.builder(context) - .name(path.absolutePath) - .callback(object : SupportSQLiteOpenHelper.Callback(version) { - override fun onCreate(db: SupportSQLiteDatabase) { - // no op - } - - override fun onUpgrade( - db: SupportSQLiteDatabase, - oldVersion: Int, - newVersion: Int - ) { - // no op - } - }) - .build() - ) - val database = helper.writableDatabase - - executeSQL( - database = database, - query = query, - ) - } catch (t: Throwable) { - DatabaseExecuteSqlResponse.Error( - message = t.message ?: "error on executeSQL", - originalSql = query, - ) - } finally { - helper?.close() - } - } - - private fun executeSQL( - database: SupportSQLiteDatabase, - query: String - ): DatabaseExecuteSqlResponse { - return try { - val firstWordUpperCase = getFirstWord(query).uppercase(Locale.getDefault()) - when (firstWordUpperCase) { - "UPDATE", "DELETE" -> executeUpdateDelete(database, query) - "INSERT" -> executeInsert(database, query) - "SELECT", "PRAGMA", "EXPLAIN" -> executeSelect(database, query) - else -> executeRawQuery(database, query) - } - } catch (t: Throwable) { - DatabaseExecuteSqlResponse.Error( - message = t.message ?: "error on executeSQL", - originalSql = query, - ) - } - } - - override fun getAllDataBases( - registeredDatabases: List - ): List { - val databasesDir = context.getDatabasePath("dummy_db").parentFile ?: return emptyList() - - val foundDatabases = mutableListOf() - // Start the recursive search from the base databases directory - scanDirectoryForDatabases( - directory = databasesDir, - depth = 0, - foundDatabases = foundDatabases - ) - - registeredDatabases.forEach { - when(it) { - is FloconFileDatabaseModel -> { - // check if file exists here - if (File(it.absolutePath).exists()) { - foundDatabases.add( - DeviceDataBaseDataModel( - id = it.absolutePath, - name = it.displayName, - ) - ) - } - } - else -> { - foundDatabases.add( - DeviceDataBaseDataModel( - id = it.displayName, - name = it.displayName, - ) - ) - } - } - } - - return foundDatabases - } - - /** - * Recursively scans a directory for SQLite database files. - * - * @param directory The current directory to scan. - * @param foundDatabases The mutable list to add found databases to. - */ - private fun scanDirectoryForDatabases( - directory: File, - depth: Int, - foundDatabases: MutableList - ) { - if (depth >= MAX_DEPTH) { - return; - } - directory.listFiles()?.forEach { file -> - if (file.isDirectory) { - // If it's a directory, recursively call this function - scanDirectoryForDatabases( - directory = file, - depth = depth + 1, - foundDatabases = foundDatabases, - ) - } else { - // If it's a file, check if it's a database file - if (file.isFile && - !file.name.endsWith("-wal") && // Write-Ahead Log - !file.name.endsWith("-shm") && // Shared-Memory - !file.name.endsWith("-journal") // Older journaling mode - ) { - foundDatabases.add( - DeviceDataBaseDataModel( - id = file.absolutePath, // Use absolute path for unique ID - name = file.name, - ) - ) - } - } - } - } -} - - -private fun executeSelect( - database: SupportSQLiteDatabase, - query: String, -): DatabaseExecuteSqlResponse { - val cursor: Cursor = database.query(query) - try { - val columnNames = cursor.columnNames.toList() - val rows = cursorToList(cursor) - return DatabaseExecuteSqlResponse.Select( - columns = columnNames, - values = rows, - ) - } finally { - cursor.close() - } -} - -private fun executeUpdateDelete( - database: SupportSQLiteDatabase, - query: String, -): DatabaseExecuteSqlResponse { - val statement = database.compileStatement(query) - val count: Int = statement.executeUpdateDelete() - return DatabaseExecuteSqlResponse.UpdateDelete(count) -} - -private fun executeInsert( - database: SupportSQLiteDatabase, - query: String, -): DatabaseExecuteSqlResponse { - val statement = database.compileStatement(query) - val insertedId: Long = statement.executeInsert() - return DatabaseExecuteSqlResponse.Insert(insertedId) -} - -private fun executeRawQuery( - database: SupportSQLiteDatabase, - query: String, -): DatabaseExecuteSqlResponse { - database.execSQL(query) - return DatabaseExecuteSqlResponse.RawSuccess -} - -private fun getFirstWord(s: String): String { - var s = s - s = s.trim { it <= ' ' } - val firstSpace = s.indexOf(' ') - return if (firstSpace >= 0) s.substring(0, firstSpace) else s -} - -private fun cursorToList(cursor: Cursor): List> { - val rows = mutableListOf>() - val numColumns = cursor.columnCount - while (cursor.moveToNext()) { - val values = mutableListOf() - for (column in 0.. null - Cursor.FIELD_TYPE_INTEGER -> cursor.getLong(column).toString() - Cursor.FIELD_TYPE_FLOAT -> cursor.getDouble(column).toString() - Cursor.FIELD_TYPE_BLOB -> cursor.getBlob(column).toString() - Cursor.FIELD_TYPE_STRING -> cursor.getString(column).toString() - else -> cursor.getString(column) - } -} - -// must use the old way to get the version... -private fun getDatabaseVersion( - path: String, -): Int { - return android.database.sqlite.SQLiteDatabase.openDatabase( - path, - null, - android.database.sqlite.SQLiteDatabase.OPEN_READONLY - ).use { db -> - db.rawQuery("PRAGMA user_version", null).use { cursor -> - if (cursor.moveToFirst()) cursor.getInt(0) else 0 - } - } -} diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconSqliteDatabaseModel.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconSqliteDatabaseModel.kt deleted file mode 100644 index 229ab494d..000000000 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconSqliteDatabaseModel.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.openflocon.flocon.plugins.database - -import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.sqlite.db.SupportSQLiteOpenHelper -import io.github.openflocon.flocon.plugins.database.model.FloconDatabaseModel - -internal class FloconSqliteDatabaseModel( - override val displayName: String, - val database: SupportSQLiteDatabase -) : FloconDatabaseModel - -fun floconRegisterDatabase(displayName: String, database: SupportSQLiteDatabase) { - floconRegisterDatabase( - FloconSqliteDatabaseModel( - displayName = displayName, - database = database, - ) - ) -} - - -fun floconRegisterDatabase(displayName: String, openHelper: SupportSQLiteOpenHelper) { - floconRegisterDatabase( - displayName = displayName, - database = openHelper.writableDatabase, - ) -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/device/FloconDevicePluginImpl.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/device/FloconDevicePluginImpl.android.kt new file mode 100644 index 000000000..88f1f8466 --- /dev/null +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/device/FloconDevicePluginImpl.android.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.pluginsold.device + +import com.jakewharton.processphoenix.ProcessPhoenix +import io.github.openflocon.flocon.FloconContext + +//internal actual fun restartApp(context: FloconContext) { +// ProcessPhoenix.triggerRebirth(context.context) +//} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/device/GetAppIconUtils.android.kt similarity index 92% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.android.kt rename to FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/device/GetAppIconUtils.android.kt index 501e3c1dc..7de9ea904 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.android.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/device/GetAppIconUtils.android.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.device +package io.github.openflocon.flocon.pluginsold.device import android.content.Context import android.graphics.Bitmap @@ -9,9 +9,9 @@ import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger import java.io.ByteArrayOutputStream -actual fun getAppIconBase64(context: FloconContext): String? { - return getAppIconBase64(context.appContext) -} +//actual fun getAppIconBase64(context: FloconContext): String? { +// return getAppIconBase64(context.context) +//} internal fun getAppIconBase64(context: Context): String? { diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.android.kt new file mode 100644 index 000000000..382fa2232 --- /dev/null +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.android.kt @@ -0,0 +1,8 @@ +package kotlinx.coroutines + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +public actual val Dispatchers.IO: CoroutineDispatcher get() = Dispatchers.IO + +public actual val IO: CoroutineDispatcher get() = Dispatchers.IO diff --git a/FloconAndroid/flocon-base/src/androidMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.android.kt similarity index 100% rename from FloconAndroid/flocon-base/src/androidMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.android.kt rename to FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.android.kt diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.android.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.android.kt index 6fa9bf0f0..92b85cda4 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.android.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.android.kt @@ -1,5 +1,6 @@ package io.github.openflocon.flocon.websocket -internal actual fun buildFloconHttpClient(): FloconHttpClient { - return FloconHttpClientAndroid() -} \ No newline at end of file +import io.github.openflocon.flocon.dsl.FloconMarker + +@FloconMarker +internal actual fun buildFloconHttpClient(): FloconHttpClient = FloconHttpClientAndroid() \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClientAndroid.kt b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClientAndroid.kt index be744b5bf..28e5bda02 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClientAndroid.kt +++ b/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClientAndroid.kt @@ -1,6 +1,7 @@ package io.github.openflocon.flocon.websocket import io.github.openflocon.flocon.FloconFile +import io.github.openflocon.flocon.dsl.FloconMarker import io.github.openflocon.flocon.model.FloconFileInfo import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody @@ -12,6 +13,7 @@ import okhttp3.RequestBody.Companion.asRequestBody * The android client uses okhttp, because for android-only project there's more chance to having okhttp than ktor * it prevent conflicts with ktor versions also */ +@FloconMarker internal class FloconHttpClientAndroid : FloconHttpClient { private val client by lazy { diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Flocon.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Flocon.kt new file mode 100644 index 000000000..7a7c434df --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Flocon.kt @@ -0,0 +1,95 @@ +@file:OptIn(FloconMarker::class) + +package io.github.openflocon.flocon + +import io.github.openflocon.flocon.FloconApp.Client +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.core.decode +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.model.FloconMessageFromServer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class Flocon internal constructor( + private val config: FloconConfig, + private val plugins: List, + private val encoder: FloconEncoder +) { + + init { + config.scope.launch { + start( + client = config.client, + context = config.context, + ) + } + } + + private suspend fun start(client: Client, context: FloconContext) { + // try to connect, it fail : try again in 3s + try { + client.connect( + onClosed = { + println("Client - Closed") + // try again to connect + config.scope.launch { + start( + client = client, + context = context, + ) + } + }, + onMessageReceived = ::onMessageReceived + ) + + plugins.forEach { it.onConnectedToServer() } + + (client as? FloconMessageSender)?.let { + // if success, just send a bonjour + it.send("bonjour", method = "bonjour", body = "bonjour") + it.sendPendingMessages() + } + } catch (t: Throwable) { + if (t.message?.contains("CLEARTEXT communication to localhost not permitted by network security policy") == true) { + withContext(Dispatchers.Main) { + displayClearTextError(context = context) + } + } else { + t.printStackTrace() + delay(3_000) + start( + client = client, + context = context, + ) + } + } + } + + private fun onMessageReceived(message: String) { + println("Message received : $message") + config.scope.launch { + try { + val serialized = encoder.decode(message) + ?: return@launch + + plugins.find { it.key == serialized.plugin } + ?.onMessageReceived( + method = serialized.method, + body = serialized.body + ) + } catch (throwable: Throwable) { + throwable.printStackTrace() + } + } + } + + companion object { + var instance: Flocon? = null + get() = field ?: error("Flocon is not initialized") + internal set + } + +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconApp.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconApp.kt new file mode 100644 index 000000000..7b2eafcf8 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconApp.kt @@ -0,0 +1,30 @@ +package io.github.openflocon.flocon + +import kotlinx.coroutines.flow.StateFlow + +abstract class FloconApp { + lateinit var context: FloconContext + + companion object { + var instance: FloconApp? = null + get() = field ?: error("FloconApp is not initialized") + internal set + } + + interface Client { + + @Throws(Throwable::class) + suspend fun connect( + onClosed: () -> Unit, + onMessageReceived: (message: String) -> Unit + ) + + suspend fun disconnect() + + } + + open val client: Client? = null + + abstract val isInitialized: StateFlow + +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconConfiguration.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconConfiguration.kt new file mode 100644 index 000000000..68c39be0e --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconConfiguration.kt @@ -0,0 +1,83 @@ +package io.github.openflocon.flocon + +import io.github.openflocon.flocon.client.FloconClient +import io.github.openflocon.flocon.core.FloconEncoder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.SupervisorJob +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.plus + +class FloconConfiguration internal constructor( + private val config: FloconConfig +) { + + private val plugins: MutableMap FloconPlugin> = mutableMapOf() + + private var serializerModule = SerializersModule {} + + /** + * Install a plugin with the given [factory] and optional [configure] block. + */ + fun install( + factory: FloconPluginFactory, + configure: Config.() -> Unit = {} + ) { + plugins[factory.pluginId] = { scope, encoder -> + val config = factory.createConfig(config.context) + .apply { configure() } + + factory.install( + pluginConfig = config, + floconConfig = scope, + encoder = encoder + ) + } + + serializerModule += factory.createEncoding().serializersModule + } + + fun build(encoder: FloconEncoder): List { + return plugins.values.map { it.invoke(config, encoder) } + } + + fun encoding() = serializerModule + +} + +@ConsistentCopyVisibility +data class FloconConfig internal constructor( + val context: FloconContext, + val scope: CoroutineScope, + val client: FloconClient +) + +fun startFlocon( + context: FloconContext, + block: FloconConfiguration.() -> Unit +) { + val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) + val client = FloconClient( + context = context, + scope = scope + ) + val config = FloconConfig( + context = context, + scope = scope, + client = client + ) + val configuration = FloconConfiguration( + config = config, + ) + .apply(block) + val encoder = FloconEncoder(module = configuration.encoding()) + + client.setupEncoder(encoder) + + Flocon( + config = config, + plugins = configuration.build(encoder), + encoder = encoder + ) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconContext.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconContext.kt new file mode 100644 index 000000000..153093b48 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconContext.kt @@ -0,0 +1,3 @@ +package io.github.openflocon.flocon + +expect class FloconContext \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconCore.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconCore.kt index ee40db561..946b32851 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconCore.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconCore.kt @@ -1,87 +1,4 @@ package io.github.openflocon.flocon -import io.github.openflocon.flocon.client.FloconClientImpl -import io.github.openflocon.flocon.core.FloconMessageSender -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -expect class FloconContext - -abstract class FloconCore : FloconApp() { - - private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - - private var _client: Client? = null - - override val client: Client? - get() { - return _client - } - - private val _isInitialized = MutableStateFlow(false) - override val isInitialized: StateFlow = _isInitialized - - protected fun initializeFlocon(context: FloconContext) { - val newClient = FloconClientImpl(context) - _client = newClient - - // Setup crash handler early to catch crashes during initialization - newClient.crashReporterPlugin.setupCrashHandler() - - _isInitialized.value = true - - scope.launch { - start( - client = newClient, - context = context - ) - } - - super.initializeFlocon() - } - - private suspend fun start(client: FloconApp.Client, context: FloconContext) { - // try to connect, it fail : try again in 3s - try { - client.connect( - onClosed = { - // try again to connect - scope.launch { - start( - client = client, - context = context, - ) - } - } - ) - (client as? FloconMessageSender)?.let { - // if success, just send a bonjour - it.send("bonjour", method = "bonjour", body = "bonjour") - it.sendPendingMessages() - } - } catch (t: Throwable) { - if(t.message?.contains("CLEARTEXT communication to localhost not permitted by network security policy") == true) { - withContext(Dispatchers.Main) { - displayClearTextError(context = context) - } - } else { - //t.printStackTrace() - delay(3_000) - start( - client = client, - context = context, - ) - } - } - } - -} internal expect fun displayClearTextError(context: FloconContext) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconEncoding.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconEncoding.kt new file mode 100644 index 000000000..a830472c6 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconEncoding.kt @@ -0,0 +1,15 @@ +package io.github.openflocon.flocon + +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule + +interface FloconEncoding { + + val serializersModule: SerializersModule + +} + +internal class DefaultEncoding : FloconEncoding { + override val serializersModule: SerializersModule + get() = EmptySerializersModule() +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconFile.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconFile.kt index 6fa28d86a..c55899b22 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconFile.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconFile.kt @@ -1,3 +1,6 @@ package io.github.openflocon.flocon -internal expect class FloconFile \ No newline at end of file +import io.github.openflocon.flocon.dsl.FloconMarker + +@FloconMarker +expect class FloconFile \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconPlugin.kt new file mode 100644 index 000000000..b8f76d15e --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/FloconPlugin.kt @@ -0,0 +1,55 @@ +package io.github.openflocon.flocon + +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.dsl.FloconMarker + +/** + * Base interface for all Flocon plugins. + * Plugins can receive messages from the server and react to connection events. + */ +interface FloconPlugin { + val key: String + + suspend fun onMessageReceived( + method: String, + body: String, + ) + + suspend fun onConnectedToServer() +} + +interface FloconPluginConfig + +/** + * A unique key for identifying a Flocon plugin. + */ +interface FloconPluginKey { + val name: String + val pluginId: String +} + +/** + * A factory for creating and installing Flocon plugins. + * This is the entry point for Ktor-style [install] calls. + */ +interface FloconPluginFactory : + FloconPluginKey { + + fun createEncoding(): FloconEncoding = DefaultEncoding() + + /** + * Create a default configuration instance for the plugin. + */ + fun createConfig(context: FloconContext): Config + + /** + * Install the plugin into the [io.github.openflocon.flocon.FloconApp] instance with the given [pluginConfig]. + */ + @OptIn(FloconMarker::class) + fun install( + pluginConfig: Config, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): PluginInstance + +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/client/FloconClientImpl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/client/FloconClientImpl.kt index ad0ddcdc9..2d762c067 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/client/FloconClientImpl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/client/FloconClientImpl.kt @@ -3,26 +3,15 @@ package io.github.openflocon.flocon.client import io.github.openflocon.flocon.FloconApp import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconFile -import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder import io.github.openflocon.flocon.core.FloconFileSender import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin +import io.github.openflocon.flocon.core.encode import io.github.openflocon.flocon.core.getAppInfos +import io.github.openflocon.flocon.dsl.FloconMarker import io.github.openflocon.flocon.getServerHost import io.github.openflocon.flocon.model.FloconFileInfo import io.github.openflocon.flocon.model.FloconMessageToServer -import io.github.openflocon.flocon.model.floconMessageFromServerFromJson -import io.github.openflocon.flocon.model.toFloconMessageToServer -import io.github.openflocon.flocon.plugins.analytics.FloconAnalyticsPluginImpl -import io.github.openflocon.flocon.plugins.dashboard.FloconDashboardPluginImpl -import io.github.openflocon.flocon.plugins.database.FloconDatabasePluginImpl -import io.github.openflocon.flocon.plugins.deeplinks.FloconDeeplinksPluginImpl -import io.github.openflocon.flocon.plugins.device.FloconDevicePluginImpl -import io.github.openflocon.flocon.plugins.files.FloconFilesPluginImpl -import io.github.openflocon.flocon.plugins.network.FloconNetworkPluginImpl -import io.github.openflocon.flocon.plugins.sharedprefs.FloconPreferencesPluginImpl -import io.github.openflocon.flocon.plugins.tables.FloconTablePluginImpl -import io.github.openflocon.flocon.plugins.crashreporter.FloconCrashReporterPluginImpl import io.github.openflocon.flocon.utils.currentTimeMillis import io.github.openflocon.flocon.websocket.FloconHttpClient import io.github.openflocon.flocon.websocket.FloconWebSocketClient @@ -30,182 +19,82 @@ import io.github.openflocon.flocon.websocket.buildFloconHttpClient import io.github.openflocon.flocon.websocket.buildFloconWebSocketClient import io.github.openflocon.flocondesktop.BuildConfig import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch -import kotlin.getValue +import kotlinx.serialization.modules.EmptySerializersModule -internal class FloconClientImpl( - private val appContext: FloconContext, +class FloconClient internal constructor( + private val context: FloconContext, + private val scope: CoroutineScope, ) : FloconApp.Client, FloconMessageSender, FloconFileSender { - private val FLOCON_WEBSOCKET_PORT = 9023 - private val FLOCON_HTTP_PORT = 9024 - - private val appInstance by lazy { - // store the start time of the sdk, for this app launch - currentTimeMillis() - } - - private val appInfos by lazy { - getAppInfos(appContext) - } - - private val versionName by lazy { - BuildConfig.APP_VERSION - } + private val appInstance by lazy { currentTimeMillis() } + private val appInfos by lazy { getAppInfos(context) } + private val versionName by lazy { BuildConfig.APP_VERSION } + private val address by lazy { getServerHost(context) } private val webSocketClient: FloconWebSocketClient = buildFloconWebSocketClient() private val httpClient: FloconHttpClient = buildFloconHttpClient() - private val address by lazy { - getServerHost(appContext) - } - private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - - // region plugins - override val databasePlugin = FloconDatabasePluginImpl(context = appContext, sender = this) - private val filesPlugin = FloconFilesPluginImpl(context = appContext, sender = this, floconFileSender = this) - override val preferencesPlugin = FloconPreferencesPluginImpl(context = appContext, sender = this, scope = coroutineScope) - override val dashboardPlugin = FloconDashboardPluginImpl(sender = this) - override val tablePlugin = FloconTablePluginImpl(sender = this) - override val deeplinksPlugin = FloconDeeplinksPluginImpl(sender = this) - override val analyticsPlugin = FloconAnalyticsPluginImpl(sender = this) - override val devicePlugin = FloconDevicePluginImpl(sender = this, context = appContext) - override val networkPlugin = FloconNetworkPluginImpl( - context = appContext, - sender = this, - coroutineScope = coroutineScope, - ) - override val crashReporterPlugin = FloconCrashReporterPluginImpl( - context = appContext, - sender = this, - coroutineScope = coroutineScope, - ) + private var encoder = FloconEncoder(EmptySerializersModule()) - private val allPlugins = listOf( - databasePlugin, - filesPlugin, - preferencesPlugin, - dashboardPlugin, - tablePlugin, - deeplinksPlugin, - analyticsPlugin, - networkPlugin, - devicePlugin, - crashReporterPlugin, - ) + // Better way ? + fun setupEncoder(encoder: FloconEncoder) { + this.encoder = encoder + } @Throws(Throwable::class) override suspend fun connect( onClosed: () -> Unit, + onMessageReceived: (message: String) -> Unit ) { webSocketClient.connect( address = address, port = FLOCON_WEBSOCKET_PORT, - onMessageReceived = ::onMessageReceived, + onMessageReceived = onMessageReceived, onClosed = onClosed, ) - allPlugins.forEach { - it.onConnectedToServer() - } } override suspend fun disconnect() { webSocketClient.disconnect() } - private fun onMessageReceived(message: String) { - coroutineScope.launch(Dispatchers.IO) { - floconMessageFromServerFromJson(message)?.let { messageFromServer -> - when (messageFromServer.plugin) { - Protocol.ToDevice.Database.Plugin -> { - databasePlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.Files.Plugin -> { - filesPlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.SharedPreferences.Plugin -> { - preferencesPlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.Device.Plugin -> { - devicePlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.Dashboard.Plugin -> { - dashboardPlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.Table.Plugin -> { - tablePlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.Analytics.Plugin -> { - analyticsPlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - - Protocol.ToDevice.Network.Plugin -> { - networkPlugin.onMessageReceived( - messageFromServer = messageFromServer, - ) - } - } - } - } - } - override fun send( plugin: String, method: String, body: String, ) { - coroutineScope.launch(Dispatchers.IO) { + scope.launch { webSocketClient.sendMessage( - message = FloconMessageToServer( - deviceId = appInfos.deviceId, - plugin = plugin, - body = body, - appName = appInfos.appName, - appPackageName = appInfos.appPackageName, - method = method, - deviceName = appInfos.deviceName, - appInstance = appInstance, - platform = appInfos.platform, - versionName = versionName, - ).toFloconMessageToServer(), + encoder.encode( + FloconMessageToServer( + deviceId = appInfos.deviceId, + plugin = plugin, + body = body, + appName = appInfos.appName, + appPackageName = appInfos.appPackageName, + method = method, + deviceName = appInfos.deviceName, + appInstance = appInstance, + platform = appInfos.platform, + versionName = versionName, + ) + ) ) } } + @FloconMarker override fun send( file: FloconFile, infos: FloconFileInfo, ) { - coroutineScope.launch(Dispatchers.IO) { + scope.launch { httpClient.send( address = address, port = FLOCON_HTTP_PORT, file = file, infos = infos, - deviceId = appInfos.deviceId, appPackageName = appInfos.appPackageName, appInstance = appInstance, @@ -214,8 +103,13 @@ internal class FloconClientImpl( } override fun sendPendingMessages() { - coroutineScope.launch(Dispatchers.IO) { + scope.launch { webSocketClient.sendPendingMessages() } } + + companion object { + private const val FLOCON_WEBSOCKET_PORT = 9023 + private const val FLOCON_HTTP_PORT = 9024 + } } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconEncoder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconEncoder.kt index 68aab88d3..51e738415 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconEncoder.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconEncoder.kt @@ -1,22 +1,42 @@ package io.github.openflocon.flocon.core -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkParameterRemote +import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic -import kotlinx.serialization.modules.subclass +import kotlinx.serialization.serializer -internal object FloconEncoder { - val json = Json { +class FloconEncoder internal constructor( + val module: SerializersModule +) { + private val json = Json { ignoreUnknownKeys = true isLenient = true encodeDefaults = false - - serializersModule = SerializersModule { - polymorphic(DeeplinkParameterRemote::class) { - subclass(DeeplinkParameterRemote.AutoComplete::class) - subclass(DeeplinkParameterRemote.Variable::class) - } - } + serializersModule = module } + + fun encode(serializer: KSerializer, body: T): String = json.encodeToString( + serializer = serializer, + value = body + ) + + fun decode(serializer: KSerializer, body: String): T = json.decodeFromString( + deserializer = serializer, + string = body + ) + +} + +inline fun FloconEncoder.encode(body: T) = encode( + serializer = module.serializer(), + body = body +) + +inline fun FloconEncoder.decode(body: String): T? = try { + decode( + serializer = module.serializer(), + body = body + ) +} catch (_: Throwable) { + null } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconFileSender.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconFileSender.kt index 929fe8411..0dbac9617 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconFileSender.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconFileSender.kt @@ -1,8 +1,12 @@ package io.github.openflocon.flocon.core import io.github.openflocon.flocon.FloconFile +import io.github.openflocon.flocon.dsl.FloconMarker import io.github.openflocon.flocon.model.FloconFileInfo -internal interface FloconFileSender { +interface FloconFileSender { + + @FloconMarker fun send(file: FloconFile, infos: FloconFileInfo) + } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconMessageSender.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconMessageSender.kt index 2038fdade..e2daa82cb 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconMessageSender.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconMessageSender.kt @@ -1,6 +1,7 @@ package io.github.openflocon.flocon.core -internal interface FloconMessageSender { +interface FloconMessageSender { + fun send( plugin: String, method: String, @@ -8,4 +9,5 @@ internal interface FloconMessageSender { ) fun sendPendingMessages() + } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconPlugin.kt deleted file mode 100644 index dd50d5c15..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/core/FloconPlugin.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.openflocon.flocon.core - -import io.github.openflocon.flocon.model.FloconMessageFromServer - -internal interface FloconPlugin { - fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) - fun onConnectedToServer() -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/dsl/FloconMarker.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/dsl/FloconMarker.kt new file mode 100644 index 000000000..80868b751 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/dsl/FloconMarker.kt @@ -0,0 +1,14 @@ +package io.github.openflocon.flocon.dsl + +@RequiresOptIn( + message = "Used to mark internal Flocon APIs", + level = RequiresOptIn.Level.ERROR +) +@Retention(AnnotationRetention.BINARY) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.FUNCTION, + AnnotationTarget.FIELD, + AnnotationTarget.PROPERTY +) +annotation class FloconMarker diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/error/PluginNotInitialized.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/error/PluginNotInitialized.kt new file mode 100644 index 000000000..99bbac42c --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/error/PluginNotInitialized.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.error + +import io.github.openflocon.flocon.dsl.FloconMarker + +// Maybe remove it, and make plugins nullable to avoid app crashing +@FloconMarker +fun pluginNotInitialized(pluginName: String): Nothing = error("$pluginName is not initialized") \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconFileInfo.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconFileInfo.kt index 1bd6f4eb8..0aeb75d9d 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconFileInfo.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconFileInfo.kt @@ -1,6 +1,9 @@ package io.github.openflocon.flocon.model -internal data class FloconFileInfo( +import io.github.openflocon.flocon.dsl.FloconMarker + +@FloconMarker +data class FloconFileInfo( val path: String, val requestId: String, ) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageFromServer.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageFromServer.kt index 68c813b3c..393ae404d 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageFromServer.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageFromServer.kt @@ -1,20 +1,7 @@ package io.github.openflocon.flocon.model -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable -internal fun floconMessageFromServerFromJson( - message: String, -): FloconMessageFromServer? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } -} - @Serializable internal data class FloconMessageFromServer( val plugin: String, diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageToServer.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageToServer.kt index 8f8b081eb..50fe8f0f4 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageToServer.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/model/FloconMessageToServer.kt @@ -1,8 +1,6 @@ package io.github.openflocon.flocon.model -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString @Serializable internal class FloconMessageToServer( @@ -17,7 +15,3 @@ internal class FloconMessageToServer( val platform: String, // android, ios, desktop val versionName: String, // ex: 1.3.0 ) - -internal fun FloconMessageToServer.toFloconMessageToServer(): String { - return FloconEncoder.json.encodeToString(this) -} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsPlugin.kt deleted file mode 100644 index 538bdb529..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/analytics/FloconAnalyticsPlugin.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.openflocon.flocon.plugins.analytics - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsItem -import io.github.openflocon.flocon.plugins.analytics.mapper.analyticsItemsToJson - -internal class FloconAnalyticsPluginImpl( - private val sender: FloconMessageSender, -) : FloconPlugin, FloconAnalyticsPlugin { - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - // no op - } - - override fun onConnectedToServer() { - // no op - } - - override fun registerAnalytics(analyticsItems: List) { - sendAnalytics(analyticsItems) - } - - private fun sendAnalytics(analyticsItems: List) { - analyticsItems.takeIf { it.isNotEmpty() }?.forEach { toSend -> - try { - sender.send( - plugin = Protocol.FromDevice.Analytics.Plugin, - method = Protocol.FromDevice.Analytics.Method.AddItems, - body = analyticsItemsToJson(toSend) - ) - } catch (t: Throwable) { - FloconLogger.logError("error on sendAnalytics", t) - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/model/CrashReportDataModel.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/model/CrashReportDataModel.kt deleted file mode 100644 index 1d10cae2f..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/model/CrashReportDataModel.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.openflocon.flocon.plugins.crashreporter.model - -import kotlinx.serialization.Serializable - -@Serializable -data class CrashReportDataModel( - val crashId: String, - val timestamp: Long, - val exceptionType: String, - val exceptionMessage: String, - val stackTrace: String, -) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/model/CrashReportMapper.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/model/CrashReportMapper.kt deleted file mode 100644 index bc944130d..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/crashreporter/model/CrashReportMapper.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.openflocon.flocon.plugins.crashreporter.model - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json - -internal fun CrashReportDataModel.toJson(): String { - return FloconEncoder.json.encodeToString(this) -} - -internal fun crashReportFromJson(jsonString: String): CrashReportDataModel? { - return try { - FloconEncoder.json.decodeFromString(jsonString) - } catch (t: Throwable) { - FloconLogger.logError("Crash report parsing error", t) - null - } -} - -internal fun crashReportsListToJson(crashes: List): String { - return FloconEncoder.json.encodeToString(crashes) -} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardDSL.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardDSL.kt index 5560f448d..f747dd047 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardDSL.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardDSL.kt @@ -1,6 +1,5 @@ package io.github.openflocon.flocon.plugins.dashboard -import io.github.openflocon.flocon.FloconApp import io.github.openflocon.flocon.plugins.dashboard.builder.FormBuilder import io.github.openflocon.flocon.plugins.dashboard.builder.SectionBuilder import io.github.openflocon.flocon.plugins.dashboard.model.DashboardConfig @@ -41,7 +40,7 @@ fun CoroutineScope.floconDashboard(id: String, block: DashboardScope.() -> Unit) sectionsFlow.collect { containers -> val config = DashboardConfig(id = id, containers = containers) - FloconApp.instance?.client?.dashboardPlugin?.registerDashboard(config) + //FloconApp.instance?.client?.dashboardPlugin?.registerDashboard(config) } } diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt index d1e15e600..e2b14a159 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/FloconDashboardPlugin.kt @@ -1,10 +1,15 @@ package io.github.openflocon.flocon.plugins.dashboard +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer +import io.github.openflocon.flocon.core.decode import io.github.openflocon.flocon.plugins.dashboard.mapper.toJson import io.github.openflocon.flocon.plugins.dashboard.model.DashboardCallback import io.github.openflocon.flocon.plugins.dashboard.model.DashboardConfig @@ -16,9 +21,29 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +class FloconDashboardConfig : FloconPluginConfig + +interface FloconDashboardPlugin : FloconPlugin { + fun registerDashboard(dashboardConfig: DashboardConfig) +} + +object FloconDashboard : FloconPluginFactory { + override val name: String = "Dashboard" + override val pluginId: String = Protocol.ToDevice.Dashboard.Plugin + override fun createConfig(context: FloconContext) = FloconDashboardConfig() + override fun install(pluginConfig: FloconDashboardConfig, floconConfig: FloconConfig, encoder: FloconEncoder): FloconDashboardPlugin { + return FloconDashboardPluginImpl( + sender = floconConfig.client as FloconMessageSender, + encoder = encoder + ) + } +} + internal class FloconDashboardPluginImpl( private val sender: FloconMessageSender, + private val encoder: FloconEncoder ) : FloconPlugin, FloconDashboardPlugin { + override val key: String = "DASHBOARD" private val DashboardDispatcher = Dispatchers.Default.limitedParallelism(1) @@ -27,18 +52,19 @@ internal class FloconDashboardPluginImpl( private val dashboards = mutableMapOf() private val callbackMap = mutableMapOf() - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, + override suspend fun onMessageReceived( + method: String, + body: String, ) { scope.launch { - when (messageFromServer.method) { + when (method) { Protocol.ToDevice.Dashboard.Method.OnClick -> { - val id = messageFromServer.body + val id = body callbackMap[id]?.let { it as? DashboardCallback.ButtonCallback }?.action?.invoke() } Protocol.ToDevice.Dashboard.Method.OnFormSubmitted -> { - ToDeviceSubmittedFormMessage.fromJson(messageFromServer.body)?.let { + encoder.decode(body)?.let { callbackMap[it.id]?.let { it as? DashboardCallback.FormCallback }?.actions?.invoke( it.values ) @@ -46,7 +72,7 @@ internal class FloconDashboardPluginImpl( } Protocol.ToDevice.Dashboard.Method.OnTextFieldSubmitted -> { - ToDeviceSubmittedTextFieldMessage.fromJson(messageFromServer.body)?.let { + encoder.decode(body)?.let { callbackMap[it.id]?.let { it as? DashboardCallback.TextFieldCallback }?.action?.invoke( it.value ) @@ -54,7 +80,7 @@ internal class FloconDashboardPluginImpl( } Protocol.ToDevice.Dashboard.Method.OnCheckBoxValueChanged -> { - ToDeviceCheckBoxValueChangedMessage.fromJson(messageFromServer.body)?.let { + encoder.decode(body)?.let { callbackMap[it.id]?.let { it as? DashboardCallback.CheckBoxCallback }?.action?.invoke( it.value ) @@ -64,7 +90,7 @@ internal class FloconDashboardPluginImpl( } } - override fun onConnectedToServer() { + override suspend fun onConnectedToServer() { // on connected, send known dashboards dashboards.values.takeIf { it.isNotEmpty() }?.forEach { dashboardConfig -> registerDashboardInternal(dashboardConfig) @@ -83,7 +109,7 @@ internal class FloconDashboardPluginImpl( }, ) - dashboards.put(dashboardConfig.id, dashboardConfig) + dashboards[dashboardConfig.id] = dashboardConfig try { sender.send( @@ -97,4 +123,3 @@ internal class FloconDashboardPluginImpl( } } } - diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/ContainerBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/ContainerBuilder.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/ContainerBuilder.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/ContainerBuilder.kt index 3fbf6a10f..e9685eb39 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/ContainerBuilder.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/ContainerBuilder.kt @@ -11,4 +11,4 @@ abstract class ContainerBuilder { } abstract fun build(): ContainerConfig -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/DashboardBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/DashboardBuilder.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/DashboardBuilder.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/DashboardBuilder.kt index 6672693f1..77c7352b9 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/DashboardBuilder.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/DashboardBuilder.kt @@ -18,4 +18,4 @@ class DashboardBuilder(private val id: String) { containers = containers ) } -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/FormBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/FormBuilder.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/FormBuilder.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/FormBuilder.kt index 8dd5b8b1f..71ecf7b7d 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/FormBuilder.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/FormBuilder.kt @@ -17,4 +17,4 @@ class FormBuilder( onSubmitted = onSubmitted ) } -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/SectionBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/SectionBuilder.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/SectionBuilder.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/SectionBuilder.kt index 952aa4261..4d7cde6d6 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/SectionBuilder.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/builder/SectionBuilder.kt @@ -7,4 +7,4 @@ class SectionBuilder(val name: String) : ContainerBuilder() { override fun build(): SectionConfig { return SectionConfig(name, elements) } -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/ButtonDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/ButtonDsl.kt similarity index 95% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/ButtonDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/ButtonDsl.kt index cf654088d..60f4fac25 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/ButtonDsl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/ButtonDsl.kt @@ -6,7 +6,7 @@ import io.github.openflocon.flocon.plugins.dashboard.model.config.ButtonConfig @DashboardDsl fun ContainerBuilder.button( text: String, - id : String, + id: String, onClick: () -> Unit, ) { add( @@ -16,4 +16,4 @@ fun ContainerBuilder.button( onClick = onClick, ) ) -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/CheckBoxDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/CheckBoxDsl.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/CheckBoxDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/CheckBoxDsl.kt diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/DashboardDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/DashboardDsl.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/DashboardDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/DashboardDsl.kt index c59db318b..097eab050 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/DashboardDsl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/DashboardDsl.kt @@ -12,4 +12,4 @@ fun dashboardConfig(id: String, block: DashboardBuilder.() -> Unit): DashboardCo block() } return builder.build() -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/FormDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/FormDsl.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/FormDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/FormDsl.kt index f73e6b817..9603e49ec 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/FormDsl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/FormDsl.kt @@ -19,4 +19,4 @@ fun DashboardBuilder.form( } add(builder.build()) -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/HtmlDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/HtmlDsl.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/HtmlDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/HtmlDsl.kt diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/MarkdownDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/MarkdownDsl.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/MarkdownDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/MarkdownDsl.kt diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/PlainTextDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/PlainTextDsl.kt similarity index 79% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/PlainTextDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/PlainTextDsl.kt index 343788f69..8fa25c287 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/PlainTextDsl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/PlainTextDsl.kt @@ -16,9 +16,11 @@ fun ContainerBuilder.plainText(label: String, value: String) { @DashboardDsl fun ContainerBuilder.json(label: String, value: String) { - add(PlainTextConfig( - label = label, - value = value, - type = "json", - )) -} \ No newline at end of file + add( + PlainTextConfig( + label = label, + value = value, + type = "json", + ) + ) +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/SectionDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/SectionDsl.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/SectionDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/SectionDsl.kt index 4e4ef095e..0bc1b3956 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/SectionDsl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/SectionDsl.kt @@ -10,4 +10,4 @@ fun DashboardBuilder.section(name: String, block: SectionBuilder.() -> Unit) { } add(builder.build()) -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextDsl.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextDsl.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextDsl.kt index 9a80dde6d..c56b3f555 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextDsl.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextDsl.kt @@ -1,8 +1,8 @@ package io.github.openflocon.flocon.plugins.dashboard.dsl import io.github.openflocon.flocon.plugins.dashboard.builder.ContainerBuilder -import io.github.openflocon.flocon.plugins.dashboard.model.config.TextConfig import io.github.openflocon.flocon.plugins.dashboard.model.config.LabelConfig +import io.github.openflocon.flocon.plugins.dashboard.model.config.TextConfig @DashboardDsl fun ContainerBuilder.text(label: String, value: String, color: Int? = null) { @@ -12,4 +12,4 @@ fun ContainerBuilder.text(label: String, value: String, color: Int? = null) { @DashboardDsl fun ContainerBuilder.label(label: String, color: Int? = null) { add(LabelConfig(label = label, color = color)) -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextField.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextField.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextField.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/dsl/TextField.kt diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/ContainerType.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/ContainerType.kt similarity index 98% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/ContainerType.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/ContainerType.kt index e33e10bf2..8bf2247aa 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/ContainerType.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/ContainerType.kt @@ -3,4 +3,4 @@ package io.github.openflocon.flocon.plugins.dashboard.model enum class ContainerType { FORM, SECTION -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/Dashboard.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/Dashboard.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardConfig.kt index 5deacba9b..87432e05a 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/Dashboard.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardConfig.kt @@ -5,4 +5,4 @@ import io.github.openflocon.flocon.plugins.dashboard.model.config.ContainerConfi data class DashboardConfig( val id: String, val containers: List -) \ No newline at end of file +) diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardScope.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardScope.kt similarity index 98% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardScope.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardScope.kt index 3089b07f8..291222091 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardScope.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/DashboardScope.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow interface DashboardScope { fun section(name: String, flow: Flow, content: SectionBuilder.(T) -> Unit) fun section(name: String, content: SectionBuilder.() -> Unit) - + fun form( name: String, submitText: String = "Submit", @@ -15,11 +15,11 @@ interface DashboardScope { flow: Flow, content: FormBuilder.(T) -> Unit ) - + fun form( name: String, submitText: String = "Submit", onSubmitted: (Map) -> Unit, content: FormBuilder.() -> Unit ) -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ButtonConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ButtonConfig.kt similarity index 90% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ButtonConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ButtonConfig.kt index 6fa9d1acb..f7703e279 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ButtonConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ButtonConfig.kt @@ -4,4 +4,4 @@ data class ButtonConfig( val text: String, val id: String, val onClick: () -> Unit, -) : ElementConfig \ No newline at end of file +) : ElementConfig diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/CheckBoxConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/CheckBoxConfig.kt similarity index 91% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/CheckBoxConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/CheckBoxConfig.kt index f963111a4..f6f534486 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/CheckBoxConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/CheckBoxConfig.kt @@ -5,4 +5,4 @@ data class CheckBoxConfig( val label: String, val value: Boolean, val onUpdated: (Boolean) -> Unit, -) : ElementConfig \ No newline at end of file +) : ElementConfig diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ContainerConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ContainerConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ContainerConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ContainerConfig.kt index 904b76885..88f77c091 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ContainerConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ContainerConfig.kt @@ -6,4 +6,4 @@ sealed interface ContainerConfig { val name: String val elements: List val containerType: ContainerType -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ElementConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ElementConfig.kt similarity index 68% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ElementConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ElementConfig.kt index 68d654c92..8a3e4e3c0 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ElementConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/ElementConfig.kt @@ -1,3 +1,3 @@ package io.github.openflocon.flocon.plugins.dashboard.model.config -sealed interface ElementConfig \ No newline at end of file +sealed interface ElementConfig diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/FormConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/FormConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/FormConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/FormConfig.kt index 51ecb3685..37e3bf917 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/FormConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/FormConfig.kt @@ -10,4 +10,4 @@ data class FormConfig( val onSubmitted: (Map) -> Unit, ) : ContainerConfig { override val containerType: ContainerType = ContainerType.FORM -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/HtmlConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/HtmlConfig.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/HtmlConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/HtmlConfig.kt diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/LabelConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/LabelConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/LabelConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/LabelConfig.kt index 24416e4cd..9dcf4e815 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/LabelConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/LabelConfig.kt @@ -4,4 +4,3 @@ data class LabelConfig( val label: String, val color: Int?, ) : ElementConfig - diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/MarkdownConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/MarkdownConfig.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/MarkdownConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/MarkdownConfig.kt diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/PlainTextConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/PlainTextConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/PlainTextConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/PlainTextConfig.kt index e42961a6e..12b74553f 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/PlainTextConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/PlainTextConfig.kt @@ -5,4 +5,3 @@ data class PlainTextConfig( val value: String, val type: String, // text, json ) : ElementConfig - diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/SectionConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/SectionConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/SectionConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/SectionConfig.kt index 98b4fe3f9..a707d5b01 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/SectionConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/SectionConfig.kt @@ -7,4 +7,4 @@ data class SectionConfig( override val elements: List, ) : ContainerConfig { override val containerType: ContainerType = ContainerType.SECTION -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextConfig.kt similarity index 99% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextConfig.kt index 97ee703a2..b33db66ba 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextConfig.kt @@ -5,4 +5,3 @@ data class TextConfig( val value: String, val color: Int?, ) : ElementConfig - diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextFieldConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextFieldConfig.kt similarity index 92% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextFieldConfig.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextFieldConfig.kt index 9e47cab88..63de1f656 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextFieldConfig.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/config/TextFieldConfig.kt @@ -6,4 +6,4 @@ data class TextFieldConfig( val placeHolder: String?, val value: String, val onSubmitted: (String) -> Unit, -) : ElementConfig \ No newline at end of file +) : ElementConfig diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceCheckBoxValueChangedMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceCheckBoxValueChangedMessage.kt index 0af4c78d4..59dfeb855 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceCheckBoxValueChangedMessage.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceCheckBoxValueChangedMessage.kt @@ -1,22 +1,9 @@ package io.github.openflocon.flocon.plugins.dashboard.model.todevice -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable @Serializable internal data class ToDeviceCheckBoxValueChangedMessage( val id: String, - val value: Boolean, -) { - companion object { - fun fromJson(message: String): ToDeviceCheckBoxValueChangedMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file + val value: Boolean +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedFormMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedFormMessage.kt index ef406d702..9660934b9 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedFormMessage.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedFormMessage.kt @@ -1,22 +1,9 @@ package io.github.openflocon.flocon.plugins.dashboard.model.todevice -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable @Serializable internal data class ToDeviceSubmittedFormMessage( val id: String, val values: Map -) { - companion object { - fun fromJson(message: String): ToDeviceSubmittedFormMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} +) diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedTextFieldMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedTextFieldMessage.kt index c789795aa..cc70676e5 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedTextFieldMessage.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/dashboard/model/todevice/ToDeviceSubmittedTextFieldMessage.kt @@ -1,22 +1,9 @@ package io.github.openflocon.flocon.plugins.dashboard.model.todevice -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable @Serializable internal data class ToDeviceSubmittedTextFieldMessage( val id: String, val value: String, -) { - companion object { - fun fromJson(message: String): ToDeviceSubmittedTextFieldMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt deleted file mode 100644 index 10c97c58c..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt +++ /dev/null @@ -1,116 +0,0 @@ -package io.github.openflocon.flocon.plugins.database - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.database.model.FloconDatabaseModel -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DatabaseExecuteSqlResponse -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DeviceDataBaseDataModel -import io.github.openflocon.flocon.plugins.database.model.fromdevice.QueryResultDataModel -import io.github.openflocon.flocon.plugins.database.model.fromdevice.listDeviceDataBaseDataModelToJson -import io.github.openflocon.flocon.plugins.database.model.fromdevice.toJson -import io.github.openflocon.flocon.plugins.database.model.todevice.DatabaseQueryMessage -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DatabaseQueryLogModel -import io.github.openflocon.flocon.utils.currentTimeMillis -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update - -internal interface FloconDatabaseDataSource { - fun executeSQL( - registeredDatabases: List, - databaseName: String, - query: String - ): DatabaseExecuteSqlResponse - - fun getAllDataBases( - registeredDatabases: List - ): List -} - -internal expect fun buildFloconDatabaseDataSource(context: FloconContext): FloconDatabaseDataSource - -internal class FloconDatabasePluginImpl( - private var sender: FloconMessageSender, - private val context: FloconContext, -) : FloconPlugin, FloconDatabasePlugin { - - private val registeredDatabases = MutableStateFlow>(emptyList()) - - private val dataSource = buildFloconDatabaseDataSource(context) - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - when (messageFromServer.method) { - Protocol.ToDevice.Database.Method.GetDatabases -> { - sendAllDatabases(sender) - } - - Protocol.ToDevice.Database.Method.Query -> { - val queryMessage = - DatabaseQueryMessage.fromJson(message = messageFromServer.body) ?: return - val result = dataSource.executeSQL( - registeredDatabases = registeredDatabases.value, - databaseName = queryMessage.database, - query = queryMessage.query, - ) - try { - sender.send( - plugin = Protocol.FromDevice.Database.Plugin, - method = Protocol.FromDevice.Database.Method.Query, - body = QueryResultDataModel( - requestId = queryMessage.requestId, - result = result.toJson(), - ).toJson(), - ) - } catch (t: Throwable) { - FloconLogger.logError("Database parsing error", t) - } - } - } - } - - override fun onConnectedToServer() { - sendAllDatabases(sender) - } - - private fun sendAllDatabases(sender: FloconMessageSender) { - val databases = dataSource.getAllDataBases( - registeredDatabases = registeredDatabases.value, - ) - try { - sender.send( - plugin = Protocol.FromDevice.Database.Plugin, - method = Protocol.FromDevice.Database.Method.GetDatabases, - body = listDeviceDataBaseDataModelToJson(databases), - ) - } catch (t: Throwable) { - FloconLogger.logError("Database parsing error", t) - } - } - - override fun register(floconDatabaseModel: FloconDatabaseModel) { - registeredDatabases.update { it + floconDatabaseModel } - sendAllDatabases(sender) - } - - override fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) { - try { - sender.send( - plugin = Protocol.FromDevice.Database.Plugin, - method = Protocol.FromDevice.Database.Method.LogQuery, - body = DatabaseQueryLogModel( - dbName = dbName, - sqlQuery = sqlQuery, - bindArgs = bindArgs.map { it.toString() }, - timestamp = currentTimeMillis(), - ).toJson(), - ) - } catch (t: Throwable) { - FloconLogger.logError("Database logging error", t) - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseExecuteSqlResponse.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseExecuteSqlResponse.kt deleted file mode 100644 index 5ca940607..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseExecuteSqlResponse.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.openflocon.flocon.plugins.database.model.fromdevice - -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.encodeToJsonElement -import kotlinx.serialization.json.put - -@Serializable -internal sealed interface DatabaseExecuteSqlResponse { - - @Serializable - // Case for successful SELECT queries - class Select( - val columns: List, - val values: List> - ) : DatabaseExecuteSqlResponse - - // Case for successful INSERT queries - @Serializable - class Insert( - val insertedId: Long - ) : DatabaseExecuteSqlResponse - - // Case for successful UPDATE or DELETE queries - @Serializable - class UpdateDelete( - val affectedCount: Int - ) : DatabaseExecuteSqlResponse - - // Case for successful "raw" queries (CREATE TABLE, DROP TABLE, etc.) - @Serializable - object RawSuccess : DatabaseExecuteSqlResponse - - // Case for an SQL execution error - @Serializable - class Error( - val message: String, // Detailed error message - val originalSql: String, // SQL query that caused the error (optional) - ) : DatabaseExecuteSqlResponse -} - -internal fun DatabaseExecuteSqlResponse.toJson(): String { - val jsonEncoder = FloconEncoder.json - val thisAsJson = jsonEncoder.encodeToJsonElement(this) - - val type = when (this) { - is DatabaseExecuteSqlResponse.Error -> "Error" - is DatabaseExecuteSqlResponse.Insert -> "Insert" - DatabaseExecuteSqlResponse.RawSuccess -> "RawSuccess" - is DatabaseExecuteSqlResponse.Select -> "Select" - is DatabaseExecuteSqlResponse.UpdateDelete -> "UpdateDelete" - } - - return buildJsonObject { - put("type", type) - put("body", thisAsJson.toString()) // warning : the desktop is waiting for a string representation of the json here - }.toString() -} - diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseQueryLogModel.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseQueryLogModel.kt deleted file mode 100644 index e8644e329..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseQueryLogModel.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.openflocon.flocon.plugins.database.model.fromdevice - -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -@Serializable -internal data class DatabaseQueryLogModel( - val dbName: String, - val sqlQuery: String, - val bindArgs: List?, - val timestamp: Long, -) { - fun toJson(): String { - return FloconEncoder.json.encodeToString(this) - } - - companion object { - fun fromJson(json: String): DatabaseQueryLogModel { - return FloconEncoder.json.decodeFromString(json) - } - } -} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DeviceDataBaseDataModel.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DeviceDataBaseDataModel.kt deleted file mode 100644 index 6842ced6f..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DeviceDataBaseDataModel.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.openflocon.flocon.plugins.database.model.fromdevice - -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -@Serializable -internal data class DeviceDataBaseDataModel( - val id: String, - val name: String, -) - -internal fun listDeviceDataBaseDataModelToJson(items: List) : String { - return FloconEncoder.json.encodeToString(items) -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/QueryResultReceivedDataModel.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/QueryResultReceivedDataModel.kt deleted file mode 100644 index 8ef4f899a..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/QueryResultReceivedDataModel.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.openflocon.flocon.plugins.database.model.fromdevice - -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -@Serializable -internal data class QueryResultDataModel( - val requestId: String, - val result: String, -) { - fun toJson(): String { - return FloconEncoder.json.encodeToString(this) - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/todevice/DatabaseQueryMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/todevice/DatabaseQueryMessage.kt deleted file mode 100644 index c6b353d51..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/todevice/DatabaseQueryMessage.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.openflocon.flocon.plugins.database.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class DatabaseQueryMessage( - val query: String, - val requestId: String, - val database: String, -) { - companion object { - fun fromJson(message: String): DatabaseQueryMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksPlugin.kt deleted file mode 100644 index b65bc40fc..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/FloconDeeplinksPlugin.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.openflocon.flocon.plugins.deeplinks - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update - -internal class FloconDeeplinksPluginImpl( - private val sender: FloconMessageSender, -) : FloconPlugin, FloconDeeplinksPlugin { - - private val deeplinks = MutableStateFlow?>(null) - private val variables = MutableStateFlow?>(null) - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - - } - - override fun onConnectedToServer() { - // on connected, send known dashboard - deeplinks.value?.let { - registerDeeplinks(it, variables.value.orEmpty()) - } - } - - override fun registerDeeplinks( - deeplinks: List, - variables: List - ) { - this.deeplinks.update { deeplinks } - this.variables.update { variables } - - try { - sender.send( - plugin = Protocol.FromDevice.Deeplink.Plugin, - method = Protocol.FromDevice.Deeplink.Method.GetDeeplinks, - body = toDeeplinksJson( - deeplinks = deeplinks, - variables = variables - ) - ) - } catch (t: Throwable) { - FloconLogger.logError("deeplink mapping error", t) - } - } -} - diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/Mapping.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/Mapping.kt deleted file mode 100644 index 4dede8700..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/deeplinks/Mapping.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.openflocon.flocon.plugins.deeplinks - -import io.github.openflocon.flocon.core.FloconEncoder -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkModel -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkParameterRemote -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkRemote -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinkVariableRemote -import io.github.openflocon.flocon.plugins.deeplinks.model.DeeplinksRemote - -internal fun toDeeplinksJson( - deeplinks: List, - variables: List -): String { - val dto = DeeplinksRemote( - deeplinks = deeplinks.map(DeeplinkModel::toRemote), - variables = variables.map(DeeplinkVariable::toRemote) - ) - - return FloconEncoder.json - .encodeToString( - serializer = DeeplinksRemote.serializer(), - value = dto - ) -} - -internal fun DeeplinkModel.toRemote(): DeeplinkRemote = DeeplinkRemote( - label = label, - link = link, - description = description, - parameters = parameters.map(DeeplinkModel.Parameter::toRemote) -) - -internal fun DeeplinkVariable.toRemote(): DeeplinkVariableRemote = DeeplinkVariableRemote( - name = name, - mode = when (val mode = mode) { - is DeeplinkVariable.Mode.AutoComplete -> DeeplinkVariableRemote.Mode.AutoComplete(suggestions = mode.suggestions) - DeeplinkVariable.Mode.Input -> DeeplinkVariableRemote.Mode.Input - }, - description = description, -) - -internal fun DeeplinkModel.Parameter.toRemote(): DeeplinkParameterRemote = when (this) { - is DeeplinkModel.Parameter.AutoComplete -> DeeplinkParameterRemote.AutoComplete( - name = paramName, - autoComplete = autoComplete - ) - - is DeeplinkModel.Parameter.Variable -> DeeplinkParameterRemote.Variable( - name = paramName, - variableName = variableName - ) -} - diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt deleted file mode 100644 index 4a2e02b15..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt +++ /dev/null @@ -1,54 +0,0 @@ -package io.github.openflocon.flocon.plugins.device - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.device.model.fromdevice.RegisterDeviceDataModel - -internal expect fun restartApp(context: FloconContext) - -internal class FloconDevicePluginImpl( - private var sender: FloconMessageSender, - private val context: FloconContext, -) : FloconPlugin, FloconDevicePlugin { - - override fun registerWithSerial(serial: String) { - try { - sender.send( - plugin = Protocol.FromDevice.Device.Plugin, - method = Protocol.FromDevice.Device.Method.RegisterDevice, - body = RegisterDeviceDataModel(serial).toJson().toString(), - ) - } catch (t: Throwable) { - FloconLogger.logError("Device parsing error", t) - } - } - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - when (messageFromServer.method) { - Protocol.ToDevice.Device.Method.GetAppIcon -> { - val icon = getAppIconBase64(context) - if (icon != null) { - sender.send( - plugin = Protocol.FromDevice.Device.Plugin, - method = Protocol.FromDevice.Device.Method.AppIcon, - body = icon, - ) - } - } - - Protocol.ToDevice.Device.Method.RestartApp -> { - restartApp(context) - } - } - } - - override fun onConnectedToServer() { - // no op - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/model/fromdevice/RegisterDeviceDataModel.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/model/fromdevice/RegisterDeviceDataModel.kt deleted file mode 100644 index 3bd366e33..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/device/model/fromdevice/RegisterDeviceDataModel.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.openflocon.flocon.plugins.device.model.fromdevice - -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -@Serializable -internal class RegisterDeviceDataModel( - val serial: String, -) { - fun toJson(): String { - return FloconEncoder.json.encodeToString(this) - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFileMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFileMessage.kt deleted file mode 100644 index d8e161301..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFileMessage.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.openflocon.flocon.plugins.files.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceDeleteFileMessage( - val requestId: String, - val parentPath: String, - val filePath: String, - val isConstantParentPath: Boolean, // ex: context.files / context.caches -) { - companion object { - fun fromJson(message: String): ToDeviceDeleteFileMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} - diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFilesMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFilesMessage.kt deleted file mode 100644 index 465d374d5..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFilesMessage.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.openflocon.flocon.plugins.files.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceDeleteFilesMessage( - val requestId: String, - val parentPath: String, - val filePaths: List, - val isConstantParentPath: Boolean, // ex: context.files / context.caches -) { - companion object { - fun fromJson(message: String): ToDeviceDeleteFilesMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFolderContentMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFolderContentMessage.kt deleted file mode 100644 index a2eebd4d2..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceDeleteFolderContentMessage.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.openflocon.flocon.plugins.files.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceDeleteFolderContentMessage( - val requestId: String, - val path: String, - val isConstantPath: Boolean, // ex: context.files / context.caches -) { - companion object { - fun fromJson(message: String): ToDeviceDeleteFolderContentMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} - diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFileMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFileMessage.kt deleted file mode 100644 index 7e95a9c28..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFileMessage.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.openflocon.flocon.plugins.files.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceGetFileMessage( - val requestId: String, - val path: String, -) { - companion object { - fun fromJson(message: String): ToDeviceGetFileMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFilesMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFilesMessage.kt deleted file mode 100644 index d8ba615d7..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/files/model/todevice/ToDeviceGetFilesMessage.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.openflocon.flocon.plugins.files.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceGetFilesMessage( - val requestId: String, - val path: String, - val isConstantPath: Boolean, // ex: context.files / context.caches - val withFoldersSize: Boolean = false, -) { - companion object { - fun fromJson(message: String): ToDeviceGetFilesMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} - - diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.kt deleted file mode 100644 index 4a58473f5..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.kt +++ /dev/null @@ -1,151 +0,0 @@ -package io.github.openflocon.flocon.plugins.network - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.network.mapper.floconNetworkCallRequestToJson -import io.github.openflocon.flocon.plugins.network.mapper.floconNetworkCallResponseToJson -import io.github.openflocon.flocon.plugins.network.mapper.floconNetworkWebSocketEventToJson -import io.github.openflocon.flocon.plugins.network.mapper.parseBadQualityConfig -import io.github.openflocon.flocon.plugins.network.mapper.parseMockResponses -import io.github.openflocon.flocon.plugins.network.mapper.parseWebSocketMockMessage -import io.github.openflocon.flocon.plugins.network.mapper.webSocketIdsToJsonArray -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketEvent -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketMockListener -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -internal const val FLOCON_NETWORK_MOCKS_JSON = "flocon_network_mocks.json" -internal const val FLOCON_NETWORK_BAD_CONFIG_JSON = "flocon_network_bad_config.json" - -internal interface FloconNetworkDataSource { - fun saveMocksToFile(mocks: List) - fun loadMocksFromFile() : List - fun saveBadNetworkConfig(config: BadQualityConfig?) - fun loadBadNetworkConfig(): BadQualityConfig? -} - -internal expect fun buildFloconNetworkDataSource(context: FloconContext): FloconNetworkDataSource - -internal class FloconNetworkPluginImpl( - private val context: FloconContext, - private var sender: FloconMessageSender, - private val coroutineScope: CoroutineScope, -) : FloconPlugin, FloconNetworkPlugin { - - private val dataSource = buildFloconNetworkDataSource(context) - - private val websocketListeners = MutableStateFlow>(emptyMap()) - - private val _mocks = MutableStateFlow>(dataSource.loadMocksFromFile()) - override val mocks : List - get() = _mocks.value - - private val _badQualityConfig = MutableStateFlow(dataSource.loadBadNetworkConfig()) - - override val badQualityConfig: BadQualityConfig? - get() { - return _badQualityConfig.value - } - - override fun logRequest(request: FloconNetworkCallRequest) { - try { - sender.send( - plugin = Protocol.FromDevice.Network.Plugin, - method = Protocol.FromDevice.Network.Method.LogNetworkCallRequest, - body = request.floconNetworkCallRequestToJson(), - ) - } catch (t: Throwable) { - FloconLogger.logError("Network json mapping error", t) - } - } - - override fun logResponse(response: FloconNetworkCallResponse) { - coroutineScope.launch { - delay(200) // to be sure the request is handled before the response, in case of mocks or direct connection refused - try { - sender.send( - plugin = Protocol.FromDevice.Network.Plugin, - method = Protocol.FromDevice.Network.Method.LogNetworkCallResponse, - body = response.floconNetworkCallResponseToJson(), - ) - } catch (t: Throwable) { - FloconLogger.logError("Network json mapping error", t) - } - } - } - - override fun logWebSocket( - event: FloconWebSocketEvent, - ) { - coroutineScope.launch { - try { - sender.send( - plugin = Protocol.FromDevice.Network.Plugin, - method = Protocol.FromDevice.Network.Method.LogWebSocketEvent, - body = event.floconNetworkWebSocketEventToJson(), - ) - } catch (t: Throwable) { - FloconLogger.logError("Network json mapping error", t) - } - } - } - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - when (messageFromServer.method) { - Protocol.ToDevice.Network.Method.SetupMocks -> { - val setup = parseMockResponses(jsonString = messageFromServer.body) - _mocks.update { setup } - dataSource.saveMocksToFile(mocks) - } - - Protocol.ToDevice.Network.Method.SetupBadNetworkConfig -> { - val config = parseBadQualityConfig(jsonString = messageFromServer.body) - _badQualityConfig.update { config } - dataSource.saveBadNetworkConfig(config) - } - - Protocol.ToDevice.Network.Method.WebsocketMockMessage -> { - val message = parseWebSocketMockMessage(jsonString = messageFromServer.body) - if(message != null) { - websocketListeners.value[message.id]?.onMessage(message.message) - } - } - } - } - - override fun onConnectedToServer() { - updateWebSocketIds() - } - - override fun registerWebSocketMockListener( - id: String, - listener: FloconWebSocketMockListener - ) { - websocketListeners.update { - it + (id to listener) - } - updateWebSocketIds() - } - - private fun updateWebSocketIds() { - sender.send( - plugin = Protocol.FromDevice.Network.Plugin, - method = Protocol.FromDevice.Network.Method.RegisterWebSocketIds, - body = webSocketIdsToJsonArray(ids = websocketListeners.value.keys), - ) - } - -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/FloconNetworkRequestToJson.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/FloconNetworkRequestToJson.kt deleted file mode 100644 index d187e7bb2..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/FloconNetworkRequestToJson.kt +++ /dev/null @@ -1,111 +0,0 @@ -@file:OptIn(ExperimentalUuidApi::class) - -package io.github.openflocon.flocon.plugins.network.mapper - -import io.github.openflocon.flocon.core.FloconEncoder -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketEvent -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString -import kotlin.uuid.ExperimentalUuidApi -import kotlin.uuid.Uuid - -@Serializable -internal class FloconNetworkCallRequestRemote( - val floconCallId: String, - val floconNetworkType : String, - val isMocked : Boolean, - - val url: String, - val method: String, - val startTime: Long, - val requestBody: String?, - val requestHeaders: Map, - val requestSize: Long?, -) - -internal fun FloconNetworkCallRequest.floconNetworkCallRequestToJson(): String { - val remoteModel = FloconNetworkCallRequestRemote( - floconCallId = floconCallId, - floconNetworkType = floconNetworkType, - isMocked = isMocked, - url = request.url, - method = request.method, - startTime = request.startTime, - requestBody = request.body, - requestHeaders = request.headers, - requestSize = request.size - ) - return FloconEncoder.json.encodeToString(remoteModel) -} - -@Serializable -internal class FloconNetworkCallResponseRemote( - val floconCallId: String, - val durationMs: Double, - val floconNetworkType: String, - val isMocked: Boolean, - val responseHttpCode: Int?, - val responseGrpcStatus: String?, - val responseContentType: String?, - val responseBody: String?, - val responseSize: Long?, - val responseHeaders: Map, - val requestHeaders: Map?, // we might receive the request headers later if the interceptor is at first position in the http interceptor chain - val responseError: String?, - val isImage: Boolean, -) - -internal fun FloconNetworkCallResponse.floconNetworkCallResponseToJson(): String { - val remoteModel = FloconNetworkCallResponseRemote( - floconCallId = floconCallId, - floconNetworkType = floconNetworkType, - isMocked = isMocked, - durationMs = durationMs, - responseHttpCode = response.httpCode, - responseGrpcStatus = response.grpcStatus, - responseContentType = response.contentType, - responseBody = response.body, - responseHeaders = response.headers, - requestHeaders = response.requestHeaders?.takeIf { - it.isNotEmpty() - }, - responseSize = response.size, - isImage = response.isImage, - responseError = response.error, - ) - - return FloconEncoder.json.encodeToString(remoteModel) -} - -@Serializable -internal class FloconWebSocketEventRemote( - val id: String, - val event: String, - val url: String, - val size: Long, - val timestamp: Long, - val message: String?, - val error: String?, -) - -internal fun FloconWebSocketEvent.floconNetworkWebSocketEventToJson(): String { - val remoteModel = FloconWebSocketEventRemote( - id = Uuid.random().toString(), - event = when (event) { - FloconWebSocketEvent.Event.Closed -> "closed" - FloconWebSocketEvent.Event.Closing -> "closing" - FloconWebSocketEvent.Event.Error -> "error" - FloconWebSocketEvent.Event.ReceiveMessage -> "received" - FloconWebSocketEvent.Event.SendMessage -> "sent" - FloconWebSocketEvent.Event.Open -> "open" - }, - url = websocketUrl, - size = size, - timestamp = timeStamp, - message = message, - error = error?.message - ) - return FloconEncoder.json.encodeToString(remoteModel) -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/Websocket.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/Websocket.kt deleted file mode 100644 index 5212883e4..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/Websocket.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.openflocon.flocon.plugins.network.mapper - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -@Serializable -internal class WebSocketMockMessage( - val id: String, - val message: String, -) - -internal fun webSocketIdsToJsonArray(ids: Collection): String { - return FloconEncoder.json.encodeToString(ids) -} - -internal fun parseWebSocketMockMessage(jsonString: String): WebSocketMockMessage? { - try { - return FloconEncoder.json.decodeFromString(jsonString) - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "mock wesocket network parsing issue", t) - } - return null -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt deleted file mode 100644 index e1818d432..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt +++ /dev/null @@ -1,167 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreferenceValue -import io.github.openflocon.flocon.plugins.sharedprefs.model.fromdevice.PreferenceRowDataModel -import io.github.openflocon.flocon.plugins.sharedprefs.model.fromdevice.SharedPreferenceValueResultDataModel -import io.github.openflocon.flocon.plugins.sharedprefs.model.listSharedPreferencesDescriptorToJson -import io.github.openflocon.flocon.plugins.sharedprefs.model.todevice.ToDeviceEditSharedPreferenceValueMessage -import io.github.openflocon.flocon.plugins.sharedprefs.model.todevice.ToDeviceGetSharedPreferenceValueMessage -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -internal interface FloconPreferencesDataSource { - fun detectLocalPreferences(): List -} - -internal expect fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource - -internal class FloconPreferencesPluginImpl( - context: FloconContext, - private var sender: FloconMessageSender, - private val scope: CoroutineScope, -) : FloconPlugin, FloconPreferencesPlugin { - - // references for quick access - private val preferences = mutableMapOf() - - private val dataSource = buildFloconPreferencesDataSource(context) - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - when (messageFromServer.method) { - Protocol.ToDevice.SharedPreferences.Method.GetSharedPreferences -> { - sendAllSharedPrefs() - } - - Protocol.ToDevice.SharedPreferences.Method.GetSharedPreferenceValue -> { - val toDeviceMessage = - ToDeviceGetSharedPreferenceValueMessage.fromJson(message = messageFromServer.body) - ?: return - - val preference = preferences[toDeviceMessage.sharedPreferenceName] ?: return - - scope.launch { - sendToServerPreferenceValues( - preference = preference, - requestId = toDeviceMessage.requestId, - sender = sender, - ) - } - } - - Protocol.ToDevice.SharedPreferences.Method.SetSharedPreferenceValue -> { - val toDeviceMessage = - ToDeviceEditSharedPreferenceValueMessage.fromJson(jsonString = messageFromServer.body) - ?: return - - val preference = preferences[toDeviceMessage.sharedPreferenceName] ?: return - - scope.launch { - try { - preference.set( - columnName = toDeviceMessage.key, - value = FloconPreferenceValue( - stringValue = toDeviceMessage.stringValue, - booleanValue = toDeviceMessage.booleanValue, - intValue = toDeviceMessage.intValue, - longValue = toDeviceMessage.longValue, - floatValue = toDeviceMessage.floatValue, - setStringValue = toDeviceMessage.setStringValue, - ), - ) - - // then send the shared pref content - sendToServerPreferenceValues( - preference = preference, - requestId = toDeviceMessage.requestId, - sender = sender, - ) - - //sender.send(Protocol.FromDevice.SharedPreferences.Plugin, "success") - } catch (t: Throwable) { - t.printStackTrace() - //sender.send(Protocol.FromDevice.SharedPreferences.Plugin, "failure") - } - } - } - } - } - - private suspend fun sendToServerPreferenceValues( - preference: FloconPreference, - requestId: String, - sender: FloconMessageSender - ) { - val columns = preference.columns() - val rows = columns.map { key -> - val value = preference.get(key) - PreferenceRowDataModel( - key = key, - stringValue = value?.stringValue, - intValue = value?.intValue, - floatValue = value?.floatValue, - booleanValue = value?.booleanValue, - longValue = value?.longValue, - setStringValue = value?.setStringValue, - ) - } - - try { - sender.send( - plugin = Protocol.FromDevice.SharedPreferences.Plugin, - method = Protocol.FromDevice.SharedPreferences.Method.GetSharedPreferenceValue, - body = SharedPreferenceValueResultDataModel( - requestId = requestId, - sharedPreferenceName = preference.name, - rows = rows, - ).toJson(), - ) - } catch (t: Throwable) { - FloconLogger.logError("SharedPreferences json mapping error", t) - } - } - - // on connected, send all shared prefs - override fun onConnectedToServer() { - dataSource.detectLocalPreferences().forEach { preference -> - registerInternal(preference) - } - sendAllSharedPrefs() - } - - override fun register(preference: FloconPreference) { - registerInternal(preference) - sendAllSharedPrefs() - } - - private fun registerInternal(preference: FloconPreference) { - if(preferences.containsKey(preference.name).not()) { - preferences[preference.name] = preference - } - } - - private fun sendAllSharedPrefs() { - val allPrefs = getAllPreferences() - try { - sender.send( - plugin = Protocol.FromDevice.SharedPreferences.Plugin, - method = Protocol.FromDevice.SharedPreferences.Method.GetSharedPreferences, - body = listSharedPreferencesDescriptorToJson(allPrefs).toString(), - ) - } catch (t: Throwable) { - FloconLogger.logError("SharedPreferences json mapping error", t) - } - } - - private fun getAllPreferences(): List { - return preferences.values.sortedBy { it.name } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreferenceWrapper.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreferenceWrapper.kt deleted file mode 100644 index bcf74a15c..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreferenceWrapper.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs.model - -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString - -@Serializable -internal data class PreferencesDescriptor( - val name: String, -) - -internal fun listSharedPreferencesDescriptorToJson(items: List): String { - val value = items.map { PreferencesDescriptor(it.name) } - return FloconEncoder.json.encodeToString(value) -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPreferenceValueMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPreferenceValueMessage.kt deleted file mode 100644 index b30a6a43c..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPreferenceValueMessage.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceGetSharedPreferenceValueMessage( - val requestId: String, - val sharedPreferenceName: String, -) { - companion object { - fun fromJson(message: String): ToDeviceGetSharedPreferenceValueMessage? { - return try { - return FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPrefsMessage.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPrefsMessage.kt deleted file mode 100644 index d549c1ced..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPrefsMessage.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs.model.todevice - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import kotlinx.serialization.Serializable - -@Serializable -internal data class ToDeviceGetSharedPrefsMessage( - val requestId: String, -) { - companion object { - fun fromJson(message: String): ToDeviceGetSharedPrefsMessage? { - return try { - FloconEncoder.json.decodeFromString(message) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesPlugin.kt deleted file mode 100644 index e2d6a7ec4..000000000 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesPlugin.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.openflocon.flocon.plugins.tables - -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.Protocol -import io.github.openflocon.flocon.core.FloconMessageSender -import io.github.openflocon.flocon.core.FloconPlugin -import io.github.openflocon.flocon.model.FloconMessageFromServer -import io.github.openflocon.flocon.plugins.tables.model.TableItem -import io.github.openflocon.flocon.plugins.tables.model.tableItemListToJson - -internal class FloconTablePluginImpl( - private val sender: FloconMessageSender, -) : FloconPlugin, FloconTablePlugin { - - override fun onMessageReceived( - messageFromServer: FloconMessageFromServer, - ) { - // no op - } - - override fun onConnectedToServer() { - // no op - } - - override fun registerTable(tableItem: TableItem) { - sendTable(tableItem) - } - - private fun sendTable(tableItem: TableItem) { - try { - sender.send( - plugin = Protocol.FromDevice.Table.Plugin, - method = Protocol.FromDevice.Table.Method.AddItems, - body = tableItemListToJson(listOf(tableItem)).toString() // desktop is expecting an array of table items - ) - } catch (t: Throwable) { - FloconLogger.logError("Table json mapping error", t) - } - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/FloconDashboardPlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/FloconDashboardPlugin.kt new file mode 100644 index 000000000..48aa3c542 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/FloconDashboardPlugin.kt @@ -0,0 +1,16 @@ +package io.github.openflocon.flocon.pluginsold.dashboard + +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.pluginsold.dashboard.model.DashboardConfig + +class FloconDashboardConfig : FloconPluginConfig + +/** + * Flocon Dashboard Plugin. + */ +//expect object FloconDashboard : FloconPluginFactory +// +interface FloconDashboardPlugin : FloconPlugin { + fun registerDashboard(dashboardConfig: DashboardConfig) +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/ContainerBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/ContainerBuilder.kt new file mode 100644 index 000000000..43a113fc3 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/ContainerBuilder.kt @@ -0,0 +1,14 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.builder + +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.ContainerConfig +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.ElementConfig + +abstract class ContainerBuilder { + open val elements = mutableListOf() + + open fun add(element: ElementConfig) { + elements.add(element) + } + + abstract fun build(): ContainerConfig +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/DashboardBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/DashboardBuilder.kt new file mode 100644 index 000000000..879c6fcc7 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/DashboardBuilder.kt @@ -0,0 +1,21 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.builder + +import io.github.openflocon.flocon.pluginsold.dashboard.dsl.DashboardDsl +import io.github.openflocon.flocon.pluginsold.dashboard.model.DashboardConfig +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.ContainerConfig + +@DashboardDsl +class DashboardBuilder(private val id: String) { + private val containers = mutableListOf() + + fun add(container: ContainerConfig) { + containers.add(container) + } + + fun build(): DashboardConfig { + return DashboardConfig( + id = id, + containers = containers + ) + } +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/FormBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/FormBuilder.kt new file mode 100644 index 000000000..1e43250d9 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/FormBuilder.kt @@ -0,0 +1,20 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.builder + +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.FormConfig + +class FormBuilder( + val name: String, + val submitText: String, + val onSubmitted: (Map) -> Unit, +) : ContainerBuilder() { + + override fun build(): FormConfig { + return FormConfig( + id = "form_$name", + name = name, + submitText = submitText, + elements = elements, + onSubmitted = onSubmitted + ) + } +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/SectionBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/SectionBuilder.kt new file mode 100644 index 000000000..bf301ca26 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/builder/SectionBuilder.kt @@ -0,0 +1,10 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.builder + +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.SectionConfig + +class SectionBuilder(val name: String) : ContainerBuilder() { + + override fun build(): SectionConfig { + return SectionConfig(name, elements) + } +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/ButtonDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/ButtonDsl.kt new file mode 100644 index 000000000..7ae6e6479 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/ButtonDsl.kt @@ -0,0 +1,19 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.ButtonConfig + +@DashboardDsl +fun ContainerBuilder.button( + text: String, + id: String, + onClick: () -> Unit, +) { + add( + ButtonConfig( + text = text, + id = id, + onClick = onClick, + ) + ) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/CheckBoxDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/CheckBoxDsl.kt new file mode 100644 index 000000000..43102e7a0 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/CheckBoxDsl.kt @@ -0,0 +1,21 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.CheckBoxConfig + +@DashboardDsl +fun ContainerBuilder.checkBox( + id: String, + label: String, + value: Boolean, + onUpdated: (Boolean) -> Unit = {}, +) { + add( + CheckBoxConfig( + id = id, + label = label, + value = value, + onUpdated = onUpdated, + ) + ) +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/DashboardDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/DashboardDsl.kt new file mode 100644 index 000000000..39e96f5cd --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/DashboardDsl.kt @@ -0,0 +1,15 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.DashboardBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.DashboardConfig + +@DslMarker +annotation class DashboardDsl + +fun dashboardConfig(id: String, block: DashboardBuilder.() -> Unit): DashboardConfig { + val builder = DashboardBuilder(id = id) + .apply { + block() + } + return builder.build() +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/FormDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/FormDsl.kt new file mode 100644 index 000000000..99ff45560 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/FormDsl.kt @@ -0,0 +1,22 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.DashboardBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.builder.FormBuilder + +@DashboardDsl +fun DashboardBuilder.form( + name: String, + submitText: String, + onSubmitted: (Map) -> Unit, + block: FormBuilder.() -> Unit +) { + val builder = FormBuilder( + name = name, + submitText = submitText, + onSubmitted = onSubmitted + ).apply { + block() + } + + add(builder.build()) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/HtmlDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/HtmlDsl.kt new file mode 100644 index 000000000..050d9f612 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/HtmlDsl.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.HtmlConfig + +@DashboardDsl +fun ContainerBuilder.html(label: String, value: String) { + add(HtmlConfig(label = label, value = value)) +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/MarkdownDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/MarkdownDsl.kt new file mode 100644 index 000000000..cebfbbf99 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/MarkdownDsl.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.MarkdownConfig + +@DashboardDsl +fun ContainerBuilder.markdown(label: String, value: String) { + add(MarkdownConfig(label = label, value = value)) +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/PlainTextDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/PlainTextDsl.kt new file mode 100644 index 000000000..9cff79157 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/PlainTextDsl.kt @@ -0,0 +1,26 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.PlainTextConfig + +@DashboardDsl +fun ContainerBuilder.plainText(label: String, value: String) { + add( + PlainTextConfig( + label = label, + value = value, + type = "text", + ) + ) +} + +@DashboardDsl +fun ContainerBuilder.json(label: String, value: String) { + add( + PlainTextConfig( + label = label, + value = value, + type = "json", + ) + ) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/SectionDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/SectionDsl.kt new file mode 100644 index 000000000..6560ba3a0 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/SectionDsl.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.DashboardBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.builder.SectionBuilder + +@DashboardDsl +fun DashboardBuilder.section(name: String, block: SectionBuilder.() -> Unit) { + val builder = SectionBuilder(name).apply { + block() + } + + add(builder.build()) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/TextDsl.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/TextDsl.kt new file mode 100644 index 000000000..2b13da09a --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/TextDsl.kt @@ -0,0 +1,15 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.LabelConfig +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.TextConfig + +@DashboardDsl +fun ContainerBuilder.text(label: String, value: String, color: Int? = null) { + add(TextConfig(label = label, value = value, color = color)) +} + +@DashboardDsl +fun ContainerBuilder.label(label: String, color: Int? = null) { + add(LabelConfig(label = label, color = color)) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/TextField.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/TextField.kt new file mode 100644 index 000000000..11717ba59 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/dsl/TextField.kt @@ -0,0 +1,23 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.dsl + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.ContainerBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.TextFieldConfig + +@DashboardDsl +fun ContainerBuilder.textField( + id: String, + label: String, + placeHolder: String?, + value: String, + onSubmitted: (String) -> Unit = {}, +) { + add( + TextFieldConfig( + id = id, + label = label, + placeHolder = placeHolder, + value = value, + onSubmitted = onSubmitted, + ) + ) +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/ContainerType.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/ContainerType.kt new file mode 100644 index 000000000..a866d9e5a --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/ContainerType.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model + +enum class ContainerType { + FORM, + SECTION +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/Dashboard.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/Dashboard.kt new file mode 100644 index 000000000..8d8a424f3 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/Dashboard.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model + +import io.github.openflocon.flocon.pluginsold.dashboard.model.config.ContainerConfig + +data class DashboardConfig( + val id: String, + val containers: List +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/DashboardScope.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/DashboardScope.kt new file mode 100644 index 000000000..725c56a07 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/DashboardScope.kt @@ -0,0 +1,25 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model + +import io.github.openflocon.flocon.pluginsold.dashboard.builder.FormBuilder +import io.github.openflocon.flocon.pluginsold.dashboard.builder.SectionBuilder +import kotlinx.coroutines.flow.Flow + +interface DashboardScope { + fun section(name: String, flow: Flow, content: SectionBuilder.(T) -> Unit) + fun section(name: String, content: SectionBuilder.() -> Unit) + + fun form( + name: String, + submitText: String = "Submit", + onSubmitted: (Map) -> Unit, + flow: Flow, + content: FormBuilder.(T) -> Unit + ) + + fun form( + name: String, + submitText: String = "Submit", + onSubmitted: (Map) -> Unit, + content: FormBuilder.() -> Unit + ) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ButtonConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ButtonConfig.kt new file mode 100644 index 000000000..3e54a5568 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ButtonConfig.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class ButtonConfig( + val text: String, + val id: String, + val onClick: () -> Unit, +) : ElementConfig \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/CheckBoxConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/CheckBoxConfig.kt new file mode 100644 index 000000000..de6453454 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/CheckBoxConfig.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class CheckBoxConfig( + val id: String, + val label: String, + val value: Boolean, + val onUpdated: (Boolean) -> Unit, +) : ElementConfig \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ContainerConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ContainerConfig.kt new file mode 100644 index 000000000..53bf15766 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ContainerConfig.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +import io.github.openflocon.flocon.pluginsold.dashboard.model.ContainerType + +sealed interface ContainerConfig { + val name: String + val elements: List + val containerType: ContainerType +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ElementConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ElementConfig.kt new file mode 100644 index 000000000..71a56097a --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/ElementConfig.kt @@ -0,0 +1,3 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +sealed interface ElementConfig \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/FormConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/FormConfig.kt new file mode 100644 index 000000000..644c96da7 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/FormConfig.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +import io.github.openflocon.flocon.pluginsold.dashboard.model.ContainerType + +data class FormConfig( + override val name: String, + override val elements: List, + val id: String, + val submitText: String, + val onSubmitted: (Map) -> Unit, +) : ContainerConfig { + override val containerType: ContainerType = ContainerType.FORM +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/HtmlConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/HtmlConfig.kt new file mode 100644 index 000000000..f3de7f22a --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/HtmlConfig.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class HtmlConfig( + val label: String, + val value: String, +) : ElementConfig diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/LabelConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/LabelConfig.kt new file mode 100644 index 000000000..3e9f9a1f7 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/LabelConfig.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class LabelConfig( + val label: String, + val color: Int?, +) : ElementConfig + diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/MarkdownConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/MarkdownConfig.kt new file mode 100644 index 000000000..7b64738d5 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/MarkdownConfig.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class MarkdownConfig( + val label: String, + val value: String, +) : ElementConfig diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/PlainTextConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/PlainTextConfig.kt new file mode 100644 index 000000000..53c37df21 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/PlainTextConfig.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class PlainTextConfig( + val label: String, + val value: String, + val type: String, // text, json +) : ElementConfig + diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/SectionConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/SectionConfig.kt new file mode 100644 index 000000000..b2654befa --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/SectionConfig.kt @@ -0,0 +1,10 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +import io.github.openflocon.flocon.pluginsold.dashboard.model.ContainerType + +data class SectionConfig( + override val name: String, + override val elements: List, +) : ContainerConfig { + override val containerType: ContainerType = ContainerType.SECTION +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/TextConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/TextConfig.kt new file mode 100644 index 000000000..5a54816b5 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/TextConfig.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class TextConfig( + val label: String, + val value: String, + val color: Int?, +) : ElementConfig + diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/TextFieldConfig.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/TextFieldConfig.kt new file mode 100644 index 000000000..bf2e5ed4a --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/dashboard/model/config/TextFieldConfig.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.pluginsold.dashboard.model.config + +data class TextFieldConfig( + val id: String, + val label: String, + val placeHolder: String?, + val value: String, + val onSubmitted: (String) -> Unit, +) : ElementConfig \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/device/FloconDevicePlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/device/FloconDevicePlugin.kt new file mode 100644 index 000000000..47aecce92 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/device/FloconDevicePlugin.kt @@ -0,0 +1,36 @@ +package io.github.openflocon.flocon.pluginsold.device + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.core.FloconEncoder + +class FloconDeviceConfig : FloconPluginConfig + +/** + * Flocon Device Plugin. + */ +object FloconDevice : FloconPluginFactory { + override fun createConfig(context: FloconContext): FloconDeviceConfig { + TODO("Not yet implemented") + } + + override fun install( + pluginConfig: FloconDeviceConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconDevicePlugin { + TODO("Not yet implemented") + } + + override val name: String + get() = TODO("Not yet implemented") + override val pluginId: String + get() = TODO("Not yet implemented") +} + +interface FloconDevicePlugin : FloconPlugin { + fun registerWithSerial(serial: String) +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/tables/builder/TableBuilder.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/tables/builder/TableBuilder.kt new file mode 100644 index 000000000..484c85387 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/tables/builder/TableBuilder.kt @@ -0,0 +1,20 @@ +@file:OptIn(ExperimentalUuidApi::class) + +package io.github.openflocon.flocon.pluginsold.tables.builder + +import kotlin.uuid.ExperimentalUuidApi + +//class TableBuilder( +// val tableName: String, +// private val tablePlugin: FloconTablePlugin?, +//) { +// fun log(vararg columns: TableColumnConfig) { +// val dashboardConfig = TableItem( +// id = Uuid.random().toString(), +// name = tableName, +// columns = columns.toList(), +// createdAt = currentTimeMillis(), +// ) +// tablePlugin?.registerTable(dashboardConfig) +// } +//} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.kt new file mode 100644 index 000000000..be94d1ed8 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.kt @@ -0,0 +1,8 @@ +package kotlinx.coroutines + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +public expect val Dispatchers.IO: CoroutineDispatcher + +public expect val IO: CoroutineDispatcher diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.kt similarity index 100% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.kt rename to FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.kt diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.kt index 7660cf030..a82cb3177 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.kt @@ -1,11 +1,14 @@ package io.github.openflocon.flocon.websocket import io.github.openflocon.flocon.FloconFile +import io.github.openflocon.flocon.dsl.FloconMarker import io.github.openflocon.flocon.model.FloconFileInfo -internal expect fun buildFloconHttpClient() : FloconHttpClient +internal expect fun buildFloconHttpClient(): FloconHttpClient internal interface FloconHttpClient { + + @FloconMarker suspend fun send( file: FloconFile, infos: FloconFileInfo, @@ -14,5 +17,5 @@ internal interface FloconHttpClient { deviceId: String, appPackageName: String, appInstance: Long - ) : Boolean + ): Boolean } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/Flocon.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/Flocon.ios.kt deleted file mode 100644 index 14a0608d9..000000000 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/Flocon.ios.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.openflocon.flocon - -object Flocon : FloconCore() { - fun initialize() { - super.initializeFlocon(context = FloconContext()) - } -} - diff --git a/FloconAndroid/flocon-base/src/iosMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt similarity index 97% rename from FloconAndroid/flocon-base/src/iosMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt rename to FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt index 32b5a87cf..c77d01ab2 100644 --- a/FloconAndroid/flocon-base/src/iosMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt +++ b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt @@ -3,18 +3,17 @@ package io.github.openflocon.flocon actual object FloconLogger { actual var enabled = false private const val TAG = "FloconLogger" - + actual fun logError(text: String, throwable: Throwable?) { if(enabled) { println("ERROR $TAG: $text") throwable?.printStackTrace() } } - + actual fun log(text: String) { if(enabled) { println("$TAG: $text") } } -} - +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.ios.kt index 73cb6ac63..bdf02ebb7 100644 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.ios.kt +++ b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.ios.kt @@ -5,7 +5,6 @@ import androidx.sqlite.driver.NativeSQLiteDriver import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.plugins.database.model.FloconDatabaseModel import io.github.openflocon.flocon.plugins.database.model.FloconFileDatabaseModel -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DatabaseExecuteSqlResponse import io.github.openflocon.flocon.plugins.database.model.fromdevice.DeviceDataBaseDataModel import platform.Foundation.NSFileManager import platform.posix.close diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.ios.kt deleted file mode 100644 index bdccd4100..000000000 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.ios.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.openflocon.flocon.plugins.network - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse - -internal actual fun buildFloconNetworkDataSource(context: FloconContext): FloconNetworkDataSource { - return FloconNetworkDataSourceIOs() -} - -// TODO -internal class FloconNetworkDataSourceIOs : FloconNetworkDataSource { - override fun saveMocksToFile(mocks: List) { - // TODO - } - - override fun loadMocksFromFile(): List { - return emptyList() - } - - override fun saveBadNetworkConfig(config: BadQualityConfig?) { - // TODO - } - - override fun loadBadNetworkConfig(): BadQualityConfig? { - return null - } - -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.ios.kt deleted file mode 100644 index eb0c19068..000000000 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.ios.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.fromdevice.PreferenceRowDataModel -import io.github.openflocon.flocon.plugins.sharedprefs.model.todevice.ToDeviceEditSharedPreferenceValueMessage - -internal actual fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource { - return FloconPreferencesDataSourceIOs() -} - -// TODO try to bind with ios storage -internal class FloconPreferencesDataSourceIOs : FloconPreferencesDataSource { - override fun detectLocalPreferences(): List { - return emptyList() - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.ios.kt new file mode 100644 index 000000000..7c711bc0a --- /dev/null +++ b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.ios.kt @@ -0,0 +1,8 @@ +package kotlinx.coroutines + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +public actual val Dispatchers.IO: CoroutineDispatcher get() = Dispatchers.Default + +public actual val IO: CoroutineDispatcher get() = Dispatchers.Default diff --git a/FloconAndroid/flocon-base/src/iosMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.ios.kt similarity index 100% rename from FloconAndroid/flocon-base/src/iosMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.ios.kt rename to FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.ios.kt diff --git a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.ios.kt b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.ios.kt index cd9fa5333..690d060fa 100644 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.ios.kt +++ b/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.ios.kt @@ -13,10 +13,13 @@ import io.ktor.http.HttpHeaders import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json +import io.github.openflocon.flocon.dsl.FloconMarker + internal actual fun buildFloconHttpClient(): FloconHttpClient { return FloconHttpClientIOs() } +@io.github.openflocon.flocon.dsl.FloconMarker internal class FloconHttpClientIOs() : FloconHttpClient { // client configurable selon la plateforme (Android, iOS, JVM, etc.) diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/Flocon.jvm.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/Flocon.jvm.kt deleted file mode 100644 index ec43cd8ff..000000000 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/Flocon.jvm.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.openflocon.flocon - -object Flocon : FloconCore() { - - fun initialize() { - super.initializeFlocon(context = FloconContext( - appName = "Flocon-sample", - packageName = "io.github.openflocon.flocon", - )) - } -} diff --git a/FloconAndroid/flocon-base/src/jvmMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt similarity index 97% rename from FloconAndroid/flocon-base/src/jvmMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt rename to FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt index c7577ef7d..f69122a84 100644 --- a/FloconAndroid/flocon-base/src/jvmMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt +++ b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/FloconLogger.kt @@ -3,18 +3,17 @@ package io.github.openflocon.flocon actual object FloconLogger { actual var enabled = false private const val TAG = "FloconLogger" - + actual fun logError(text: String, throwable: Throwable?) { if(enabled) { System.err.println("$TAG: $text") throwable?.printStackTrace() } } - + actual fun log(text: String) { if(enabled) { println("$TAG: $text") } } -} - +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.jvm.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.jvm.kt index 01708953c..dc49389d3 100644 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.jvm.kt +++ b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.jvm.kt @@ -3,7 +3,6 @@ package io.github.openflocon.flocon.plugins.database import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.plugins.database.model.FloconDatabaseModel import io.github.openflocon.flocon.plugins.database.model.FloconFileDatabaseModel -import io.github.openflocon.flocon.plugins.database.model.fromdevice.DatabaseExecuteSqlResponse import io.github.openflocon.flocon.plugins.database.model.fromdevice.DeviceDataBaseDataModel import java.io.File import java.sql.Connection diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.jvm.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.jvm.kt deleted file mode 100644 index 1fcfea362..000000000 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.jvm.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.openflocon.flocon.plugins.sharedprefs - -import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference - -internal actual fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource { - return FloconPreferencesDataSourceJvm() -} - -internal class FloconPreferencesDataSourceJvm : FloconPreferencesDataSource { - override fun detectLocalPreferences(): List { - return emptyList() - } -} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.jvm.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.jvm.kt new file mode 100644 index 000000000..382fa2232 --- /dev/null +++ b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.jvm.kt @@ -0,0 +1,8 @@ +package kotlinx.coroutines + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +public actual val Dispatchers.IO: CoroutineDispatcher get() = Dispatchers.IO + +public actual val IO: CoroutineDispatcher get() = Dispatchers.IO diff --git a/FloconAndroid/flocon-base/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.jvm.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.jvm.kt similarity index 100% rename from FloconAndroid/flocon-base/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.jvm.kt rename to FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.jvm.kt diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconContext.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconContext.wasmJs.kt new file mode 100644 index 000000000..c6158c9c2 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconContext.wasmJs.kt @@ -0,0 +1,3 @@ +package io.github.openflocon.flocon + +actual class FloconContext diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconCore.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconCore.wasmJs.kt new file mode 100644 index 000000000..22cbf2293 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconCore.wasmJs.kt @@ -0,0 +1,4 @@ +package io.github.openflocon.flocon + +internal actual fun displayClearTextError(context: FloconContext) { +} diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconFile.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconFile.wasmJs.kt new file mode 100644 index 000000000..5b4a91e15 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconFile.wasmJs.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon + +import io.github.openflocon.flocon.dsl.FloconMarker + +@FloconMarker +actual class FloconFile diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconLogger.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconLogger.wasmJs.kt new file mode 100644 index 000000000..637d7a7c9 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/FloconLogger.wasmJs.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.flocon + +actual object FloconLogger { + actual var enabled = false + + actual fun logError(text: String, throwable: Throwable?) { + if(enabled) { + println("ERROR: $text") + throwable?.printStackTrace() + } + } + + actual fun log(text: String) { + if(enabled) { + println("DEBUG: $text") + } + } +} diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/ServerHost.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/ServerHost.wasmJs.kt new file mode 100644 index 000000000..489dcbce0 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/ServerHost.wasmJs.kt @@ -0,0 +1,3 @@ +package io.github.openflocon.flocon + +internal actual fun getServerHost(floconContext: FloconContext): String = "localhost" diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/core/AppInfos.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/core/AppInfos.wasmJs.kt new file mode 100644 index 000000000..e971bc432 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/core/AppInfos.wasmJs.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.core + +import io.github.openflocon.flocon.FloconContext + +internal actual fun getAppInfos(floconContext: FloconContext): AppInfos = AppInfos( + deviceId = "wasm-device", + deviceName = "Browser", + appName = "Flocon Wasm", + appPackageName = "io.github.openflocon.wasm", + platform = "wasm" +) diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.wasmJs.kt new file mode 100644 index 000000000..7c711bc0a --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/utils/DispatcherUtils.wasmJs.kt @@ -0,0 +1,8 @@ +package kotlinx.coroutines + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +public actual val Dispatchers.IO: CoroutineDispatcher get() = Dispatchers.Default + +public actual val IO: CoroutineDispatcher get() = Dispatchers.Default diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.wasmJs.kt new file mode 100644 index 000000000..ec9cb0ebe --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/utils/PlatformUtils.wasmJs.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.flocon.utils + +// Using a simple polyfill/wrapper for time in wasmJs if needed, +// but for now we'll try to use what's available or stubs to make it compile. +// Note: In a real app, you'd use a library like kotlinx-datetime. + +actual fun currentTimeMillis(): Long = 0L // Stub for now to ensure compilation + +actual fun currentTimeNanos(): Long = 0L // Stub for now to ensure compilation + +actual fun createThrowableFromClassName(className: String): Throwable? = null diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.wasmJs.kt new file mode 100644 index 000000000..c5bb72acf --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.wasmJs.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.flocon.websocket + +import io.github.openflocon.flocon.FloconFile +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.model.FloconFileInfo + +internal actual fun buildFloconHttpClient(): FloconHttpClient = object : FloconHttpClient { + @FloconMarker + override suspend fun send( + file: FloconFile, + infos: FloconFileInfo, + address: String, + port: Int, + deviceId: String, + appPackageName: String, + appInstance: Long + ): Boolean = false +} diff --git a/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/websocket/FloconWebSocketClient.wasmJs.kt b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/websocket/FloconWebSocketClient.wasmJs.kt new file mode 100644 index 000000000..ca5e53a22 --- /dev/null +++ b/FloconAndroid/flocon/src/wasmJsMain/kotlin/io/github/openflocon/flocon/websocket/FloconWebSocketClient.wasmJs.kt @@ -0,0 +1,19 @@ +package io.github.openflocon.flocon.websocket + +internal actual fun buildFloconWebSocketClient(): FloconWebSocketClient = object : FloconWebSocketClient { + override suspend fun connect( + address: String, + port: Int, + onMessageReceived: (String) -> Unit, + onClosed: () -> Unit + ) { + } + + override suspend fun sendPendingMessages() { + } + + override suspend fun sendMessage(message: String): Boolean = false + + override suspend fun disconnect() { + } +} diff --git a/FloconAndroid/gradle/gradle-daemon-jvm.properties b/FloconAndroid/gradle/gradle-daemon-jvm.properties new file mode 100644 index 000000000..cfd299ab0 --- /dev/null +++ b/FloconAndroid/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/584d2f01a3c6e59ebb9478a182f5f714/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/7352c4c0c11b2db21fdd7541204de287/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/584d2f01a3c6e59ebb9478a182f5f714/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/7352c4c0c11b2db21fdd7541204de287/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/12ed6bcbab330f7afa37d16220b272a3/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/a7e1d8e6e800a81047d4aec26156ef5c/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/584d2f01a3c6e59ebb9478a182f5f714/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/7352c4c0c11b2db21fdd7541204de287/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3676ee7aa5095d7f22645eb0f22ca159/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/fc98f2d434c5796fe6ec02f0f22957b3/redirect +toolchainVendor=AZUL +toolchainVersion=17 diff --git a/FloconAndroid/gradle/libs.versions.toml b/FloconAndroid/gradle/libs.versions.toml index 32ea39e80..c3a757f69 100644 --- a/FloconAndroid/gradle/libs.versions.toml +++ b/FloconAndroid/gradle/libs.versions.toml @@ -1,44 +1,45 @@ [versions] -agp = "8.11.0-rc02" +agp = "8.13.2" apollo = "4.0.0" coilCompose = "3.2.0" compose = "1.9.0" datastorePreferences = "1.1.7" -kotlin = "2.1.0" +kotlin = "2.1.21" mavenPublish = "0.34.0" coreKtx = "1.16.0" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" -kotlinxCoroutinesBom = "1.10.2" -kotlinxSerialization = "1.8.0" +kotlinxCoroutines = "1.10.2" +kotlinxSerialization = "1.7.1" ktor = "3.2.3" lifecycleRuntimeKtx = "2.9.1" activityCompose = "1.10.1" composeBom = "2025.06.01" appcompat = "1.7.1" material = "1.12.0" -okhttpBom = "4.12.0" -room = "2.7.2" +okhttp = "4.12.0" +room = "2.8.4" # for grpc gson = "2.11.0" -grpc = "1.70.0" +grpc = "1.73.0" protobufPlugin = "0.9.5" grpcKotlin = "1.4.3" protobuf = "4.26.1" -ksp = "2.1.0-1.0.29" +ksp = "2.1.21-2.0.2" processPhoenix = "3.0.0" -sqlite = "2.5.2" +sqlite = "2.6.2" +sqlite-alpha = "2.7.0-alpha02" sqliteJdbc = "3.50.3.0" buildconfig = "5.6.8" brotli = "0.1.2" [libraries] +android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" } +kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } +vanniktech-mavenPublish-gradlePlugin = { group = "com.vanniktech", name = "gradle-maven-publish-plugin", version.ref = "mavenPublish" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } -androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } -androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" } -androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } brotli-dec = { module = "org.brotli:dec", version.ref = "brotli" } apollo-http-okhttprealization = { module = "com.apollographql.apollo:apollo-http-okhttprealization", version.ref = "apollo" } apollo-runtime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo" } @@ -46,9 +47,6 @@ coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCo coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coilCompose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coilCompose" } jakewharton-process-phoenix = { group = "com.jakewharton", name = "process-phoenix", version.ref = "processPhoenix" } -jetbrains-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } -jetbrains-kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } -jetbrains-kotlinx-coroutines-core-fixed = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesBom" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -62,17 +60,22 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } + # for grpc gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } grpc-android = { group = "io.grpc", name = "grpc-android", version.ref = "grpc" } grpc-okhttp = { group = "io.grpc", name = "grpc-okhttp", version.ref = "grpc" } grpc-kotlin-stub = { group = "io.grpc", name = "grpc-kotlin-stub", version.ref = "grpcKotlin" } grpc-protobuf-lite = { group = "io.grpc", name = "grpc-protobuf-lite", version.ref = "grpc" } -kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } -kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "kotlinxCoroutinesBom" } -kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } -kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutinesBom" } +grpc-gen-java = { group = "io.grpc", name = "protoc-gen-grpc-java", version.ref = "grpc" } +grpc-gen-kotlin = { group = "io.grpc", name = "protoc-gen-grpc-kotlin", version.ref = "grpcKotlin" } + +kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutines" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } + ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } @@ -81,18 +84,24 @@ ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "kto ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-clientJava = { module = "io.ktor:ktor-client-java", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } -okhttp = { module = "com.squareup.okhttp3:okhttp" } -okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttpBom" } -okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp" } -org-jetbrains-kotlinx-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } -org-jetbrains-kotlinx-kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } + +okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } + +protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version = "3.25.1" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf"} protobuf-util = { group = "com.google.protobuf", name = "protobuf-java-util", version.ref = "protobuf" } + sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqliteJdbc" } -squareup-okhttp = { module = "com.squareup.okhttp3:okhttp" } -sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" } -androidx-sqlite = { group = "androidx.sqlite", name = "sqlite", version.ref = "sqlite" } -androidx-sqlite-framework = { group = "androidx.sqlite", name = "sqlite-framework", version.ref = "sqlite" } + +androidx-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite-alpha" } +androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } +androidx-room-sqlite-wrapper = { module = "androidx.room:room-sqlite-wrapper", version.ref = "room" } + +androidx-room3-runtime = { module = "androidx.room3:room3-runtime", version = "3.0.0-alpha01" } +androidx-room3-compiler = { module = "androidx.room3:room3-compiler", version = "3.0.0-alpha01" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } @@ -107,3 +116,4 @@ android-library = { id = "com.android.library", version.ref = "agp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" } buildconfig = { id = "com.github.gmazzo.buildconfig", version.ref = "buildconfig" } +protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } diff --git a/FloconAndroid/grpc/grpc-interceptor-base/build.gradle.kts b/FloconAndroid/grpc/grpc-interceptor-base/build.gradle.kts index e43bb3211..a6d942dc0 100644 --- a/FloconAndroid/grpc/grpc-interceptor-base/build.gradle.kts +++ b/FloconAndroid/grpc/grpc-interceptor-base/build.gradle.kts @@ -1,46 +1,18 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" + id("flocon.android.library") + id("flocon.publish") } + android { namespace = "io.github.openflocon.flocon.grpc.base" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } } -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} dependencies { - implementation(project(":flocon-base")) + implementation(projects.network.core) implementation(platform(libs.kotlinx.coroutines.bom)) implementation(libs.kotlinx.coroutines.core) @@ -50,44 +22,9 @@ dependencies { mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } - coordinates( groupId = project.property("floconGroupId") as String, artifactId = "flocon-grpc-interceptor-base", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - - - pom { - name = "Flocon Grpc Interceptor" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } -} \ No newline at end of file +} \ No newline at end of file diff --git a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/BadQuality.kt b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/BadQuality.kt index ae1b9d370..d410a2a2d 100644 --- a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/BadQuality.kt +++ b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/BadQuality.kt @@ -1,6 +1,6 @@ package io.github.openflocon.flocon.grpc -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.BadQualityConfig import java.io.IOException @Throws(IOException::class) diff --git a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt index 4edd65a0e..4f321f3f1 100644 --- a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt +++ b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt @@ -2,13 +2,12 @@ package io.github.openflocon.flocon.grpc -import io.github.openflocon.flocon.FloconApp import io.github.openflocon.flocon.FloconLogger import io.github.openflocon.flocon.grpc.model.RequestHolder import io.github.openflocon.flocon.grpc.model.toHeaders -import io.github.openflocon.flocon.plugins.network.FloconNetworkPlugin -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse +import io.github.openflocon.flocon.network.core.FloconNetworkPlugin +import io.github.openflocon.flocon.network.core.model.FloconNetworkRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkResponse import io.grpc.CallOptions import io.grpc.Channel import io.grpc.ClientCall @@ -33,7 +32,7 @@ abstract class FloconGrpcBaseInterceptor( callOptions: CallOptions, next: Channel, ): ClientCall { - val networkPlugin = FloconApp.instance?.client?.networkPlugin + val networkPlugin = TODO()//FloconApp.instance?.client?.networkPlugin if (networkPlugin == null) { // do not intercept if no network plugin, just call return next.newCall(method, callOptions) @@ -101,9 +100,9 @@ private class LoggingForwardingClientCall( callId = callId, request = request ) - floconNetworkPlugin.badQualityConfig?.let { - executeBadQuality(it) - } +// floconNetworkPlugin.badQualityConfig?.let { +// executeBadQuality(it) +// } super.sendMessage(message) } } diff --git a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt index be3219ae4..732a4a925 100644 --- a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt +++ b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt @@ -1,22 +1,19 @@ package io.github.openflocon.flocon.grpc -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse +import io.github.openflocon.flocon.network.core.model.FloconNetworkRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkResponse internal class FloconGrpcPlugin() { fun reportRequest(callId: String, request: FloconNetworkRequest) { - FloconApp.instance?.client?.networkPlugin?.logRequest( - FloconNetworkCallRequest( - floconCallId = callId, - floconNetworkType = "grpc", - request = request, - isMocked = false, - ) - ) +// FloconApp.instance?.client?.networkPlugin?.logRequest( +// FloconNetworkCallRequest( +// floconCallId = callId, +// floconNetworkType = "grpc", +// request = request, +// isMocked = false, +// ) +// ) } fun reportResponse( @@ -25,16 +22,16 @@ internal class FloconGrpcPlugin() { response: FloconNetworkResponse ) { val responseTime = System.currentTimeMillis() - val durationMs = (responseTime - request.startTime).toDouble() + (responseTime - request.startTime).toDouble() - FloconApp.instance?.client?.networkPlugin?.logResponse( - FloconNetworkCallResponse( - floconCallId = callId, - floconNetworkType = "grpc", - response = response, - durationMs = durationMs, - isMocked = false, - ) - ) +// FloconApp.instance?.client?.networkPlugin?.logResponse( +// FloconNetworkCallResponse( +// floconCallId = callId, +// floconNetworkType = "grpc", +// response = response, +// durationMs = durationMs, +// isMocked = false, +// ) +// ) } } diff --git a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/model/RequestHolder.kt b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/model/RequestHolder.kt index 9518cb4e4..5ad926c13 100644 --- a/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/model/RequestHolder.kt +++ b/FloconAndroid/grpc/grpc-interceptor-base/src/main/kotlin/io/github/openflocon/flocon/grpc/model/RequestHolder.kt @@ -1,8 +1,8 @@ package io.github.openflocon.flocon.grpc.model -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkRequest import kotlinx.coroutines.CompletableDeferred internal data class RequestHolder( - val request: CompletableDeferred = CompletableDeferred() + val request: CompletableDeferred = CompletableDeferred() ) \ No newline at end of file diff --git a/FloconAndroid/grpc/grpc-interceptor-lite/build.gradle.kts b/FloconAndroid/grpc/grpc-interceptor-lite/build.gradle.kts index cc99dcd78..9f49224d7 100644 --- a/FloconAndroid/grpc/grpc-interceptor-lite/build.gradle.kts +++ b/FloconAndroid/grpc/grpc-interceptor-lite/build.gradle.kts @@ -1,90 +1,27 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" -} - -android { - namespace = "io.github.openflocon.flocon.grpc.lite" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - -dependencies { - api(project(":grpc:grpc-interceptor-base")) - - implementation(libs.grpc.android) - implementation(libs.gson) -} - -mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } - - coordinates( - groupId = project.property("floconGroupId") as String, - artifactId = "flocon-grpc-interceptor-lite", - version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String - ) - - - pom { - name = "Flocon Grpc Interceptor Lite" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } -} \ No newline at end of file +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("flocon.android.library") + id("flocon.publish") +} + + +android { + namespace = "io.github.openflocon.flocon.grpc.lite" +} + + +dependencies { + api(projects.grpc.grpcInterceptorBase) + + implementation(libs.grpc.android) + implementation(libs.gson) +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-grpc-interceptor-lite", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} \ No newline at end of file diff --git a/FloconAndroid/grpc/grpc-interceptor/build.gradle.kts b/FloconAndroid/grpc/grpc-interceptor/build.gradle.kts index c39e77e0f..ef4b916ff 100644 --- a/FloconAndroid/grpc/grpc-interceptor/build.gradle.kts +++ b/FloconAndroid/grpc/grpc-interceptor/build.gradle.kts @@ -1,91 +1,27 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - id("com.vanniktech.maven.publish") version "0.34.0" -} - -android { - namespace = "io.github.openflocon.flocon.grpc" - compileSdk = 36 - - defaultConfig { - minSdk = 23 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - -dependencies { - api(project(":grpc:grpc-interceptor-base")) - - implementation(libs.grpc.android) - implementation(libs.protobuf.util) -} - - -mavenPublishing { - publishToMavenCentral(automaticRelease = true) - - if (project.hasProperty("signing.required") && project.property("signing.required") == "false") { - // Skip signing - } else { - signAllPublications() - } - - coordinates( - groupId = project.property("floconGroupId") as String, - artifactId = "flocon-grpc-interceptor", - version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String - ) - - - pom { - name = "Flocon Grpc Interceptor" - description = project.property("floconDescription") as String - inceptionYear = "2025" - url = "https://github.com/openflocon/Flocon" - licenses { - license { - name = "The Apache License, Version 2.0" - url = "https://www.apache.org/licenses/LICENSE-2.0.txt" - distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } - developers { - developer { - id = "openflocon" - name = "Open Flocon" - url = "https://github.com/openflocon" - } - } - scm { - url = "https://github.com/openflocon/Flocon" - connection = "scm:git:git://github.com/openflocon/Flocon.git" - developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" - } - } -} \ No newline at end of file +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("flocon.android.library") + id("flocon.publish") +} + +android { + namespace = "io.github.openflocon.flocon.grpc" +} + + +dependencies { + api(projects.grpc.grpcInterceptorBase) + + implementation(libs.grpc.android) + implementation(libs.protobuf.util) +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-grpc-interceptor", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} \ No newline at end of file diff --git a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt b/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt deleted file mode 100644 index 5a947fea8..000000000 --- a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt +++ /dev/null @@ -1,220 +0,0 @@ -@file:OptIn(ExperimentalUuidApi::class) - -package io.github.openflocon.flocon.ktor - -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse -import io.ktor.client.HttpClientConfig -import io.ktor.client.plugins.api.createClientPlugin -import io.ktor.client.request.HttpRequest -import io.ktor.client.request.HttpSendPipeline -import io.ktor.client.statement.HttpReceivePipeline -import io.ktor.client.statement.HttpResponse -import io.ktor.client.statement.bodyAsChannel -import io.ktor.http.contentType -import io.ktor.util.AttributeKey -import io.ktor.utils.io.ByteReadChannel -import io.ktor.utils.io.toByteArray -import io.github.openflocon.flocon.utils.currentTimeMillis -import io.github.openflocon.flocon.utils.currentTimeNanos -import io.ktor.client.call.save -import io.ktor.client.request.HttpRequestBuilder -import kotlin.uuid.ExperimentalUuidApi -import kotlin.uuid.Uuid - - -data class FloconNetworkIsImageParams( - val request: HttpRequest, - val response: HttpResponse, - val responseContentType: String?, -) - -class FloconKtorPluginConfig { - var isImage: ((param: FloconNetworkIsImageParams) -> Boolean)? = null - var shouldLog: ((request: HttpRequestBuilder) -> Boolean) = { true } -} - -val FloconKtorPlugin = createClientPlugin("FloconKtorPlugin", ::FloconKtorPluginConfig) { - - val theClient = client - val isImageCallback = pluginConfig.isImage - val shouldLogCallback = pluginConfig.shouldLog - - // Intercept requests - client.sendPipeline.intercept(HttpSendPipeline.Monitoring) { - val floconNetworkPlugin = FloconApp.instance?.client?.networkPlugin - val request: HttpRequestBuilder = context - - if (floconNetworkPlugin == null || !shouldLogCallback(request)) { - request.attributes.put(FLOCON_SHOULD_LOG, false) - proceed() - return@intercept - } - - val floconCallId = Uuid.random().toString() - val floconNetworkType = "http" - val requestedAt = currentTimeMillis() - - // Reads the body without consuming it - val requestBodyString = extractAndReplaceRequestBody(request) - val requestSize = requestBodyString?.encodeToByteArray()?.size?.toLong() - val requestHeadersMap = - request.headers.entries().associate { it.key to it.value.joinToString(",") } - - val mockConfig = findMock(request, floconNetworkPlugin) - val isMocked = mockConfig != null - - val floconNetworkRequest = FloconNetworkRequest( - url = request.url.toString(), - method = request.method.value, - startTime = requestedAt, - headers = requestHeadersMap, - body = requestBodyString, - size = requestSize, - isMocked = isMocked - ) - - floconNetworkPlugin.logRequest( - FloconNetworkCallRequest( - floconCallId = floconCallId, - floconNetworkType = floconNetworkType, - isMocked = isMocked, - request = floconNetworkRequest - ) - ) - - val startTime = currentTimeNanos() - request.attributes.put(FLOCON_CALL_ID_KEY, floconCallId) - request.attributes.put(FLOCON_START_TIME_KEY, startTime) - request.attributes.put(FLOCON_IS_MOCKED_KEY, isMocked) - - try { - if (isMocked) { - val fakeCall = executeMock(client = theClient, request = request, mock = mockConfig) - proceedWith(fakeCall) - return@intercept - } - - floconNetworkPlugin.badQualityConfig?.let { badQualityConfig -> - executeBadQuality( - badQualityConfig = badQualityConfig, - client = theClient, - request = request - ) - } ?: run { - proceed() - } - } catch (t: Throwable) { - val endTime = currentTimeNanos() - - val durationMs: Double = (endTime - startTime) / 1e6 - - val floconCallResponse = FloconNetworkResponse( - httpCode = null, - contentType = null, - body = null, - headers = emptyMap(), - size = null, - grpcStatus = null, - error = t.message ?: t::class.simpleName ?: "Unknown", - requestHeaders = requestHeadersMap, - isImage = false, - ) - - floconNetworkPlugin.logResponse( - FloconNetworkCallResponse( - floconCallId = floconCallId, - durationMs = durationMs, - floconNetworkType = floconNetworkType, - isMocked = isMocked, - response = floconCallResponse, - ) - ) - throw t - } - } - - // Intercepts responses - client.receivePipeline.intercept(HttpReceivePipeline.After) { response -> - val floconNetworkPlugin = FloconApp.instance?.client?.networkPlugin ?: return@intercept - - val savedCall = response.call.save() - val savedResponse = savedCall.response - val call = response.call - val request: HttpRequest = call.request - - val floconShouldLog = request.attributes.getOrNull(FLOCON_SHOULD_LOG) ?: true - if(!floconShouldLog) { - proceed() - return@intercept - } - - val floconCallId = request.attributes.getOrNull(FLOCON_CALL_ID_KEY) ?: return@intercept - val startTime = request.attributes.getOrNull(FLOCON_START_TIME_KEY) ?: return@intercept - val isMocked = request.attributes.getOrNull(FLOCON_IS_MOCKED_KEY) ?: false - - val endTime = currentTimeNanos() - val durationMs = (endTime - startTime) / 1e6 - - val originalBodyBytes = response.bodyAsChannel().toByteArray() - val responseSize = originalBodyBytes.size.toLong() - - val responseHeadersMap = - response.headers.entries().associate { it.key to it.value.joinToString(",") } - val contentType = response.contentType()?.toString() - - val requestHeadersMap = - request.headers.entries().associate { it.key to it.value.joinToString(",") } - - val isImage = contentType?.startsWith("image/") == true || isImageCallback?.invoke( - FloconNetworkIsImageParams( - request = request, - response = response, - responseContentType = contentType, - ) - ) == true - - val floconCallResponse = FloconNetworkResponse( - httpCode = response.status.value, - contentType = contentType, - body = if (isImage) null else { - if (responseHeadersMap.isBrotli()) { - decodeNetworkBody(originalBodyBytes, responseHeadersMap) - } else { - originalBodyBytes.decodeToString() - } - }, - headers = responseHeadersMap, - size = responseSize, - grpcStatus = null, - error = null, - requestHeaders = requestHeadersMap, - isImage = isImage, - ) - - floconNetworkPlugin.logResponse( - FloconNetworkCallResponse( - floconCallId = floconCallId, - durationMs = durationMs, - floconNetworkType = "http", - isMocked = isMocked, - response = floconCallResponse - ) - ) - - proceedWith(savedResponse) - } -} - -fun HttpClientConfig<*>.floconInterceptor() { - install(FloconKtorPlugin) -} - -private val FLOCON_CALL_ID_KEY = AttributeKey("floconCallId") -private val FLOCON_START_TIME_KEY = AttributeKey("floconStartTime") -private val FLOCON_IS_MOCKED_KEY = AttributeKey("floconIsMocked") -private val FLOCON_SHOULD_LOG = AttributeKey("floconShouldLog") diff --git a/FloconAndroid/network/core-no-op/build.gradle.kts b/FloconAndroid/network/core-no-op/build.gradle.kts new file mode 100644 index 000000000..5438ed867 --- /dev/null +++ b/FloconAndroid/network/core-no-op/build.gradle.kts @@ -0,0 +1,58 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") + alias(libs.plugins.kotlin.serialization) +} + +kotlin { + wasmJs { + moduleName = "flocon_network_core_no_op" + browser() + binaries.executable() + } + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.flocon) + implementation(projects.network.core) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + + val androidMain by getting { + dependencies { + } + } + + val jvmMain by getting { + dependencies { + } + } + + val iosX64Main by getting + val iosArm64Main by getting + val iosSimulatorArm64Main by getting + val wasmJsMain by getting + val iosMain by creating { + dependsOn(commonMain) + iosX64Main.dependsOn(this) + iosArm64Main.dependsOn(this) + iosSimulatorArm64Main.dependsOn(this) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.network.core.noop" +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-network-core-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/network/core-no-op/src/androidMain/AndroidManifest.xml b/FloconAndroid/network/core-no-op/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..9a40236b9 --- /dev/null +++ b/FloconAndroid/network/core-no-op/src/androidMain/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/FloconAndroid/network/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/noop/FloconNetwork.kt b/FloconAndroid/network/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/noop/FloconNetwork.kt new file mode 100644 index 000000000..cdf9cebd7 --- /dev/null +++ b/FloconAndroid/network/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/noop/FloconNetwork.kt @@ -0,0 +1,36 @@ +package io.github.openflocon.flocon.network.core.noop + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.error.pluginNotInitialized +import io.github.openflocon.flocon.network.core.noop.plugin.FloconNetworkPluginImpl +import io.github.openflocon.flocon.network.core.FloconNetworkConfig +import io.github.openflocon.flocon.network.core.FloconNetworkPlugin + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.core.FloconEncoder + +object FloconNetwork : FloconPluginFactory { + override val name: String = "Network" + override val pluginId: String = Protocol.ToDevice.Network.Plugin + + override fun createConfig(context: FloconContext) = FloconNetworkConfig() + + @OptIn(FloconMarker::class) + override fun install( + pluginConfig: FloconNetworkConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconNetworkPlugin { + return FloconNetworkPluginImpl() + .also { FloconNetworkPluginImpl.plugin = it } + } + +} + +@OptIn(FloconMarker::class) +val Flocon.Companion.networkPlugin: FloconNetworkPlugin + get() = FloconNetworkPluginImpl.plugin ?: pluginNotInitialized("Network") \ No newline at end of file diff --git a/FloconAndroid/network/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/noop/plugin/FloconNetworkPluginImpl.kt b/FloconAndroid/network/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/noop/plugin/FloconNetworkPluginImpl.kt new file mode 100644 index 000000000..a2b02b316 --- /dev/null +++ b/FloconAndroid/network/core-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/noop/plugin/FloconNetworkPluginImpl.kt @@ -0,0 +1,32 @@ +package io.github.openflocon.flocon.network.core.noop.plugin + +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.network.core.FloconNetworkPlugin +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallResponse +import io.github.openflocon.flocon.network.core.model.FloconWebSocketEvent +import io.github.openflocon.flocon.network.core.model.FloconWebSocketMockListener +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse + +internal class FloconNetworkPluginImpl : FloconPlugin, FloconNetworkPlugin { + override val key: String = "NETWORK" + override val mocks: Collection = emptyList() + override val badQualityConfig: BadQualityConfig? = null + + override suspend fun onMessageReceived(method: String, body: String) = Unit // No op + override suspend fun onConnectedToServer() = Unit // No op + + override fun logRequest(request: FloconNetworkCallRequest) = Unit // No op + override fun logResponse(response: FloconNetworkCallResponse) = Unit // No op + + override suspend fun logWebSocket(event: FloconWebSocketEvent) = Unit // No op + override suspend fun registerWebSocketMockListener( + id: String, + listener: FloconWebSocketMockListener + ) = Unit // No op + + companion object { + var plugin: FloconNetworkPlugin? = null + } +} \ No newline at end of file diff --git a/FloconAndroid/network/core/build.gradle.kts b/FloconAndroid/network/core/build.gradle.kts new file mode 100644 index 000000000..22cfe5c78 --- /dev/null +++ b/FloconAndroid/network/core/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") + alias(libs.plugins.kotlin.serialization) +} + +kotlin { + wasmJs { + moduleName = "flocon_network_core" + browser() + binaries.executable() + } + sourceSets { + val commonMain by getting { + dependencies { + api(projects.flocon) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + + val androidMain by getting { + dependencies { + } + } + + val jvmMain by getting { + dependencies { + } + } + + val iosX64Main by getting + val iosArm64Main by getting + val iosSimulatorArm64Main by getting + val wasmJsMain by getting + val iosMain by creating { + dependsOn(commonMain) + iosX64Main.dependsOn(this) + iosArm64Main.dependsOn(this) + iosSimulatorArm64Main.dependsOn(this) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.network.core" +} + + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-network-core", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} + diff --git a/FloconAndroid/network/core/src/androidMain/AndroidManifest.xml b/FloconAndroid/network/core/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..9a40236b9 --- /dev/null +++ b/FloconAndroid/network/core/src/androidMain/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/FloconAndroid/network/core/src/androidMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.android.kt b/FloconAndroid/network/core/src/androidMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.android.kt new file mode 100644 index 000000000..844d10f8a --- /dev/null +++ b/FloconAndroid/network/core/src/androidMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.android.kt @@ -0,0 +1,12 @@ +package io.github.openflocon.flocon.network.core.datasource + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.core.FloconEncoder + +internal actual inline fun buildFloconNetworkDataSource( + context: FloconContext, + encoder: FloconEncoder +): FloconNetworkDataSource = FloconNetworkDataSourceAndroid( + context = context.context, + encoder = encoder +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.android.kt b/FloconAndroid/network/core/src/androidMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceAndroid.kt similarity index 65% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.android.kt rename to FloconAndroid/network/core/src/androidMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceAndroid.kt index 254934ef4..7c763823d 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.android.kt +++ b/FloconAndroid/network/core/src/androidMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceAndroid.kt @@ -1,29 +1,26 @@ -package io.github.openflocon.flocon.plugins.network +package io.github.openflocon.flocon.network.core.datasource import android.content.Context -import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.network.mapper.parseBadQualityConfig -import io.github.openflocon.flocon.plugins.network.mapper.parseMockResponses -import io.github.openflocon.flocon.plugins.network.mapper.toJsonString -import io.github.openflocon.flocon.plugins.network.mapper.writeMockResponsesToJson -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.decode +import io.github.openflocon.flocon.core.encode +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse +import io.github.openflocon.flocon.network.core.plugin.FLOCON_NETWORK_BAD_CONFIG_JSON +import io.github.openflocon.flocon.network.core.plugin.FLOCON_NETWORK_MOCKS_JSON import java.io.File import java.io.FileInputStream import java.io.FileOutputStream -internal actual fun buildFloconNetworkDataSource(context: FloconContext): FloconNetworkDataSource { - return FloconNetworkDataSourceAndroid( - context = context.appContext, - ) -} - -internal class FloconNetworkDataSourceAndroid(private val context: Context) : FloconNetworkDataSource { +internal class FloconNetworkDataSourceAndroid( + private val context: Context, + private val encoder: FloconEncoder +) : FloconNetworkDataSource { override fun saveMocksToFile(mocks: List) { try { val file = File(context.filesDir, FLOCON_NETWORK_MOCKS_JSON) - val jsonString = writeMockResponsesToJson(mocks = mocks) + val jsonString = encoder.encode(mocks) FileOutputStream(file).use { it.write(jsonString.toByteArray()) } @@ -42,7 +39,9 @@ internal class FloconNetworkDataSourceAndroid(private val context: Context) : Fl val jsonString = FileInputStream(file).use { it.readBytes().toString(Charsets.UTF_8) } - parseMockResponses(jsonString = jsonString) + + encoder.decode>(jsonString) + .orEmpty() } catch (t: Throwable) { FloconLogger.logError("issue in loadMocksFromFile", t) emptyList() @@ -59,7 +58,8 @@ internal class FloconNetworkDataSourceAndroid(private val context: Context) : Fl val jsonString = FileInputStream(file).use { it.readBytes().toString(Charsets.UTF_8) } - parseBadQualityConfig(jsonString = jsonString) + + encoder.decode(jsonString) } catch (t: Throwable) { FloconLogger.logError("issue in loadBadNetworkConfig", t) null @@ -72,7 +72,7 @@ internal class FloconNetworkDataSourceAndroid(private val context: Context) : Fl if (config == null) { file.delete() } else { - val jsonString = config.toJsonString() + val jsonString = encoder.encode(config) FileOutputStream(file).use { it.write(jsonString.toByteArray()) } diff --git a/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/FloconNetwork.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/FloconNetwork.kt new file mode 100644 index 000000000..0484e2975 --- /dev/null +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/FloconNetwork.kt @@ -0,0 +1,70 @@ +package io.github.openflocon.flocon.network.core + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.error.pluginNotInitialized +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallResponse +import io.github.openflocon.flocon.network.core.model.FloconWebSocketEvent +import io.github.openflocon.flocon.network.core.model.FloconWebSocketMockListener +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse +import io.github.openflocon.flocon.network.core.plugin.FloconNetworkPluginImpl +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.SupervisorJob + +class FloconNetworkConfig : FloconPluginConfig { + var badQualityConfig: BadQualityConfig? = null + val mocks = mutableListOf() +} + +interface FloconNetworkPlugin : FloconPlugin { + val mocks: Collection + val badQualityConfig: BadQualityConfig? + + fun logRequest(request: FloconNetworkCallRequest) + fun logResponse(response: FloconNetworkCallResponse) + + suspend fun logWebSocket( + event: FloconWebSocketEvent, + ) + + suspend fun registerWebSocketMockListener(id: String, listener: FloconWebSocketMockListener) +} + +object FloconNetwork : FloconPluginFactory { + override val name: String = "Network" + override val pluginId: String = Protocol.ToDevice.Network.Plugin + + override fun createConfig(context: FloconContext) = FloconNetworkConfig() + + @OptIn(FloconMarker::class) + override fun install( + pluginConfig: FloconNetworkConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconNetworkPlugin { + return FloconNetworkPluginImpl( + context = floconConfig.context, + sender = floconConfig.client as FloconMessageSender, + coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), + encoder = encoder + ) + .also { FloconNetworkPluginImpl.plugin = it } + } + +} + +@OptIn(FloconMarker::class) +val Flocon.Companion.networkPlugin: FloconNetworkPlugin + get() = FloconNetworkPluginImpl.plugin ?: pluginNotInitialized("Network") \ No newline at end of file diff --git a/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.kt new file mode 100644 index 000000000..17036b257 --- /dev/null +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.flocon.network.core.datasource + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse + +internal interface FloconNetworkDataSource { + fun saveMocksToFile(mocks: List) + fun loadMocksFromFile(): List + fun saveBadNetworkConfig(config: BadQualityConfig?) + fun loadBadNetworkConfig(): BadQualityConfig? +} + +internal expect inline fun buildFloconNetworkDataSource( + context: FloconContext, + encoder: FloconEncoder +): FloconNetworkDataSource \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/BadQualityToJson.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/BadQualityToJson.kt similarity index 75% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/BadQualityToJson.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/BadQualityToJson.kt index fbda0f50f..8099b662f 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/BadQualityToJson.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/BadQualityToJson.kt @@ -1,29 +1,8 @@ -package io.github.openflocon.flocon.plugins.network.mapper +package io.github.openflocon.flocon.network.core.mapper -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.BadQualityConfig import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -internal fun BadQualityConfig.toJsonString(): String { - return FloconEncoder.json.encodeToString( - toSerializable() - ) -} - -internal fun parseBadQualityConfig(jsonString: String): BadQualityConfig? { - return try { - val parsed = FloconEncoder.json.decodeFromString( - jsonString - ) - parsed.toDomain() - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "bad connection network parsing issue", t) - null - } -} @Serializable internal class BadQualityConfigSerializable( val latency: LatencySerializable, @@ -63,6 +42,7 @@ internal fun BadQualityConfig.toSerializable(): BadQualityConfigSerializable { errorBody = t.errorBody, errorContentType = t.errorContentType ) + is BadQualityConfig.Error.Type.ErrorThrow -> BadQualityConfigSerializable.ErrorSerializable( weight = error.weight, errorException = t.classPath diff --git a/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/FloconNetworkRequestToJson.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/FloconNetworkRequestToJson.kt new file mode 100644 index 000000000..666a358f2 --- /dev/null +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/FloconNetworkRequestToJson.kt @@ -0,0 +1,99 @@ +@file:OptIn(ExperimentalUuidApi::class) + +package io.github.openflocon.flocon.network.core.mapper + +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallResponse +import io.github.openflocon.flocon.network.core.model.FloconWebSocketEvent +import kotlinx.serialization.Serializable +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +@Serializable +internal class FloconNetworkCallRequestRemote( + val floconCallId: String, + val floconNetworkType: String, + val isMocked: Boolean, + + val url: String, + val method: String, + val startTime: Long, + val requestBody: String?, + val requestHeaders: Map, + val requestSize: Long?, +) + +internal fun FloconNetworkCallRequest.floconNetworkCallRequestToJson() = FloconNetworkCallRequestRemote( + floconCallId = floconCallId, + floconNetworkType = floconNetworkType, + isMocked = isMocked, + url = request.url, + method = request.method, + startTime = request.startTime, + requestBody = request.body, + requestHeaders = request.headers, + requestSize = request.size +) + +@Serializable +internal class FloconNetworkCallResponseRemote( + val floconCallId: String, + val durationMs: Double, + val floconNetworkType: String, + val isMocked: Boolean, + val responseHttpCode: Int?, + val responseGrpcStatus: String?, + val responseContentType: String?, + val responseBody: String?, + val responseSize: Long?, + val responseHeaders: Map, + val requestHeaders: Map?, // we might receive the request headers later if the interceptor is at first position in the http interceptor chain + val responseError: String?, + val isImage: Boolean, +) + +internal fun FloconNetworkCallResponse.floconNetworkCallResponseToJson() = FloconNetworkCallResponseRemote( + floconCallId = floconCallId, + floconNetworkType = floconNetworkType, + isMocked = isMocked, + durationMs = durationMs, + responseHttpCode = response.httpCode, + responseGrpcStatus = response.grpcStatus, + responseContentType = response.contentType, + responseBody = response.body, + responseHeaders = response.headers, + requestHeaders = response.requestHeaders?.takeIf { + it.isNotEmpty() + }, + responseSize = response.size, + isImage = response.isImage, + responseError = response.error, +) + +@Serializable +internal class FloconWebSocketEventRemote( + val id: String, + val event: String, + val url: String, + val size: Long, + val timestamp: Long, + val message: String?, + val error: String?, +) + +internal fun FloconWebSocketEvent.floconNetworkWebSocketEventToJson() = FloconWebSocketEventRemote( + id = Uuid.random().toString(), + event = when (event) { + FloconWebSocketEvent.Event.Closed -> "closed" + FloconWebSocketEvent.Event.Closing -> "closing" + FloconWebSocketEvent.Event.Error -> "error" + FloconWebSocketEvent.Event.ReceiveMessage -> "received" + FloconWebSocketEvent.Event.SendMessage -> "sent" + FloconWebSocketEvent.Event.Open -> "open" + }, + url = websocketUrl, + size = size, + timestamp = timeStamp, + message = message, + error = error?.message +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/MockResponseToJson.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/MockResponseToJson.kt similarity index 77% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/MockResponseToJson.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/MockResponseToJson.kt index 94d681dd8..dc3cf7079 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/mapper/MockResponseToJson.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/MockResponseToJson.kt @@ -1,8 +1,8 @@ -package io.github.openflocon.flocon.plugins.network.mapper +package io.github.openflocon.flocon.network.core.mapper import io.github.openflocon.flocon.FloconLogger import io.github.openflocon.flocon.core.FloconEncoder -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString @@ -28,19 +28,6 @@ internal class MockNetworkResponseDataModel( ) } - -internal fun parseMockResponses(jsonString: String): List { - try { - val remote = FloconEncoder.json.decodeFromString>(jsonString) - return remote.mapNotNull { - it.toDomain() - } - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "mock network parsing issue", t) - return emptyList() - } -} - internal fun MockNetworkResponseDataModel.toDomain(): MockNetworkResponse? { return MockNetworkResponse( expectation = MockNetworkResponse.Expectation( @@ -75,17 +62,7 @@ private fun MockNetworkResponseDataModel.mapResponseToDomain(): MockNetworkRespo } } - -internal fun writeMockResponsesToJson(mocks: List): String { - return try { - FloconEncoder.json.encodeToString(mocks.map { it.toRemote() }) - } catch (t: Throwable) { - FloconLogger.logError(t.message ?: "mock network writing issue", t) - return "[]" - } -} - -private fun MockNetworkResponse.toRemote(): MockNetworkResponseDataModel? { +private fun MockNetworkResponse.toRemote(): MockNetworkResponseDataModel { return MockNetworkResponseDataModel( expectation = MockNetworkResponseDataModel.Expectation( urlPattern = expectation.urlPattern, diff --git a/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/Websocket.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/Websocket.kt new file mode 100644 index 000000000..967ed6ff1 --- /dev/null +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/mapper/Websocket.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.network.core.mapper + +import kotlinx.serialization.Serializable + +@Serializable +internal class WebSocketMockMessage( + val id: String, + val message: String, +) \ No newline at end of file diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/BadQualityConfig.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/BadQualityConfig.kt similarity index 87% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/BadQualityConfig.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/BadQualityConfig.kt index fdac3de51..fb1293f90 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/BadQualityConfig.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/BadQualityConfig.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model import io.github.openflocon.flocon.FloconLogger import kotlin.random.Random @@ -57,13 +57,9 @@ data class BadQualityConfig( return null } - // Calculer la somme totale des poids val totalWeight = errors.sumOf { it.weight.toDouble() } - - // Générer un nombre aléatoire entre 0 et la somme totale des poids var randomNumber = Random.nextDouble(0.0, totalWeight) - // Parcourir la liste pour trouver l'erreur sélectionnée for (error in errors) { randomNumber -= error.weight.toDouble() if (randomNumber <= 0) { @@ -71,7 +67,6 @@ data class BadQualityConfig( } } - // Cas de secours (ne devrait pas arriver si les poids sont positifs) return errors.first() } } diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconHttpRequest.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconHttpRequest.kt similarity index 91% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconHttpRequest.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconHttpRequest.kt index 913e72351..2b2029b59 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconHttpRequest.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconHttpRequest.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model data class FloconNetworkRequest( val url: String, @@ -20,4 +20,4 @@ data class FloconNetworkResponse( val requestHeaders: Map?, // we might receive the request headers later if the interceptor is at first position in the http interceptor chain val error: String?, val isImage: Boolean, -) \ No newline at end of file +) diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconNetworkCallRequest.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconNetworkCallRequest.kt similarity index 74% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconNetworkCallRequest.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconNetworkCallRequest.kt index cfcce2eda..bcaf31628 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconNetworkCallRequest.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconNetworkCallRequest.kt @@ -1,8 +1,8 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model data class FloconNetworkCallRequest( val floconCallId: String, val request: FloconNetworkRequest, val floconNetworkType: String, val isMocked: Boolean, -) \ No newline at end of file +) diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconNetworkCallResponse.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconNetworkCallResponse.kt similarity index 77% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconNetworkCallResponse.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconNetworkCallResponse.kt index 898287131..20cb62bf3 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconNetworkCallResponse.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconNetworkCallResponse.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model data class FloconNetworkCallResponse( val floconCallId: String, @@ -6,4 +6,4 @@ data class FloconNetworkCallResponse( val durationMs: Double, val floconNetworkType: String, val isMocked: Boolean, -) \ No newline at end of file +) diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconWebSocketEvent.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconWebSocketEvent.kt similarity index 87% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconWebSocketEvent.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconWebSocketEvent.kt index f2be27bc9..df16fbbc4 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconWebSocketEvent.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconWebSocketEvent.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model import io.github.openflocon.flocon.utils.currentTimeMillis @@ -18,4 +18,4 @@ class FloconWebSocketEvent( SendMessage, Open, } -} \ No newline at end of file +} diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconWebSocketMockListener.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconWebSocketMockListener.kt similarity index 57% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconWebSocketMockListener.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconWebSocketMockListener.kt index f6021ee1f..3c1928a7f 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/FloconWebSocketMockListener.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/FloconWebSocketMockListener.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model interface FloconWebSocketMockListener { fun onMessage(message: String) diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/MockNetworkResponse.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/MockNetworkResponse.kt similarity index 95% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/MockNetworkResponse.kt rename to FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/MockNetworkResponse.kt index 535ca4fa9..09562fe3c 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/network/model/MockNetworkResponse.kt +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/model/MockNetworkResponse.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.flocon.plugins.network.model +package io.github.openflocon.flocon.network.core.model data class MockNetworkResponse( val expectation: Expectation, @@ -36,4 +36,4 @@ data class MockNetworkResponse( } } } -} \ No newline at end of file +} diff --git a/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/plugin/FloconNetworkPluginImpl.kt b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/plugin/FloconNetworkPluginImpl.kt new file mode 100644 index 000000000..15b43004d --- /dev/null +++ b/FloconAndroid/network/core/src/commonMain/kotlin/io/github/openflocon/flocon/network/core/plugin/FloconNetworkPluginImpl.kt @@ -0,0 +1,151 @@ +package io.github.openflocon.flocon.network.core.plugin + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.core.decode +import io.github.openflocon.flocon.core.encode +import io.github.openflocon.flocon.network.core.FloconNetworkPlugin +import io.github.openflocon.flocon.network.core.datasource.buildFloconNetworkDataSource +import io.github.openflocon.flocon.network.core.mapper.WebSocketMockMessage +import io.github.openflocon.flocon.network.core.mapper.floconNetworkCallRequestToJson +import io.github.openflocon.flocon.network.core.mapper.floconNetworkCallResponseToJson +import io.github.openflocon.flocon.network.core.mapper.floconNetworkWebSocketEventToJson +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallResponse +import io.github.openflocon.flocon.network.core.model.FloconWebSocketEvent +import io.github.openflocon.flocon.network.core.model.FloconWebSocketMockListener +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +internal const val FLOCON_NETWORK_MOCKS_JSON = "flocon_network_mocks.json" +internal const val FLOCON_NETWORK_BAD_CONFIG_JSON = "flocon_network_bad_config.json" + +internal class FloconNetworkPluginImpl( + context: FloconContext, + private var sender: FloconMessageSender, + private val coroutineScope: CoroutineScope, + private val encoder: FloconEncoder +) : FloconPlugin, FloconNetworkPlugin { + override val key: String = "NETWORK" + + private val dataSource = buildFloconNetworkDataSource(context = context, encoder = encoder) + + private val websocketListeners = + MutableStateFlow>(emptyMap()) + + private val _mocks = MutableStateFlow(dataSource.loadMocksFromFile()) + override val mocks: List + get() = _mocks.value + + private val _badQualityConfig = MutableStateFlow(dataSource.loadBadNetworkConfig()) + + override val badQualityConfig: BadQualityConfig? + get() = _badQualityConfig.value + + override fun logRequest(request: FloconNetworkCallRequest) { + try { + coroutineScope.launch { + sender.send( + plugin = Protocol.FromDevice.Network.Plugin, + method = Protocol.FromDevice.Network.Method.LogNetworkCallRequest, + body = encoder.encode(request.floconNetworkCallRequestToJson()) + ) + } + } catch (t: Throwable) { + FloconLogger.logError("Network json mapping error", t) + } + } + + override fun logResponse(response: FloconNetworkCallResponse) { + coroutineScope.launch { + delay(200) // to be sure the request is handled before the response + try { + sender.send( + plugin = Protocol.FromDevice.Network.Plugin, + method = Protocol.FromDevice.Network.Method.LogNetworkCallResponse, + body = encoder.encode(response.floconNetworkCallResponseToJson()) + ) + } catch (t: Throwable) { + FloconLogger.logError("Network json mapping error", t) + } + } + } + + override suspend fun logWebSocket( + event: FloconWebSocketEvent, + ) { + coroutineScope.launch { + try { + sender.send( + plugin = Protocol.FromDevice.Network.Plugin, + method = Protocol.FromDevice.Network.Method.LogWebSocketEvent, + body = encoder.encode(event.floconNetworkWebSocketEventToJson()) + ) + } catch (t: Throwable) { + FloconLogger.logError("Network json mapping error", t) + } + } + } + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + when (method) { + Protocol.ToDevice.Network.Method.SetupMocks -> { + val setup = encoder.decode>(body) + .orEmpty() + _mocks.update { setup } + dataSource.saveMocksToFile(mocks) + } + + Protocol.ToDevice.Network.Method.SetupBadNetworkConfig -> { + val config = encoder.decode(body) + _badQualityConfig.update { config } + dataSource.saveBadNetworkConfig(config) + } + + Protocol.ToDevice.Network.Method.WebsocketMockMessage -> { + val message = encoder.decode(body) + if (message != null) { + websocketListeners.value[message.id]?.onMessage(message.message) + } + } + } + } + + override suspend fun onConnectedToServer() { + updateWebSocketIds() + } + + override suspend fun registerWebSocketMockListener( + id: String, + listener: FloconWebSocketMockListener + ) { + websocketListeners.update { + it + (id to listener) + } + updateWebSocketIds() + } + + private fun updateWebSocketIds() { + sender.send( + plugin = Protocol.FromDevice.Network.Plugin, + method = Protocol.FromDevice.Network.Method.RegisterWebSocketIds, + body = encoder.encode(websocketListeners.value.keys) + ) + } + + companion object { + var plugin: FloconNetworkPlugin? = null + } +} \ No newline at end of file diff --git a/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.ios.kt b/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.ios.kt new file mode 100644 index 000000000..580dd4815 --- /dev/null +++ b/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.ios.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.network.core.datasource + +import io.github.openflocon.flocon.FloconContext + +internal actual inline fun buildFloconNetworkDataSource( + context: FloconContext +): FloconNetworkDataSource = FloconNetworkDataSourceImpl() \ No newline at end of file diff --git a/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt b/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt new file mode 100644 index 000000000..c062ee677 --- /dev/null +++ b/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt @@ -0,0 +1,24 @@ +package io.github.openflocon.flocon.network.core.datasource + +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse + +internal class FloconNetworkDataSourceImpl : FloconNetworkDataSource { + + override fun saveMocksToFile(mocks: List) { + // TODO + } + + override fun loadMocksFromFile(): List { + return emptyList() + } + + override fun saveBadNetworkConfig(config: BadQualityConfig?) { + // TODO + } + + override fun loadBadNetworkConfig(): BadQualityConfig? { + return null + } + +} \ No newline at end of file diff --git a/FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.jvm.kt b/FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.jvm.kt new file mode 100644 index 000000000..580dd4815 --- /dev/null +++ b/FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.jvm.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.network.core.datasource + +import io.github.openflocon.flocon.FloconContext + +internal actual inline fun buildFloconNetworkDataSource( + context: FloconContext +): FloconNetworkDataSource = FloconNetworkDataSourceImpl() \ No newline at end of file diff --git a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.jvm.kt b/FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt similarity index 76% rename from FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.jvm.kt rename to FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt index c6729c0f3..be864eafa 100644 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/network/FloconNetworkPluginImpl.jvm.kt +++ b/FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt @@ -1,22 +1,19 @@ -package io.github.openflocon.flocon.plugins.network +package io.github.openflocon.flocon.network.core.datasource -import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.plugins.network.mapper.parseBadQualityConfig -import io.github.openflocon.flocon.plugins.network.mapper.parseMockResponses -import io.github.openflocon.flocon.plugins.network.mapper.toJsonString -import io.github.openflocon.flocon.plugins.network.mapper.writeMockResponsesToJson -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse +import io.github.openflocon.flocon.network.core.mapper.parseBadQualityConfig +import io.github.openflocon.flocon.network.core.mapper.parseMockResponses +import io.github.openflocon.flocon.network.core.mapper.toJsonString +import io.github.openflocon.flocon.network.core.mapper.writeMockResponsesToJson +import io.github.openflocon.flocon.network.core.plugin.FLOCON_NETWORK_BAD_CONFIG_JSON +import io.github.openflocon.flocon.network.core.plugin.FLOCON_NETWORK_MOCKS_JSON +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse import java.io.File import java.io.FileInputStream import java.io.FileOutputStream -internal actual fun buildFloconNetworkDataSource(context: FloconContext): FloconNetworkDataSource { - return FloconNetworkDataSourceJvm() -} - -internal class FloconNetworkDataSourceJvm( +internal class FloconNetworkDataSourceImpl( ) : FloconNetworkDataSource { private val baseDir: File = File(System.getProperty("user.home"), ".flocon") diff --git a/FloconAndroid/network/core/src/wasmJsMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.wasmJs.kt b/FloconAndroid/network/core/src/wasmJsMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.wasmJs.kt new file mode 100644 index 000000000..271cc6a48 --- /dev/null +++ b/FloconAndroid/network/core/src/wasmJsMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.wasmJs.kt @@ -0,0 +1,14 @@ +package io.github.openflocon.flocon.network.core.datasource + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.network.core.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse + +internal actual inline fun buildFloconNetworkDataSource(context: FloconContext): FloconNetworkDataSource = FloconNetworkDataSourceWasmJs() + +internal class FloconNetworkDataSourceWasmJs : FloconNetworkDataSource { + override fun saveMocksToFile(mocks: List) {} + override fun loadMocksFromFile(): List = emptyList() + override fun saveBadNetworkConfig(config: BadQualityConfig?) {} + override fun loadBadNetworkConfig(): BadQualityConfig? = null +} diff --git a/FloconAndroid/flocon-base/.gitignore b/FloconAndroid/network/ktor-interceptor-no-op/.gitignore similarity index 100% rename from FloconAndroid/flocon-base/.gitignore rename to FloconAndroid/network/ktor-interceptor-no-op/.gitignore diff --git a/FloconAndroid/network/ktor-interceptor-no-op/build.gradle.kts b/FloconAndroid/network/ktor-interceptor-no-op/build.gradle.kts new file mode 100644 index 000000000..852463ec0 --- /dev/null +++ b/FloconAndroid/network/ktor-interceptor-no-op/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + wasmJs { + moduleName = "flocon_ktor_interceptor_no_op" + browser() + binaries.executable() + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(projects.network.coreNoOp) + implementation(libs.ktor.client.core) + } + } + + 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) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.ktor" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-ktor-interceptor-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/flocon-base/consumer-rules.pro b/FloconAndroid/network/ktor-interceptor-no-op/consumer-rules.pro similarity index 100% rename from FloconAndroid/flocon-base/consumer-rules.pro rename to FloconAndroid/network/ktor-interceptor-no-op/consumer-rules.pro diff --git a/FloconAndroid/flocon-base/proguard-rules.pro b/FloconAndroid/network/ktor-interceptor-no-op/proguard-rules.pro similarity index 100% rename from FloconAndroid/flocon-base/proguard-rules.pro rename to FloconAndroid/network/ktor-interceptor-no-op/proguard-rules.pro diff --git a/FloconAndroid/ktor-interceptor-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt b/FloconAndroid/network/ktor-interceptor-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt similarity index 100% rename from FloconAndroid/ktor-interceptor-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt rename to FloconAndroid/network/ktor-interceptor-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt diff --git a/FloconAndroid/ktor-interceptor-no-op/.gitignore b/FloconAndroid/network/ktor-interceptor/.gitignore similarity index 100% rename from FloconAndroid/ktor-interceptor-no-op/.gitignore rename to FloconAndroid/network/ktor-interceptor/.gitignore diff --git a/FloconAndroid/network/ktor-interceptor/build.gradle.kts b/FloconAndroid/network/ktor-interceptor/build.gradle.kts new file mode 100644 index 000000000..2c44a9a8c --- /dev/null +++ b/FloconAndroid/network/ktor-interceptor/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + wasmJs { + moduleName = "flocon_ktor_interceptor" + browser() + binaries.executable() + } + + sourceSets { + val commonMain by getting { + dependencies { + + api(projects.flocon) + api(projects.network.core) + + implementation(libs.kotlinx.coroutines.core) + implementation(libs.ktor.client.core) + } + } + + val androidMain by getting { + dependencies { + implementation(libs.brotli.dec) + } + } + + val jvmMain by getting { + dependencies { + implementation(libs.brotli.dec) + } + } + + val iosX64Main by getting + val iosArm64Main by getting + val iosSimulatorArm64Main by getting + val wasmJsMain by getting + val iosMain by creating { + dependsOn(commonMain) + iosX64Main.dependsOn(this) + iosArm64Main.dependsOn(this) + iosSimulatorArm64Main.dependsOn(this) + } + } +} + +android { + namespace = "io.github.openflocon.flocon.ktor" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-ktor-interceptor", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/ktor-interceptor-no-op/consumer-rules.pro b/FloconAndroid/network/ktor-interceptor/consumer-rules.pro similarity index 100% rename from FloconAndroid/ktor-interceptor-no-op/consumer-rules.pro rename to FloconAndroid/network/ktor-interceptor/consumer-rules.pro diff --git a/FloconAndroid/ktor-interceptor-no-op/proguard-rules.pro b/FloconAndroid/network/ktor-interceptor/proguard-rules.pro similarity index 100% rename from FloconAndroid/ktor-interceptor-no-op/proguard-rules.pro rename to FloconAndroid/network/ktor-interceptor/proguard-rules.pro diff --git a/FloconAndroid/ktor-interceptor/src/androidMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt b/FloconAndroid/network/ktor-interceptor/src/androidMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt similarity index 100% rename from FloconAndroid/ktor-interceptor/src/androidMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt rename to FloconAndroid/network/ktor-interceptor/src/androidMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt diff --git a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/BadQuality.kt b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/BadQuality.kt similarity index 96% rename from FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/BadQuality.kt rename to FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/BadQuality.kt index 21cc111ae..b56f7fd25 100644 --- a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/BadQuality.kt +++ b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/BadQuality.kt @@ -1,6 +1,6 @@ package io.github.openflocon.flocon.ktor -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.BadQualityConfig import io.ktor.client.HttpClient import io.ktor.client.call.HttpClientCall import io.ktor.client.request.HttpRequestBuilder diff --git a/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt new file mode 100644 index 000000000..c20f200e0 --- /dev/null +++ b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/FloconKtorPlugin.kt @@ -0,0 +1,204 @@ +@file:OptIn(ExperimentalUuidApi::class) + +package io.github.openflocon.flocon.ktor + +import io.ktor.client.HttpClientConfig +import io.ktor.client.plugins.api.createClientPlugin +import io.ktor.client.request.HttpRequest +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.statement.HttpResponse +import io.ktor.util.AttributeKey +import kotlin.uuid.ExperimentalUuidApi + + +data class FloconNetworkIsImageParams( + val request: HttpRequest, + val response: HttpResponse, + val responseContentType: String?, +) + +class FloconKtorPluginConfig { + var isImage: ((param: FloconNetworkIsImageParams) -> Boolean)? = null + var shouldLog: ((request: HttpRequestBuilder) -> Boolean) = { true } +} + +val FloconKtorPlugin = createClientPlugin("FloconKtorPlugin", ::FloconKtorPluginConfig) { + + val theClient = client + val isImageCallback = pluginConfig.isImage + val shouldLogCallback = pluginConfig.shouldLog + + // Intercept requests +// client.sendPipeline.intercept(HttpSendPipeline.Monitoring) { +// val floconNetworkPlugin = FloconApp.instance?.client?.networkPlugin +// val request: HttpRequestBuilder = context +// +// if (floconNetworkPlugin == null || !shouldLogCallback(request)) { +// request.attributes.put(FLOCON_SHOULD_LOG, false) +// proceed() +// return@intercept +// } +// +// val floconCallId = Uuid.random().toString() +// val floconNetworkType = "http" +// val requestedAt = currentTimeMillis() +// +// // Reads the body without consuming it +// val requestBodyString = extractAndReplaceRequestBody(request) +// val requestSize = requestBodyString?.encodeToByteArray()?.size?.toLong() +// val requestHeadersMap = +// request.headers.entries().associate { it.key to it.value.joinToString(",") } +// +// val mockConfig = findMock(request, floconNetworkPlugin) +// val isMocked = mockConfig != null +// +// val floconNetworkRequest = FloconNetworkRequest( +// url = request.url.toString(), +// method = request.method.value, +// startTime = requestedAt, +// headers = requestHeadersMap, +// body = requestBodyString, +// size = requestSize, +// isMocked = isMocked +// ) +// +// floconNetworkPlugin.logRequest( +// FloconNetworkCallRequest( +// floconCallId = floconCallId, +// floconNetworkType = floconNetworkType, +// isMocked = isMocked, +// request = floconNetworkRequest +// ) +// ) +// +// val startTime = currentTimeNanos() +// request.attributes.put(FLOCON_CALL_ID_KEY, floconCallId) +// request.attributes.put(FLOCON_START_TIME_KEY, startTime) +// request.attributes.put(FLOCON_IS_MOCKED_KEY, isMocked) +// +// try { +// if (isMocked) { +// val fakeCall = executeMock(client = theClient, request = request, mock = mockConfig) +// proceedWith(fakeCall) +// return@intercept +// } +// +// floconNetworkPlugin.badQualityConfig?.let { badQualityConfig -> +// executeBadQuality( +// badQualityConfig = badQualityConfig, +// client = theClient, +// request = request +// ) +// } ?: run { +// proceed() +// } +// } catch (t: Throwable) { +// val endTime = currentTimeNanos() +// +// val durationMs: Double = (endTime - startTime) / 1e6 +// +// val floconCallResponse = FloconNetworkResponse( +// httpCode = null, +// contentType = null, +// body = null, +// headers = emptyMap(), +// size = null, +// grpcStatus = null, +// error = t.message ?: t::class.simpleName ?: "Unknown", +// requestHeaders = requestHeadersMap, +// isImage = false, +// ) +// +// floconNetworkPlugin.logResponse( +// FloconNetworkCallResponse( +// floconCallId = floconCallId, +// durationMs = durationMs, +// floconNetworkType = floconNetworkType, +// isMocked = isMocked, +// response = floconCallResponse, +// ) +// ) +// throw t +// } +// } +// +// // Intercepts responses +// client.receivePipeline.intercept(HttpReceivePipeline.After) { response -> +// val floconNetworkPlugin = FloconApp.instance?.client?.networkPlugin ?: return@intercept +// +// val savedCall = response.call.save() +// val savedResponse = savedCall.response +// val call = response.call +// val request: HttpRequest = call.request +// +// val floconShouldLog = request.attributes.getOrNull(FLOCON_SHOULD_LOG) ?: true +// if(!floconShouldLog) { +// proceed() +// return@intercept +// } +// +// val floconCallId = request.attributes.getOrNull(FLOCON_CALL_ID_KEY) ?: return@intercept +// val startTime = request.attributes.getOrNull(FLOCON_START_TIME_KEY) ?: return@intercept +// val isMocked = request.attributes.getOrNull(FLOCON_IS_MOCKED_KEY) ?: false +// +// val endTime = currentTimeNanos() +// val durationMs = (endTime - startTime) / 1e6 +// +// val originalBodyBytes = response.bodyAsChannel().toByteArray() +// val responseSize = originalBodyBytes.size.toLong() +// +// val responseHeadersMap = +// response.headers.entries().associate { it.key to it.value.joinToString(",") } +// val contentType = response.contentType()?.toString() +// +// val requestHeadersMap = +// request.headers.entries().associate { it.key to it.value.joinToString(",") } +// +// val isImage = contentType?.startsWith("image/") == true || isImageCallback?.invoke( +// FloconNetworkIsImageParams( +// request = request, +// response = response, +// responseContentType = contentType, +// ) +// ) == true +// +// val floconCallResponse = FloconNetworkResponse( +// httpCode = response.status.value, +// contentType = contentType, +// body = if (isImage) null else { +// if (responseHeadersMap.isBrotli()) { +// decodeNetworkBody(originalBodyBytes, responseHeadersMap) +// } else { +// originalBodyBytes.decodeToString() +// } +// }, +// headers = responseHeadersMap, +// size = responseSize, +// grpcStatus = null, +// error = null, +// requestHeaders = requestHeadersMap, +// isImage = isImage, +// ) +// +// floconNetworkPlugin.logResponse( +// FloconNetworkCallResponse( +// floconCallId = floconCallId, +// durationMs = durationMs, +// floconNetworkType = "http", +// isMocked = isMocked, +// response = floconCallResponse +// ) +// ) +// +// proceedWith(savedResponse) +// } +} + +fun HttpClientConfig<*>.floconInterceptor() { + install(FloconKtorPlugin) +} + +private val FLOCON_CALL_ID_KEY = AttributeKey("floconCallId") +private val FLOCON_START_TIME_KEY = AttributeKey("floconStartTime") +private val FLOCON_IS_MOCKED_KEY = AttributeKey("floconIsMocked") +private val FLOCON_SHOULD_LOG = AttributeKey("floconShouldLog") diff --git a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Mocks.kt b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Mocks.kt similarity index 85% rename from FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Mocks.kt rename to FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Mocks.kt index 03c06c009..1f68c28e5 100644 --- a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Mocks.kt +++ b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Mocks.kt @@ -1,7 +1,7 @@ package io.github.openflocon.flocon.ktor -import io.github.openflocon.flocon.plugins.network.FloconNetworkPlugin -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse +import io.github.openflocon.flocon.network.core.FloconNetworkPlugin +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse import io.ktor.client.HttpClient import io.ktor.client.call.HttpClientCall import io.ktor.client.request.HttpRequestBuilder @@ -10,17 +10,16 @@ import io.ktor.http.HeadersBuilder import io.ktor.http.HttpProtocolVersion import io.ktor.http.HttpStatusCode import io.ktor.util.date.GMTDate +import io.ktor.util.logging.Logger import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.InternalAPI import kotlinx.coroutines.delay -import kotlin.collections.component1 -import kotlin.collections.component2 internal fun findMock( request: HttpRequestBuilder, floconNetworkPlugin: FloconNetworkPlugin, ): MockNetworkResponse? { - val url = request.url.toString() + val url = request.url.toString() val method = request.method.value return floconNetworkPlugin.mocks.firstOrNull { it.expectation.matches( @@ -30,6 +29,14 @@ internal fun findMock( } } +//fun test() { +// HttpClient { +// install() { +// +// } +// } +//} + @OptIn(InternalAPI::class) internal suspend fun executeMock( request: HttpRequestBuilder, @@ -40,7 +47,7 @@ internal suspend fun executeMock( delay(mock.response.delay) } - when(val response = mock.response) { + when (val response = mock.response) { is MockNetworkResponse.Response.Body -> { val bodyBytes = response.body.encodeToByteArray() val headers = HeadersBuilder().apply { @@ -58,6 +65,7 @@ internal suspend fun executeMock( return HttpClientCall(client, request.build(), responseData) } + is MockNetworkResponse.Response.ErrorThrow -> { val error = response.generate() if (error != null) { diff --git a/FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Utils.kt b/FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Utils.kt similarity index 100% rename from FloconAndroid/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Utils.kt rename to FloconAndroid/network/ktor-interceptor/src/commonMain/kotlin/io/github/openflocon/flocon/ktor/Utils.kt diff --git a/FloconAndroid/ktor-interceptor/src/iosMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt b/FloconAndroid/network/ktor-interceptor/src/iosMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt similarity index 100% rename from FloconAndroid/ktor-interceptor/src/iosMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt rename to FloconAndroid/network/ktor-interceptor/src/iosMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt diff --git a/FloconAndroid/ktor-interceptor/src/jvmMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt b/FloconAndroid/network/ktor-interceptor/src/jvmMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt similarity index 100% rename from FloconAndroid/ktor-interceptor/src/jvmMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt rename to FloconAndroid/network/ktor-interceptor/src/jvmMain/kotlin/io/github/openflocon/flocon/ktor/DecodeUtils.kt diff --git a/FloconAndroid/network/ktor-interceptor/src/wasmJsMain/kotlin/io/github/openflocon/flocon/ktor/Utils.wasmJs.kt b/FloconAndroid/network/ktor-interceptor/src/wasmJsMain/kotlin/io/github/openflocon/flocon/ktor/Utils.wasmJs.kt new file mode 100644 index 000000000..d4511a451 --- /dev/null +++ b/FloconAndroid/network/ktor-interceptor/src/wasmJsMain/kotlin/io/github/openflocon/flocon/ktor/Utils.wasmJs.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon.ktor + +internal actual fun decodeNetworkBody( + bytes: ByteArray, + headers: Map +): String = bytes.decodeToString() diff --git a/FloconAndroid/ktor-interceptor/.gitignore b/FloconAndroid/network/okhttp-interceptor-no-op/.gitignore similarity index 100% rename from FloconAndroid/ktor-interceptor/.gitignore rename to FloconAndroid/network/okhttp-interceptor-no-op/.gitignore diff --git a/FloconAndroid/network/okhttp-interceptor-no-op/build.gradle.kts b/FloconAndroid/network/okhttp-interceptor-no-op/build.gradle.kts new file mode 100644 index 000000000..094f66831 --- /dev/null +++ b/FloconAndroid/network/okhttp-interceptor-no-op/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + id("flocon.android.library") + id("flocon.publish") +} + +android { + namespace = "io.github.openflocon.flocon.okhttp" +} + +dependencies { + implementation(projects.network.coreNoOp) + implementation(platform(libs.okhttp.bom)) + implementation(libs.okhttp) +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-okhttp-interceptor-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/ktor-interceptor/consumer-rules.pro b/FloconAndroid/network/okhttp-interceptor-no-op/consumer-rules.pro similarity index 100% rename from FloconAndroid/ktor-interceptor/consumer-rules.pro rename to FloconAndroid/network/okhttp-interceptor-no-op/consumer-rules.pro diff --git a/FloconAndroid/ktor-interceptor/proguard-rules.pro b/FloconAndroid/network/okhttp-interceptor-no-op/proguard-rules.pro similarity index 100% rename from FloconAndroid/ktor-interceptor/proguard-rules.pro rename to FloconAndroid/network/okhttp-interceptor-no-op/proguard-rules.pro diff --git a/FloconAndroid/okhttp-interceptor-no-op/src/main/AndroidManifest.xml b/FloconAndroid/network/okhttp-interceptor-no-op/src/main/AndroidManifest.xml similarity index 100% rename from FloconAndroid/okhttp-interceptor-no-op/src/main/AndroidManifest.xml rename to FloconAndroid/network/okhttp-interceptor-no-op/src/main/AndroidManifest.xml diff --git a/FloconAndroid/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt b/FloconAndroid/network/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt similarity index 100% rename from FloconAndroid/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt rename to FloconAndroid/network/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt diff --git a/FloconAndroid/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt b/FloconAndroid/network/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt similarity index 100% rename from FloconAndroid/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt rename to FloconAndroid/network/okhttp-interceptor-no-op/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt diff --git a/FloconAndroid/okhttp-interceptor-no-op/.gitignore b/FloconAndroid/network/okhttp-interceptor/.gitignore similarity index 100% rename from FloconAndroid/okhttp-interceptor-no-op/.gitignore rename to FloconAndroid/network/okhttp-interceptor/.gitignore diff --git a/FloconAndroid/network/okhttp-interceptor/build.gradle.kts b/FloconAndroid/network/okhttp-interceptor/build.gradle.kts new file mode 100644 index 000000000..ca055d13d --- /dev/null +++ b/FloconAndroid/network/okhttp-interceptor/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + id("flocon.android.library") + id("flocon.publish") +} + +android { + namespace = "io.github.openflocon.flocon.okhttp" +} + +dependencies { + api(projects.network.core) + + implementation(platform(libs.kotlinx.coroutines.bom)) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.coroutines.android) + + implementation(platform(libs.okhttp.bom)) + implementation(libs.okhttp) + implementation(libs.brotli.dec) + + testImplementation(libs.junit) +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-okhttp-interceptor", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/okhttp-interceptor-no-op/consumer-rules.pro b/FloconAndroid/network/okhttp-interceptor/consumer-rules.pro similarity index 100% rename from FloconAndroid/okhttp-interceptor-no-op/consumer-rules.pro rename to FloconAndroid/network/okhttp-interceptor/consumer-rules.pro diff --git a/FloconAndroid/okhttp-interceptor-no-op/proguard-rules.pro b/FloconAndroid/network/okhttp-interceptor/proguard-rules.pro similarity index 100% rename from FloconAndroid/okhttp-interceptor-no-op/proguard-rules.pro rename to FloconAndroid/network/okhttp-interceptor/proguard-rules.pro diff --git a/FloconAndroid/okhttp-interceptor/src/main/AndroidManifest.xml b/FloconAndroid/network/okhttp-interceptor/src/main/AndroidManifest.xml similarity index 100% rename from FloconAndroid/okhttp-interceptor/src/main/AndroidManifest.xml rename to FloconAndroid/network/okhttp-interceptor/src/main/AndroidManifest.xml diff --git a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/BadQuality.kt b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/BadQuality.kt similarity index 90% rename from FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/BadQuality.kt rename to FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/BadQuality.kt index ec62b25dc..b632a7e9e 100644 --- a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/BadQuality.kt +++ b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/BadQuality.kt @@ -1,6 +1,6 @@ package io.github.openflocon.flocon.okhttp -import io.github.openflocon.flocon.plugins.network.model.BadQualityConfig +import io.github.openflocon.flocon.network.core.model.BadQualityConfig import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Protocol import okhttp3.Request @@ -36,7 +36,8 @@ internal fun failResponseIfNeeded( badQualityConfig.selectRandomError()?.let { selectedError -> when (val t = selectedError.type) { is BadQualityConfig.Error.Type.Body -> { - val errorBody = t.errorBody.toResponseBody(t.errorContentType.toMediaTypeOrNull()) + val errorBody = + t.errorBody.toResponseBody(t.errorContentType.toMediaTypeOrNull()) return Response.Builder() .request(request) diff --git a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Mock.kt b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Mock.kt similarity index 81% rename from FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Mock.kt rename to FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Mock.kt index 8ded87c24..a597b6702 100644 --- a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Mock.kt +++ b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Mock.kt @@ -1,12 +1,12 @@ package io.github.openflocon.flocon.okhttp -import io.github.openflocon.flocon.plugins.network.FloconNetworkPlugin -import io.github.openflocon.flocon.plugins.network.model.MockNetworkResponse +import io.github.openflocon.flocon.network.core.FloconNetworkPlugin +import io.github.openflocon.flocon.network.core.model.MockNetworkResponse import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Protocol import okhttp3.Request import okhttp3.Response -import okhttp3.Response.* +import okhttp3.Response.Builder import okhttp3.ResponseBody.Companion.toResponseBody import okio.Buffer import okio.GzipSink @@ -17,7 +17,7 @@ internal fun findMock( request: Request, floconNetworkPlugin: FloconNetworkPlugin, ): MockNetworkResponse? { - val url = request.url.toString() + val url = request.url.toString() val method = request.method return floconNetworkPlugin.mocks.firstOrNull { it.expectation.matches( @@ -28,7 +28,11 @@ internal fun findMock( } @Throws(IOException::class) -internal fun executeMock(request: Request, requestHeaders: Map, mock: MockNetworkResponse): Response { +internal fun executeMock( + request: Request, + requestHeaders: Map, + mock: MockNetworkResponse +): Response { if (mock.response.delay > 0) { try { Thread.sleep(mock.response.delay) @@ -37,13 +41,14 @@ internal fun executeMock(request: Request, requestHeaders: Map, } } - when(val response = mock.response) { + when (val response = mock.response) { is MockNetworkResponse.Response.Body -> { val mediaType = response.mediaType.toMediaTypeOrNull() var bodyBytes = response.body.toByteArray() // TODO maybe check the mocked response headers - val isGzipped = requestHeaders["Accept-Encoding"] == "gzip" || requestHeaders["accept-encoding"] == "gzip" + val isGzipped = + requestHeaders["Accept-Encoding"] == "gzip" || requestHeaders["accept-encoding"] == "gzip" if (isGzipped) { val buffer = Buffer() diff --git a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt similarity index 90% rename from FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt rename to FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt index 5312cd0a9..3d0b712ba 100644 --- a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt +++ b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/OkHttpInterceptor.kt @@ -2,11 +2,12 @@ package io.github.openflocon.flocon.okhttp -import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkCallResponse -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest -import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.network.core.networkPlugin +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkCallResponse +import io.github.openflocon.flocon.network.core.model.FloconNetworkRequest +import io.github.openflocon.flocon.network.core.model.FloconNetworkResponse import okhttp3.Interceptor import okhttp3.MediaType import okhttp3.Request @@ -28,8 +29,9 @@ class FloconOkhttpInterceptor( @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { - val floconNetworkPlugin = FloconApp.instance?.client?.networkPlugin - if (floconNetworkPlugin == null || !shouldLog(chain)) { + val floconNetworkPlugin = Flocon.networkPlugin + + if (!shouldLog(chain)) { // on no op, do not intercept the call, just execute it return chain.proceed(chain.request()) } @@ -86,7 +88,11 @@ class FloconOkhttpInterceptor( try { val response = if (isMocked) { - executeMock(request = request, mock = mockConfig, requestHeaders = requestHeadersMap) + executeMock( + request = request, + mock = mockConfig, + requestHeaders = requestHeadersMap + ) } else { floconNetworkPlugin.badQualityConfig?.let { badQualityConfig -> executeBadQuality( @@ -99,7 +105,6 @@ class FloconOkhttpInterceptor( } val endTime = System.nanoTime() - val durationMs: Double = (endTime - startTime) / 1e6 // To get the response body, be careful @@ -161,11 +166,8 @@ class FloconOkhttpInterceptor( // Just return the original response if you don't modify the body itself. return response } catch (e: IOException) { - val endTime = System.nanoTime() - val durationMs: Double = (endTime - startTime) / 1e6 - val floconCallResponse = FloconNetworkResponse( httpCode = null, contentType = null, diff --git a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Utils.kt b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Utils.kt similarity index 100% rename from FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Utils.kt rename to FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/Utils.kt diff --git a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt similarity index 80% rename from FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt rename to FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt index 4d02a7d01..09d5593fb 100644 --- a/FloconAndroid/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt +++ b/FloconAndroid/network/okhttp-interceptor/src/main/kotlin/io/github/openflocon/flocon/okhttp/websocket/FloconWebSocket.kt @@ -1,9 +1,7 @@ package io.github.openflocon.flocon.okhttp.websocket import io.github.openflocon.flocon.FloconApp -import io.github.openflocon.flocon.plugins.network.floconLogWebSocketEvent -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketEvent -import io.github.openflocon.flocon.plugins.network.model.FloconWebSocketMockListener +import io.github.openflocon.flocon.network.core.model.FloconWebSocketEvent import okhttp3.Response import okhttp3.WebSocket import okhttp3.WebSocketListener @@ -19,15 +17,15 @@ object FloconWebSocket { error: Throwable? = null, ) { val size = message?.toByteArray()?.size?.toLong() - floconLogWebSocketEvent( - FloconWebSocketEvent( - websocketUrl = webSocket.request().url.toString(), - event = event, - message = message, - error = error, - size = size ?: 0L, - ) - ) +// floconLogWebSocketEvent( +// FloconWebSocketEvent( +// websocketUrl = webSocket.request().url.toString(), +// event = event, +// message = message, +// error = error, +// size = size ?: 0L, +// ) +// ) } fun send(webSocket: WebSocket, text: String) : Boolean { @@ -53,13 +51,13 @@ object FloconWebSocket { private var websocketRef : WeakReference? = null init { - FloconApp.instance?.client?.networkPlugin?.registerWebSocketMockListener(id = id, listener = object: FloconWebSocketMockListener { - override fun onMessage(message: String) { - websocketRef?.get()?.let { websocket -> - onMessage(websocket, message) - } - } - }) +// FloconApp.instance?.client?.networkPlugin?.registerWebSocketMockListener(id = id, listener = object: FloconWebSocketMockListener { +// override fun onMessage(message: String) { +// websocketRef?.get()?.let { websocket -> +// onMessage(websocket, message) +// } +// } +// }) } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { diff --git a/FloconAndroid/okhttp-interceptor/src/test/kotlin/io/github/openflocon/flocon/okhttp/UtilsTest.kt b/FloconAndroid/network/okhttp-interceptor/src/test/kotlin/io/github/openflocon/flocon/okhttp/UtilsTest.kt similarity index 100% rename from FloconAndroid/okhttp-interceptor/src/test/kotlin/io/github/openflocon/flocon/okhttp/UtilsTest.kt rename to FloconAndroid/network/okhttp-interceptor/src/test/kotlin/io/github/openflocon/flocon/okhttp/UtilsTest.kt diff --git a/FloconAndroid/okhttp-interceptor/.gitignore b/FloconAndroid/okhttp-interceptor/.gitignore deleted file mode 100644 index 42afabfd2..000000000 --- a/FloconAndroid/okhttp-interceptor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/FloconAndroid/okhttp-interceptor/consumer-rules.pro b/FloconAndroid/okhttp-interceptor/consumer-rules.pro deleted file mode 100644 index e69de29bb..000000000 diff --git a/FloconAndroid/okhttp-interceptor/proguard-rules.pro b/FloconAndroid/okhttp-interceptor/proguard-rules.pro deleted file mode 100644 index 481bb4348..000000000 --- a/FloconAndroid/okhttp-interceptor/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/build.gradle.kts b/FloconAndroid/sample-android-only/build.gradle.kts index 562891197..545be5263 100644 --- a/FloconAndroid/sample-android-only/build.gradle.kts +++ b/FloconAndroid/sample-android-only/build.gradle.kts @@ -7,7 +7,8 @@ plugins { alias(libs.plugins.kotlin.compose) alias(libs.plugins.ksp) alias(libs.plugins.apollo) - id("com.google.protobuf") + + alias(libs.plugins.protobuf) } android { @@ -81,15 +82,36 @@ dependencies { //implementation("io.github.openflocon:flocon-okhttp-interceptor-no-op:$floconVersion") implementation("io.github.openflocon:flocon-ktor-interceptor:$floconVersion") } else { - debugImplementation(project(":flocon")) - releaseImplementation(project(":flocon-no-op")) - debugImplementation(project(":okhttp-interceptor")) - releaseImplementation(project(":okhttp-interceptor-no-op")) - implementation(project(":grpc:grpc-interceptor-lite")) - debugImplementation(project(":ktor-interceptor")) - releaseImplementation(project(":ktor-interceptor-no-op")) - debugImplementation(project(":datastores")) - releaseImplementation(project(":datastores-no-op")) + debugImplementation(projects.flocon) + releaseImplementation(projects.floconNoOp) + + debugImplementation(projects.deeplinks) + releaseImplementation(projects.deeplinksNoOp) + + debugImplementation(projects.tables) + releaseImplementation(projects.tablesNoOp) + + debugImplementation(projects.analytics) + releaseImplementation(projects.analyticsNoOp) + + debugImplementation(projects.crashreporter) + releaseImplementation(projects.crashreporterNoOp) + + debugImplementation(project(":database:room")) + releaseImplementation(project(":database:room-no-op")) + debugImplementation(project(":database:room3")) + releaseImplementation(project(":database:room3-no-op")) + + debugImplementation(projects.network.okhttpInterceptor) + releaseImplementation(projects.network.okhttpInterceptorNoOp) + + implementation(projects.grpc.grpcInterceptorLite) + + debugImplementation(projects.network.ktorInterceptor) + releaseImplementation(projects.network.ktorInterceptorNoOp) + + debugImplementation(projects.datastores) + releaseImplementation(projects.datastoresNoOp) } @@ -155,12 +177,12 @@ apollo { protobuf { protoc { - artifact = "com.google.protobuf:protoc:3.25.1" + artifact = libs.protobuf.protoc.get().toString() } generateProtoTasks { - val protocGenJava = "io.grpc:protoc-gen-grpc-java:1.73.0" - val protocGenKotlin = "io.grpc:protoc-gen-grpc-kotlin:1.4.3" + ":jdk8@jar" + val protocGenJava = libs.grpc.gen.java.get().toString() + val protocGenKotlin = libs.grpc.gen.kotlin.get().toString() + ":jdk8@jar" plugins { id("java") { diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt index 8f0737c2f..f808131a7 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt @@ -20,39 +20,32 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import io.github.openflocon.flocon.Flocon -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.myapplication.dashboard.initializeDashboard +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.FloconDatabase +import io.github.openflocon.flocon.database.room.room import io.github.openflocon.flocon.myapplication.database.DogDatabase import io.github.openflocon.flocon.myapplication.database.initializeDatabases import io.github.openflocon.flocon.myapplication.database.initializeInMemoryDatabases import io.github.openflocon.flocon.myapplication.database.model.DogEntity -import io.github.openflocon.flocon.myapplication.deeplinks.initializeDeeplinks -import io.github.openflocon.flocon.myapplication.graphql.GraphQlTester import io.github.openflocon.flocon.myapplication.grpc.GrpcController -import io.github.openflocon.flocon.myapplication.images.initializeImages -import io.github.openflocon.flocon.myapplication.sharedpreferences.initializeDatastores -import io.github.openflocon.flocon.myapplication.sharedpreferences.initializeSharedPreferences -import io.github.openflocon.flocon.myapplication.sharedpreferences.initializeSharedPreferencesAfterInit import io.github.openflocon.flocon.myapplication.table.initializeTable import io.github.openflocon.flocon.myapplication.ui.ImagesListView import io.github.openflocon.flocon.myapplication.ui.theme.MyApplicationTheme +import io.github.openflocon.flocon.network.core.FloconNetwork import io.github.openflocon.flocon.okhttp.FloconOkhttpInterceptor -import io.github.openflocon.flocon.plugins.analytics.analytics -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsEvent -import io.github.openflocon.flocon.plugins.analytics.model.analyticsProperty -import io.github.openflocon.flocon.plugins.tables.model.toParam -import io.github.openflocon.flocon.plugins.tables.table +import io.github.openflocon.flocon.analytics.FloconAnalytics +import io.github.openflocon.flocon.deeplinks.FloconDeeplinks +import io.github.openflocon.flocon.tables.FloconTable +import io.github.openflocon.flocon.startFlocon import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import okhttp3.OkHttpClient -import kotlin.uuid.Uuid import kotlin.random.Random import kotlin.uuid.ExperimentalUuidApi class MainActivity : ComponentActivity() { - lateinit var inMemoryDb : DogDatabase + lateinit var inMemoryDb: DogDatabase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -62,38 +55,41 @@ class MainActivity : ComponentActivity() { Toast.makeText(this, "opend with : $it", Toast.LENGTH_LONG).show() } + initFlocon() + val okHttpClient = OkHttpClient() .newBuilder() - .addInterceptor(FloconOkhttpInterceptor( - isImage = { - it.request.url.toString().contains("picsum") - }, - /*shouldLog = { - val url = it.request().url.toString() - println("url: $url") - url.contains("1").not() - }*/ - )) + .addInterceptor( + FloconOkhttpInterceptor( + isImage = { + it.request.url.toString().contains("picsum") + }, + /*shouldLog = { + val url = it.request().url.toString() + println("url: $url") + url.contains("1").not() + }*/ + ) + ) .build() - initializeSharedPreferences(applicationContext) +// initializeSharedPreferences(applicationContext) initializeDatabases(context = applicationContext) - FloconLogger.enabled = true - Flocon.initialize(this) - initializeDeeplinks() +// FloconLogger.enabled = true +// Flocon.initialize(this) inMemoryDb = initializeInMemoryDatabases(applicationContext) - initializeSharedPreferencesAfterInit(applicationContext) - initializeDatastores(applicationContext) +// initializeSharedPreferencesAfterInit(applicationContext) +// initializeDatastores(applicationContext) val dummyHttpCaller = DummyHttpCaller(client = okHttpClient) - val dummyWebsocketCaller = DummyWebsocketCaller(client = okHttpClient) - GlobalScope.launch { dummyWebsocketCaller.connectToWebsocket() } - val graphQlTester = GraphQlTester(client = okHttpClient) - initializeImages(context = this, okHttpClient = okHttpClient) - initializeDashboard(this) - initializeTable(this) +// val dummyWebsocketCaller = DummyWebsocketCaller(client = okHttpClient) +// GlobalScope.launch { dummyWebsocketCaller.connectToWebsocket() } +// val graphQlTester = GraphQlTester(client = okHttpClient) +// initializeImages(context = this, okHttpClient = okHttpClient) +// initializeDashboard(this) + initializeTable() setContent { MyApplicationTheme { @@ -101,7 +97,11 @@ class MainActivity : ComponentActivity() { val scope = rememberCoroutineScope() val context = LocalContext.current - Column(Modifier.fillMaxSize().padding(innerPadding)) { + Column( + Modifier + .fillMaxSize() + .padding(innerPadding) + ) { FlowRow( modifier = Modifier .fillMaxWidth() @@ -118,7 +118,7 @@ class MainActivity : ComponentActivity() { } Button( onClick = { - dummyHttpCaller.callGzip() + //dummyHttpCaller.callGzip() } ) { Text("okhttp gzip test") @@ -126,7 +126,7 @@ class MainActivity : ComponentActivity() { Button( onClick = { GlobalScope.launch { - graphQlTester.fetchViewerInfo() + //graphQlTester.fetchViewerInfo() } } ) { @@ -150,7 +150,7 @@ class MainActivity : ComponentActivity() { } Button( onClick = { - dummyWebsocketCaller.send(Uuid.random().toString()) + //dummyWebsocketCaller.send(Uuid.random().toString()) } ) { Text("websocket test") @@ -164,32 +164,32 @@ class MainActivity : ComponentActivity() { } Button( onClick = { - val value = Random.nextInt(from = 0, until = 1000).toString() - Flocon.table("analytics").log( - "name" toParam "new name $value", - "value1" toParam "value1 $value", - "value2" toParam "value2 $value", - ) + Random.nextInt(from = 0, until = 1000).toString() +// Flocon.table("analytics").log( +// "name" toParam "new name $value", +// "value1" toParam "value1 $value", +// "value2" toParam "value2 $value", +// ) } ) { Text("send table event") } Button( onClick = { - Flocon.analytics("firebase").logEvents( - AnalyticsEvent( - eventName = "clicked user", - "userId" analyticsProperty "1024", - "username" analyticsProperty "florent", - "index" analyticsProperty "3", - ), - AnalyticsEvent( - eventName = "opened profile", - "userId" analyticsProperty "2048", - "username" analyticsProperty "kevin", - "age" analyticsProperty "34", - ), - ) +// Flocon.analytics("firebase").logEvents( +// AnalyticsEvent( +// eventName = "clicked user", +// "userId" analyticsProperty "1024", +// "username" analyticsProperty "florent", +// "index" analyticsProperty "3", +// ), +// AnalyticsEvent( +// eventName = "opened profile", +// "userId" analyticsProperty "2048", +// "username" analyticsProperty "kevin", +// "age" analyticsProperty "34", +// ), +// ) } ) { Text("send analytics event") @@ -219,4 +219,29 @@ class MainActivity : ComponentActivity() { } } } + + private fun initFlocon() { + startFlocon(FloconContext(this)) { + install(FloconDeeplinks) { + deeplink("flocon://home") + deeplink("flocon://test") + deeplink("flocon://user/[userId]") { + label = "User" + "userId" withAutoComplete listOf("Florent", "David", "Guillaume") + } + deeplink("flocon://post/[postId]?comment=[commentText]") { + label = "Post" + description = "Open a post and send a comment" + } + } + + install(FloconNetwork) + install(FloconTable) + install(FloconAnalytics) + install(FloconDatabase) { + room() + } + } + } + } \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/InitializeDashboard.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/InitializeDashboard.kt index 02777a5af..e0761a5d8 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/InitializeDashboard.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/InitializeDashboard.kt @@ -6,20 +6,20 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.lifecycle.lifecycleScope +import com.apollographql.apollo.api.label import io.github.openflocon.flocon.myapplication.dashboard.device.deviceFlow import io.github.openflocon.flocon.myapplication.dashboard.device.initializeDeviceFlow import io.github.openflocon.flocon.myapplication.dashboard.tokens.tokensFlow import io.github.openflocon.flocon.myapplication.dashboard.user.userFlow +import io.github.openflocon.flocon.plugins.dashboard.floconDashboard import io.github.openflocon.flocon.plugins.dashboard.dsl.button import io.github.openflocon.flocon.plugins.dashboard.dsl.checkBox import io.github.openflocon.flocon.plugins.dashboard.dsl.html import io.github.openflocon.flocon.plugins.dashboard.dsl.json -import io.github.openflocon.flocon.plugins.dashboard.dsl.label import io.github.openflocon.flocon.plugins.dashboard.dsl.markdown import io.github.openflocon.flocon.plugins.dashboard.dsl.plainText import io.github.openflocon.flocon.plugins.dashboard.dsl.text import io.github.openflocon.flocon.plugins.dashboard.dsl.textField -import io.github.openflocon.flocon.plugins.dashboard.floconDashboard import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt index cce1719d3..e3c13da9c 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt @@ -4,12 +4,11 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import io.github.openflocon.flocon.database.room.extensions.floconLogs import io.github.openflocon.flocon.myapplication.database.dao.DogDao import io.github.openflocon.flocon.myapplication.database.model.DogEntity import io.github.openflocon.flocon.myapplication.database.model.HumanEntity import io.github.openflocon.flocon.myapplication.database.model.HumanWithDogEntity -import io.github.openflocon.flocon.plugins.database.floconLogDatabaseQuery -import java.util.concurrent.Executors @Database( entities = [ @@ -34,10 +33,9 @@ abstract class DogDatabase : RoomDatabase() { context.applicationContext, DogDatabase::class.java, dbName - ).setQueryCallback({ sqlQuery, bindArgs -> floconLogDatabaseQuery( - dbName = dbName, sqlQuery = sqlQuery, bindArgs = bindArgs - ) }, Executors.newSingleThreadExecutor()) - .fallbackToDestructiveMigration() + ) + .floconLogs() + .fallbackToDestructiveMigration(dropAllTables = true) .build() INSTANCE = instance instance diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/InitializeDatabases.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/InitializeDatabases.kt index 2afc24fb1..d380b0602 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/InitializeDatabases.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/InitializeDatabases.kt @@ -2,11 +2,11 @@ package io.github.openflocon.flocon.myapplication.database import android.content.Context import androidx.room.Room +import io.github.openflocon.flocon.database.room.floconRegisterDatabase import io.github.openflocon.flocon.myapplication.database.model.DogEntity import io.github.openflocon.flocon.myapplication.database.model.FoodEntity import io.github.openflocon.flocon.myapplication.database.model.HumanEntity import io.github.openflocon.flocon.myapplication.database.model.HumanWithDogEntity -import io.github.openflocon.flocon.plugins.database.floconRegisterDatabase import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -17,7 +17,7 @@ fun initializeInMemoryDatabases(context: Context): DogDatabase { ).build().also { floconRegisterDatabase( displayName = "inmemory_dogs", - openHelper = it.openHelper + database = it ) } } @@ -26,6 +26,11 @@ fun initializeDatabases(context: Context) { val dogDatabase = DogDatabase.getDatabase(context) val foodDatabase = FoodDatabase.getDatabase(context) + floconRegisterDatabase( + displayName = "dogs", + database = dogDatabase + ) + GlobalScope.launch { dogDatabase.dogDao().insertDog( DogEntity( @@ -126,7 +131,7 @@ fun initializeDatabases(context: Context) { id = 10L + i, name = "dog$i", breed = randomBreed, - pictureUrl = "https://picsum.photos/500/50${i%10}.jpg", + pictureUrl = "https://picsum.photos/500/50${i % 10}.jpg", age = (1..15).random() ) ) diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/deeplinks/InitializeDeeplinks.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/deeplinks/InitializeDeeplinks.kt deleted file mode 100644 index b456c7acd..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/deeplinks/InitializeDeeplinks.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.openflocon.flocon.myapplication.deeplinks - -import io.github.openflocon.flocon.Flocon -import io.github.openflocon.flocon.plugins.deeplinks.deeplinks - -fun initializeDeeplinks() { - Flocon.deeplinks { - variable("test_variable") - variable("host") { - description = "Host variable" - autoComplete(listOf("flocon", "flocon2", "flocon3")) - } - deeplink("[host]://home") { - "host" withVariable "host" - } - deeplink("[host]://test") { - "host" withVariable "host" - } - deeplink("[host]://user/[userId]") { - label = "User" - "userId" withAutoComplete listOf("Florent", "David", "Guillaume") - "host" withVariable "host" - } - deeplink("[host]://post/[postId]?comment=[commentText]") { - label = "Post" - description = "Open a post and send a comment" - "commentText" withVariable "test_variable" - "host" withVariable "host" - } - } -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/Datastores.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/Datastores.kt index 4b5fab96c..d6aefeec9 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/Datastores.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/Datastores.kt @@ -5,8 +5,6 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import io.github.openflocon.flocon.plugins.sharedprefs.floconRegisterPreference -import io.github.openflocon.flocon.preferences.datastores.model.FloconDatastorePreference import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -38,7 +36,7 @@ class Datastores(private val context: Context) { } init { - floconRegisterPreference(FloconDatastorePreference("datastore", context.dataStore)) + //floconRegisterPreference(FloconDatastorePreference("datastore", context.dataStore)) GlobalScope.launch { saveUsername("John Doe") saveUserAge(30) diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/SharedPreferences.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/SharedPreferences.kt index 681d24d91..099d6d9f6 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/SharedPreferences.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/SharedPreferences.kt @@ -3,12 +3,8 @@ package io.github.openflocon.flocon.myapplication.sharedpreferences import android.content.Context -import android.content.SharedPreferences import android.preference.PreferenceManager import androidx.core.content.edit -import io.github.openflocon.flocon.plugins.sharedprefs.FloconSharedPreference -import io.github.openflocon.flocon.plugins.sharedprefs.floconRegisterPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference import org.json.JSONArray import org.json.JSONObject import kotlin.uuid.Uuid @@ -17,7 +13,7 @@ import kotlin.uuid.ExperimentalUuidApi fun initializeSharedPreferencesAfterInit(context: Context) { val referencedPref = context.getSharedPreferences("ref_pref", Context.MODE_PRIVATE) - floconRegisterPreference(FloconSharedPreference("my_custom_name", referencedPref)) + //floconRegisterPreference(FloconSharedPreference("my_custom_name", referencedPref)) referencedPref.edit { putString("works", "yes") diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/table/InitializeDashboard.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/table/InitializeDashboard.kt deleted file mode 100644 index 9784e3f66..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/table/InitializeDashboard.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.openflocon.flocon.myapplication.table - -import android.content.Context -import io.github.openflocon.flocon.Flocon -import io.github.openflocon.flocon.plugins.tables.model.toParam -import io.github.openflocon.flocon.plugins.tables.table - -fun initializeTable(context: Context) { - Flocon.table("analytics").log( - "name" toParam "nameValue", - "value1" toParam "value1Value", - "value2" toParam "value2Value", - ) -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/table/InitializeTable.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/table/InitializeTable.kt new file mode 100644 index 000000000..a054832b2 --- /dev/null +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/table/InitializeTable.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.flocon.myapplication.table + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.tables.dsl.table +import io.github.openflocon.flocon.tables.tablePlugin + +fun initializeTable() { + Flocon.tablePlugin.table("analytics") { + column("name", "nameValue") + column("value1", "value1Value") + column("value2", "value2Value") + } +} \ No newline at end of file diff --git a/FloconAndroid/sample-multiplatform/build.gradle.kts b/FloconAndroid/sample-multiplatform/build.gradle.kts index e2aa52888..89d400280 100644 --- a/FloconAndroid/sample-multiplatform/build.gradle.kts +++ b/FloconAndroid/sample-multiplatform/build.gradle.kts @@ -33,9 +33,9 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation(project(":flocon")) - implementation(project(":flocon-base")) - implementation(project(":ktor-interceptor")) + implementation(projects.flocon) + implementation(projects.deeplinks) + implementation(projects.network.ktorInterceptor) implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.serialization.json) @@ -76,7 +76,7 @@ kotlin { implementation(libs.ktor.client.cio) implementation(libs.sqlite.jdbc) - implementation(libs.sqlite.bundled) + implementation(libs.androidx.sqlite.bundled) // Compose Desktop implementation(compose.desktop.currentOs) diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt index 6a6199926..2fb927254 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt @@ -2,11 +2,8 @@ package io.github.openflocon.flocon.myapplication.multi import android.content.Context import androidx.room.Room -import androidx.room.RoomDatabase import io.github.openflocon.flocon.myapplication.multi.database.DogDatabase import io.github.openflocon.flocon.myapplication.multi.database.FoodDatabase -import io.github.openflocon.flocon.plugins.database.floconLogDatabaseQuery -import java.util.concurrent.Executor import java.util.concurrent.Executors object Databases { @@ -14,17 +11,20 @@ object Databases { private var dogDatabase: DogDatabase? = null fun getDogDatabase(context: Context): DogDatabase { - val dbName = "dogs_database" + "dogs_database" return dogDatabase ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, DogDatabase::class.java, "dogs_database" ) - .setQueryCallback({ sqlQuery, bindArgs -> floconLogDatabaseQuery( - dbName = dbName, sqlQuery = sqlQuery, bindArgs = bindArgs - ) }, Executors.newSingleThreadExecutor()) - .fallbackToDestructiveMigration().build() +// .setQueryCallback({ sqlQuery, bindArgs -> +// floconLogDatabaseQuery( +// dbName = dbName, sqlQuery = sqlQuery, bindArgs = bindArgs +// ) +// }, Executors.newSingleThreadExecutor()) + .fallbackToDestructiveMigration(dropAllTables = true) + .build() dogDatabase = instance instance } diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt index 5878a2038..d2c637325 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt @@ -5,20 +5,18 @@ import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconContext import io.github.openflocon.flocon.FloconLogger import io.github.openflocon.flocon.ktor.FloconKtorPlugin import io.github.openflocon.flocon.myapplication.multi.Databases.getDogDatabase import io.github.openflocon.flocon.myapplication.multi.Databases.getFoodDatabase -import io.github.openflocon.flocon.myapplication.multi.database.FoodDatabase import io.github.openflocon.flocon.myapplication.multi.database.initializeDatabases -import io.github.openflocon.flocon.myapplication.multi.database.model.DogEntity import io.github.openflocon.flocon.myapplication.multi.sharedpreferences.initializeSharedPreferences import io.github.openflocon.flocon.myapplication.multi.ui.App +import io.github.openflocon.flocon.deeplinks.FloconDeeplinks +import io.github.openflocon.flocon.startFlocon import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -55,7 +53,10 @@ class MainActivity : ComponentActivity() { ) FloconLogger.enabled = true - Flocon.initialize(this) + + startFlocon(FloconContext(this)) { + install(FloconDeeplinks) + } setContent { App() diff --git a/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt b/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt index 140e68bf3..02afbfb09 100644 --- a/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt +++ b/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt @@ -2,7 +2,6 @@ package io.github.openflocon.flocon.myapplication.multi.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -16,14 +15,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.openflocon.flocon.myapplication.multi.DummyHttpKtorCaller import io.github.openflocon.flocon.myapplication.multi.dashboard.initializeDashboard -import io.github.openflocon.flocon.myapplication.multi.database.model.DogEntity -import io.github.openflocon.flocon.plugins.analytics.floconAnalytics -import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsEvent -import io.github.openflocon.flocon.plugins.analytics.model.analyticsProperty -import io.github.openflocon.flocon.plugins.tables.floconTable -import io.github.openflocon.flocon.plugins.tables.model.toParam -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import kotlin.random.Random @Composable @@ -44,7 +35,7 @@ fun App() { text = "Flocon Multi App", style = MaterialTheme.typography.headlineMedium ) - + Column( modifier = Modifier.fillMaxWidth(), ) { @@ -64,32 +55,32 @@ fun App() { } Button( onClick = { - val value = Random.nextInt(from = 0, until = 1000).toString() - floconTable("analytics").log( - "name" toParam "new name $value", - "value1" toParam "value1 $value", - "value2" toParam "value2 $value", - ) + Random.nextInt(from = 0, until = 1000).toString() +// floconTable("analytics").log( +// "name" toParam "new name $value", +// "value1" toParam "value1 $value", +// "value2" toParam "value2 $value", +// ) } ) { Text("send table event") } Button( onClick = { - floconAnalytics("firebase").logEvents( - AnalyticsEvent( - eventName = "clicked user", - "userId" analyticsProperty "1024", - "username" analyticsProperty "florent", - "index" analyticsProperty "3", - ), - AnalyticsEvent( - eventName = "opened profile", - "userId" analyticsProperty "2048", - "username" analyticsProperty "kevin", - "age" analyticsProperty "34", - ), - ) +// floconAnalytics("firebase").logEvents( +// AnalyticsEvent( +// eventName = "clicked user", +// "userId" analyticsProperty "1024", +// "username" analyticsProperty "florent", +// "index" analyticsProperty "3", +// ), +// AnalyticsEvent( +// eventName = "opened profile", +// "userId" analyticsProperty "2048", +// "username" analyticsProperty "kevin", +// "age" analyticsProperty "34", +// ), +// ) } ) { Text("send analytics event") diff --git a/FloconAndroid/settings.gradle.kts b/FloconAndroid/settings.gradle.kts index 511b0712c..c4781ac24 100644 --- a/FloconAndroid/settings.gradle.kts +++ b/FloconAndroid/settings.gradle.kts @@ -1,3 +1,7 @@ +rootProject.name = "Flocon-Sample-App" + +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + pluginManagement { repositories { google() @@ -5,6 +9,10 @@ pluginManagement { gradlePluginPortal() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { @@ -14,18 +22,41 @@ dependencyResolutionManagement { } } -rootProject.name = "My Application" include(":sample-android-only") include(":sample-multiplatform") -include(":flocon-base") include(":flocon") include(":flocon-no-op") -include(":okhttp-interceptor") -include(":okhttp-interceptor-no-op") +include(":network:okhttp-interceptor") +include(":network:okhttp-interceptor-no-op") include(":grpc:grpc-interceptor") include(":grpc:grpc-interceptor-base") include(":grpc:grpc-interceptor-lite") -include(":ktor-interceptor") -include(":ktor-interceptor-no-op") +include(":network:ktor-interceptor") +include(":network:ktor-interceptor-no-op") include(":datastores") include(":datastores-no-op") +include(":deeplinks") +include(":deeplinks-no-op") +include(":tables") +include(":tables-no-op") +include(":analytics") +include(":analytics-no-op") +include(":crashreporter") +include(":crashreporter-no-op") +include(":device") +include(":device-no-op") +include(":files") +include(":files-no-op") +include(":sharedprefs") +include(":sharedprefs-no-op") +include(":network:core") +include(":network:core-no-op") +include(":database:core") +include(":database:core-no-op") +include(":database:room") +include(":database:room-no-op") +include(":database:room3") +include(":database:room3-no-op") + +includeBuild("build-logic") + diff --git a/FloconAndroid/sharedprefs-no-op/build.gradle.kts b/FloconAndroid/sharedprefs-no-op/build.gradle.kts new file mode 100644 index 000000000..9de465592 --- /dev/null +++ b/FloconAndroid/sharedprefs-no-op/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.sharedprefs.noop" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-sharedprefs-no-op", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt new file mode 100644 index 000000000..05e27c6c0 --- /dev/null +++ b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt @@ -0,0 +1,48 @@ +package io.github.openflocon.flocon.plugins.sharedprefs + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconSharedPreferenceModel + +class FloconPreferencesConfig : FloconPluginConfig + +interface FloconPreferencesPlugin : FloconPlugin { + fun register(sharedPreference: FloconSharedPreferenceModel) +} + +object FloconPreferences : FloconPluginFactory { + override val name: String = "Preferences" + override val pluginId: String = Protocol.ToDevice.SharedPreferences.Plugin + override fun createConfig(context: FloconContext) = FloconPreferencesConfig() + override fun install( + pluginConfig: FloconPreferencesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconPreferencesPlugin { + return FloconSharedPrefsPluginNoOpImpl() + } +} + +internal class FloconSharedPrefsPluginNoOpImpl : FloconPlugin, FloconPreferencesPlugin { + override val key: String = "SHARED_PREF" + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } + + override fun register(sharedPreference: FloconSharedPreferenceModel) { + // no op + } +} diff --git a/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconSharedPreferenceModel.kt b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconSharedPreferenceModel.kt new file mode 100644 index 000000000..a0b612b92 --- /dev/null +++ b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconSharedPreferenceModel.kt @@ -0,0 +1,4 @@ +package io.github.openflocon.flocon.plugins.sharedprefs.model + +class FloconSharedPreferenceModel { +} diff --git a/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.kt b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.kt new file mode 100644 index 000000000..4ff1e05f8 --- /dev/null +++ b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.kt @@ -0,0 +1,50 @@ +package io.github.openflocon.flocon.pluginsold.sharedprefs + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconSharedPreferenceModel + +class FloconPreferencesConfig : FloconPluginConfig + +object FloconPreferences : FloconPluginFactory { + override val name: String = "Preferences" + override val pluginId: String = "preferences" + override fun createConfig(context: FloconContext): FloconPreferencesConfig { + return FloconPreferencesConfig() + } + + override fun install( + pluginConfig: FloconPreferencesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconPreferencesPlugin { + return FloconSharedPrefsPluginNoOpImpl() + } +} + +interface FloconPreferencesPlugin : FloconPlugin { + fun register(sharedPreference: FloconSharedPreferenceModel) +} + +internal class FloconSharedPrefsPluginNoOpImpl : FloconPlugin, FloconPreferencesPlugin { + override val key: String = "SHARED_PREF" + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } + + override fun register(sharedPreference: FloconSharedPreferenceModel) { + // no op + } +} diff --git a/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconSharedPreferenceModel.kt b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconSharedPreferenceModel.kt new file mode 100644 index 000000000..86fb0e0b6 --- /dev/null +++ b/FloconAndroid/sharedprefs-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconSharedPreferenceModel.kt @@ -0,0 +1,4 @@ +package io.github.openflocon.flocon.pluginsold.sharedprefs.model + +class FloconSharedPreferenceModel { +} diff --git a/FloconAndroid/sharedprefs/build.gradle.kts b/FloconAndroid/sharedprefs/build.gradle.kts new file mode 100644 index 000000000..9cee3f151 --- /dev/null +++ b/FloconAndroid/sharedprefs/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":flocon")) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + } + } + } +} + +android { + namespace = "io.github.openflocon.flocon.sharedprefs" +} + +mavenPublishing { + coordinates( + groupId = project.property("floconGroupId") as String, + artifactId = "flocon-sharedprefs", + version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String + ) +} diff --git a/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.android.kt b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.android.kt new file mode 100644 index 000000000..cc2ee1768 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.android.kt @@ -0,0 +1,7 @@ +package io.github.openflocon.flocon.plugins.sharedprefs + +import io.github.openflocon.flocon.FloconContext + +internal actual fun buildFloconSharedPreferenceDataSource(context: FloconContext): FloconSharedPreferenceDataSource { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPreference.kt b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPreference.kt similarity index 90% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPreference.kt rename to FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPreference.kt index 1bbe8d4ac..fd54d1dab 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPreference.kt +++ b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPreference.kt @@ -1,8 +1,8 @@ -package io.github.openflocon.flocon.plugins.sharedprefs +package io.github.openflocon.flocon.pluginsold.sharedprefs import android.content.SharedPreferences -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreferenceValue +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreference +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreferenceValue data class FloconSharedPreference( override val name: String, diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.android.kt b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.android.kt similarity index 56% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.android.kt rename to FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.android.kt index 08b75dbec..9661da305 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.android.kt +++ b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.android.kt @@ -1,8 +1,8 @@ -package io.github.openflocon.flocon.plugins.sharedprefs +package io.github.openflocon.flocon.pluginsold.sharedprefs import android.content.Context import io.github.openflocon.flocon.FloconContext -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreference // Got some code from Flipper client // https://github.com/facebook/flipper/blob/main/android/src/main/java/com/facebook/flipper/plugins/sharedpreferences/SharedPreferencesFlipperPlugin.java @@ -16,6 +16,10 @@ internal class FloconPreferencesDataSourceAndroid( } } -internal actual fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource { - return FloconPreferencesDataSourceAndroid(context = context.appContext) -} \ No newline at end of file +interface FloconPreferencesDataSource { + fun detectLocalPreferences(): List +} + +//internal actual fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource { +// return FloconPreferencesDataSourceAndroid(context = context.context) +//} \ No newline at end of file diff --git a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/SharedPreferencesFinder.kt b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/SharedPreferencesFinder.kt similarity index 75% rename from FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/SharedPreferencesFinder.kt rename to FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/SharedPreferencesFinder.kt index eef8b5068..b25ec6260 100644 --- a/FloconAndroid/flocon/src/androidMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/SharedPreferencesFinder.kt +++ b/FloconAndroid/sharedprefs/src/androidMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/SharedPreferencesFinder.kt @@ -1,10 +1,10 @@ -package io.github.openflocon.flocon.plugins.sharedprefs +package io.github.openflocon.flocon.pluginsold.sharedprefs import android.content.Context import android.content.Context.MODE_PRIVATE import android.os.Build import android.preference.PreferenceManager -import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconPreference import java.io.File internal object SharedPreferencesFinder { @@ -23,8 +23,11 @@ internal object SharedPreferencesFinder { for (each in list) { val prefName = each.substring(0, each.indexOf(".xml")) descriptors.add( - FloconSharedPreference(prefName, sharedPreferences = context.getSharedPreferences(prefName, MODE_PRIVATE) - )) + FloconSharedPreference( + prefName, + sharedPreferences = context.getSharedPreferences(prefName, MODE_PRIVATE) + ) + ) } } @@ -32,7 +35,10 @@ internal object SharedPreferencesFinder { descriptors.add( FloconSharedPreference( name = defaultSharedPrefName, - sharedPreferences = context.getSharedPreferences(defaultSharedPrefName, MODE_PRIVATE) + sharedPreferences = context.getSharedPreferences( + defaultSharedPrefName, + MODE_PRIVATE + ) ) ) diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt new file mode 100644 index 000000000..60a90ebe8 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.kt @@ -0,0 +1,101 @@ +package io.github.openflocon.flocon.plugins.sharedprefs + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconSharedPreferenceModel + +class FloconPreferencesConfig : FloconPluginConfig + +interface FloconPreferencesPlugin : FloconPlugin { + fun register(sharedPreference: FloconSharedPreferenceModel) +} + +object FloconPreferences : FloconPluginFactory { + override val name: String = "Preferences" + override val pluginId: String = Protocol.ToDevice.SharedPreferences.Plugin + override fun createConfig(context: FloconContext) = FloconPreferencesConfig() + override fun install( + pluginConfig: FloconPreferencesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconPreferencesPlugin { + return FloconSharedPrefsPluginImpl( + context = floconConfig.context, + sender = floconConfig.client as FloconMessageSender + ) + } +} + +internal interface FloconSharedPreferenceDataSource { + fun getSharedPreferences(): List + fun getSharedPreferenceValue(fileName: String, key: String): String? + fun setSharedPreferenceValue(fileName: String, key: String, value: String) +} + +internal expect fun buildFloconSharedPreferenceDataSource(context: FloconContext): FloconSharedPreferenceDataSource + +internal class FloconSharedPrefsPluginImpl( + private val context: FloconContext, + private val sender: FloconMessageSender, +) : FloconPlugin, FloconPreferencesPlugin { + override val key: String = "SHARED_PREF" + + private val dataSource = buildFloconSharedPreferenceDataSource(context) + private val preferenceModels = mutableListOf() + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { +// when (method) { +// Protocol.ToDevice.SharedPreferences.Method.GetSharedPreferences -> { +// sendSharedPreferences() +// } +// +// Protocol.ToDevice.SharedPreferences.Method.GetSharedPreferenceValue -> { +// // Not implemented yet on device side, usually handled by getSharedPreferences +// } +// +// Protocol.ToDevice.SharedPreferences.Method.SetSharedPreferenceValue -> { +// SetSharedPreferenceValueMessage.fromJson(body)?.let { message -> +// dataSource.setSharedPreferenceValue( +// fileName = message.fileName, +// key = message.key, +// value = message.value +// ) +// // Refresh view +// sendSharedPreferences() +// } +// } +// } + } + + override suspend fun onConnectedToServer() { + sendSharedPreferences() + } + + override fun register(sharedPreference: FloconSharedPreferenceModel) { + preferenceModels.add(sharedPreference) + sendSharedPreferences() + } + + private fun sendSharedPreferences() { + dataSource.getSharedPreferences() + preferenceModels + try { +// sender.send( +// plugin = Protocol.FromDevice.SharedPreferences.Plugin, +// method = Protocol.FromDevice.SharedPreferences.Method.GetSharedPreferences, +// body = allPrefs.toJson().toString() +// ) + } catch (t: Throwable) { + FloconLogger.logError("SharedPreferences json mapping error", t) + } + } +} \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreferenceWrapper.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreferenceWrapper.kt new file mode 100644 index 000000000..b8a05cf04 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreferenceWrapper.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.plugins.sharedprefs.model + +import kotlinx.serialization.Serializable + +@Serializable +internal data class PreferencesDescriptor( + val name: String, +) \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconSharedPreferenceModel.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconSharedPreferenceModel.kt new file mode 100644 index 000000000..47b0e946f --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconSharedPreferenceModel.kt @@ -0,0 +1,5 @@ +package io.github.openflocon.flocon.plugins.sharedprefs.model + +// TODO Get model from git +class FloconSharedPreferenceModel { +} diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/PreferenceRowDataModel.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/PreferenceRowDataModel.kt similarity index 100% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/PreferenceRowDataModel.kt rename to FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/PreferenceRowDataModel.kt diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/SharedPreferenceValueResultDataModel.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/SharedPreferenceValueResultDataModel.kt similarity index 60% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/SharedPreferenceValueResultDataModel.kt rename to FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/SharedPreferenceValueResultDataModel.kt index a7f90e476..76ece2ed6 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/SharedPreferenceValueResultDataModel.kt +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/fromdevice/SharedPreferenceValueResultDataModel.kt @@ -1,16 +1,10 @@ package io.github.openflocon.flocon.plugins.sharedprefs.model.fromdevice -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString @Serializable internal data class SharedPreferenceValueResultDataModel( val requestId: String, val sharedPreferenceName: String, val rows: List, -) { - fun toJson(): String { - return FloconEncoder.json.encodeToString(this) - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceEditSharedPreferenceValueMessage.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceEditSharedPreferenceValueMessage.kt similarity index 52% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceEditSharedPreferenceValueMessage.kt rename to FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceEditSharedPreferenceValueMessage.kt index 30023f510..14c33a941 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceEditSharedPreferenceValueMessage.kt +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceEditSharedPreferenceValueMessage.kt @@ -1,7 +1,5 @@ package io.github.openflocon.flocon.plugins.sharedprefs.model.todevice -import io.github.openflocon.flocon.FloconLogger -import io.github.openflocon.flocon.core.FloconEncoder import kotlinx.serialization.Serializable @Serializable @@ -15,15 +13,4 @@ internal data class ToDeviceEditSharedPreferenceValueMessage( val booleanValue: Boolean? = null, val longValue: Long? = null, val setStringValue: Set? = null, -) { - companion object { - fun fromJson(jsonString: String): ToDeviceEditSharedPreferenceValueMessage? { - return try { - FloconEncoder.json.decodeFromString(jsonString) - } catch (t: Throwable) { - FloconLogger.logError("parsing issue", t) - null - } - } - } -} +) diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPreferenceValueMessage.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPreferenceValueMessage.kt new file mode 100644 index 000000000..daaa128fa --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPreferenceValueMessage.kt @@ -0,0 +1,9 @@ +package io.github.openflocon.flocon.plugins.sharedprefs.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceGetSharedPreferenceValueMessage( + val requestId: String, + val sharedPreferenceName: String, +) \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPrefsMessage.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPrefsMessage.kt new file mode 100644 index 000000000..00d03f9a2 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/todevice/ToDeviceGetSharedPrefsMessage.kt @@ -0,0 +1,8 @@ +package io.github.openflocon.flocon.plugins.sharedprefs.model.todevice + +import kotlinx.serialization.Serializable + +@Serializable +internal data class ToDeviceGetSharedPrefsMessage( + val requestId: String, +) \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.kt new file mode 100644 index 000000000..f9d06c388 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/FloconSharedPrefsPlugin.kt @@ -0,0 +1,42 @@ +package io.github.openflocon.flocon.pluginsold.sharedprefs + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.pluginsold.sharedprefs.model.FloconSharedPreferenceModel + +class FloconPreferencesConfig : FloconPluginConfig + +/** + * Flocon Preferences Plugin. + * Used to inspect SharedPreferences or other key-value stores. + */ +object FloconPreferences : FloconPluginFactory { + override fun createConfig(context: FloconContext): FloconPreferencesConfig { + TODO("Not yet implemented") + } + + override fun install( + pluginConfig: FloconPreferencesConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconPreferencesPlugin { + TODO("Not yet implemented") + } + + override val name: String + get() = TODO("Not yet implemented") + override val pluginId: String + get() = TODO("Not yet implemented") +} + +//fun floconRegisterSharedPreference(sharedPreference: FloconSharedPreferenceModel) { +// FloconApp.instance?.client?.preferencesPlugin?.register(sharedPreference) +//} + +interface FloconPreferencesPlugin : FloconPlugin { + fun register(sharedPreference: FloconSharedPreferenceModel) +} \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/buildFloconPreferencesDataSource.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/buildFloconPreferencesDataSource.kt new file mode 100644 index 000000000..9df5cd32a --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/buildFloconPreferencesDataSource.kt @@ -0,0 +1,2 @@ +package io.github.openflocon.flocon.pluginsold.sharedprefs + diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreference.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconPreference.kt similarity index 82% rename from FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreference.kt rename to FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconPreference.kt index 5a3810f9f..5bbe4634f 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/model/FloconPreference.kt +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconPreference.kt @@ -1,11 +1,11 @@ -package io.github.openflocon.flocon.plugins.sharedprefs.model +package io.github.openflocon.flocon.pluginsold.sharedprefs.model interface FloconPreference { val name: String suspend fun set( columnName: String, - value: FloconPreferenceValue, + value: FloconPreferenceValue ) suspend fun columns(): List diff --git a/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconSharedPreferenceModel.kt b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconSharedPreferenceModel.kt new file mode 100644 index 000000000..e5b8f6b71 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/commonMain/kotlin/io/github/openflocon/flocon/pluginsold/sharedprefs/model/FloconSharedPreferenceModel.kt @@ -0,0 +1,5 @@ +package io.github.openflocon.flocon.pluginsold.sharedprefs.model + +// TODO Get model from git +class FloconSharedPreferenceModel { +} \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.ios.kt b/FloconAndroid/sharedprefs/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.ios.kt new file mode 100644 index 000000000..0290a8e76 --- /dev/null +++ b/FloconAndroid/sharedprefs/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.ios.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.flocon.plugins.sharedprefs + +import io.github.openflocon.flocon.FloconContext + +//internal actual fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource { +// return FloconPreferencesDataSourceIOs() +//} + +// TODO try to bind with ios storage +//internal class FloconPreferencesDataSourceIOs : FloconPreferencesDataSource { +// override fun detectLocalPreferences(): List { +// return emptyList() +// } +//} + +internal actual fun buildFloconSharedPreferenceDataSource(context: FloconContext): FloconSharedPreferenceDataSource { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.jvm.kt b/FloconAndroid/sharedprefs/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.jvm.kt new file mode 100644 index 000000000..9d596dd9a --- /dev/null +++ b/FloconAndroid/sharedprefs/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.jvm.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.flocon.plugins.sharedprefs + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconPreference + +//internal actual fun buildFloconPreferencesDataSource(context: FloconContext): FloconPreferencesDataSource { +// return FloconPreferencesDataSourceJvm() +//} + +//internal class FloconPreferencesDataSourceJvm : FloconPreferencesDataSource { +// override fun detectLocalPreferences(): List { +// return emptyList() +// } +//} + +internal actual fun buildFloconSharedPreferenceDataSource(context: FloconContext): FloconSharedPreferenceDataSource { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/FloconAndroid/sharedprefs/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.wasmJs.kt b/FloconAndroid/sharedprefs/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.wasmJs.kt new file mode 100644 index 000000000..3dc844cfd --- /dev/null +++ b/FloconAndroid/sharedprefs/src/wasmJsMain/kotlin/io/github/openflocon/flocon/plugins/sharedprefs/FloconSharedPrefsPlugin.wasmJs.kt @@ -0,0 +1,10 @@ +package io.github.openflocon.flocon.plugins.sharedprefs + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.plugins.sharedprefs.model.FloconSharedPreferenceModel + +internal actual fun buildFloconSharedPreferenceDataSource(context: FloconContext): FloconSharedPreferenceDataSource = object : FloconSharedPreferenceDataSource { + override fun getSharedPreferences(): List = emptyList() + override fun getSharedPreferenceValue(fileName: String, key: String): String? = null + override fun setSharedPreferenceValue(fileName: String, key: String, value: String) {} +} diff --git a/FloconAndroid/flocon-base/build.gradle.kts b/FloconAndroid/tables-no-op/build.gradle.kts similarity index 90% rename from FloconAndroid/flocon-base/build.gradle.kts rename to FloconAndroid/tables-no-op/build.gradle.kts index 4c63dfe76..58c5af864 100644 --- a/FloconAndroid/flocon-base/build.gradle.kts +++ b/FloconAndroid/tables-no-op/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.library) @@ -8,8 +6,10 @@ plugins { kotlin { androidTarget { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + compilations.all { + kotlinOptions { + jvmTarget = "11" + } } } @@ -22,7 +22,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation(libs.jetbrains.kotlinx.coroutines.core.fixed) + implementation(project(":flocon")) } } @@ -49,7 +49,7 @@ kotlin { } android { - namespace = "io.github.openflocon.flocon.base" + namespace = "io.github.openflocon.flocon.tables.noop" compileSdk = 36 defaultConfig { @@ -75,6 +75,7 @@ android { } } + mavenPublishing { publishToMavenCentral(automaticRelease = true) @@ -86,12 +87,13 @@ mavenPublishing { coordinates( groupId = project.property("floconGroupId") as String, - artifactId = "flocon-base", + artifactId = "flocon-tables-no-op", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) + pom { - name = "Flocon" + name = "Flocon Tables No-Op" description = project.property("floconDescription") as String inceptionYear = "2025" url = "https://github.com/openflocon/Flocon" @@ -115,4 +117,4 @@ mavenPublishing { developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" } } -} \ No newline at end of file +} diff --git a/FloconAndroid/tables-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesNoOp.kt b/FloconAndroid/tables-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesNoOp.kt new file mode 100644 index 000000000..53f21521d --- /dev/null +++ b/FloconAndroid/tables-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/FloconTablesNoOp.kt @@ -0,0 +1,45 @@ +package io.github.openflocon.flocon.plugins.tables + +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.plugins.tables.model.TableItem + +interface FloconTablePlugin : FloconPlugin { + fun registerItems(tableItems: List) +} + +class FloconTableConfig internal constructor() : FloconPluginConfig + +object FloconTable : FloconPluginFactory { + override val name: String = "Table" + override val pluginId: String = Protocol.ToDevice.Table.Plugin + override fun createConfig(context: FloconContext) = FloconTableConfig() + override fun install( + pluginConfig: FloconTableConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconTablePlugin { + return FloconTablePluginNoOp + } +} + +private object FloconTablePluginNoOp : FloconTablePlugin { + override val key: String = "TABLE" + + override suspend fun onMessageReceived(method: String, body: String) { + // no-op + } + + override suspend fun onConnectedToServer() { + // no-op + } + + override fun registerItems(tableItems: List) { + // no-op + } +} diff --git a/FloconAndroid/tables-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt b/FloconAndroid/tables-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt new file mode 100644 index 000000000..bbe3ca64c --- /dev/null +++ b/FloconAndroid/tables-no-op/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt @@ -0,0 +1,4 @@ +package io.github.openflocon.flocon.plugins.tables.model + +class TableItem { +} \ No newline at end of file diff --git a/FloconAndroid/ktor-interceptor/build.gradle.kts b/FloconAndroid/tables/build.gradle.kts similarity index 86% rename from FloconAndroid/ktor-interceptor/build.gradle.kts rename to FloconAndroid/tables/build.gradle.kts index b7d332a7f..f6cb5e71d 100644 --- a/FloconAndroid/ktor-interceptor/build.gradle.kts +++ b/FloconAndroid/tables/build.gradle.kts @@ -1,15 +1,16 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.library) alias(libs.plugins.vanniktech.maven.publish) + alias(libs.plugins.kotlin.serialization) } kotlin { androidTarget { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + compilations.all { + kotlinOptions { + jvmTarget = "11" + } } } @@ -22,9 +23,8 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation(project(":flocon-base")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") - implementation(libs.ktor.client.core) + implementation(project(":flocon")) + implementation(libs.kotlinx.serialization.json) } } @@ -53,7 +53,7 @@ kotlin { } android { - namespace = "io.github.openflocon.flocon.ktor" + namespace = "io.github.openflocon.flocon.tables" compileSdk = 36 defaultConfig { @@ -72,14 +72,12 @@ android { ) } } - compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } - mavenPublishing { publishToMavenCentral(automaticRelease = true) @@ -91,13 +89,12 @@ mavenPublishing { coordinates( groupId = project.property("floconGroupId") as String, - artifactId = "flocon-ktor-interceptor", + artifactId = "flocon-tables", version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String ) - pom { - name = "Flocon Ktor Interceptor" + name = "Flocon Tables" description = project.property("floconDescription") as String inceptionYear = "2025" url = "https://github.com/openflocon/Flocon" @@ -121,4 +118,4 @@ mavenPublishing { developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git" } } -} \ No newline at end of file +} diff --git a/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/FloconTablesPlugin.kt b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/FloconTablesPlugin.kt new file mode 100644 index 000000000..05f41b7cd --- /dev/null +++ b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/FloconTablesPlugin.kt @@ -0,0 +1,83 @@ +package io.github.openflocon.flocon.tables + +import io.github.openflocon.flocon.Flocon +import io.github.openflocon.flocon.FloconConfig +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.FloconLogger +import io.github.openflocon.flocon.FloconPlugin +import io.github.openflocon.flocon.FloconPluginConfig +import io.github.openflocon.flocon.FloconPluginFactory +import io.github.openflocon.flocon.Protocol +import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.core.FloconMessageSender +import io.github.openflocon.flocon.core.encode +import io.github.openflocon.flocon.dsl.FloconMarker +import io.github.openflocon.flocon.error.pluginNotInitialized +import io.github.openflocon.flocon.tables.model.TableItem +import io.github.openflocon.flocon.tables.model.toRemote +import kotlin.collections.map + +class FloconTableConfig : FloconPluginConfig + +interface FloconTablePlugin : FloconPlugin { + fun registerItems(tableItems: List) +} + +object FloconTable : FloconPluginFactory { + override val name: String = "Table" + override val pluginId: String = Protocol.ToDevice.Table.Plugin + override fun createConfig(context: FloconContext) = FloconTableConfig() + override fun install( + pluginConfig: FloconTableConfig, + floconConfig: FloconConfig, + encoder: FloconEncoder + ): FloconTablePlugin { + return FloconTablePluginImpl( + sender = floconConfig.client as FloconMessageSender, + encoder = encoder + ) + .also { FloconTablePluginImpl.plugin = it } + } +} + +@OptIn(FloconMarker::class) +val Flocon.Companion.tablePlugin: FloconTablePlugin + get() = FloconTablePluginImpl.plugin ?: pluginNotInitialized("table") + +internal class FloconTablePluginImpl( + private val sender: FloconMessageSender, + private val encoder: FloconEncoder +) : FloconPlugin, FloconTablePlugin { + override val key: String = "TABLE" + + override suspend fun onMessageReceived( + method: String, + body: String, + ) { + // no op + } + + override suspend fun onConnectedToServer() { + // no op + } + + override fun registerItems(tableItems: List) { + sendTable(tableItems) + } + + private fun sendTable(tableItems: List) { + try { + sender.send( + plugin = Protocol.FromDevice.Table.Plugin, + method = Protocol.FromDevice.Table.Method.AddItems, + body = encoder.encode(tableItems.map(TableItem::toRemote)) + ) + } catch (t: Throwable) { + FloconLogger.logError("Table json mapping error", t) + } + } + + companion object { + var plugin: FloconTablePlugin? = null + } +} diff --git a/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/dsl/TableItemDsl.kt b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/dsl/TableItemDsl.kt new file mode 100644 index 000000000..09fb74fc6 --- /dev/null +++ b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/dsl/TableItemDsl.kt @@ -0,0 +1,35 @@ +@file:OptIn(ExperimentalUuidApi::class, ExperimentalTime::class) + +package io.github.openflocon.flocon.tables.dsl + +import io.github.openflocon.flocon.tables.FloconTablePlugin +import io.github.openflocon.flocon.tables.model.TableItem +import io.github.openflocon.flocon.pluginsold.tables.model.TableColumnConfig +import kotlin.time.Clock +import kotlin.time.ExperimentalTime +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +fun FloconTablePlugin.table(tableName: String, block: TableItemDefinition.() -> Unit = {}) { + val item = TableItemDefinition(tableName).apply(block) + .build() + + registerItems(tableItems = listOf(item)) +} + +class TableItemDefinition internal constructor(private val name: String) { + + private val columns: MutableList = mutableListOf() + + fun column(name: String, value: String) { + columns.add(TableColumnConfig(columnName = name, value = value)) + } + + internal fun build() = TableItem( + id = Uuid.random().toHexString(), + name = name, + createdAt = Clock.System.now().toEpochMilliseconds(), + columns = emptyList() + ) + +} \ No newline at end of file diff --git a/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/model/TableColumnConfig.kt b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/model/TableColumnConfig.kt new file mode 100644 index 000000000..aa1c2f5a7 --- /dev/null +++ b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/model/TableColumnConfig.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.flocon.pluginsold.tables.model + +data class TableColumnConfig( + val columnName: String, + val value: String, +) diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/model/TableItem.kt similarity index 62% rename from FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt rename to FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/model/TableItem.kt index 63f8bd385..11245d37e 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/tables/model/TableItem.kt +++ b/FloconAndroid/tables/src/commonMain/kotlin/io/github/openflocon/flocon/tables/model/TableItem.kt @@ -1,12 +1,14 @@ -package io.github.openflocon.flocon.plugins.tables.model +package io.github.openflocon.flocon.tables.model -import io.github.openflocon.flocon.core.FloconEncoder +import io.github.openflocon.flocon.pluginsold.tables.model.TableColumnConfig import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString -internal fun tableItemListToJson(items: Collection): String { - return FloconEncoder.json.encodeToString(items.map { it.toRemote() }) -} +data class TableItem( + val id: String, + val name: String, + val createdAt: Long, + val columns: List, +) @Serializable internal class TableItemRemote( @@ -28,10 +30,10 @@ internal fun TableItem.toRemote(): TableItemRemote = TableItemRemote( id = id, name = name, createdAt = createdAt, - columns = columns.map { it.toRemote() } + columns = columns.map(TableColumnConfig::toRemote) ) internal fun TableColumnConfig.toRemote(): TableColumnRemote = TableColumnRemote( column = columnName, value = value -) \ No newline at end of file +) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/settings/SettingsScreen.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/settings/SettingsScreen.kt index 115d0c254..ed54cbc7e 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/settings/SettingsScreen.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/settings/SettingsScreen.kt @@ -87,12 +87,12 @@ private fun SettingsScreen( initialValue = true ) { Column( - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), modifier = Modifier - .padding(8.dp) + .padding(FloconTheme.spacing.small) .clip(FloconTheme.shapes.medium) .background(FloconTheme.colorPalette.primary) - .padding(all = 8.dp) + .padding(all = FloconTheme.spacing.small) ) { if (needsAdbSetup) { Text( @@ -103,7 +103,7 @@ private fun SettingsScreen( } else { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.extraSmall) ) { FloconIcon( imageVector = Icons.Outlined.Check, @@ -125,7 +125,7 @@ private fun SettingsScreen( modifier = Modifier.fillMaxWidth() ) Row( - horizontalArrangement = Arrangement.spacedBy(8.dp) + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small) ) { SettingsButton( text = stringResource(Res.string.general_save), @@ -144,10 +144,10 @@ private fun SettingsScreen( ) { Column( modifier = Modifier - .padding(8.dp) + .padding(FloconTheme.spacing.small) .clip(FloconTheme.shapes.medium) .background(FloconTheme.colorPalette.primary) - .padding(all = 8.dp) + .padding(all = FloconTheme.spacing.small) ) { FloconSlider( value = uiState.fontSizeMultiplier, @@ -164,7 +164,7 @@ private fun SettingsScreen( SettingsButton( onClick = { showLicenses = true }, text = stringResource(Res.string.settings_licenses), - modifier = Modifier.padding(8.dp) + modifier = Modifier.padding(FloconTheme.spacing.small) ) } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelDivider.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelDivider.kt index 94aa59acc..deacee2f8 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelDivider.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelDivider.kt @@ -7,10 +7,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import io.github.openflocon.library.designsystem.FloconTheme + @Composable fun LeftPannelDivider(modifier: Modifier = Modifier) { HorizontalDivider( - modifier = modifier.padding(horizontal = 4.dp), + modifier = modifier.padding(horizontal = FloconTheme.spacing.extraSmall), thickness = 1.dp, color = Color.Gray, // TODO Change ) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelView.kt index e798846bc..cae56e29a 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/LeftPannelView.kt @@ -45,7 +45,7 @@ fun LeftPanelView( modifier = modifier .clip(FloconTheme.shapes.medium) .background(FloconTheme.colorPalette.primary) - .padding(8.dp) + .padding(FloconTheme.spacing.small) ) { MenuSection( current = current, @@ -53,7 +53,7 @@ fun LeftPanelView( expanded = expanded, onClickItem = onClickItem, ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(FloconTheme.spacing.medium)) Spacer(Modifier.weight(1f)) MenuItems( current = current, @@ -105,7 +105,7 @@ private fun ColumnScope.MenuItems( onClick = { onClickItem(item) }, ) if (index != items.lastIndex) - Spacer(Modifier.height(4.dp)) + Spacer(Modifier.height(FloconTheme.spacing.extraSmall)) } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelLabel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelLabel.kt index 0575fbb49..c3434236d 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelLabel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelLabel.kt @@ -35,7 +35,7 @@ fun PannelLabel( Text( modifier = Modifier .fillMaxWidth() - .padding(start = 12.dp, bottom = 4.dp), + .padding(start = FloconTheme.spacing.medium, bottom = FloconTheme.spacing.extraSmall), text = text, style = FloconTheme.typography.bodyLarge.copy( fontWeight = FontWeight.Thin, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelView.kt index 5130743f0..3e31fb1f4 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/leftpannel/PannelView.kt @@ -72,7 +72,7 @@ fun PanelView( 0.3f } else 1f ) - val horizontalPadding = 12.dp + val horizontalPadding = FloconTheme.spacing.medium Row( modifier = modifier @@ -83,13 +83,13 @@ fun PanelView( alpha = lineAlpha } .clickable(onClick = onClick, interactionSource = interactionSource, indication = null) - .padding(horizontal = horizontalPadding, vertical = 4.dp), + .padding(horizontal = horizontalPadding, vertical = FloconTheme.spacing.extraSmall), verticalAlignment = Alignment.CenterVertically, ) { Icon( modifier = Modifier .size(PanelContentMinSize - horizontalPadding.times(2)) - .padding(4.dp), + .padding(FloconTheme.spacing.extraSmall), imageVector = icon, contentDescription = "Description de mon image", tint = iconColor, @@ -104,7 +104,7 @@ fun PanelView( color = FloconTheme.colorPalette.onSurface, style = FloconTheme.typography.bodyMedium, maxLines = 1, - modifier = Modifier.padding(start = 12.dp), + modifier = Modifier.padding(start = FloconTheme.spacing.medium), ) } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/MainScreenTopBar.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/MainScreenTopBar.kt index 4a5d43c46..fb938ae9b 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/MainScreenTopBar.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/MainScreenTopBar.kt @@ -45,7 +45,7 @@ fun MainScreenTopBar( Row( modifier = modifier .background(FloconTheme.colorPalette.surface) - .padding(vertical = 8.dp, horizontal = 12.dp), + .padding(vertical = FloconTheme.spacing.small, horizontal = FloconTheme.spacing.medium), verticalAlignment = Alignment.CenterVertically, ) { Title() @@ -75,7 +75,7 @@ private fun Title( ) { Row( modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically, ) { Image( diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/TopBarDeviceAndAppView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/TopBarDeviceAndAppView.kt index 5bcaa0bf0..c4dfc316a 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/TopBarDeviceAndAppView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/TopBarDeviceAndAppView.kt @@ -17,6 +17,7 @@ import io.github.openflocon.flocondesktop.app.ui.model.DeviceItemUiModel import io.github.openflocon.flocondesktop.app.ui.model.DevicesStateUiModel import io.github.openflocon.flocondesktop.app.ui.view.topbar.app.TopBarAppDropdown import io.github.openflocon.flocondesktop.app.ui.view.topbar.device.TopBarDeviceDropdown +import io.github.openflocon.library.designsystem.FloconTheme @Composable internal fun TopBarDeviceAndAppView( @@ -30,7 +31,7 @@ internal fun TopBarDeviceAndAppView( ) { Row( modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically, ) { TopBarDeviceDropdown( diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/actions/TopBarActions.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/actions/TopBarActions.kt index aff056108..50a4430fb 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/actions/TopBarActions.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/actions/TopBarActions.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.openflocon.flocondesktop.app.ui.model.DevicesStateUiModel import io.github.openflocon.flocondesktop.app.ui.model.RecordVideoStateUiModel +import io.github.openflocon.library.designsystem.FloconTheme @Composable internal fun TopBarActions( @@ -26,7 +27,7 @@ internal fun TopBarActions( Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.extraSmall) ) { TopBarButton( active = false, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/app/TopBarAppView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/app/TopBarAppView.kt index 6c9f5eb22..0dc599573 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/app/TopBarAppView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/app/TopBarAppView.kt @@ -46,8 +46,8 @@ internal fun TopBarAppView( deleteClick: (() -> Unit)? = null, ) { Row( - modifier = modifier.padding(horizontal = 8.dp, vertical = 4.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier.padding(horizontal = FloconTheme.spacing.small, vertical = FloconTheme.spacing.extraSmall), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically, ) { AppImage( diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/device/TopBarDeviceView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/device/TopBarDeviceView.kt index 0af13fe71..c1aff68c6 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/device/TopBarDeviceView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/app/ui/view/topbar/device/TopBarDeviceView.kt @@ -51,8 +51,8 @@ internal fun TopBarDeviceView( deleteClick: (() -> Unit)? = null, ) { Row( - modifier = modifier.padding(horizontal = 8.dp, vertical = 4.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier.padding(horizontal = FloconTheme.spacing.small, vertical = FloconTheme.spacing.extraSmall), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically, ) { Box( diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt index b28f5c9f0..3c63480f9 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseTabViewModel.kt @@ -59,7 +59,7 @@ class DatabaseTabViewModel( var query = mutableStateOf("") val lastQueries = observeLastSuccessQueriesUseCase(params.databaseId) - .map { it.filterNot { it.isBlank() } } + .map { it.filterNot(String::isBlank) } .flowOn(dispatcherProvider.data) .stateIn( viewModelScope, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseScreen.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseScreen.kt index 4fb5cadc1..a502d248f 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseScreen.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseScreen.kt @@ -21,6 +21,7 @@ import io.github.openflocon.flocondesktop.features.database.model.DatabaseScreen import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabState import io.github.openflocon.flocondesktop.features.database.model.DatabasesStateUiModel import io.github.openflocon.flocondesktop.features.database.view.databases_tables.DatabasesAndTablesView +import io.github.openflocon.library.designsystem.FloconTheme import io.github.openflocon.library.designsystem.components.FloconFeature import org.koin.compose.viewmodel.koinViewModel @@ -70,11 +71,11 @@ fun DatabaseScreen( favorites = favorites, onAction = onAction ) - Spacer(modifier = Modifier.width(12.dp)) + Spacer(modifier = Modifier.width(FloconTheme.spacing.medium)) Column(modifier = Modifier.fillMaxWidth()) { DatabaseTabsView( - modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp), + modifier = Modifier.fillMaxWidth().padding(horizontal = FloconTheme.spacing.medium), tabs = tabs, selected = selectedTab, onAction = { diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt index e835b9b0b..270602c02 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt @@ -113,7 +113,7 @@ private fun DatabaseView( }, onDoubleClick = { onDatabaseDoubleClicked(state) } - ).padding(horizontal = 12.dp, vertical = 8.dp), + ).padding(horizontal = FloconTheme.spacing.medium, vertical = FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically, ) { Image( @@ -122,7 +122,7 @@ private fun DatabaseView( contentDescription = null, colorFilter = ColorFilter.tint(textColor), ) - Spacer(modifier = Modifier.width(4.dp)) + Spacer(modifier = Modifier.width(FloconTheme.spacing.extraSmall)) Text( state.name, color = textColor, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt index dbbda17a4..13d9a7a6b 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt @@ -51,8 +51,8 @@ fun DatabasesAndTablesView( Modifier.fillMaxWidth() .weight(1f) .verticalScroll(rememberScrollState()) - .padding(all = 4.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) + .padding(all = FloconTheme.spacing.extraSmall), + verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.extraSmall) ) { Text( "Databases", @@ -62,7 +62,7 @@ fun DatabasesAndTablesView( ), maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp) + modifier = Modifier.padding(horizontal = FloconTheme.spacing.medium, vertical = FloconTheme.spacing.small) ) when (state) { DatabasesStateUiModel.Empty -> Unit @@ -104,8 +104,8 @@ fun DatabasesAndTablesView( Modifier.fillMaxWidth() .weight(0.4f) .verticalScroll(rememberScrollState()) - .padding(all = 4.dp), - verticalArrangement = Arrangement.spacedBy(4.dp) + .padding(all = FloconTheme.spacing.extraSmall), + verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.extraSmall) ) { Text( stringResource(Res.string.databases_favorites), @@ -115,7 +115,7 @@ fun DatabasesAndTablesView( ), maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp) + modifier = Modifier.padding(horizontal = FloconTheme.spacing.medium, vertical = FloconTheme.spacing.small) ) favorites.forEach { diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/TableItemView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/TableItemView.kt index f4fa41006..b7f65b910 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/TableItemView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/TableItemView.kt @@ -99,10 +99,10 @@ private fun TableView( onTableDoubleClicked(item) } ) - .padding(horizontal = 12.dp) - .padding(vertical = 4.dp), + .padding(horizontal = FloconTheme.spacing.medium) + .padding(vertical = FloconTheme.spacing.extraSmall), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.extraSmall), ) { val color = FloconTheme.colorPalette.onSurface Image( diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkLabelView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkLabelView.kt index 90aafc693..5ec8bbc32 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkLabelView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkLabelView.kt @@ -12,7 +12,7 @@ import io.github.openflocon.library.designsystem.FloconTheme internal fun MockNetworkLabelView(label: String) { Text( label, - modifier = Modifier.padding(start = 4.dp), + modifier = Modifier.padding(start = FloconTheme.spacing.extraSmall), color = FloconTheme.colorPalette.onSurface, style = FloconTheme.typography.bodyMedium.copy( fontWeight = FontWeight.Thin, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkMethodDropdown.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkMethodDropdown.kt index c51909a30..4b9b1cea5 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkMethodDropdown.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/MockNetworkMethodDropdown.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.openflocon.flocondesktop.features.network.mock.edition.model.MockNetworkMethodUi +import io.github.openflocon.library.designsystem.FloconTheme import io.github.openflocon.library.designsystem.components.FloconDropdownMenu @Composable @@ -23,7 +24,7 @@ fun MockNetworkMethodDropdown( ) { var expanded by remember { mutableStateOf(false) } - Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) { + Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.extraSmall)) { MockNetworkLabelView(label) Box { @@ -39,7 +40,7 @@ fun MockNetworkMethodDropdown( ) { MockNetworkMethodUi.entries.forEach { method -> MockNetworkMethodView( - modifier = Modifier.padding(all = 4.dp), + modifier = Modifier.padding(all = FloconTheme.spacing.extraSmall), method = method, onClick = { onValueChange(method) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/NetworkEditionWindow.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/NetworkEditionWindow.kt index ef01e24f6..887c5413f 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/NetworkEditionWindow.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/edition/view/NetworkEditionWindow.kt @@ -117,7 +117,7 @@ fun MockEditorScreen( Column( modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), ) { FloconDialogHeader( modifier = Modifier @@ -127,7 +127,7 @@ fun MockEditorScreen( error?.let { Text( - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier.padding(horizontal = FloconTheme.spacing.large), text = it, color = MaterialTheme.colorScheme.error, ) } @@ -140,8 +140,8 @@ fun MockEditorScreen( Column( modifier = Modifier .weight(2f) - .padding(horizontal = 16.dp, vertical = 8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), + .padding(horizontal = FloconTheme.spacing.large, vertical = FloconTheme.spacing.small), + verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), ) { // Section Expectation Text( @@ -182,8 +182,8 @@ fun MockEditorScreen( Column( modifier = Modifier .weight(3f) - .padding(horizontal = 16.dp, vertical = 8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), + .padding(horizontal = FloconTheme.spacing.large, vertical = FloconTheme.spacing.small), + verticalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), ) { // Section Response Text( @@ -287,7 +287,7 @@ fun MockEditorScreen( FlowRow( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), ) { NetworkMockMediaType( text = "application/json", @@ -424,7 +424,7 @@ fun MockEditorScreen( color = FloconTheme.colorPalette.primary, shape = FloconTheme.shapes.medium, ) - .padding(vertical = 4.dp, horizontal = 8.dp), + .padding(vertical = FloconTheme.spacing.extraSmall, horizontal = FloconTheme.spacing.small), ) { val throwable = jsonError if(throwable == null) { @@ -447,8 +447,8 @@ fun MockEditorScreen( } } Row( - modifier = Modifier.fillMaxWidth().padding(all = 8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth().padding(all = FloconTheme.spacing.small), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically ) { FloconCheckbox( @@ -489,7 +489,7 @@ fun NetworkMockMediaType(text: String, onClicked: (text: String) -> Unit) { .clickable { onClicked(text) } - .padding(horizontal = 12.dp, vertical = 2.dp), + .padding(horizontal = FloconTheme.spacing.medium, vertical = 2.dp), ) } @@ -504,7 +504,7 @@ private fun HeaderInputField( Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(FloconTheme.spacing.small), ) { FloconTextField( value = key, @@ -527,7 +527,7 @@ private fun HeaderInputField( .clip(RoundedCornerShape(2.dp)) .clickable { onRemove() - }.padding(all = 4.dp), + }.padding(all = FloconTheme.spacing.extraSmall), contentAlignment = Alignment.Center, ) { Image( diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/list/view/MockLineView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/list/view/MockLineView.kt index da97c77c5..137c53ba3 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/list/view/MockLineView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/mock/list/view/MockLineView.kt @@ -40,12 +40,12 @@ fun MockLineView( Row( modifier = modifier .clickable { onClicked(item.id) } - .padding(vertical = 8.dp), + .padding(vertical = FloconTheme.spacing.small), verticalAlignment = Alignment.CenterVertically, ) { Box( modifier = Modifier - .padding(horizontal = 8.dp) + .padding(horizontal = FloconTheme.spacing.small) .width(50.dp) .height(12.dp), contentAlignment = Alignment.Center, @@ -83,7 +83,7 @@ fun MockLineView( style = FloconTheme.typography.titleSmall, color = FloconTheme.colorPalette.onSurface, ) - Spacer(Modifier.height(8.dp)) + Spacer(Modifier.height(FloconTheme.spacing.small)) Text( text = item.urlPattern, maxLines = 1, @@ -96,7 +96,7 @@ fun MockLineView( Row( modifier = Modifier - .padding(horizontal = 8.dp) + .padding(horizontal = FloconTheme.spacing.small) .height(12.dp) ) { FloconCheckbox( diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/deeplink/repository/DeeplinkRepositoryImpl.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/deeplink/repository/DeeplinkRepositoryImpl.kt index 8e8bc6d0b..ed63264f7 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/deeplink/repository/DeeplinkRepositoryImpl.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/deeplink/repository/DeeplinkRepositoryImpl.kt @@ -31,8 +31,6 @@ class DeeplinkRepositoryImpl( Protocol.FromDevice.Deeplink.Method.GetDeeplinks -> { val deeplinks = remote.getItems(message) ?: return - println(deeplinks.toString()) - localDeeplinkDataSource.update( deviceIdAndPackageNameDomainModel = deviceIdAndPackageName, deeplinks = deeplinks diff --git a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/FloconTheme.kt b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/FloconTheme.kt index c61554029..0ce18208b 100644 --- a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/FloconTheme.kt +++ b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/FloconTheme.kt @@ -23,8 +23,10 @@ import androidx.compose.ui.unit.sp import io.github.openflocon.library.designsystem.components.FloconMenuRepresentation import io.github.openflocon.library.designsystem.theme.FloconColorPaletteNew import io.github.openflocon.library.designsystem.theme.FloconShape +import io.github.openflocon.library.designsystem.theme.FloconSpacing import io.github.openflocon.library.designsystem.theme.LocalFloconColorPalette import io.github.openflocon.library.designsystem.theme.LocalFloconShape +import io.github.openflocon.library.designsystem.theme.LocalFloconSpacing import io.github.openflocon.library.designsystem.theme.darkPalette import io.github.openflocon.library.designsystem.theme.lightPalette import io.github.openflocon.library.designsystem.theme.materialDarkScheme @@ -44,6 +46,10 @@ object FloconTheme { val shapes: FloconShape @Composable @ReadOnlyComposable get() = LocalFloconShape.current + + val spacing: FloconSpacing + @Composable @ReadOnlyComposable + get() = LocalFloconSpacing.current } @Composable @@ -92,6 +98,7 @@ fun FloconTheme( CompositionLocalProvider( LocalIndication provides ripple, LocalFloconColorPalette provides colorPalette, + LocalFloconSpacing provides FloconSpacing(), LocalTextSelectionColors provides selectionTextColor, LocalScrollbarStyle provides scrollbarStyle, LocalMinimumInteractiveComponentSize provides Dp.Unspecified, diff --git a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/theme/FloconSpacing.kt b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/theme/FloconSpacing.kt new file mode 100644 index 000000000..12cc58455 --- /dev/null +++ b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/theme/FloconSpacing.kt @@ -0,0 +1,21 @@ +package io.github.openflocon.library.designsystem.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +@Immutable +data class FloconSpacing( + val none: Dp = 0.dp, + val extraSmall: Dp = 4.dp, + val small: Dp = 8.dp, + val medium: Dp = 12.dp, + val large: Dp = 16.dp, + val extraLarge: Dp = 24.dp, + val doubleExtraLarge: Dp = 32.dp, + val tripleExtraLarge: Dp = 48.dp, + val huge: Dp = 64.dp +) + +val LocalFloconSpacing = staticCompositionLocalOf { FloconSpacing() }