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 76% rename from FloconAndroid/okhttp-interceptor-no-op/build.gradle.kts rename to FloconAndroid/analytics-no-op/build.gradle.kts index 897fd4fc9..47aae9f7c 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 { + compilerOptions { + jvmTarget = JvmTarget.JVM_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..958849e6f 100644 --- a/FloconAndroid/build.gradle.kts +++ b/FloconAndroid/build.gradle.kts @@ -7,5 +7,6 @@ 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 + alias(libs.plugins.buildconfig) 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..4a7c416ea --- /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 { + outputModuleName = "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..d56b0797e --- /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 { + outputModuleName = "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..1df9411b7 --- /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.sqlite.bundled) + } + } + + val androidMain by getting { + dependencies { + implementation(libs.kotlinx.coroutines.android) + implementation(libs.androidx.room.sqlite.wrapper) + } + } + + 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/iosMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.ios.kt b/FloconAndroid/database/room/src/iosMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.ios.kt new file mode 100644 index 000000000..c9c874bdb --- /dev/null +++ b/FloconAndroid/database/room/src/iosMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.ios.kt @@ -0,0 +1,20 @@ +package io.github.openflocon.flocon.database.room + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +internal actual class FloconRoomDatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoomDatabaseProvider { + + actual override fun register() { + // no op on iOS + } + + @OptIn(FloconMarker::class) + actual override fun getAllDataBases(registeredDatabases: List): List { + return emptyList() + } +} diff --git a/FloconAndroid/database/room/src/jvmMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.jvm.kt b/FloconAndroid/database/room/src/jvmMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.jvm.kt new file mode 100644 index 000000000..d81f8c8b1 --- /dev/null +++ b/FloconAndroid/database/room/src/jvmMain/kotlin/io/github/openflocon/flocon/database/room/FloconRoomDatabaseProviderImpl.jvm.kt @@ -0,0 +1,20 @@ +package io.github.openflocon.flocon.database.room + +import io.github.openflocon.flocon.FloconContext +import io.github.openflocon.flocon.database.core.model.FloconDatabaseModel +import io.github.openflocon.flocon.dsl.FloconMarker + +internal actual class FloconRoomDatabaseProviderImpl actual constructor( + private val context: FloconContext, + paths: List +) : FloconRoomDatabaseProvider { + + actual override fun register() { + // no op on JVM + } + + @OptIn(FloconMarker::class) + actual override fun getAllDataBases(registeredDatabases: List): List { + return emptyList() + } +} 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..7c444a640 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 { + outputModuleName = "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..3d72bb14e 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 { + outputModuleName = "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..d562638b1 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 { + outputModuleName = "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..1973ff515 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 { + outputModuleName = "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..8bab10320 --- /dev/null +++ b/FloconAndroid/deeplinks-no-op/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + wasmJs { + outputModuleName = "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..66f14cffc --- /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 { + outputModuleName = "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..290417b16 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 { + outputModuleName = "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..518ea7daf 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 { + outputModuleName = "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,19 @@ 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" - ) + sourceSets { + getByName("main") { + manifest.srcFile("src/androidMain/AndroidManifest.xml") + res.srcDirs("src/androidMain/res") } } - 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 deleted file mode 100644 index 73cb6ac63..000000000 --- a/FloconAndroid/flocon/src/iosMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.ios.kt +++ /dev/null @@ -1,128 +0,0 @@ -package io.github.openflocon.flocon.plugins.database - -import androidx.sqlite.SQLiteConnection -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 - -internal actual fun buildFloconDatabaseDataSource(context: FloconContext): FloconDatabaseDataSource { - return FloconDatabaseDataSourceIos(context) -} - -internal class FloconDatabaseDataSourceIos( - private val context: FloconContext -) : FloconDatabaseDataSource { - - override fun executeSQL( - registeredDatabases: List, - databaseName: String, - query: String - ): DatabaseExecuteSqlResponse { - val fileManager = NSFileManager.defaultManager - if (!fileManager.fileExistsAtPath(databaseName)) { - return DatabaseExecuteSqlResponse.Error( - message = "Database file not found: $databaseName", - originalSql = query - ) - } - - val driver = NativeSQLiteDriver() - val connection = driver.open(fileName = databaseName) - - return try { - val firstWord = getFirstWord(query).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() - } - } - - override fun getAllDataBases( - registeredDatabases: List - ): List { - val fileManager = NSFileManager.defaultManager - return registeredDatabases.mapNotNull { - if(it is FloconFileDatabaseModel) { - if (fileManager.fileExistsAtPath(it.absolutePath)) { - DeviceDataBaseDataModel( - id = it.absolutePath, - name = it.displayName - ) - } else null - } else null - } - } -} - -// --- SQL execution helpers --- - -private fun executeSelect(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { - val cursor = 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 -> - statement.getText(idx) - } - rows.add(row) - } - - statement.close() // maybe remove - DatabaseExecuteSqlResponse.Select(columns, rows) - } - return cursor -} - -private fun executeUpdateDelete(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { - connection.prepare(query).use { statement -> - statement.close() - } - // sqlite-kt n'expose pas encore `changes()`, on renvoie 0 - return DatabaseExecuteSqlResponse.UpdateDelete(affectedCount = 0) -} - -private fun executeInsert(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { - connection.prepare(query).use { statement -> - statement.close() - } - - // Récupération du dernier ID inséré - var id = -1L - connection.prepare("SELECT last_insert_rowid()").use { - id = if (it.step()) it.getLong(0) else -1L - it.close() // maybe remove - } - - return DatabaseExecuteSqlResponse.Insert(id) -} - -private fun executeRawQuery(connection: SQLiteConnection, query: String): DatabaseExecuteSqlResponse { - connection.prepare(query).use { statement -> - statement.close() // maybe remove - } - return DatabaseExecuteSqlResponse.RawSuccess -} - -// --- Utilities --- -private fun getFirstWord(s: String): String { - val trimmed = s.trim() - val firstSpace = trimmed.indexOf(' ') - return if (firstSpace >= 0) trimmed.substring(0, firstSpace) else trimmed -} 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 deleted file mode 100644 index 01708953c..000000000 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.jvm.kt +++ /dev/null @@ -1,130 +0,0 @@ -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 -import java.sql.DriverManager -import java.sql.ResultSet -import java.sql.Statement -import java.util.Locale - -internal actual fun buildFloconDatabaseDataSource(context: FloconContext): FloconDatabaseDataSource { - return FloconDatabaseDataSourceJvm(context) -} - -internal class FloconDatabaseDataSourceJvm( - private val context: FloconContext -) : FloconDatabaseDataSource { - - override fun executeSQL( - registeredDatabases: List, - databaseName: String, - query: String - ): DatabaseExecuteSqlResponse { - var connection: Connection? = null - return try { - val dbFile = File(databaseName) - if (!dbFile.exists()) { - return DatabaseExecuteSqlResponse.Error( - message = "Database file not found: ${dbFile.absolutePath}", - originalSql = query - ) - } - - connection = DriverManager.getConnection("jdbc:sqlite:${dbFile.absolutePath}") - val firstWord = getFirstWord(query).uppercase(Locale.getDefault()) - - when (firstWord) { - "UPDATE", "DELETE" -> executeUpdateDelete(connection, query) - "INSERT" -> executeInsert(connection, query) - "SELECT", "PRAGMA", "EXPLAIN" -> executeSelect(connection, query) - else -> executeRawQuery(connection, query) - } - } catch (t: Throwable) { - DatabaseExecuteSqlResponse.Error( - message = t.message ?: "Error executing SQL", - originalSql = query, - ) - } finally { - connection?.close() - } - } - - override fun getAllDataBases( - registeredDatabases: List - ): List { - return registeredDatabases.mapNotNull { - if(it is FloconFileDatabaseModel) { - if (File(it.absolutePath).exists()) { - DeviceDataBaseDataModel( - id = it.absolutePath, - name = it.displayName, - ) - } else null - } else null - } - - } -} - -// --- SQL execution helpers --- - -private fun executeSelect(connection: Connection, query: String): DatabaseExecuteSqlResponse { - connection.createStatement().use { statement -> - val resultSet = statement.executeQuery(query) - val columns = getColumnNames(resultSet) - val rows = resultSetToList(resultSet, columns.size) - return DatabaseExecuteSqlResponse.Select(columns, rows) - } -} - -private fun executeUpdateDelete(connection: Connection, query: String): DatabaseExecuteSqlResponse { - connection.createStatement().use { statement -> - val count = statement.executeUpdate(query) - return DatabaseExecuteSqlResponse.UpdateDelete(count) - } -} - -private fun executeInsert(connection: Connection, query: String): DatabaseExecuteSqlResponse { - connection.createStatement().use { statement -> - statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS) - val keys = statement.generatedKeys - val id = if (keys.next()) keys.getLong(1) else -1L - return DatabaseExecuteSqlResponse.Insert(id) - } -} - -private fun executeRawQuery(connection: Connection, query: String): DatabaseExecuteSqlResponse { - connection.createStatement().use { statement -> - statement.execute(query) - return DatabaseExecuteSqlResponse.RawSuccess - } -} - -// --- Utilities --- - -private fun getFirstWord(s: String): String { - val trimmed = s.trim() - val firstSpace = trimmed.indexOf(' ') - return if (firstSpace >= 0) trimmed.substring(0, firstSpace) else trimmed -} - -private fun getColumnNames(rs: ResultSet): List { - val meta = rs.metaData - return (1..meta.columnCount).map { meta.getColumnName(it) } -} - -private fun resultSetToList(rs: ResultSet, columnCount: Int): List> { - val rows = mutableListOf>() - while (rs.next()) { - val row = (1..columnCount).map { idx -> - rs.getObject(idx)?.toString() - } - rows.add(row) - } - return rows -} \ No newline at end of file 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/jvmMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.jvm.kt b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.jvm.kt index fad472402..faa603336 100644 --- a/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.jvm.kt +++ b/FloconAndroid/flocon/src/jvmMain/kotlin/io/github/openflocon/flocon/websocket/FloconHttpClient.jvm.kt @@ -20,6 +20,7 @@ internal actual fun buildFloconHttpClient(): FloconHttpClient { return FloconHttpClientJvm() } +@OptIn(io.github.openflocon.flocon.dsl.FloconMarker::class) internal class FloconHttpClientJvm() : FloconHttpClient { // client configurable selon la plateforme (Android, iOS, JVM, etc.) 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.properties b/FloconAndroid/gradle.properties index 412892d80..bf962b8ef 100644 --- a/FloconAndroid/gradle.properties +++ b/FloconAndroid/gradle.properties @@ -30,4 +30,18 @@ floconDescription=A powerful desktop companion for Android apps, inspect, debug, # Kotlin Multiplatform kotlin.mpp.androidGradlePluginCompatibility.nowarn=true kotlin.mpp.applyDefaultHierarchyTemplate=false -kotlin.mpp.enableCInteropCommonization=true \ No newline at end of file +kotlin.mpp.enableCInteropCommonization=true + +kotlin.native.ignoreDisabledTargets=true + +android.defaults.buildfeatures.resvalues=true +android.sdk.defaultTargetSdkToCompileSdkIfUnset=false +android.enableAppCompileTimeRClass=false +android.usesSdkInManifest.disallowed=false +android.uniquePackageNames=false +android.dependency.useConstraints=true +android.r8.strictFullModeForKeepRules=false +android.r8.optimizedResourceShrinking=false +android.builtInKotlin=false +android.newDsl=false + diff --git a/FloconAndroid/gradle/gradle-daemon-jvm.properties b/FloconAndroid/gradle/gradle-daemon-jvm.properties new file mode 100644 index 000000000..baa28d154 --- /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/491f83666ae7f4d6ebb28fee72ebb035/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/0d1a1acdc708062093673f65aa9aba4b/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/491f83666ae7f4d6ebb28fee72ebb035/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/0d1a1acdc708062093673f65aa9aba4b/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7083b89563e7ce20943037b8cd2b8cc2/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/060bbb778a1f55ea705fdebd2ccfeab9/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/491f83666ae7f4d6ebb28fee72ebb035/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/0d1a1acdc708062093673f65aa9aba4b/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d09679dc60fe5aa05ef7d03efdefac20/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/ed4e3bf2f5e7c5d9aabc4cbd8acd555e/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/FloconAndroid/gradle/libs.versions.toml b/FloconAndroid/gradle/libs.versions.toml index 32ea39e80..6f88485d5 100644 --- a/FloconAndroid/gradle/libs.versions.toml +++ b/FloconAndroid/gradle/libs.versions.toml @@ -1,44 +1,45 @@ [versions] -agp = "8.11.0-rc02" +agp = "9.2.1" apollo = "4.0.0" coilCompose = "3.2.0" compose = "1.9.0" datastorePreferences = "1.1.7" -kotlin = "2.1.0" +kotlin = "2.2.10" 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.3.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" +buildconfig = "6.0.10" 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/gradle/wrapper/gradle-wrapper.properties b/FloconAndroid/gradle/wrapper/gradle-wrapper.properties index 80987f0cc..a9fec00cf 100644 --- a/FloconAndroid/gradle/wrapper/gradle-wrapper.properties +++ b/FloconAndroid/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Jul 04 14:05:14 CEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists 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..38c893c1b --- /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 { + outputModuleName = "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..dbb4a631f --- /dev/null +++ b/FloconAndroid/network/core/build.gradle.kts @@ -0,0 +1,60 @@ +@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) +@file:Suppress("DEPRECATION") + +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") + alias(libs.plugins.kotlin.serialization) +} + +kotlin { + wasmJs { + outputModuleName.set("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..e375bdfc7 --- /dev/null +++ b/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.ios.kt @@ -0,0 +1,9 @@ +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 = FloconNetworkDataSourceImpl(encoder = encoder) \ 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..cb411ccee --- /dev/null +++ b/FloconAndroid/network/core/src/iosMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSourceImpl.kt @@ -0,0 +1,27 @@ +package io.github.openflocon.flocon.network.core.datasource + +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 class FloconNetworkDataSourceImpl( + private val encoder: FloconEncoder +) : 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..e375bdfc7 --- /dev/null +++ b/FloconAndroid/network/core/src/jvmMain/kotlin/io/github/openflocon/flocon/network/core/datasource/FloconNetworkDataSource.jvm.kt @@ -0,0 +1,9 @@ +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 = FloconNetworkDataSourceImpl(encoder = encoder) \ 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 69% 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..2e175f38d 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.core.FloconEncoder +import io.github.openflocon.flocon.core.decode +import io.github.openflocon.flocon.core.encode +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( + private val encoder: FloconEncoder ) : FloconNetworkDataSource { private val baseDir: File = File(System.getProperty("user.home"), ".flocon") @@ -30,7 +27,7 @@ internal class FloconNetworkDataSourceJvm( override fun saveMocksToFile(mocks: List) { try { val file = File(baseDir, FLOCON_NETWORK_MOCKS_JSON) - val jsonString = writeMockResponsesToJson(mocks) + val jsonString = encoder.encode(mocks) FileOutputStream(file).use { it.write(jsonString.toByteArray(Charsets.UTF_8)) } @@ -49,7 +46,7 @@ internal class FloconNetworkDataSourceJvm( val jsonString = FileInputStream(file).use { it.readBytes().toString(Charsets.UTF_8) } - parseMockResponses(jsonString) + encoder.decode>(jsonString).orEmpty() } catch (t: Throwable) { FloconLogger.logError("issue in loadMocksFromFile", t) emptyList() @@ -66,7 +63,7 @@ internal class FloconNetworkDataSourceJvm( val jsonString = FileInputStream(file).use { it.readBytes().toString(Charsets.UTF_8) } - parseBadQualityConfig(jsonString) + encoder.decode(jsonString) } catch (t: Throwable) { FloconLogger.logError("issue in loadBadNetworkConfig", t) null @@ -79,7 +76,7 @@ internal class FloconNetworkDataSourceJvm( if (config == null) { file.delete() } else { - val jsonString = config.toJsonString() + val jsonString = encoder.encode(config) FileOutputStream(file).use { it.write(jsonString.toByteArray(Charsets.UTF_8)) } 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..691b4b8aa --- /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 { + outputModuleName = "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..52643ead3 --- /dev/null +++ b/FloconAndroid/network/ktor-interceptor/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + id("flocon.kotlin.multiplatform") + id("flocon.publish") +} + +kotlin { + wasmJs { + outputModuleName = "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/.gitignore b/FloconAndroid/sample-android-only/.gitignore deleted file mode 100644 index 42afabfd2..000000000 --- a/FloconAndroid/sample-android-only/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/build.gradle.kts b/FloconAndroid/sample-android-only/build.gradle.kts deleted file mode 100644 index 562891197..000000000 --- a/FloconAndroid/sample-android-only/build.gradle.kts +++ /dev/null @@ -1,196 +0,0 @@ -import com.google.protobuf.gradle.id -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.kotlin.compose) - alias(libs.plugins.ksp) - alias(libs.plugins.apollo) - id("com.google.protobuf") -} - -android { - namespace = "io.github.openflocon.flocon.myapplication" - compileSdk = 36 - - defaultConfig { - applicationId = "io.github.openflocon.flocon.myapplication" - minSdk = 23 - targetSdk = 36 - versionCode = 1 - versionName = "1.0" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - val githubToken = System.getenv("GITHUB_TOKEN_GRPC") ?: "" - - signingConfigs { - named("debug") { - // just a dummy keystore to be able to test the release build - keyAlias = "release" - keyPassword = "release" - storeFile = file("release.jks") - storePassword = "release" - } - register("release") { - keyAlias = "release" - keyPassword = "release" - storeFile = file("release.jks") - storePassword = "release" - } - } - - buildTypes { - debug { - buildConfigField("String", "GITHUB_TOKEN", "\"$githubToken\"") - signingConfig = signingConfigs.getByName("debug") - } - release { - isMinifyEnabled = true - buildConfigField("String", "GITHUB_TOKEN", "\"$githubToken\"") - signingConfig = signingConfigs.getByName("release") - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - buildFeatures { - compose = true - buildConfig = true - } -} - -kotlin { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) - } -} - -val useMaven = false -dependencies { - if(useMaven) { - val floconVersion = "1.4.0" - implementation("io.github.openflocon:flocon:$floconVersion") - //implementation("io.github.openflocon:flocon-no-op:$floconVersion") - implementation("io.github.openflocon:flocon-grpc-interceptor-lite:$floconVersion") - implementation("io.github.openflocon:flocon-okhttp-interceptor:$floconVersion") - //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")) - } - - - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.ui) - implementation(libs.androidx.ui.graphics) - implementation(libs.androidx.ui.tooling.preview) - implementation(libs.androidx.material3) - testImplementation(libs.junit) - debugImplementation(libs.androidx.ui.tooling) - debugImplementation(libs.androidx.ui.test.manifest) - - implementation(platform(libs.kotlinx.coroutines.bom)) - implementation(libs.kotlinx.coroutines.core) - implementation(libs.kotlinx.coroutines.android) - - // region okhttp - implementation(platform(libs.okhttp.bom)) - implementation(libs.okhttp) - // endregion - - // region grpc - implementation(libs.grpc.android) - implementation(libs.grpc.kotlin.stub) - implementation(libs.grpc.protobuf.lite) - implementation(libs.grpc.okhttp) - implementation(libs.protobuf.kotlin.lite) - // endregion - - // region coil - implementation(libs.coil.compose) - implementation(libs.coil.network.okhttp) - // endregion - - // region room - implementation(libs.androidx.room.runtime) - ksp(libs.androidx.room.compiler) - // endregion - - // region graphql - implementation(libs.apollo.runtime) - //implementation(libs.apollo.http.okhttprealization) - // endregion - - // region ktor - implementation(libs.ktor.client.cio) - implementation(libs.ktor.client.okhttp) - implementation(libs.ktor.client.core) - //endregion - - // region datastore - implementation(libs.androidx.datastore.preferences) - // endregion -} - -apollo { - service("github") { - packageName.set("com.github") - } -} - -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:3.25.1" - } - - 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" - - plugins { - id("java") { - artifact = protocGenJava - } - id("grpc") { - artifact = protocGenJava - } - id("grpckt") { - artifact = protocGenKotlin - } - } - - all().forEach { - it.plugins { - id("java") { - option("lite") - } - id("grpc") { - option("lite") - } - id("grpckt") { - option("lite") - } - } - it.builtins { - id("kotlin") { - option("lite") - } - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/proguard-rules.pro b/FloconAndroid/sample-android-only/proguard-rules.pro deleted file mode 100644 index 481bb4348..000000000 --- a/FloconAndroid/sample-android-only/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/src/main/AndroidManifest.xml b/FloconAndroid/sample-android-only/src/main/AndroidManifest.xml deleted file mode 100644 index 8131109c0..000000000 --- a/FloconAndroid/sample-android-only/src/main/AndroidManifest.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/ic_launcher-playstore.png b/FloconAndroid/sample-android-only/src/main/ic_launcher-playstore.png deleted file mode 100644 index 72d7b88d3..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/ic_launcher-playstore.png and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/DummyHttpKtorCaller.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/DummyHttpKtorCaller.kt deleted file mode 100644 index ed6b33039..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/DummyHttpKtorCaller.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.github.openflocon.flocon.myapplication - -import io.github.openflocon.flocon.ktor.FloconKtorPlugin -import io.ktor.client.HttpClient -import io.ktor.client.call.body -import io.ktor.client.engine.cio.CIO -import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.request.post -import io.ktor.client.request.setBody -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import java.net.Socket -import java.security.cert.X509Certificate -import javax.net.ssl.SSLEngine -import javax.net.ssl.X509ExtendedTrustManager - -object DummyHttpKtorCaller { - - val cioClient = HttpClient(CIO) { - install(FloconKtorPlugin) - - engine { - https { - // for CIO we need to disable the trust manager for jsonplaceholder - trustManager = object : X509ExtendedTrustManager() { - override fun checkClientTrusted(chain: Array?, authType: String?, socket: Socket?) {} - override fun checkServerTrusted(chain: Array?, authType: String?, socket: Socket?) {} - override fun checkClientTrusted(chain: Array?, authType: String?, engine: SSLEngine?) {} - override fun checkServerTrusted(chain: Array?, authType: String?, engine: SSLEngine?) {} - override fun getAcceptedIssuers(): Array = emptyArray() - override fun checkClientTrusted(chain: Array?, authType: String?) {} - override fun checkServerTrusted(chain: Array?, authType: String?) {} - } - } - } - } - - val okHttpClient = HttpClient(OkHttp) { - install(FloconKtorPlugin) { - //isImage = { - // it.responseContentType?.startsWith("image/") == true - //} - } - } - - fun call() { - GlobalScope.launch(Dispatchers.IO) { - try { - val response = okHttpClient.post("https://jsonplaceholder.typicode.com/posts") { - setBody("{ \"test\" : \"yes\" }") - } - - if (response.status.value in 200..299) { - val responseBody: String = response.body() - println("SUCCESS: $responseBody") - } else { - println("FAILURE: ${response.status}") - } - } catch (t: Throwable) { - println("ERROR: ${t.message}") - t.printStackTrace() - } - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 8f0737c2f..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt +++ /dev/null @@ -1,222 +0,0 @@ -@file:OptIn(ExperimentalUuidApi::class) - -package io.github.openflocon.flocon.myapplication - -import android.os.Bundle -import android.widget.Toast -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -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.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.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 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 - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - - intent.data?.let { - Toast.makeText(this, "opend with : $it", Toast.LENGTH_LONG).show() - } - - 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() - }*/ - )) - .build() - - initializeSharedPreferences(applicationContext) - initializeDatabases(context = applicationContext) - - FloconLogger.enabled = true - Flocon.initialize(this) - initializeDeeplinks() - inMemoryDb = initializeInMemoryDatabases(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) - - setContent { - MyApplicationTheme { - Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> - val scope = rememberCoroutineScope() - val context = LocalContext.current - - Column(Modifier.fillMaxSize().padding(innerPadding)) { - FlowRow( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp), - horizontalArrangement = Arrangement.spacedBy(6.dp), - verticalArrangement = Arrangement.spacedBy(6.dp), - ) { - Button( - onClick = { - dummyHttpCaller.call() - } - ) { - Text("okhttp test") - } - Button( - onClick = { - dummyHttpCaller.callGzip() - } - ) { - Text("okhttp gzip test") - } - Button( - onClick = { - GlobalScope.launch { - graphQlTester.fetchViewerInfo() - } - } - ) { - Text("graphql test") - } - Button( - onClick = { - GlobalScope.launch { - GrpcController.sayHello() - } - } - ) { - Text("grpc test") - } - Button( - onClick = { - DummyHttpKtorCaller.call() - } - ) { - Text("ktor test") - } - Button( - onClick = { - dummyWebsocketCaller.send(Uuid.random().toString()) - } - ) { - Text("websocket test") - } - Button( - onClick = { - throw Throwable("my custom crash") - } - ) { - Text("crash") - } - 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", - ) - } - ) { - 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", - ), - ) - } - ) { - Text("send analytics event") - } - Button( - onClick = { - scope.launch { - DogDatabase.getDatabase(context).dogDao().insertDog( - DogEntity( - id = System.currentTimeMillis(), - name = "Flocon", - breed = "Golden Retriever ${System.currentTimeMillis()}", - age = 6, - pictureUrl = "https://picsum.photos/501/500.jpg", - ) - ) - } - } - ) { - Text("Insert dog in DB") - } - } - - ImagesListView(modifier = Modifier.fillMaxSize()) - } - } - } - } - } -} \ 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 deleted file mode 100644 index 02777a5af..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/InitializeDashboard.kt +++ /dev/null @@ -1,194 +0,0 @@ -package io.github.openflocon.flocon.myapplication.dashboard - -import android.util.Log -import android.widget.Toast -import androidx.activity.ComponentActivity -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import androidx.lifecycle.lifecycleScope -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.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 - -fun initializeDashboard(activity: ComponentActivity) { - initializeDeviceFlow(activity) - activity.lifecycleScope.launch { - floconDashboard(id = "main") { - section(name = "User", userFlow) { user -> - text(label = "username", value = user.userName) - text(label = "fullName", value = user.fullName, color = Color.Red.toArgb()) - text(label = "user id", value = user.id) - label(label = "actions :") - button( - text = "Change User Id", - id = "changeUserId", - onClick = { - userFlow.update { it.copy(userName = "__flo__") } - } - ) - textField( - label = "Update Name", - placeHolder = "name", - id = "changeUserName", - value = user.fullName, - onSubmitted = { value -> - userFlow.update { it.copy(fullName = value) } - }) - } - section(name = "Device", deviceFlow) { device -> - text(label = "name", value = device?.name ?: "") - text(label = "androidVersion", value = device?.androidVersion ?: "") - text(label = "language", value = device?.language ?: "") - text(label = "width", value = device?.width?.toString() ?: "") - text(label = "height", value = device?.height?.toString() ?: "") - text(label = "density", value = device?.density?.toString() ?: "") - checkBox( - id = "darkTheme", - label = "darkTheme", - value = device?.darkTheme ?: false, - onUpdated = { newValue -> - deviceFlow.update { - it?.copy( - darkTheme = newValue, - ) - } - } - ) - } - section(name = "Tokens", tokensFlow) { tokens -> - text(label = "accessToken", value = tokens.accessToken) - text(label = "refreshToken", value = tokens.refreshToken) - text(label = "expiration", value = tokens.expiration) - button( - text = "Clear Access Token", - id = "clearAccessToken", - onClick = { - activity.lifecycleScope.launch(Dispatchers.Main) { - tokensFlow.update { - it.copy(accessToken = "") - } - Toast.makeText( - activity, - "cleaned access token", - Toast.LENGTH_LONG - ).show() - } - } - ) - } - } - } - activity.lifecycleScope.launch { - floconDashboard(id = "plainText") { - section(name = "Test") { - plainText( - label = "lorem", - value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ullamcorper elit. Pellentesque turpis ex, cursus cursus urna sed, iaculis sagittis nisl. Curabitur vehicula nunc eu metus rhoncus placerat. Vivamus at placerat ligula. Morbi ullamcorper cursus tellus, vitae molestie lorem sollicitudin euismod. Sed ullamcorper, risus vitae facilisis tempor, elit leo accumsan purus, ut ultricies augue erat et justo. Duis efficitur mauris eu finibus tincidunt. Aenean magna libero, auctor quis turpis et, viverra porta lorem. Ut tempus odio sit amet vestibulum condimentum. Donec et augue quis arcu blandit sodales. In laoreet odio id turpis ultricies, eu ornare dui blandit. Morbi hendrerit velit turpis, eget ornare ex consequat id. Nullam rhoncus, libero et sollicitudin tristique, risus ipsum luctus neque, ultricies ullamcorper felis metus non turpis. Nullam sed accumsan sem, at fermentum tortor." - ) - json( - label = "json", value = """ - { - "testData": { - "name": "John Doe", - "age": 30, - "isStudent": false, - "courses": [ - { - "title": "History I", - "credits": 3 - }, - { - "title": "Math II", - "credits": 4 - } - ], - "address": { - "street": "123 Main St", - "city": "Anytown", - "zipCode": "12345" - } - }, - "status": "success", - "message": "Test data loaded successfully." - } - """.trimIndent() - ) - } - } - } - - activity.lifecycleScope.launch { - floconDashboard(id = "form") { - form( - name = "Test form", - submitText = "Submit form test button", - onSubmitted = { values -> - values.forEach { (key, value) -> - Log.e("TAG", "$key - $value") - // input_1 - test - // checkbox_1 - true - activity.lifecycleScope.launch(Dispatchers.Main) { - Toast.makeText( - activity, - "submitted : $values", - Toast.LENGTH_LONG - ).show() - } - } - } - ) { - textField( - id = "input_1", - label = "Input field 1", - placeHolder = "placeholder", - value = "test" - ) - checkBox( - id = "checkbox_1", - label = "Checkbox 1", - value = true, - ) - } - } - } - activity.lifecycleScope.floconDashboard(id = "markdown") { - section(name = "Markdown") { - markdown( - label = "Release Note", - value = """ - # Release Note - - This is a **markdown** text. - - * [x] item 1 - * [ ] item 2 - - Hello `world` ! - """.trimIndent() - ) - } - } - activity.lifecycleScope.floconDashboard(id = "html") { - section(name = "HTML Section") { - html( - label = "HtmlTitle", - value = "

Title

Paragraph with bold text.

" - ) - } - } - -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/device/Device.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/device/Device.kt deleted file mode 100644 index 4c5b75293..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/device/Device.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.openflocon.flocon.myapplication.dashboard.device - -import android.app.Activity -import android.os.Build -import android.util.DisplayMetrics -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update - - -data class Device( - val name: String, - val androidVersion: String, - val language: String, - val width: Int, - val height: Int, - val density: Float, - val darkTheme: Boolean, -) - -val deviceFlow = MutableStateFlow(null) - -fun initializeDeviceFlow(activity: Activity) { - val language = activity.resources.configuration.locales.get(0).language - - val displayMetrics = DisplayMetrics() - activity.windowManager.defaultDisplay.getMetrics(displayMetrics) - - deviceFlow.update { - Device( - name = "${Build.BRAND} ${Build.MODEL}", - androidVersion = Build.VERSION.RELEASE, - language = language, - width = displayMetrics.widthPixels, - height = displayMetrics.heightPixels, - density = displayMetrics.density, - darkTheme = true, - ) - } -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/tokens/Tokens.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/tokens/Tokens.kt deleted file mode 100644 index 19e9972a2..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/tokens/Tokens.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.openflocon.flocon.myapplication.dashboard.tokens - -import kotlinx.coroutines.flow.MutableStateFlow - -data class Tokens( - val accessToken: String, - val refreshToken: String, - val expiration: String, -) - -val tokensFlow = MutableStateFlow( - Tokens( - accessToken = "1234567890", - refreshToken = "dndkjcncjzaksp", - expiration = "12:30:43" - ) -) diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/user/User.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/user/User.kt deleted file mode 100644 index 0de793ad8..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/dashboard/user/User.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.openflocon.flocon.myapplication.dashboard.user - -import kotlinx.coroutines.flow.MutableStateFlow - -data class User( - val id: String, - val userName: String, - val fullName: String, -) - -val userFlow = MutableStateFlow( - User( - id = "1234", - userName = "flo", - fullName = "Florent", - ) -) 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 deleted file mode 100644 index cce1719d3..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -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 = [ - DogEntity::class, - HumanEntity::class, - HumanWithDogEntity::class, - ], - version = 3, - exportSchema = false, -) -abstract class DogDatabase : RoomDatabase() { - abstract fun dogDao(): DogDao - - companion object { - @Volatile - private var INSTANCE: DogDatabase? = null - - fun getDatabase(context: Context): DogDatabase { - val dbName = "dogs_database" - return INSTANCE ?: synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - DogDatabase::class.java, - dbName - ).setQueryCallback({ sqlQuery, bindArgs -> floconLogDatabaseQuery( - dbName = dbName, sqlQuery = sqlQuery, bindArgs = bindArgs - ) }, Executors.newSingleThreadExecutor()) - .fallbackToDestructiveMigration() - .build() - INSTANCE = instance - instance - } - } - } -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/FoodDatabase.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/FoodDatabase.kt deleted file mode 100644 index 035c1b8de..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/FoodDatabase.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import io.github.openflocon.flocon.myapplication.database.dao.FoodDao -import io.github.openflocon.flocon.myapplication.database.model.FoodEntity - -@Database( - entities = [FoodEntity::class], - version = 1, - exportSchema = false, -) -abstract class FoodDatabase : RoomDatabase() { - abstract fun foodDao(): FoodDao - - companion object { - @Volatile - private var INSTANCE: FoodDatabase? = null - - fun getDatabase(context: Context): FoodDatabase { - return INSTANCE ?: synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - FoodDatabase::class.java, - "food_database" // Nom du fichier de la base de données FoodEntity - ).build() - INSTANCE = instance - instance - } - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 2afc24fb1..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/InitializeDatabases.kt +++ /dev/null @@ -1,141 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database - -import android.content.Context -import androidx.room.Room -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 - -fun initializeInMemoryDatabases(context: Context): DogDatabase { - return Room.inMemoryDatabaseBuilder( - context, - DogDatabase::class.java, - ).build().also { - floconRegisterDatabase( - displayName = "inmemory_dogs", - openHelper = it.openHelper - ) - } -} - -fun initializeDatabases(context: Context) { - val dogDatabase = DogDatabase.getDatabase(context) - val foodDatabase = FoodDatabase.getDatabase(context) - - GlobalScope.launch { - dogDatabase.dogDao().insertDog( - DogEntity( - id = 1, name = "Flocon", breed = "Golden Retriever", age = 6, - pictureUrl = "https://picsum.photos/501/500.jpg", - ) - ) - dogDatabase.dogDao().insertHuman( - HumanEntity( - id = 1, - firstName = "Florent", - name = "Champigny", - ) - ) - dogDatabase.dogDao().insertHuman( - HumanEntity( - id = 2, - firstName = "Camille", - name = "Champigny", - ) - ) - dogDatabase.dogDao().insertHumanWithDogEntity( - HumanWithDogEntity( - humanId = 1, // florent - dogId = 1, - ) - ) - dogDatabase.dogDao().insertHumanWithDogEntity( - HumanWithDogEntity( - humanId = 2, // camille - dogId = 1, - ) - ) - - dogDatabase.dogDao().insertDog( - DogEntity( - id = 2, name = "Vanille", breed = "Basset", age = 2, - pictureUrl = "https://picsum.photos/501/501.jpg", - ) - ) - dogDatabase.dogDao().insertHuman( - HumanEntity( - id = 3, - firstName = "Auguste", - name = "Dum", - ) - ) - dogDatabase.dogDao().insertHumanWithDogEntity( - HumanWithDogEntity( - humanId = 3, // auguste - dogId = 2, - ) - ) - - dogDatabase.dogDao().insertDog( - DogEntity( - id = 3, name = "Scoubi", breed = "Yorkshire", age = 2, - pictureUrl = "https://picsum.photos/501/502.jpg", - ) - ) - dogDatabase.dogDao().insertHuman( - HumanEntity( - id = 4, - firstName = "Jean", - name = "Paul", - ) - ) - dogDatabase.dogDao().insertHumanWithDogEntity( - HumanWithDogEntity( - humanId = 4, // auguste - dogId = 3, - ) - ) - - val longBreeds = listOf( - "Golden Retriever royal de la lignée légendaire des chiens des montagnes dorées du nord-ouest canadien, " + - "descendant direct des plus nobles compagnons de chasse de l’ère victorienne, connu pour sa loyauté infinie, " + - "sa douceur de caractère et sa passion inébranlable pour les flaques d’eau et les bâtons jetés dans les rivières.", - - "Basset des plaines méridionales au flair infaillible, issu d’une dynastie ancienne de chiens détectives spécialisés " + - "dans la recherche de friandises disparues, capable de renifler la moindre trace de biscuit à plusieurs kilomètres, " + - "tout en gardant un regard plein de mélancolie et de sagesse millénaire.", - - "Husky sibérien au pelage argenté, né pour courir dans les neiges éternelles et hurler à la lune les chants oubliés " + - "des aurores boréales. Sa résistance légendaire et son regard bleu perçant en font un symbole de liberté, " + - "d’endurance et d’amitié indestructible entre l’homme et le chien.", - - "Berger australien des terres rouges, gardien des troupeaux et des rêves, au regard vif et au cœur débordant d’énergie. " + - "Il danse entre les collines et les vents, tissant des liens invisibles entre la nature et l’esprit des vivants, " + - "incarnation du courage et de la curiosité sans limites." - ) - - for (i in 1..20) { - val randomBreed = longBreeds.random() - - dogDatabase.dogDao().insertDog( - DogEntity( - id = 10L + i, - name = "dog$i", - breed = randomBreed, - pictureUrl = "https://picsum.photos/500/50${i%10}.jpg", - age = (1..15).random() - ) - ) - } - - foodDatabase.foodDao().insertFood( - FoodEntity( - id = 1, name = "Cheese Burger", type = "Sandwitch", calories = 1234, - ) - ) - } -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/dao/DogDao.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/dao/DogDao.kt deleted file mode 100644 index 4905192ab..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/dao/DogDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Upsert -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 kotlinx.coroutines.flow.Flow - -@Dao -interface DogDao { - @Query("SELECT * FROM DogEntity") - fun getAllDogs(): Flow> - - @Upsert - suspend fun insertDog(dog: DogEntity) - - @Upsert - suspend fun insertHuman(human: HumanEntity) - - @Upsert - suspend fun insertHumanWithDogEntity(humanWithDog: HumanWithDogEntity) -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/dao/FoodDao.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/dao/FoodDao.kt deleted file mode 100644 index 71fd33707..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/dao/FoodDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import io.github.openflocon.flocon.myapplication.database.model.FoodEntity -import kotlinx.coroutines.flow.Flow - -// DAO pour la nourriture -@Dao -interface FoodDao { - @Query("SELECT * FROM FoodEntity") - fun getAllFoods(): Flow> - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertFood(food: FoodEntity) -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/DogEntity.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/DogEntity.kt deleted file mode 100644 index a6902dbe8..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/DogEntity.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database.model - -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity -data class DogEntity( - @PrimaryKey(autoGenerate = true) val id: Long = 0, - val name: String, - val breed: String, - val pictureUrl: String, - val age: Int, -) - - diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/FoodEntity.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/FoodEntity.kt deleted file mode 100644 index abd8c3a47..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/FoodEntity.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database.model - -import androidx.room.Entity -import androidx.room.PrimaryKey - -// Pour la base de données "FoodEntity" -@Entity -data class FoodEntity( - @PrimaryKey(autoGenerate = true) val id: Long = 0, - val name: String, - val type: String, // e.g., "fruit", "vegetable", "meat" - val calories: Int, -) \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/HumanEntity.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/HumanEntity.kt deleted file mode 100644 index e1f2afcd1..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/HumanEntity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database.model - -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity -data class HumanEntity( - @PrimaryKey(autoGenerate = true) val id: Long = 0, - val firstName: String, - val name: String, -) \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/HumanWithDogEntity.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/HumanWithDogEntity.kt deleted file mode 100644 index 6d117e798..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/model/HumanWithDogEntity.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.openflocon.flocon.myapplication.database.model - -import androidx.room.Entity -import androidx.room.ForeignKey - -@Entity( - primaryKeys = ["humanId", "dogId"], - foreignKeys = [ - ForeignKey( - entity = HumanEntity::class, - parentColumns = ["id"], - childColumns = ["humanId"], - onDelete = ForeignKey.CASCADE, - ), - ForeignKey( - entity = DogEntity::class, - parentColumns = ["id"], - childColumns = ["dogId"], - onDelete = ForeignKey.CASCADE, - ) - ] -) -data class HumanWithDogEntity( - val humanId: Long, - val dogId: Long, -) \ No newline at end of file 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/SharedPreferences.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/SharedPreferences.kt deleted file mode 100644 index 681d24d91..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/sharedpreferences/SharedPreferences.kt +++ /dev/null @@ -1,74 +0,0 @@ -@file:OptIn(ExperimentalUuidApi::class) - -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 -import kotlin.random.Random -import kotlin.uuid.ExperimentalUuidApi - -fun initializeSharedPreferencesAfterInit(context: Context) { - val referencedPref = context.getSharedPreferences("ref_pref", Context.MODE_PRIVATE) - floconRegisterPreference(FloconSharedPreference("my_custom_name", referencedPref)) - - referencedPref.edit { - putString("works", "yes") - } -} - -fun initializeSharedPreferences(context: Context) { - PreferenceManager.getDefaultSharedPreferences(context).edit { putFloat("flocon_validity", 42f) } - - context.getSharedPreferences("user_pref", Context.MODE_PRIVATE).apply { - edit { - putInt("age", 34) - putBoolean("isHuman", true) - putString("name", "flo") - } - } - - context.getSharedPreferences("settings_pref", Context.MODE_PRIVATE).apply { - edit { - putBoolean("isValid", true) - putString("settings_dummy_variable", "variable value") - putString("settings_dummy_variable_2", "variable value 2") - } - } - - context.getSharedPreferences("groups_pref", Context.MODE_PRIVATE).apply { - edit { - putString("group_one", generateUsersJson(3).toString(4)) - putString("group_two", generateUsersJson(5).toString(4)) - putString("group_three", generateUsersJson(10).toString(4)) - putString("group_four", generateUsersJson(2).toString(4)) - } - } -} - -private fun generateUsersJson(number: Int) : JSONArray { - val usersArray = JSONArray() - - for (i in 1..number) { - val randomUsername = "user_${Uuid.random().toString().substring(0, 8)}" - val randomEmail = "$randomUsername@example.com" - val isActive = Random.nextBoolean() - - val userObject = JSONObject() - userObject.put("id", i) - userObject.put("username", randomUsername) - userObject.put("email", randomEmail) - userObject.put("is_active", isActive) - - usersArray.put(userObject) - } - - return usersArray -} \ No newline at end of file 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/ui/ImagesListView.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/ImagesListView.kt deleted file mode 100644 index 84ac6eb8f..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/ImagesListView.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.openflocon.flocon.myapplication.ui - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage - -@Composable -fun ImagesListView(modifier: Modifier = Modifier) { - val images by remember { - mutableStateOf( - List(100) { i -> - "https://picsum.photos/id/$i/1000/1200" - } - ) - } - LazyColumn(modifier = modifier) { - items(images) { - AsyncImage( - model = it, - contentDescription = null, - modifier = Modifier - .fillMaxWidth() - .height(200.dp) - ) - } - } -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Color.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Color.kt deleted file mode 100644 index 40469f323..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.openflocon.flocon.myapplication.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Theme.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Theme.kt deleted file mode 100644 index 4be454eda..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Theme.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.openflocon.flocon.myapplication.ui.theme - -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 -) - -private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ -) - -@Composable -fun MyApplicationTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit -) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Type.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Type.kt deleted file mode 100644 index e79a5567f..000000000 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/ui/theme/Type.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.openflocon.flocon.myapplication.ui.theme - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/res/drawable/ic_launcher_background.xml b/FloconAndroid/sample-android-only/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index ca3826a46..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FloconAndroid/sample-android-only/src/main/res/drawable/ic_launcher_foreground.xml b/FloconAndroid/sample-android-only/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d114..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/FloconAndroid/sample-android-only/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 82cd4a2bf..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/FloconAndroid/sample-android-only/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 82cd4a2bf..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 913a32ff0..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index dfba028a5..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index 09a456c5d..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 3661e71e9..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index d2859f8dc..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index f6f9e3b3b..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index d0ec95d44..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index b069263aa..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 3a1ce0a2d..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 9cfa9820f..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 51464b860..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index cc06cbf55..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index 87523aa79..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 54af2be4b..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 3d45e88bc..000000000 Binary files a/FloconAndroid/sample-android-only/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/FloconAndroid/sample-android-only/src/main/res/values/colors.xml b/FloconAndroid/sample-android-only/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127d3..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/res/values/strings.xml b/FloconAndroid/sample-android-only/src/main/res/values/strings.xml deleted file mode 100644 index 7e4319641..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Flocon Sample - \ No newline at end of file diff --git a/FloconAndroid/sample-android-only/src/main/res/values/themes.xml b/FloconAndroid/sample-android-only/src/main/res/values/themes.xml deleted file mode 100644 index e48770ab8..000000000 --- a/FloconAndroid/sample-android-only/src/main/res/values/themes.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - -