├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── Constants.kt │ └── Functions.kt ├── example ├── androidApp │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── piasy │ │ │ └── kmp │ │ │ └── socketio │ │ │ └── android │ │ │ └── MainActivity.kt │ │ └── res │ │ └── values │ │ └── styles.xml ├── iosApp │ ├── Podfile │ ├── iosApp │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── Info.plist │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ └── iOSApp.swift │ └── project.yml └── shared │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── example │ │ └── Greeting.kt │ ├── jsMain │ ├── kotlin │ │ └── com │ │ │ └── piasy │ │ │ └── kmp │ │ │ └── socketio │ │ │ └── example │ │ │ └── Platform.kt │ └── resources │ │ └── index.html │ └── nativeMain │ └── kotlin │ └── com │ └── piasy │ └── kmp │ └── socketio │ └── example │ └── Platform.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kmp-socketio ├── build.gradle.kts └── src │ ├── appleMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── engineio │ │ └── transports │ │ └── transport_impl.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ ├── emitter │ │ └── Emitter.kt │ │ ├── engineio │ │ ├── EngineSocket.kt │ │ ├── On.kt │ │ ├── Transport.kt │ │ ├── defs.kt │ │ └── transports │ │ │ ├── PollingXHR.kt │ │ │ ├── WebSocket.kt │ │ │ └── transport.kt │ │ ├── global │ │ └── Global.kt │ │ ├── parseqs │ │ └── ParseQS.kt │ │ └── socketio │ │ ├── Ack.kt │ │ ├── Backoff.kt │ │ ├── BinaryPacketReconstructor.kt │ │ ├── IO.kt │ │ ├── Manager.kt │ │ └── Socket.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── socketio │ │ └── ConnectionTest.kt │ ├── jsMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── engineio │ │ └── transports │ │ └── transport_impl.kt │ ├── jsTest │ ├── kotlin │ │ └── com │ │ │ └── piasy │ │ │ └── kmp │ │ │ └── socketio │ │ │ └── socketio │ │ │ └── ConnectionTestJs.kt │ ├── start_server.sh │ └── stop_server.sh │ ├── jvmMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── engineio │ │ └── transports │ │ └── transport_impl.kt │ ├── jvmTest │ ├── java │ │ └── io │ │ │ └── socket │ │ │ ├── backo │ │ │ └── BackoffTest.java │ │ │ ├── client │ │ │ ├── Connection.java │ │ │ ├── ConnectionTest.java │ │ │ ├── ServerConnectionTest.java │ │ │ └── SocketTest.java │ │ │ ├── emitter │ │ │ ├── EmitterTest.java │ │ │ └── EmitterTestWithImplDetails.java │ │ │ ├── engineio │ │ │ └── client │ │ │ │ ├── BinaryPollingTest.java │ │ │ │ ├── BinaryWSTest.java │ │ │ │ ├── Connection.java │ │ │ │ ├── ConnectionTest.java │ │ │ │ ├── ServerConnectionTest.java │ │ │ │ ├── SocketTest.java │ │ │ │ └── TransportTest.java │ │ │ ├── global │ │ │ └── GlobalTest.java │ │ │ ├── parseqs │ │ │ └── ParseQSTest.java │ │ │ └── util │ │ │ └── Optional.java │ ├── kotlin │ │ └── com │ │ │ └── piasy │ │ │ └── kmp │ │ │ └── socketio │ │ │ ├── emitter │ │ │ └── EmitterTest.kt │ │ │ ├── engineio │ │ │ ├── EngineSocketTest.kt │ │ │ ├── TestUtil.kt │ │ │ ├── transports │ │ │ │ ├── PollingXHRTest.kt │ │ │ │ └── WebSocketTest.kt │ │ │ └── utils.kt │ │ │ └── socketio │ │ │ └── BinaryPacketReconstructorTest.kt │ └── resources │ │ ├── cert.pem │ │ ├── engine-server.js │ │ ├── key.pem │ │ ├── keystore.jks │ │ ├── package-lock.json │ │ ├── package.json │ │ └── socket-server.js │ ├── linuxMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── engineio │ │ └── transports │ │ └── transport_impl.kt │ ├── linuxTest │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── socketio │ │ └── ConnectionTestLinux.kt │ ├── macosTest │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── socketio │ │ └── ConnectionTestMacos.kt │ ├── mingwMain │ └── kotlin │ │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── engineio │ │ └── transports │ │ └── transport_impl.kt │ └── mingwTest │ ├── kotlin │ └── com │ │ └── piasy │ │ └── kmp │ │ └── socketio │ │ └── socketio │ │ └── ConnectionTestMingw.kt │ ├── start_server.ps1 │ └── stop_server.ps1 ├── kotlin-js-store └── yarn.lock └── settings.gradle.kts /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # https://github.com/Kotlin/multiplatform-library-template/blob/main/.github/workflows/gradle.yml 2 | # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 3 | 4 | name: CI 5 | 6 | on: 7 | push: 8 | branches: ["main"] 9 | pull_request: 10 | branches: ["main"] 11 | workflow_call: 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | build: 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - os: macos-latest 23 | cov: on 24 | cmd: "./gradlew :kmp-socketio:jvmTest --info" 25 | - os: macos-latest 26 | cmd: "./gradlew :kmp-socketio:macosX64Test --info && ./kmp-socketio/src/jsTest/start_server.sh && ./gradlew :kmp-socketio:jsBrowserTest --info && ./kmp-socketio/src/jsTest/stop_server.sh" 27 | - os: ubuntu-latest 28 | cmd: "./gradlew :kmp-socketio:linuxX64Test --info" 29 | - os: windows-latest 30 | cmd: "./gradlew :kmp-socketio:mingwX64Test --info" 31 | runs-on: ${{ matrix.os }} 32 | permissions: 33 | pull-requests: write 34 | contents: write 35 | id-token: write 36 | steps: 37 | - uses: actions/checkout@v4 38 | with: 39 | submodules: "recursive" 40 | - uses: actions/setup-java@v4 41 | with: 42 | java-version: "17" 43 | distribution: "temurin" 44 | cache: "gradle" 45 | - uses: gradle/actions/setup-gradle@v3 46 | - uses: actions/setup-node@v4 47 | with: 48 | node-version: 14 49 | architecture: 'x64' 50 | - name: Setup Test Env 51 | run: | 52 | cd kmp-socketio/src/jvmTest/resources/ 53 | npm install 54 | - name: Run tests 55 | run: ${{ matrix.cmd }} 56 | - name: Gen coverage 57 | if: ${{ matrix.cov == 'on' }} 58 | run: | 59 | ./gradlew koverXmlReport 60 | - name: Add coverage to PR 61 | if: ${{ matrix.cov == 'on' }} 62 | id: jacoco 63 | uses: madrapps/jacoco-report@v1.7.1 64 | with: 65 | paths: ${{ github.workspace }}/kmp-socketio/build/reports/kover/report.xml 66 | token: ${{ secrets.GITHUB_TOKEN }} 67 | min-coverage-overall: 80 68 | min-coverage-changed-files: 80 69 | pass-emoji: ":white_check_mark:" 70 | fail-emoji: ":negative_squared_cross_mark:" 71 | - name: Overall coverage check 72 | if: ${{ matrix.cov == 'on' && (steps.jacoco.outputs.coverage-overall < 80 || steps.jacoco.outputs.coverage-changed-files < 80) }} 73 | uses: actions/github-script@v7 74 | with: 75 | script: | 76 | core.setFailed("Coverage check failed! overall ${{ steps.jacoco.outputs.coverage-overall }}, diff ${{ steps.jacoco.outputs.coverage-changed-files }}.") 77 | - name: Create coverage summary json 78 | if: ${{ matrix.cov == 'on' }} 79 | uses: jsdaniell/create-json@v1.2.3 80 | with: 81 | dir: kmp-socketio/build/reports/kover/ 82 | name: "coverage.json" 83 | json: '{"total":{"instructions":{"pct":${{ steps.jacoco.outputs.coverage-overall }} }}}' 84 | - name: Create Coverage Badges 85 | if: ${{ matrix.cov == 'on' }} 86 | uses: jaywcjlove/coverage-badges-cli@main 87 | with: 88 | source: ${{ github.workspace }}/kmp-socketio/build/reports/kover/coverage.json 89 | output: dist/badges.svg 90 | jsonPath: total.instructions.pct 91 | - name: Deploy 92 | uses: peaceiris/actions-gh-pages@v4 93 | if: ${{ matrix.cov == 'on' && github.ref == 'refs/heads/main' }} 94 | with: 95 | commit_message: ${{ github.event.head_commit.message }} 96 | github_token: ${{ secrets.GITHUB_TOKEN }} 97 | publish_dir: ./dist 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **.iml 2 | .gradle 3 | .idea 4 | .kotlin 5 | .DS_Store 6 | build 7 | captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | 12 | xcuserdata 13 | **.podspec 14 | **.xcodeproj 15 | **.xcworkspace 16 | Podfile.lock 17 | Pods 18 | 19 | libs 20 | node_modules 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 HackWebRTC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kmp-socketio 2 | 3 | KMP (pure Kotlin) implementation of SocketIO client. 4 | 5 | ![Maven Central Version](https://img.shields.io/maven-central/v/com.piasy/kmp-socketio) ![Main branch status](https://github.com/HackWebRTC/kmp-socketio/actions/workflows/ci.yaml/badge.svg?branch=main) ![Coverage](https://hackwebrtc.github.io/kmp-socketio/badges.svg) 6 | 7 | ## Supported platforms 8 | 9 | | Platform | 🛠Builds🛠 + 🔬Tests🔬 | 10 | | :----------------: | :------------------: | 11 | | `JVM` 17 | 🚀 | 12 | | `JS` (Chrome) | 🚀 | 13 | | `Android` | 🚀 | 14 | | `iOS` | 🚀 | 15 | | `macOS` | 🚀 | 16 | | `Windows X64` | 🚀 | 17 | | `Linux X64` | 🚀 | 18 | 19 | ## Dependency 20 | 21 | You only need to add gradle dependency: 22 | 23 | ```kotlin 24 | // add common source set dependency 25 | kotlin { 26 | sourceSets { 27 | val commonMain by getting { 28 | dependencies { 29 | implementation("com.piasy:kmp-socketio:$version") 30 | } 31 | } 32 | } 33 | } 34 | ``` 35 | 36 | ## Usage 37 | 38 | ```kotlin 39 | IO.socket("http://localhost:3000", IO.Options()) { socket -> 40 | socket.on(Socket.EVENT_CONNECT) { args -> 41 | println("on connect ${args.joinToString()}") 42 | 43 | val bin = UnsafeByteStringOperations.wrapUnsafe(byteArrayOf(0x1, 0x3, 0x1, 0x4)) 44 | socket.emit("echo", 1, "2", bin, GMTDate()) 45 | }.on("echoBack") { args -> 46 | println("on echoBack ${args.joinToString()}") 47 | } 48 | 49 | socket.open() 50 | } 51 | ``` 52 | 53 | Most of the APIs are the same as socket.io-client-java, here are some differences: 54 | 55 | - Create socket is asynchronous, to make it's easier to guarantee thread safety. 56 | - Binary messages can't be nested, because `emit` only accepts String/Boolean/Number/JsonElement/ByteString, other types will be converted to String using `toString()`, so there is no way to put ByteString in JsonElement. 57 | 58 | ### Logging with [kmp-xlog](https://github.com/HackWebRTC/kmp-xlog) 59 | 60 | ## Development 61 | 62 | To check coverage details, run `./gradlew :kmp-socketio:jvmTest --info && ./gradlew koverHtmlReport`, 63 | then check `kmp-socketio/build/reports/kover/html/index.html`. 64 | 65 | ## Example 66 | 67 | Before running examples, run `node kmp-socketio/src/jvmTest/resources/socket-server.js` to start the socket-io echo server, 68 | and update the local IP address in `example/shared/src/commonMain/kotlin/com/piasy/kmp/socketio/example/Greeting.kt`. 69 | 70 | ### Android 71 | 72 | Open the project (the repo root dir) in Android studio, and run the example.androidApp target. 73 | 74 | ### iOS 75 | 76 | ```bash 77 | brew install cocoapods xcodegen 78 | # if you have installed them earlier, you need to remove them at first, 79 | # or run brew link --overwrite xcodegen cocoapods 80 | 81 | cd example/iosApp 82 | xcodegen 83 | pod install 84 | # open iosApp.xcworkspace in Xcode, and run it. 85 | ``` 86 | 87 | ### JS 88 | 89 | Use Chrome CORS Unblock extension to workaround with CORS error. 90 | 91 | ```bash 92 | ./gradlew :example:shared:jsBrowserRun 93 | ``` 94 | 95 | ### Windows 96 | 97 | ```bash 98 | .\gradlew runKmp_socketioDebugExecutableMingwX64 99 | ``` 100 | 101 | ### Linux 102 | 103 | ```bash 104 | ./gradlew runKmp_socketioDebugExecutableLinuxX64 105 | ``` 106 | 107 | ### macOS 108 | 109 | ```bash 110 | ./gradlew runKmp_socketioDebugExecutableMacosX64 111 | ``` 112 | 113 | ## Publish 114 | 115 | Maven central portal credentials and signing configs are set in `~/.gradle/gradle.properties`. 116 | 117 | ```bash 118 | # on Linux: need manual release on website 119 | ./gradlew clean publishLinuxX64PublicationToMavenCentralRepository --no-configuration-cache 120 | # on Windows: need manual release on website 121 | .\gradlew clean publishMingwX64PublicationToMavenCentralRepository --no-configuration-cache 122 | # on macOS: need manual release on website 123 | ./gradlew clean \ 124 | publishKotlinMultiplatformPublicationToMavenCentralRepository \ 125 | publishJvmPublicationToMavenCentralRepository \ 126 | publishIosArm64PublicationToMavenCentralRepository \ 127 | publishIosSimulatorArm64PublicationToMavenCentralRepository \ 128 | publishIosX64PublicationToMavenCentralRepository \ 129 | publishMacosArm64PublicationToMavenCentralRepository \ 130 | publishMacosX64PublicationToMavenCentralRepository \ 131 | publishJsPublicationToMavenCentralRepository \ 132 | --no-configuration-cache 133 | ``` 134 | 135 | Login to https://central.sonatype.com/publishing/deployments, and release them manually. 136 | 137 | ## Credit 138 | 139 | - [joffrey-bion/socketio-kotlin](https://github.com/joffrey-bion/socketio-kotlin) 140 | - [dyte-io/socketio-kotlin](https://github.com/dyte-io/socketio-kotlin) 141 | - [socketio/socket.io-client-java](https://github.com/socketio/socket.io-client-java) 142 | - [socketio/engine.io-client-java](https://github.com/socketio/engine.io-client-java) 143 | - [socketio/socket.io](https://github.com/socketio/socket.io) 144 | - [ktorio/ktor](https://github.com/ktorio/ktor) 145 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | plugins { 10 | alias(libs.plugins.android.library) apply false 11 | alias(libs.plugins.android.application) apply false 12 | 13 | alias(libs.plugins.kotlin.android) apply false 14 | alias(libs.plugins.compose.compiler) apply false 15 | alias(libs.plugins.kmp) apply false 16 | 17 | alias(libs.plugins.vanniktech.mavenPublish) apply false 18 | 19 | alias(libs.plugins.versions) 20 | alias(libs.plugins.versionUpdate) 21 | } 22 | 23 | // ./gradlew versionCatalogUpdate 24 | versionCatalogUpdate { 25 | sortByKey = false 26 | keep { 27 | keepUnusedVersions = true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | gradlePluginPortal() 8 | } 9 | 10 | //tasks.withType().configureEach { 11 | // kotlinOptions { 12 | // jvmTarget = "17" 13 | // } 14 | //} 15 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Constants.kt: -------------------------------------------------------------------------------- 1 | object Consts { 2 | const val releaseGroup = "com.piasy" 3 | const val releaseName = "kmp-socketio" 4 | const val releaseVersion = "1.3.0" 5 | 6 | val androidNS = "$releaseGroup.${releaseName.replace('-', '.')}" 7 | } 8 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Functions.kt: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import java.util.Properties 3 | import org.gradle.api.Project; 4 | 5 | private fun loadProps(props: File, ext: HashMap) { 6 | if (props.exists()) { 7 | props.reader().use { 8 | Properties().apply { 9 | load(it) 10 | } 11 | }.onEach { (name, value) -> 12 | ext[name.toString()] = value.toString() 13 | } 14 | } 15 | } 16 | 17 | fun getPropString(project: Project, name: String): String { 18 | val ext = HashMap() 19 | loadProps(project.rootProject.file("local.properties"), ext) 20 | loadProps(project.rootProject.file("gradle.properties"), ext) 21 | return ext[name] ?: "" 22 | } 23 | -------------------------------------------------------------------------------- /example/androidApp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) 3 | alias(libs.plugins.kotlin.android) 4 | alias(libs.plugins.compose.compiler) 5 | } 6 | 7 | android { 8 | namespace = "${Consts.androidNS}.android" 9 | compileSdk = libs.versions.compileSdk.get().toInt() 10 | defaultConfig { 11 | applicationId = "${Consts.androidNS}.android" 12 | minSdk = libs.versions.minSdk.get().toInt() 13 | targetSdk = libs.versions.targetSdk.get().toInt() 14 | versionCode = 1 15 | versionName = Consts.releaseVersion 16 | } 17 | buildFeatures { 18 | compose = true 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility = JavaVersion.toVersion(libs.versions.jvm.get().toInt()) 23 | targetCompatibility = JavaVersion.toVersion(libs.versions.jvm.get().toInt()) 24 | } 25 | 26 | kotlin { 27 | jvmToolchain(libs.versions.jvm.get().toInt()) 28 | } 29 | packaging { 30 | resources { 31 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 32 | } 33 | } 34 | buildTypes { 35 | getByName("release") { 36 | isMinifyEnabled = false 37 | } 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation(project(":example:shared")) 43 | implementation(libs.androidx.compose.ui) 44 | implementation(libs.androidx.compose.ui.tooling) 45 | implementation(libs.androidx.compose.ui.tooling.preview) 46 | implementation(libs.androidx.compose.foundation) 47 | implementation(libs.androidx.compose.material) 48 | implementation(libs.androidx.activity.compose) 49 | } 50 | -------------------------------------------------------------------------------- /example/androidApp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/androidApp/src/main/java/com/piasy/kmp/socketio/android/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.piasy.kmp.socketio.android 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.isSystemInDarkTheme 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.shape.RoundedCornerShape 10 | import androidx.compose.material.MaterialTheme 11 | import androidx.compose.material.Shapes 12 | import androidx.compose.material.Surface 13 | import androidx.compose.material.Text 14 | import androidx.compose.material.Typography 15 | import androidx.compose.material.darkColors 16 | import androidx.compose.material.lightColors 17 | import androidx.compose.runtime.Composable 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.text.TextStyle 22 | import androidx.compose.ui.text.font.FontFamily 23 | import androidx.compose.ui.text.font.FontWeight 24 | import androidx.compose.ui.tooling.preview.Preview 25 | import androidx.compose.ui.unit.dp 26 | import androidx.compose.ui.unit.sp 27 | import com.piasy.kmp.socketio.example.Greeting 28 | 29 | @Composable 30 | fun MyApplicationTheme( 31 | darkTheme: Boolean = isSystemInDarkTheme(), 32 | content: @Composable () -> Unit 33 | ) { 34 | val colors = if (darkTheme) { 35 | darkColors( 36 | primary = Color(0xFFBB86FC), 37 | primaryVariant = Color(0xFF3700B3), 38 | secondary = Color(0xFF03DAC5) 39 | ) 40 | } else { 41 | lightColors( 42 | primary = Color(0xFF6200EE), 43 | primaryVariant = Color(0xFF3700B3), 44 | secondary = Color(0xFF03DAC5) 45 | ) 46 | } 47 | val typography = Typography( 48 | body1 = TextStyle( 49 | fontFamily = FontFamily.Default, 50 | fontWeight = FontWeight.Normal, 51 | fontSize = 16.sp 52 | ) 53 | ) 54 | val shapes = Shapes( 55 | small = RoundedCornerShape(4.dp), 56 | medium = RoundedCornerShape(4.dp), 57 | large = RoundedCornerShape(0.dp) 58 | ) 59 | 60 | MaterialTheme( 61 | colors = colors, 62 | typography = typography, 63 | shapes = shapes, 64 | content = content 65 | ) 66 | } 67 | 68 | class MainActivity : ComponentActivity() { 69 | override fun onCreate(savedInstanceState: Bundle?) { 70 | super.onCreate(savedInstanceState) 71 | 72 | setContent { 73 | MyApplicationTheme { 74 | Surface( 75 | modifier = Modifier.fillMaxSize(), 76 | color = MaterialTheme.colors.background 77 | ) { 78 | Box( 79 | modifier = Modifier.fillMaxSize(), 80 | contentAlignment = Alignment.Center, 81 | ) { 82 | Greeting("hello world") 83 | } 84 | } 85 | } 86 | } 87 | 88 | Greeting().greet() 89 | } 90 | } 91 | 92 | @Composable 93 | fun Greeting(text: String) { 94 | Text(text = text) 95 | } 96 | 97 | @Preview 98 | @Composable 99 | fun DefaultPreview() { 100 | MyApplicationTheme { 101 | Greeting("Hello, Android!") 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /example/androidApp/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 |