Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
09294ee
Rename :compose-app -> :compose-android-app Gradle module
vitaliystoyanov Feb 23, 2024
ce07055
Configured basic multiplatform project ios, android, desktop-jvm and …
vitaliystoyanov Feb 24, 2024
b64fef0
Configured shared Koin modules for desktop & android + created Koin a…
vitaliystoyanov Feb 24, 2024
70c210f
Configured :core:runtime:configuration module as multiplatform
vitaliystoyanov Feb 24, 2024
8a9bd31
Removed unused .gitignore
vitaliystoyanov Feb 24, 2024
0f5b7e7
Declared AndroidLibraryConventionPlugin to remove android {} duplicat…
vitaliystoyanov Feb 25, 2024
b92e15a
Configure compileOptions in modules. Was mismatch
vitaliystoyanov Feb 25, 2024
0ca5db3
Apply default hierarchy template for hierarchical nultiplatform proje…
vitaliystoyanov Feb 25, 2024
ea4c109
Migrated jvm-targeted :core:common, :core:model and :core:runtime:log…
vitaliystoyanov Feb 25, 2024
cf9507b
Migrated jvm-targeted :core:data, :core:database/datasource:api and :…
vitaliystoyanov Feb 25, 2024
6ce5c72
Migrated other jvm-targeted :core:* to multiplatform modules, removed…
vitaliystoyanov Feb 25, 2024
a862f4b
Disabled wasmJs target due to bugs in ktorVersion = "3.0.0-wasm1", re…
vitaliystoyanov Feb 26, 2024
34f344c
Renamed module: :core:compose-web-app -> :core:compose-web-wasm-app
vitaliystoyanov Feb 26, 2024
b799bfc
Migration of composable funs: Jetpack Compose -> Compose Multiplatform
vitaliystoyanov Feb 26, 2024
6340d56
Added :compose-web-app (js) target. Coroutine DI module per target, d…
vitaliystoyanov Feb 26, 2024
ae2103c
Basic cross-platform ComposeApplication.kt with launched coroutines (…
vitaliystoyanov Feb 26, 2024
9044feb
Revert :compose-web-wasm-app module
vitaliystoyanov Feb 27, 2024
dddd266
Ktor version 3.0.0-wasm1 -> 3.0.0-wasm2 (experimental), Multiplatform…
vitaliystoyanov Feb 27, 2024
6a82c54
Speed up dependency resolution by reorder repositories, removed unuse…
vitaliystoyanov Feb 27, 2024
da3cabe
Extracted :shared module per targets, each target has own responsibil…
vitaliystoyanov Feb 27, 2024
d8a9cd3
Mapbox Compose HTML for jsMain target, composite Gradle build setting…
vitaliystoyanov Feb 27, 2024
68ed975
Revert LICENSE of Derek Ellis compose-web-mapbox
vitaliystoyanov Feb 27, 2024
843b05d
Setup basic multiplatform library template for :compose-mapbox-library
vitaliystoyanov Feb 28, 2024
3b694d5
Configuration compose_mapbox_library and compose-ios-app to have MapB…
vitaliystoyanov Feb 29, 2024
3d49fa6
Combine :compose-ios-app and :shared to have all-related codebase in …
vitaliystoyanov Feb 29, 2024
302df0a
Moved libs.mapbox-library.versions.toml to own Gradle module
vitaliystoyanov Feb 29, 2024
72bb9da
Added notice to README.md
vitaliystoyanov Feb 29, 2024
bcceac5
Added notice to README.md
vitaliystoyanov Feb 29, 2024
0354a4b
Reorganize :feature:map composeables to have MapComposable per platform
vitaliystoyanov Mar 2, 2024
f95e798
Introduced inMemoryLocalDatasourceModule instead of emptyMockLocalDat…
vitaliystoyanov Mar 2, 2024
d919db2
Cross-platform fonts typography and theme in :core:designsystem
vitaliystoyanov Mar 2, 2024
8849e23
Publish ignored classes
vitaliystoyanov Mar 3, 2024
3507bdd
Added /docs/demo/ per platform, project configuration without arm64
vitaliystoyanov Mar 4, 2024
09b42bd
Merge branch 'mapbox-compose-lib' into kmp
vitaliystoyanov Mar 4, 2024
0571ae9
Add files via upload
vitaliystoyanov Mar 4, 2024
dd72d5e
Update README.md
vitaliystoyanov Mar 4, 2024
835d65a
Remove mov videos
vitaliystoyanov Mar 4, 2024
e508ac4
Create LICENSE.md
vitaliystoyanov Mar 5, 2024
b0eaf5b
Installed UserAgent Ktor.
vitaliystoyanov Mar 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
56 changes: 50 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
.idea
.gradle/
app/build/
.gradle
**/build/

# Local configuration file (sdk path, etc)
local.properties

# Log/OS Files
*.log
.DS_Store

# Android Studio generated files and folders
captures/
Expand All @@ -33,14 +36,55 @@ google-services.json
# Android Profiling
*.hprof

# Diagram backups
*.drawio.bkp
.DS_Store

server/
ktor-server/build/
/docs/*.drawio.bkp

build/

app/src/main/res/values/developer-config.xml
# Android
**/developer-config.xml

# Internal
/TODO.md
/docs/.$design.drawio.bkp


# Xcode
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
# Swift Package Manager
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
Packages/
Package.pins
Package.resolved

# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
.swiftpm
# CocoaPods - Only use to conserve bandwidth / Save time on Pushing
# - Also handy if you have a large number of dependant pods
# - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
Pods/
.build/
/compose-ios-app/Podfile.lock

## Xcode 8 and earlier

### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings

xcuserdata
!src/**/build/
captures
.externalNativeBuild
.cxx
24 changes: 24 additions & 0 deletions .run/Desktop app.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Desktop app" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":compose-desktop-app:run" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<ForceTestExec>false</ForceTestExec>
<method v="2" />
</configuration>
</component>
7 changes: 7 additions & 0 deletions .run/IOS app.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="IOS app" type="KmmRunConfiguration" factoryName="iOS Application" CONFIG_VERSION="1" EXEC_TARGET_ID="90E2B698-5866-4ADA-8DB5-4B07AB652589" XCODE_PROJECT="$PROJECT_DIR$/compose-ios-app/compose-ios-app.xcworkspace" XCODE_CONFIGURATION="Debug" XCODE_SCHEME="compose-ios-app">
<method v="2">
<option name="com.jetbrains.kmm.ios.BuildIOSAppTask" enabled="true" />
</method>
</configuration>
</component>
File renamed without changes.
24 changes: 24 additions & 0 deletions .run/Web app (Wasm + JS).run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Web app (Wasm + JS)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="compose-web-js-wasm-app:jsBrowserDevelopmentRun" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<ForceTestExec>false</ForceTestExec>
<method v="2" />
</configuration>
</component>
24 changes: 24 additions & 0 deletions .run/Web app (Wasm).run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Web app (Wasm)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/compose-web-wasm-app" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="wasmJsBrowserDevelopmentRun" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Vitalii Stoianov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
61 changes: 47 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,62 @@
# PoC of design implementation for processing large GPS node trace data effectively
# Kotlin/Multiplatform + Compose/Multiplatform PoC of design implementation for processing large GPS node trace data effectively


[![.github/workflows/main.yml](https://github.com/vitaliystoyanov/trace-node-tracking-playground/actions/workflows/main.yml/badge.svg?branch=gitworkflow)](https://github.com/vitaliystoyanov/trace-node-tracking-playground/actions/workflows/main.yml)
![API](https://img.shields.io/badge/API-23%2B-brightgreen.svg?style=flat)
![API](https://img.shields.io/badge/API-23%2B-brightgreen.svg?style=flat)

![badge][badge-android]
![badge][badge-ios]
![badge][badge-mac]
![badge][badge-jvm]
![badge][badge-js]
![badge][badge-wasm]
![badge][badge-windows]
![badge][badge-linux]

> [!WARNING]
> This branch in development. The project setup guide will be later. This has XCode projects, CocoaPods tool, different AndroidStudio plugins and run configurations that should be guided with instructions.

## Supported targets

- `android`.
- `jvm`.
- `js` (`IR`).
- `wasmJs`.
- `iosArm64`, `iosX64`, `iosSimulatorArm64`.
- `macosX64`, `macosArm64`.
- `mingwX64`
- `linuxX64`, `linuxArm64`.

The application processes GPS node trace data using the Scarlet websocket client, deserializes it using Gson and writes the data to a local Room database for further data rendering based on Jetpack Compose and MapBox SDK built-in capabilities.
On the other side, the Node.js backend generates sample data using the turf.js library. For each node, the route, direction and speed are generated at runtime.
# Demo

Some considerations:
* The application processes 10 thousand websocket messages and then renders efficiently. The websocket message format is a string, but it is better to implement a binary message format. These nodes are dynamic moving, which is CPU and GPU-intensive to render and process highly frequently updated node movements. (However, there is a slight stuttering in UI rendering frames due to MapBox rendering on the native C++ side). All buffers have been disabled in the MapBox configuration.
* At each layer of the architecture: db entity, external and network models have mappers for each other. Used object pool design pattern to avoid intensive allocation/deallocation of objects.
[android_demo.webm](https://github.com/vitaliystoyanov/trace-node-tracking-playground/assets/9073014/07302946-b544-4078-b541-a0fe382d6969)

[desktop_jvm_demo.webm](https://github.com/vitaliystoyanov/trace-node-tracking-playground/assets/9073014/f8e1b550-b688-4a76-94f6-f7aaed80972a)

<p align="center">
<img alt="Light" src="/docs/demo3.gif" width="45%">
&nbsp; &nbsp; &nbsp; &nbsp;
<img alt="Dark" src="/docs/demo1.gif" width="45%">
</p>
[ios_demo.webm](https://github.com/vitaliystoyanov/trace-node-tracking-playground/assets/9073014/76453330-0538-463e-9396-8a94163519b6)

[web_wasm_demo.webm](https://github.com/vitaliystoyanov/trace-node-tracking-playground/assets/9073014/756f118f-5923-40dd-92fe-37ca7b03c941)

## Tech stack

Nothing special :)

* [Kotlin/Multiplatform](https://www.jetbrains.com/kotlin-multiplatform/)
* [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multiplatform/)
* [Kotlin/Multiplatform Skiko for Skia: The 2D Graphics Library](https://github.com/JetBrains/skiko?tab=readme-ov-file)
* [KotlinX/Coroutine](https://github.com/Kotlin/kotlinx.coroutines)
* [KotlinX/Multiplatform date/time library](https://github.com/Kotlin/kotlinx-datetime)
* [Koin: Kotlin & Kotlin Multiplatform DI framework](https://insert-koin.io/)
* Kotlin Channels & Flows APIs
* (experimental) Kotlin multiplatform / multi-format reflectionless serialization
* [Kotlin Serialization ProtoBuf](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/formats.md#protobuf-experimental)
* Jetpack Compose with Material3 design
* [Ktor.io Websocket Client](https://ktor.io/docs/websocket.html)
* [MapBox SDK for Android](https://docs.mapbox.com/android/maps/guides/)
* [Mapbox Maps Compose Extension](https://github.com/mapbox/mapbox-maps-android/tree/extension-compose-v0.1.0/extension-compose)
* [Scarlet: A Retrofit inspired WebSocket client](https://github.com/Tinder/Scarlet)
* [Scarlet coroutines stream adapter](https://github.com/Tinder/Scarlet/tree/main/scarlet-stream-adapter-coroutines)
* [Room database](https://developer.android.com/training/data-storage/room)
* [OkHttp](http://square.github.io/okhttp/)
* [Koin: Kotlin & Kotlin Multiplatform DI framework](https://insert-koin.io/)

## Functionality
* Supports websocket connections in background
Expand Down Expand Up @@ -170,3 +194,12 @@ If your dev environment is emulator:

To be a part of ... you know :)

[badge-android]: http://img.shields.io/badge/android-6EDB8D.svg?style=flat
[badge-ios]: http://img.shields.io/badge/ios-CDCDCD.svg?style=flat
[badge-js]: http://img.shields.io/badge/js-F8DB5D.svg?style=flat
[badge-jvm]: http://img.shields.io/badge/jvm-DB413D.svg?style=flat
[badge-linux]: http://img.shields.io/badge/linux-2D3F6C.svg?style=flat
[badge-windows]: http://img.shields.io/badge/windows-4D76CD.svg?style=flat
[badge-mac]: http://img.shields.io/badge/macos-111111.svg?style=flat
[badge-wasm]: https://img.shields.io/badge/wasm-624FE8.svg?style=flat

47 changes: 47 additions & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
`kotlin-dsl`
}

group = "io.architecture.plugin.convention"

// Configure the build-logic plugins to target JDK 17
// This matches the JDK used to build the project, and is not related to what is running on device.
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
}

dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.android.tools.common)
compileOnly(libs.kotlin.gradlePlugin)
}

tasks {
validatePlugins {
enableStricterValidation = true
failOnWarning = true
}
}

gradlePlugin {
plugins {
register("androidLibrary") {
id = "io.architecture.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
}
plugins {
register("multiplatformDefaultTargetPreset") {
id = "io.architecture.multiplatform.target.preset.default"
implementationClass = "MultiplatformTargetPresetConventionPlugin"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.android.build.gradle.LibraryExtension
import io.architecture.build.logic.configureKotlinAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.kotlin

class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
}
extensions.configure<LibraryExtension> {
configureKotlinAndroid(this)
}
extensions.configure<LibraryAndroidComponentsExtension> {
}
dependencies {
add("testImplementation", kotlin("test"))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl

class MultiplatformTargetPresetConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("org.jetbrains.kotlin.multiplatform")
}
extensions.configure<KotlinMultiplatformExtension> {
applyDefaultHierarchyTemplate()
@OptIn(ExperimentalWasmDsl::class)
wasmJs { browser() }
js(IR) { browser() }
androidTarget()
iosX64()
// iosArm64()
// iosSimulatorArm64()
jvm()
}
}
}
}
Loading