diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-appleMain.cinteropLibraries b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-appleMain.cinteropLibraries new file mode 100644 index 00000000..060dde8b --- /dev/null +++ b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-appleMain.cinteropLibraries @@ -0,0 +1,3 @@ +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib \ No newline at end of file diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-appleTest.cinteropLibraries b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-appleTest.cinteropLibraries new file mode 100644 index 00000000..060dde8b --- /dev/null +++ b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-appleTest.cinteropLibraries @@ -0,0 +1,3 @@ +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib \ No newline at end of file diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-iosMain.cinteropLibraries b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-iosMain.cinteropLibraries new file mode 100644 index 00000000..060dde8b --- /dev/null +++ b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-iosMain.cinteropLibraries @@ -0,0 +1,3 @@ +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib \ No newline at end of file diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-iosTest.cinteropLibraries b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-iosTest.cinteropLibraries new file mode 100644 index 00000000..060dde8b --- /dev/null +++ b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-iosTest.cinteropLibraries @@ -0,0 +1,3 @@ +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib \ No newline at end of file diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-nativeMain.cinteropLibraries b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-nativeMain.cinteropLibraries new file mode 100644 index 00000000..060dde8b --- /dev/null +++ b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-nativeMain.cinteropLibraries @@ -0,0 +1,3 @@ +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib \ No newline at end of file diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-nativeTest.cinteropLibraries b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-nativeTest.cinteropLibraries new file mode 100644 index 00000000..060dde8b --- /dev/null +++ b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/.shared-nativeTest.cinteropLibraries @@ -0,0 +1,3 @@ +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib +/Users/jancortiel/Documents/More/LBI/more-multiplatform-app/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib \ No newline at end of file diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib new file mode 100644 index 00000000..ca651ddb Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.ktor-ktor-utils-2.3.12-iosMain-cinterop/io.ktor_ktor-utils-cinterop-threadUtils-TE4abA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib new file mode 100644 index 00000000..729c60ea Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-cinterop/io.realm.kotlin_cinterop-cinterop-realm_wrapper-nt9oMQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib new file mode 100644 index 00000000..15d6a208 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedCInteropMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-cinterop/org.jetbrains.kotlinx_atomicfu-cinterop-interop-wFq7cg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/dev.tmapps-konnection-1.4.1-commonMain-1qj7aA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/dev.tmapps-konnection-1.4.1-commonMain-1qj7aA.klib new file mode 100644 index 00000000..52e41176 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/dev.tmapps-konnection-1.4.1-commonMain-1qj7aA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/dev.tmapps-konnection-1.4.1-ios-BePiUw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/dev.tmapps-konnection-1.4.1-ios-BePiUw.klib new file mode 100644 index 00000000..8b847fe8 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/dev.tmapps-konnection-1.4.1-ios-BePiUw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.github.aakira-napier-2.7.1-commonMain-UpJokw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.github.aakira-napier-2.7.1-commonMain-UpJokw.klib new file mode 100644 index 00000000..5a2f649c Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.github.aakira-napier-2.7.1-commonMain-UpJokw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.github.aakira-napier-2.7.1-darwinMain-EnitAw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.github.aakira-napier-2.7.1-darwinMain-EnitAw.klib new file mode 100644 index 00000000..4ba5570d Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.github.aakira-napier-2.7.1-darwinMain-EnitAw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-auth-2.3.12-commonMain-tgNnVQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-auth-2.3.12-commonMain-tgNnVQ.klib new file mode 100644 index 00000000..98da7559 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-auth-2.3.12-commonMain-tgNnVQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-content-negotiation-2.3.12-commonMain-jI37cw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-content-negotiation-2.3.12-commonMain-jI37cw.klib new file mode 100644 index 00000000..745c2195 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-content-negotiation-2.3.12-commonMain-jI37cw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-content-negotiation-2.3.12-posixMain-jI37cw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-content-negotiation-2.3.12-posixMain-jI37cw.klib new file mode 100644 index 00000000..47bfdb02 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-content-negotiation-2.3.12-posixMain-jI37cw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-core-2.3.12-commonMain-FU-9lg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-core-2.3.12-commonMain-FU-9lg.klib new file mode 100644 index 00000000..d5eb44a3 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-core-2.3.12-commonMain-FU-9lg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-core-2.3.12-posixMain-FU-9lg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-core-2.3.12-posixMain-FU-9lg.klib new file mode 100644 index 00000000..f33f7f85 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-core-2.3.12-posixMain-FU-9lg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-darwin-2.3.12-darwinMain-CnRCQQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-darwin-2.3.12-darwinMain-CnRCQQ.klib new file mode 100644 index 00000000..27421f95 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-darwin-2.3.12-darwinMain-CnRCQQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-logging-2.3.12-commonMain-grxlVw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-logging-2.3.12-commonMain-grxlVw.klib new file mode 100644 index 00000000..1a0f4bd9 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-logging-2.3.12-commonMain-grxlVw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-logging-2.3.12-posixMain-grxlVw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-logging-2.3.12-posixMain-grxlVw.klib new file mode 100644 index 00000000..a3e08f24 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-client-logging-2.3.12-posixMain-grxlVw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-events-2.3.12-commonMain-Q6-xkw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-events-2.3.12-commonMain-Q6-xkw.klib new file mode 100644 index 00000000..d9a2627a Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-events-2.3.12-commonMain-Q6-xkw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-http-2.3.12-commonMain-W5sIeA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-http-2.3.12-commonMain-W5sIeA.klib new file mode 100644 index 00000000..be50fd4d Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-http-2.3.12-commonMain-W5sIeA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-http-2.3.12-posixMain-W5sIeA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-http-2.3.12-posixMain-W5sIeA.klib new file mode 100644 index 00000000..8dcf145e Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-http-2.3.12-posixMain-W5sIeA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-commonMain-3YsjwQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-commonMain-3YsjwQ.klib new file mode 100644 index 00000000..4a89b145 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-commonMain-3YsjwQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-darwinMain-sbySvA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-darwinMain-sbySvA.klib new file mode 100644 index 00000000..b45daa05 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-darwinMain-sbySvA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-posixMain-3YsjwQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-posixMain-3YsjwQ.klib new file mode 100644 index 00000000..635a72f6 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-io-2.3.12-posixMain-3YsjwQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-2.3.12-commonMain-NxrIfg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-2.3.12-commonMain-NxrIfg.klib new file mode 100644 index 00000000..265f7193 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-2.3.12-commonMain-NxrIfg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-2.3.12-commonMain-s53Slg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-2.3.12-commonMain-s53Slg.klib new file mode 100644 index 00000000..9e5d8b52 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-2.3.12-commonMain-s53Slg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-2.3.12-posixMain-s53Slg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-2.3.12-posixMain-s53Slg.klib new file mode 100644 index 00000000..9d87792e Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-2.3.12-posixMain-s53Slg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-json-2.3.12-commonMain-sJ8SDA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-json-2.3.12-commonMain-sJ8SDA.klib new file mode 100644 index 00000000..45f1885b Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-json-2.3.12-commonMain-sJ8SDA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-json-2.3.12-posixMain-sJ8SDA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-json-2.3.12-posixMain-sJ8SDA.klib new file mode 100644 index 00000000..a327a028 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-serialization-kotlinx-json-2.3.12-posixMain-sJ8SDA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-commonMain-kEcFvw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-commonMain-kEcFvw.klib new file mode 100644 index 00000000..8eb66ad6 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-commonMain-kEcFvw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-darwinMain-TE4abA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-darwinMain-TE4abA.klib new file mode 100644 index 00000000..6deb9542 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-darwinMain-TE4abA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-nixMain-kEcFvw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-nixMain-kEcFvw.klib new file mode 100644 index 00000000..cd74f993 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-nixMain-kEcFvw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-posixMain-kEcFvw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-posixMain-kEcFvw.klib new file mode 100644 index 00000000..a464aafc Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-utils-2.3.12-posixMain-kEcFvw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websocket-serialization-2.3.12-commonMain-8xBQEg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websocket-serialization-2.3.12-commonMain-8xBQEg.klib new file mode 100644 index 00000000..160024bc Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websocket-serialization-2.3.12-commonMain-8xBQEg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websockets-2.3.12-commonMain-8-9-_g.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websockets-2.3.12-commonMain-8-9-_g.klib new file mode 100644 index 00000000..6217010f Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websockets-2.3.12-commonMain-8-9-_g.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websockets-2.3.12-posixMain-8-9-_g.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websockets-2.3.12-posixMain-8-9-_g.klib new file mode 100644 index 00000000..a3799471 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.ktor-ktor-websockets-2.3.12-posixMain-8-9-_g.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-commonMain-zZQVnw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-commonMain-zZQVnw.klib new file mode 100644 index 00000000..dee97933 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-commonMain-zZQVnw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-nt9oMQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-nt9oMQ.klib new file mode 100644 index 00000000..9e495c50 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-cinterop-1.13.0-nativeDarwin-nt9oMQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-commonMain-0LVaVg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-commonMain-0LVaVg.klib new file mode 100644 index 00000000..05ca0842 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-commonMain-0LVaVg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-nativeDarwin-RwRULA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-nativeDarwin-RwRULA.klib new file mode 100644 index 00000000..629031e4 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-nativeDarwin-RwRULA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-nativeIos-RwRULA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-nativeIos-RwRULA.klib new file mode 100644 index 00000000..0b80091e Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/io.realm.kotlin-library-base-1.13.0-nativeIos-RwRULA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-stdlib-2.0.0-commonMain-2bbUHA.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-stdlib-2.0.0-commonMain-2bbUHA.klib new file mode 100644 index 00000000..ed256742 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-stdlib-2.0.0-commonMain-2bbUHA.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-test-2.0.0-annotationsCommonMain-24eTFQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-test-2.0.0-annotationsCommonMain-24eTFQ.klib new file mode 100644 index 00000000..250ee082 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-test-2.0.0-annotationsCommonMain-24eTFQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-test-2.0.0-assertionsCommonMain-24eTFQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-test-2.0.0-assertionsCommonMain-24eTFQ.klib new file mode 100644 index 00000000..75ffad47 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlin-kotlin-test-2.0.0-assertionsCommonMain-24eTFQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-commonMain-wFq7cg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-commonMain-wFq7cg.klib new file mode 100644 index 00000000..7a42bd23 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-commonMain-wFq7cg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-wFq7cg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-wFq7cg.klib new file mode 100644 index 00000000..3c54c5c3 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-atomicfu-0.23.1-nativeMain-wFq7cg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-commonMain-XanZ2w.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-commonMain-XanZ2w.klib new file mode 100644 index 00000000..7d489eb0 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-commonMain-XanZ2w.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-concurrentMain-XanZ2w.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-concurrentMain-XanZ2w.klib new file mode 100644 index 00000000..04231f8d Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-concurrentMain-XanZ2w.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-nativeDarwinMain-sy5nKg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-nativeDarwinMain-sy5nKg.klib new file mode 100644 index 00000000..0ea2d1c9 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-nativeDarwinMain-sy5nKg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-nativeMain-XanZ2w.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-nativeMain-XanZ2w.klib new file mode 100644 index 00000000..9273ad95 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-coroutines-core-1.8.1-nativeMain-XanZ2w.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-commonMain-k5yUlQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-commonMain-k5yUlQ.klib new file mode 100644 index 00000000..7fea36f1 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-commonMain-k5yUlQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-darwinMain-bPkWaQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-darwinMain-bPkWaQ.klib new file mode 100644 index 00000000..7073571c Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-darwinMain-bPkWaQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-nativeMain-k5yUlQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-nativeMain-k5yUlQ.klib new file mode 100644 index 00000000..36477fc6 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-datetime-0.4.0-nativeMain-k5yUlQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-core-1.7.1-commonMain-8gwKMQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-core-1.7.1-commonMain-8gwKMQ.klib new file mode 100644 index 00000000..ea79905d Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-core-1.7.1-commonMain-8gwKMQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-core-1.7.1-nativeMain-8gwKMQ.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-core-1.7.1-nativeMain-8gwKMQ.klib new file mode 100644 index 00000000..514eaa94 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-core-1.7.1-nativeMain-8gwKMQ.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-json-1.7.1-commonMain-Ii3AMw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-json-1.7.1-commonMain-Ii3AMw.klib new file mode 100644 index 00000000..0ad0396e Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-json-1.7.1-commonMain-Ii3AMw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-json-1.7.1-nativeMain-Ii3AMw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-json-1.7.1-nativeMain-Ii3AMw.klib new file mode 100644 index 00000000..6b5b966c Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.jetbrains.kotlinx-kotlinx-serialization-json-1.7.1-nativeMain-Ii3AMw.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.mongodb.kbson-kbson-0.3.0-commonMain-eKbbTg.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.mongodb.kbson-kbson-0.3.0-commonMain-eKbbTg.klib new file mode 100644 index 00000000..36a82e5f Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.mongodb.kbson-kbson-0.3.0-commonMain-eKbbTg.klib differ diff --git a/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.mongodb.kbson-kbson-0.3.0-iosMain-l8A9Sw.klib b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.mongodb.kbson-kbson-0.3.0-iosMain-l8A9Sw.klib new file mode 100644 index 00000000..84046587 Binary files /dev/null and b/.kotlin/metadata/kotlinTransformedMetadataLibraries/org.mongodb.kbson-kbson-0.3.0-iosMain-l8A9Sw.klib differ diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 8608272d..38c58e0e 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "ac.at.lbg.dhp.more" minSdk = 29 targetSdk = 34 - versionCode = 10 - versionName = "4.0.12" + versionCode = 12 + versionName = "4.0.14" } buildFeatures { compose = true @@ -50,7 +50,7 @@ android { val composeVersion = "1.6.8" val workVersion = "2.9.0" val navVersion = "2.7.7" -val polarSDKVersion = "5.5.0" +val polarSDKVersion = "5.6.0" dependencies { implementation(project(":shared")) @@ -61,8 +61,8 @@ dependencies { implementation("androidx.compose.material:material:$composeVersion") implementation("androidx.compose.material:material-icons-core:$composeVersion") implementation("androidx.compose.material:material-icons-extended:$composeVersion") - implementation("androidx.fragment:fragment:1.8.1") - implementation("androidx.activity:activity-compose:1.9.0") + implementation("androidx.fragment:fragment:1.8.2") + implementation("androidx.activity:activity-compose:1.9.1") implementation("io.realm.kotlin:library-base:1.13.0") implementation("androidx.navigation:navigation-compose:$navVersion") implementation("androidx.work:work-runtime-ktx:$workVersion") @@ -80,5 +80,5 @@ dependencies { implementation("com.google.firebase:firebase-inappmessaging-display-ktx") implementation("com.google.code.gson:gson:2.10.1") implementation("com.github.acsbendi:Android-Request-Inspector-WebView:1.0.3") - implementation("androidx.lifecycle:lifecycle-process:2.8.3") + implementation("androidx.lifecycle:lifecycle-process:2.8.4") } diff --git a/androidApp/src/main/java/io/redlink/more/app/android/observations/HR/PolarObserverCallback.kt b/androidApp/src/main/java/io/redlink/more/app/android/observations/HR/PolarObserverCallback.kt index b6bbc3d8..80b2e514 100644 --- a/androidApp/src/main/java/io/redlink/more/app/android/observations/HR/PolarObserverCallback.kt +++ b/androidApp/src/main/java/io/redlink/more/app/android/observations/HR/PolarObserverCallback.kt @@ -11,6 +11,7 @@ package io.redlink.more.app.android.observations.HR +import com.polar.androidcommunications.api.ble.model.DisInfo import com.polar.sdk.api.PolarBleApi import com.polar.sdk.api.PolarBleApiCallback import com.polar.sdk.api.model.PolarDeviceInfo @@ -45,35 +46,60 @@ class PolarObserverCallback : PolarBleApiCallback() { override fun deviceConnected(polarDeviceInfo: PolarDeviceInfo) { super.deviceConnected(polarDeviceInfo) - Napier.d("CONNECTED: ${polarDeviceInfo.deviceId}", tag = "PolarObserverCallback::deviceConnected") + Napier.d( + "CONNECTED: ${polarDeviceInfo.deviceId}", + tag = "PolarObserverCallback::deviceConnected" + ) connectionListener?.onDeviceConnected(polarDeviceInfo) } override fun deviceConnecting(polarDeviceInfo: PolarDeviceInfo) { super.deviceConnecting(polarDeviceInfo) - Napier.d("CONNECTING: ${polarDeviceInfo.deviceId}", tag = "PolarObserverCallback::deviceConnecting") + Napier.d( + "CONNECTING: ${polarDeviceInfo.deviceId}", + tag = "PolarObserverCallback::deviceConnecting" + ) connectionListener?.onDeviceConnecting(polarDeviceInfo) } override fun deviceDisconnected(polarDeviceInfo: PolarDeviceInfo) { super.deviceDisconnected(polarDeviceInfo) - Napier.i("Device disconnected: ${polarDeviceInfo.name}", tag = "PolarObserverCallback::deviceDisconnected") + Napier.i( + "Device disconnected: ${polarDeviceInfo.name}", + tag = "PolarObserverCallback::deviceDisconnected" + ) connectionListener?.onDeviceDisconnected(polarDeviceInfo) } override fun bleSdkFeatureReady(identifier: String, feature: PolarBleApi.PolarBleSdkFeature) { super.bleSdkFeatureReady(identifier, feature) - Napier.i("SDK Feature ready: ${feature.name}, identifier: $identifier", tag = "PolarObserverCallback::bleSdkFeatureReady") + Napier.i( + "SDK Feature ready: ${feature.name}, identifier: $identifier", + tag = "PolarObserverCallback::bleSdkFeatureReady" + ) connectionListener?.onPolarFeatureReady(feature) } override fun disInformationReceived(identifier: String, uuid: UUID, value: String) { super.disInformationReceived(identifier, uuid, value) - Napier.i("Disinformation: $identifier, UUID: $uuid, Value: $value", tag = "PolarObserverCallback::disInformationReceived") + Napier.i( + "Disinformation: $identifier, UUID: $uuid, Value: $value", + tag = "PolarObserverCallback::disInformationReceived" + ) + } + + override fun disInformationReceived(identifier: String, disInfo: DisInfo) { + Napier.i( + "Disinformation: $identifier, DisInfo: $disInfo", + tag = "PolarObserverCallback::disInformationReceived" + ) } override fun batteryLevelReceived(identifier: String, level: Int) { super.batteryLevelReceived(identifier, level) - Napier.i( "Battery Level Received: $level", tag = "PolarObserverCallback::batteryLevelReceived") + Napier.i( + "Battery Level Received: $level", + tag = "PolarObserverCallback::batteryLevelReceived" + ) } } \ No newline at end of file diff --git a/androidApp/src/main/java/io/redlink/more/app/android/services/bluetooth/PolarConnector.kt b/androidApp/src/main/java/io/redlink/more/app/android/services/bluetooth/PolarConnector.kt index bf0cb600..98cc6b2f 100644 --- a/androidApp/src/main/java/io/redlink/more/app/android/services/bluetooth/PolarConnector.kt +++ b/androidApp/src/main/java/io/redlink/more/app/android/services/bluetooth/PolarConnector.kt @@ -124,7 +124,7 @@ class PolarConnector(context: Context) : BluetoothConnector, PolarConnectorListe override fun onPolarFeatureReady(feature: PolarBleApi.PolarBleSdkFeature) { if (feature == PolarBleApi.PolarBleSdkFeature.FEATURE_HR) { Napier.i(tag = "PolarConnector::onPolarFeatureReady") { "HR ready!" } - PolarHeartRateObservation.setHRFeature(false) + PolarHeartRateObservation.setHRFeature(true) } } diff --git a/androidApp/src/main/res/values-de/bluetooth-strings.xml b/androidApp/src/main/res/values-de/bluetooth-strings.xml index 21f5d44b..cf92099f 100644 --- a/androidApp/src/main/res/values-de/bluetooth-strings.xml +++ b/androidApp/src/main/res/values-de/bluetooth-strings.xml @@ -13,7 +13,7 @@ Manche Aufgaben benötigen Bluetooth Geräte um abgeschlossen zu werden. Stelle sicher, dass folgende Geräte verbunden sind Sie können sich jederzeit mit Geräten verbinden in: Info > Geräte Setup überspringen - Bluetooth ist deaktiviert. Bitte aktivieren zum verbinden! + Bluetooth oder GPS deaktiviert! Bitte aktivieren Sie beides für die Nutzung! Verbinde ein Gerät, indem du es aus der Liste auswählst, nachdem du dieses mit deinem Smartphone gekoppelt hast. Bitte verbinde das geforderte Gerät mit deinem Smartphone, um es mit der App zu verbinden. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index d1cc58c6..97133f66 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ buildscript { dependencies { - classpath("com.google.gms:google-services:4.4.1") - classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9") + classpath("com.google.gms:google-services:4.4.2") + classpath("com.google.firebase:firebase-crashlytics-gradle:3.0.2") } repositories { google() // Google's Maven repository @@ -13,7 +13,7 @@ plugins { //trick: for the same plugin versions in all sub-modules id("com.android.application").version("8.2.2").apply(false) id("com.android.library").version("8.2.2").apply(false) - kotlin("android").version("1.9.22").apply(false) + kotlin("android").version("1.9.23").apply(false) kotlin("multiplatform").version("1.9.23").apply(false) kotlin("plugin.serialization").version("1.9.23").apply(false) } diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 9afab252..e2744f02 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -1798,7 +1798,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0.12; + MARKETING_VERSION = 4.0.14; OTHER_LDFLAGS = ( "$(inherited)", "-framework", @@ -1838,7 +1838,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0.12; + MARKETING_VERSION = 4.0.14; OTHER_LDFLAGS = ( "$(inherited)", "-framework", diff --git a/iosApp/iosApp/AppDelegate.swift b/iosApp/iosApp/AppDelegate.swift index 94a3f94d..db72fd24 100644 --- a/iosApp/iosApp/AppDelegate.swift +++ b/iosApp/iosApp/AppDelegate.swift @@ -52,7 +52,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { fcmService.register() AppDelegate.registerForNotifications() - registerBackgroundTasks() + DataUploadBackgroundTask.setupBackgroundTasks() AppDelegate.shared.deeplinkManager.addAvailableDeepLinks(deepLinks: Set(NavigationScreen.allCases.map { $0.values.navigationLink })) @@ -78,14 +78,6 @@ class AppDelegate: NSObject, UIApplicationDelegate { print("App did fail to register for remote notifications: \(error)") } - private func registerBackgroundTasks() { - BGTaskScheduler.shared.register(forTaskWithIdentifier: DataUploadBackgroundTask.taskID, using: nil) { task in - if let task = task as? BGProcessingTask { - DataUploadBackgroundTask().handleProcessingTask(task: task) - } - } - } - func cancelBackgroundTasks() { BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: DataUploadBackgroundTask.taskID) } diff --git a/iosApp/iosApp/BackgroundTasks/DataUploadBackgroundTask.swift b/iosApp/iosApp/BackgroundTasks/DataUploadBackgroundTask.swift index 8c2e2a2b..49543ae0 100644 --- a/iosApp/iosApp/BackgroundTasks/DataUploadBackgroundTask.swift +++ b/iosApp/iosApp/BackgroundTasks/DataUploadBackgroundTask.swift @@ -1,26 +1,15 @@ -// -// DataUploadBackgroundTask.swift -// iosApp -// -// Created by Jan Cortiel on 23.03.23. -// Copyright © 2023 Ludwig Boltzmann Institute for -// Digital Health and Prevention - A research institute -// of the Ludwig Boltzmann Gesellschaft, -// Oesterreichische Vereinigung zur Foerderung -// der wissenschaftlichen Forschung -// Licensed under the Apache 2.0 license with Commons Clause -// (see https://www.apache.org/licenses/LICENSE-2.0 and -// https://commonsclause.com/). -// - import BackgroundTasks -import Foundation class DataUploadBackgroundTask { static let taskID = "io.redlink.more.app.multiplatform.data-upload" - static func schedule() { + + static func schedule(earliestBeginDate: Date? = nil) { let request = BGProcessingTaskRequest(identifier: taskID) - request.earliestBeginDate = Calendar.current.date(byAdding: .minute, value: 15, to: Date()) + if let earliestDate = earliestBeginDate { + request.earliestBeginDate = earliestDate + } else { + request.earliestBeginDate = Calendar.current.date(byAdding: .minute, value: 15, to: Date()) + } request.requiresNetworkConnectivity = true request.requiresExternalPower = false @@ -28,12 +17,12 @@ class DataUploadBackgroundTask { try BGTaskScheduler.shared.submit(request) print("DataUploadBackgroundTask::schedule - Background Task scheduled") } catch { - print("DataUploadBackgroundTask:schedule - Error requesting for a background task") + print("DataUploadBackgroundTask::schedule - Error requesting a background task: \(error.localizedDescription)") } } private let dataCollector = ObservationDataCollector() - + private func collectRecordedData(completion: @escaping () -> Void) { print("DataUploadBackgroundTask::collectRecordedData - \(Date()): Collecting recorded data...") dataCollector.collectData { dataCollected in @@ -45,8 +34,9 @@ class DataUploadBackgroundTask { completion() } } - + private func close() { + print("DataUploadBackgroundTask::close - Cleaning up resources") } } @@ -54,6 +44,7 @@ extension DataUploadBackgroundTask: BackgroundTaskHandler { @MainActor func handleProcessingTask(task: BGProcessingTask) { print("DataUploadBackgroundTask::handleProcessingTask - Starting Background Processing Task") + task.expirationHandler = { print("\(Date()): Task will soon expire! Cleaning up...") self.close() @@ -63,24 +54,35 @@ extension DataUploadBackgroundTask: BackgroundTaskHandler { task.setTaskCompleted(success: false) } } + collectRecordedData { [weak self] in guard let strongSelf = self else { DataUploadBackgroundTask.schedule() task.setTaskCompleted(success: false) return } - + strongSelf.close() DataUploadBackgroundTask.schedule() DispatchQueue.main.async { task.setTaskCompleted(success: true) } } - } func handleRefreshTask(task: BGAppRefreshTask) { + print("DataUploadBackgroundTask::handleRefreshTask - Handling Refresh Task") task.setTaskCompleted(success: true) } - + + static func setupBackgroundTasks() { + BGTaskScheduler.shared.register(forTaskWithIdentifier: DataUploadBackgroundTask.taskID, using: nil) { task in + if let processingTask = task as? BGProcessingTask { + let backgroundTaskHandler = DataUploadBackgroundTask() + Task { + await backgroundTaskHandler.handleProcessingTask(task: processingTask) + } + } + } + } } diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index efb75cbe..4a0878f0 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 4.0.12 + 4.0.14 CFBundleURLTypes @@ -36,7 +36,7 @@ CFBundleVersion - 4.0.12 + 4.0.14 FirebaseAppDelegateProxyEnabled ITSAppUsesNonExemptEncryption diff --git a/iosApp/iosApp/Observations/AccelerometerBackgroundObservation.swift b/iosApp/iosApp/Observations/AccelerometerBackgroundObservation.swift index 5142c1bb..4b72ecd2 100644 --- a/iosApp/iosApp/Observations/AccelerometerBackgroundObservation.swift +++ b/iosApp/iosApp/Observations/AccelerometerBackgroundObservation.swift @@ -7,14 +7,14 @@ // Digital Health and Prevention - A research institute // of the Ludwig Boltzmann Gesellschaft, // Oesterreichische Vereinigung zur Foerderung -// der wissenschaftlichen Forschung -// Licensed under the Apache 2.0 license with Commons Clause +// der wissenschaftlichen Forschung +// Licensed under the Apache 2.0 license with Commons Clause // (see https://www.apache.org/licenses/LICENSE-2.0 and // https://commonsclause.com/). // -import Foundation import CoreMotion +import Foundation import shared import UIKit @@ -22,21 +22,21 @@ class AccelerometerBackgroundObservation: Observation_ { private var recordForDurationInSec: Double = 60 * 10 private let recorder = CMSensorRecorder() private var startRecording: Date = Date() - + private var timer: Timer? private let semaphore = Semaphore() private let observationRepository: ObservationRepository = { ObservationRepository() }() - + init(sensorPermissions: Set) { super.init(observationType: AccelerometerType(sensorPermissions: sensorPermissions)) } - + override func start() -> Bool { if observerAccessible() { recorder.recordAccelerometer(forDuration: recordForDurationInSec) - self.startRecording = Date() + startRecording = Date() print("CMSensorRecorder started recording accelerometer data for the next \(recordForDurationInSec)s...") DispatchQueue.main.async { [weak self] in self?.timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { [weak self] timer in @@ -51,41 +51,40 @@ class AccelerometerBackgroundObservation: Observation_ { } } } - + return true } return false } - + override func stop(onCompletion: @escaping () -> Void) { timer?.invalidate() - self.collectData(start: Date(timeIntervalSince1970: TimeInterval(self.lastCollectionTimestamp.epochSeconds)), end: Date(), completion: onCompletion) + collectData(start: Date(timeIntervalSince1970: TimeInterval(lastCollectionTimestamp.epochSeconds)), end: Date(), completion: onCompletion) } - + override func store(start: Int64, end: Int64, onCompletion: @escaping () -> Void) { - self.collectData(start: Date(timeIntervalSince1970: TimeInterval(start)), end: Date(timeIntervalSince1970: TimeInterval(end))) { + collectData(start: Date(timeIntervalSince1970: TimeInterval(start)), end: Date(timeIntervalSince1970: TimeInterval(end))) { print("\(Date()): Data collected") super.store(start: start, end: end, onCompletion: {}) print("\(Date()): Returning from store function") onCompletion() } } - - override func applyObservationConfig(settings: [String : Any]) { + + override func applyObservationConfig(settings: [String: Any]) { if var start = settings[Observation_.Companion().CONFIG_TASK_START] as? Int64, let end = settings[Observation_.Companion().CONFIG_TASK_STOP] as? Int64, - Date(timeIntervalSince1970: TimeInterval(end)) > Date() - { + Date(timeIntervalSince1970: TimeInterval(end)) > Date() { let startDate = Date(timeIntervalSince1970: TimeInterval(start)) let endDate = Date(timeIntervalSince1970: TimeInterval(end)) if startDate < Date() { start = Int64(Date().timeIntervalSince1970) } print("Recording time from \(startDate) to \(endDate); \(Double(end - start))s") - self.recordForDurationInSec = Double(end - start) + recordForDurationInSec = Double(end - start) } } - + override func observerErrors() -> Set { var errors: Set = [] if !CMSensorRecorder.isAccelerometerRecordingAvailable() { @@ -102,23 +101,25 @@ class AccelerometerBackgroundObservation: Observation_ { extension AccelerometerBackgroundObservation: ObservationCollector { func collectData(start: Date, end: Date, completion: @escaping () -> Void) { if start < end { - DispatchQueue.global(qos: .background).async { - if let sensorData = self.recorder.accelerometerData(from: start, to: end) { - self.collectionTimestampToNow() - let data = sensorData.compactMap { data in - if let accDatum = data as? CMRecordedAccelerometerData { - let accel = accDatum.acceleration - let timestamp = accDatum.startDate.timeIntervalSince1970 - let dict = ["x": accel.x, "y": accel.y, "z": accel.z] - return ObservationBulkModel(data: dict, timestamp: Int64(timestamp)) + DispatchQueue.global(qos: .background).async { [weak self] in + if let self { + if let sensorData = self.recorder.accelerometerData(from: start, to: end) { + self.collectionTimestampToNow() + let data = sensorData.enumerated().compactMap { index, data -> ObservationBulkModel? in + if index % 2 == 0, let accDatum = data as? CMRecordedAccelerometerData { + let accel = accDatum.acceleration + let timestamp = accDatum.startDate.timeIntervalSince1970 + let dict = ["x": accel.x, "y": accel.y, "z": accel.z] + return ObservationBulkModel(data: dict, timestamp: Int64(timestamp)) + } + return nil } - return nil - } - self.storeData(data: data) { + self.storeData(data: data) { + completion() + } + } else { completion() } - } else { - completion() } } } else { diff --git a/iosApp/iosApp/Services/DataUploadManager.swift b/iosApp/iosApp/Services/DataUploadManager.swift index 41b39760..5b0a9f74 100644 --- a/iosApp/iosApp/Services/DataUploadManager.swift +++ b/iosApp/iosApp/Services/DataUploadManager.swift @@ -17,16 +17,16 @@ import Foundation import shared class DataUploadManager { - private let observationDataRepository = ObservationDataRepository() private let semaphore = Semaphore() private var currentlyUploading = false func uploadData(completion: @escaping (Bool) -> Void) { if !currentlyUploading { currentlyUploading = true - DispatchQueue.global(qos: .background).async { + DispatchQueue.global(qos: .background).async { [weak self] in print("Fetching Data Bulk...") - self.observationDataRepository.allAsBulk { [weak self] dataBulk in + let observationDataRepository = ObservationDataRepository() + observationDataRepository.allAsBulk { dataBulk in if let dataBulk, !dataBulk.dataPoints.isEmpty { print("Sending data to backend...") AppDelegate.shared.networkService.iosSendData(data: dataBulk) { pair in @@ -36,7 +36,7 @@ class DataUploadManager { completion(false) } else if let self, let idSet = pair.first as? Set { print("Sent data! Deleting local data...") - self.observationDataRepository.deleteAllWithId(idSet: idSet) + observationDataRepository.deleteAllWithId(idSet: idSet) print("Deleted data!") self.currentlyUploading = false completion(true) diff --git a/iosApp/iosApp/iosApp.entitlements b/iosApp/iosApp/iosApp.entitlements index 39b021ea..c686016d 100644 --- a/iosApp/iosApp/iosApp.entitlements +++ b/iosApp/iosApp/iosApp.entitlements @@ -4,6 +4,8 @@ aps-environment development + com.apple.developer.kernel.extended-virtual-addressing + com.apple.developer.kernel.increased-memory-limit com.apple.developer.usernotifications.time-sensitive diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 824ae9df..a3fc65b3 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -14,8 +14,8 @@ val mobileAppApiOutputDir = "$openApiOutputDir/mobile_app_api" val mobileAppApiPackage = "io.redlink.more.more_app_multiplatform.openapi" val openapiIgnore = "$openApiInputDir/openapi-ignore" -val coroutinesVersion = "1.7.3" -val ktorVersion = "2.3.10" +val coroutinesVersion = "1.8.1" +val ktorVersion = "2.3.12" val napierVersion = "2.7.1" val serializationVersion = "1.6.0" val gsonVersion = "2.10.1" @@ -52,6 +52,7 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("io.ktor:ktor-client-auth:$ktorVersion") implementation("io.ktor:ktor-client-logging:$ktorVersion") + implementation("dev.tmapps:konnection:1.4.1") } commonTest.dependencies { diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/RealmDatabase.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/RealmDatabase.kt index 3309a80a..4f172b50 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/RealmDatabase.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/RealmDatabase.kt @@ -20,12 +20,10 @@ import io.realm.kotlin.types.RealmObject import io.realm.kotlin.types.TypedRealmObject import io.redlink.more.more_app_mutliplatform.extensions.asMappedFlow import io.redlink.more.more_app_mutliplatform.extensions.firstAsFlow -import io.redlink.more.more_app_mutliplatform.util.Scope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.transform import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import kotlin.reflect.KClass private const val DB_SCHEMA_VERSION: Long = 4 @@ -54,23 +52,23 @@ object RealmDatabase { fun store( realmObjects: Collection, - updatePolicy: UpdatePolicy = UpdatePolicy.ALL - ) { + updatePolicy: UpdatePolicy = UpdatePolicy.ALL, + ): Int { if (realmObjects.isNotEmpty()) { - Scope.launch { - mutex.withLock { - realm?.write { - realmObjects.forEach { - try { - copyToRealm(it, updatePolicy) - } catch (e: Exception) { - Napier.i { "Copy to Realm exception: $e" } - } - } + var storedObjects = realmObjects.size + realm?.writeBlocking { + realmObjects.forEach { + try { + copyToRealm(it, updatePolicy) + } catch (e: Exception) { + Napier.i { "Copy to Realm exception: $e" } + storedObjects--; } } } + return storedObjects } + return 0 } inline fun count(): Flow { diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/DataPointCountRepository.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/DataPointCountRepository.kt index b77e05bc..2d690e2b 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/DataPointCountRepository.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/DataPointCountRepository.kt @@ -13,38 +13,65 @@ package io.redlink.more.more_app_mutliplatform.database.repository import io.realm.kotlin.ext.query import io.redlink.more.more_app_mutliplatform.database.schemas.DataPointCountSchema import io.redlink.more.more_app_mutliplatform.util.StudyScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock class DataPointCountRepository : Repository() { private val mutex = Mutex() + private val countQueue = mutableMapOf() + private var storeJob: Job? = null + override fun count(): Flow { return realmDatabase().count() } fun incrementCount(scheduleIdSet: Set, addCount: Long = 1) { if (scheduleIdSet.isNotEmpty()) { - StudyScope.launch { + StudyScope.launch(Dispatchers.IO) { mutex.withLock { - realm()?.write { - val dataPointCounts = this.query() - .find() - .filter { it.scheduleId in scheduleIdSet } - val dataPointScheduleIds = dataPointCounts.map { it.scheduleId }.toSet() - val (existing, nonExisting) = scheduleIdSet.partition { it in dataPointScheduleIds } - existing.map { id -> - dataPointCounts.firstOrNull { it.scheduleId == id }?.let { - it.count += addCount - } - } - nonExisting.map { id -> - this.copyToRealm(DataPointCountSchema().apply { - count = addCount - this.scheduleId = id - }) + scheduleIdSet.forEach { scheduleId -> + countQueue[scheduleId] = countQueue.getOrElse(scheduleId) { 0 } + addCount + } + } + if (storeJob == null || storeJob?.isActive == false) { + storeJob = StudyScope.repeatedLaunch(5000L) { + storeCounts() + }.second + storeJob?.invokeOnCompletion { + storeJob = null + } + } + } + } + } + + private suspend fun storeCounts() { + val countsToStore: Map + mutex.withLock { + countsToStore = countQueue.toMap() + countQueue.clear() + } + if (countsToStore.isNotEmpty()) { + StudyScope.launch { + realm()?.write { + val dataPointCounts = this.query().find() + val dataPointScheduleIds = dataPointCounts.map { it.scheduleId }.toSet() + val (existing, nonExisting) = countsToStore.keys.partition { it in dataPointScheduleIds } + existing.forEach { id -> + dataPointCounts.firstOrNull { it.scheduleId == id }?.let { + it.count += countsToStore[id] ?: 0 } } + nonExisting.forEach { id -> + this.copyToRealm(DataPointCountSchema().apply { + count = countsToStore[id] ?: 0 + this.scheduleId = id + }) + } } } } diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationDataRepository.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationDataRepository.kt index f67bbded..017e2b61 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationDataRepository.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationDataRepository.kt @@ -16,6 +16,7 @@ import io.realm.kotlin.ext.query import io.redlink.more.more_app_mutliplatform.database.schemas.ObservationDataSchema import io.redlink.more.more_app_mutliplatform.extensions.mapAsBulkData import io.redlink.more.more_app_mutliplatform.services.network.openapi.model.DataBulk +import io.redlink.more.more_app_mutliplatform.util.Scope import io.redlink.more.more_app_mutliplatform.util.StudyScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO @@ -28,14 +29,20 @@ class ObservationDataRepository : Repository() { private var queue = mutableSetOf() private val mutex = Mutex() + + init { + Scope.repeatedLaunch(10000L) { + if (queue.isNotEmpty()) { + store() + } + } + } + fun addData(dataList: List) { StudyScope.launch { mutex.withLock { queue.addAll(dataList) } - if (queue.size > QUEUE_THRESHOLD) { - store() - } } } @@ -44,7 +51,7 @@ class ObservationDataRepository : Repository() { StudyScope.launch { val queueCopy = mutex.withLock { val queueCopy = queue.toSet() - queue = mutableSetOf() + queue.clear() queueCopy } realmDatabase().store(queueCopy, UpdatePolicy.ERROR) @@ -54,9 +61,8 @@ class ObservationDataRepository : Repository() { override fun count() = realmDatabase().count() - suspend fun allAsBulk(): DataBulk? { - return mutex().withLock { + return mutex.withLock { realmDatabase().query(limit = 5000).firstOrNull() ?.mapAsBulkData() } @@ -71,14 +77,13 @@ class ObservationDataRepository : Repository() { fun deleteAllWithId(idSet: Set) { Napier.i { "Deleting ${idSet.size} elements..." } val objectIdSet = idSet.map { ObjectId(it) }.toSet() - StudyScope.launch { - mutex().withLock { + StudyScope.launch(Dispatchers.IO) { + mutex.withLock { realm()?.write { this.query().find().filter { it.dataId in objectIdSet } - .forEach { - delete(it) - } + .forEach { delete(it) } } + } } } diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationRepository.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationRepository.kt index a7232777..bd1ce9aa 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationRepository.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ObservationRepository.kt @@ -16,6 +16,7 @@ import io.realm.kotlin.types.RealmInstant import io.redlink.more.more_app_mutliplatform.database.schemas.ObservationSchema import io.redlink.more.more_app_mutliplatform.database.schemas.ScheduleSchema import io.redlink.more.more_app_mutliplatform.extensions.asClosure +import io.redlink.more.more_app_mutliplatform.util.Scope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.firstOrNull @@ -34,12 +35,21 @@ class ObservationRepository : Repository() { } fun lastCollection(type: String, timestamp: Long) { + Scope.launch { + realm()?.write { + this.query("observationType == $0", type) + .find() + .forEach { + it.collectionTimestamp = RealmInstant.from(timestamp, 0) + } + } + } + } + + fun lastCollection(type: Set, timestamp: Long) { realm()?.writeBlocking { - this.query("observationType == $0", type) - .find() - .forEach { - it.collectionTimestamp = RealmInstant.from(timestamp, 0) - } + this.query("observationType IN $0", type).find() + .forEach { it.collectionTimestamp = RealmInstant.from(timestamp, 0) } } } @@ -55,6 +65,17 @@ class ObservationRepository : Repository() { fun collectAllTimestamps() = observations().transform { emit(it.associate { it.observationType to it.collectionTimestamp }) } + fun collectTimestampForObservationIds(observationIds: Set) = + realmDatabase().queryAllWhereFieldInList( + "observationType", + observationIds + ).transform { + emit( + it.maxByOrNull { it.collectionTimestamp }?.collectionTimestamp + ?: RealmInstant.now() + ) + } + fun collectTimestampOfType(type: String, newState: (RealmInstant?) -> Unit): Closeable { return collectionTimestamp(type).asClosure(newState) } diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ScheduleRepository.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ScheduleRepository.kt index 971530d4..60fd7bd2 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ScheduleRepository.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/database/repository/ScheduleRepository.kt @@ -20,14 +20,18 @@ import io.redlink.more.more_app_mutliplatform.extensions.areAllNamesIn import io.redlink.more.more_app_mutliplatform.extensions.asClosure import io.redlink.more.more_app_mutliplatform.extensions.asMappedFlow import io.redlink.more.more_app_mutliplatform.extensions.firstAsFlow +import io.redlink.more.more_app_mutliplatform.extensions.localDateTime +import io.redlink.more.more_app_mutliplatform.extensions.toLocalDate import io.redlink.more.more_app_mutliplatform.models.ScheduleState import io.redlink.more.more_app_mutliplatform.observations.DataRecorder import io.redlink.more.more_app_mutliplatform.observations.ObservationFactory +import io.redlink.more.more_app_mutliplatform.observations.observationTypes.ObservationType import io.redlink.more.more_app_mutliplatform.services.bluetooth.BluetoothDeviceManager import io.redlink.more.more_app_mutliplatform.util.StudyScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.transform import kotlinx.datetime.Clock import org.mongodb.kbson.ObjectId @@ -66,6 +70,19 @@ class ScheduleRepository : Repository() { } ?: emptyFlow() } + fun allSchedulesToday(observationType: ObservationType): Flow> { + val today = Clock.System.now().localDateTime().date + return realm()?.query( + "observationType = $0", + observationType.observationType + ) + ?.asMappedFlow()?.transform { list -> + emit(list.filter { + !it.getState().completed() + && (it.start?.toLocalDate() == today || it.end?.toLocalDate() == today) + }) + } ?: flow { emit(emptyList()) } + } fun firstScheduleIdAvailableForObservationId(observationId: String): Flow = firstScheduleAvailableForObservationId(observationId).transform { it?.scheduleId?.toHexString() } @@ -157,13 +174,14 @@ class ScheduleRepository : Repository() { if (scheduleSchema.getState() != newState) { Napier.i { "State update for Schema: $scheduleSchema; ${scheduleSchema.getState()} -> $newState" } } - if (autoStartingObservations.isNotEmpty() - && scheduleSchema.hidden - && newState.active() - && scheduleSchema.observationType in autoStartingObservations - && observationFactory.observation(scheduleSchema.observationType) + if (newState == ScheduleState.RUNNING + || (autoStartingObservations.isNotEmpty() + && scheduleSchema.hidden + && newState.active() + && scheduleSchema.observationType in autoStartingObservations + && observationFactory.observation(scheduleSchema.observationType) ?.bleDevicesNeeded() - ?.areAllNamesIn(BluetoothDeviceManager.connectedDevices.value) != false + ?.areAllNamesIn(BluetoothDeviceManager.connectedDevices.value) != false) ) { scheduleSchema.scheduleId.toHexString() } else { diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/extensions/RealmExtensions.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/extensions/RealmExtensions.kt index b50f6895..52478f14 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/extensions/RealmExtensions.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/extensions/RealmExtensions.kt @@ -11,14 +11,18 @@ package io.redlink.more.more_app_mutliplatform.extensions import io.realm.kotlin.query.RealmQuery +import io.realm.kotlin.types.RealmInstant import io.realm.kotlin.types.TypedRealmObject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.transform +import kotlinx.datetime.LocalDate -fun RealmQuery.asMappedFlow(): Flow> { +fun RealmQuery.asMappedFlow(): Flow> { return asFlow().transform { emit(it.list) } } -fun RealmQuery.firstAsFlow(): Flow { +fun RealmQuery.firstAsFlow(): Flow { return first().asFlow().transform { emit(it.obj) } -} \ No newline at end of file +} + +fun RealmInstant.toLocalDate(): LocalDate = this.toInstant().localDateTime().date \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/Observation.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/Observation.kt index 0cc4ff45..bed41362 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/Observation.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/Observation.kt @@ -11,6 +11,7 @@ package io.redlink.more.more_app_mutliplatform.observations import io.github.aakira.napier.Napier +import io.redlink.more.more_app_mutliplatform.database.repository.ObservationRepository import io.redlink.more.more_app_mutliplatform.database.repository.ScheduleRepository import io.redlink.more.more_app_mutliplatform.database.schemas.NotificationSchema import io.redlink.more.more_app_mutliplatform.database.schemas.ObservationDataSchema @@ -20,10 +21,12 @@ import io.redlink.more.more_app_mutliplatform.services.notification.Notification import io.redlink.more.more_app_mutliplatform.util.StudyScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.update import kotlinx.coroutines.withContext import kotlinx.datetime.Clock @@ -33,6 +36,7 @@ abstract class Observation(val observationType: ObservationType) { private val scheduleRepository = ScheduleRepository() private var dataManager: ObservationDataManager? = null private var notificationManager: NotificationManager? = null + private val observationRepository = ObservationRepository() private var running = false private val observationIds = mutableSetOf() @@ -43,6 +47,8 @@ abstract class Observation(val observationType: ObservationType) { protected var lastCollectionTimestamp: Instant = Clock.System.now() + var timestampCollectionJob: Job? = null + private val _observationErrors = MutableStateFlow>>( Pair( this.observationType.observationType, @@ -53,6 +59,16 @@ abstract class Observation(val observationType: ObservationType) { fun start(observationId: String, scheduleId: String, notificationId: String? = null): Boolean { observationIds.add(observationId) + timestampCollectionJob?.cancel() + timestampCollectionJob = StudyScope.launch { + observationRepository.collectTimestampForObservationIds(observationIds).collect { + lastCollectionTimestamp = Instant.fromEpochSeconds(it.epochSeconds) + Napier.d(tag = "Observation::start") { "Last collection $lastCollectionTimestamp" } + } + }.second + timestampCollectionJob?.invokeOnCompletion { + timestampCollectionJob = null + } scheduleIds[scheduleId] = observationId notificationId?.let { notificationIds[scheduleId] = notificationId @@ -73,6 +89,7 @@ abstract class Observation(val observationType: ObservationType) { Napier.i(tag = "Observation::stop") { "Stopping observation of type ${observationType.observationType} for schedule $scheduleId." } if (observationIds.size <= 1) { stop { + timestampCollectionJob?.cancel() saveAndSend() observationShutdown(scheduleId) } @@ -115,7 +132,12 @@ abstract class Observation(val observationType: ObservationType) { } protected fun collectionTimestampToNow() { - this.lastCollectionTimestamp = Clock.System.now() + Napier.d(tag = "Observation::collectionTimeStampToNow") { "Collecting timestamp" } + lastCollectionTimestamp = Clock.System.now() + observationRepository.lastCollection( + observationIds.toSet(), + lastCollectionTimestamp.epochSeconds + ) } protected abstract fun start(): Boolean @@ -125,7 +147,7 @@ abstract class Observation(val observationType: ObservationType) { fun observerAccessible(): Boolean { val errors = observerErrors() Napier.d(tag = "Observation::observerAccessible") { errors.toString() } - this._observationErrors.update { Pair(observationType.observationType, errors) } + updateObservationErrors() return errors.isEmpty() } @@ -133,8 +155,18 @@ abstract class Observation(val observationType: ObservationType) { fun updateObservationErrors() { StudyScope.launch(Dispatchers.IO) { - Napier.d(tag = "Observation::updateObservationErrors") { "ObservationErrors for ${observationType.observationType}" } - _observationErrors.update { Pair(observationType.observationType, observerErrors()) } + scheduleRepository.allSchedulesToday(observationType).firstOrNull()?.let { + if (it.isNotEmpty()) { + Napier.d(tag = "Observation::updateObservationErrors") { "ObservationErrors for ${observationType.observationType}" } + + _observationErrors.update { + Pair( + observationType.observationType, + observerErrors() + ) + } + } + } } } @@ -164,6 +196,7 @@ abstract class Observation(val observationType: ObservationType) { fun stopAndFinish(scheduleId: String) { Napier.i(tag = "Observation::stopAndFinish") { "Stopping and finishing observation ${observationType.observationType} for observationIds: $observationIds" } stop { + timestampCollectionJob?.cancel() saveAndSend() observationShutdown(scheduleId) } @@ -173,6 +206,7 @@ abstract class Observation(val observationType: ObservationType) { fun stopAndSetState(state: ScheduleState = ScheduleState.ACTIVE, scheduleId: String?) { Napier.d(tag = "Observation::stopAndSetState") { "Stopping observation of type ${observationType.observationType} and setting state to $state for schedule $scheduleId." } stop { + timestampCollectionJob?.cancel() saveAndSend() scheduleIds.keys.forEach { scheduleRepository.setRunningStateFor(it, state) } scheduleId?.let { @@ -185,6 +219,7 @@ abstract class Observation(val observationType: ObservationType) { fun stopAndSetDone(scheduleId: String) { Napier.d(tag = "Observation::stopAndSetDone") { "Stopping observation of type ${observationType.observationType} and setting done for schedule $scheduleId." } stop { + timestampCollectionJob?.cancel() saveAndSend() scheduleIds.keys.forEach { scheduleRepository.setCompletionStateFor(it, true) } observationShutdown(scheduleId) diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationDataManager.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationDataManager.kt index 50bc59b1..20f9d17c 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationDataManager.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationDataManager.kt @@ -10,33 +10,26 @@ */ package io.redlink.more.more_app_mutliplatform.observations +import dev.tmapps.konnection.Konnection import io.github.aakira.napier.Napier import io.redlink.more.more_app_mutliplatform.database.repository.DataPointCountRepository import io.redlink.more.more_app_mutliplatform.database.repository.ObservationDataRepository -import io.redlink.more.more_app_mutliplatform.database.repository.ObservationRepository import io.redlink.more.more_app_mutliplatform.database.schemas.ObservationDataSchema import io.redlink.more.more_app_mutliplatform.util.Scope -import io.redlink.more.more_app_mutliplatform.util.StudyScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO -import kotlinx.coroutines.flow.cancellable +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.datetime.Clock abstract class ObservationDataManager { - private val mutex = Mutex() - private val timeStampMutex = Mutex() private val observationDataRepository = ObservationDataRepository() - private val observationRepository = ObservationRepository() private val dataPointCountRepository = DataPointCountRepository() - private var countJob: String? = null - private var scheduleCountJob: String? = null - private var observationTimestampJob: String? = null + private var countJob: Job? = null private var scheduleCount = mutableMapOf() - private var observationCollectionTimestamp = mutableMapOf() + private val konnection = Konnection.instance + + private var uploadJob: Job? = null init { Napier.i(tag = "ObservationDataManager::init") { "ObservationDataManager init!" } @@ -46,123 +39,58 @@ abstract class ObservationDataManager { if (dataList.isNotEmpty()) { Napier.i(tag = "ObservationDataManager::add") { "Adding ${dataList.size} observations for schedule IDs: $scheduleIdList" } observationDataRepository.addData(dataList) - if (scheduleCountJob == null) { - listenToDatapointCountChanges() - } - StudyScope.launch(Dispatchers.IO) { - mutex.withLock { - if (countJob == null) { - listenToDatapointCountChanges() - } - scheduleIdList.forEach { - scheduleCount[it] = scheduleCount.getOrElse(it) { 0 } + dataList.size - } - } - } - StudyScope.launch { - val now = Clock.System.now().epochSeconds - val ids = dataList.map { it.observationId }.toSet() - if (observationTimestampJob == null) { - listenToDatapointCountChanges() - } - timeStampMutex.withLock { - ids.forEach { - observationCollectionTimestamp[it] = now - } - } - } + dataPointCountRepository.incrementCount(scheduleIdList, dataList.size.toLong()) } } fun saveAndSend() { Napier.i(tag = "ObservationDataManager::saveAndSend") { "Saving and sending observations" } - StudyScope.launch { - observationDataRepository.store() - observationDataRepository.count().firstOrNull()?.let { - if (it in 1 until DATA_COUNT_THRESHOLD) { - sendData() - } - } - } + observationDataRepository.store() } fun store() { Napier.i(tag = "ObservationDataManager::store") { "Storing observations" } observationDataRepository.store() - storeCount() - storeTimestamps() - } - - private fun storeCount() { - if (scheduleCount.isNotEmpty()) { - Napier.i(tag = "ObservationDataManager::storeCount") { "Storing count of observations" } - StudyScope.launch { - val copiedScheduleCount = mutex.withLock { - val copiedScheduleCount = scheduleCount.toMap() - scheduleCount = mutableMapOf() - copiedScheduleCount - } - copiedScheduleCount.forEach { - dataPointCountRepository.incrementCount(setOf(it.key), it.value) - } - } - } - } - - private fun storeTimestamps() { - if (observationCollectionTimestamp.isNotEmpty()) { - Napier.d(tag = "ObservationDataManager::storeTimestamps") { "Storing timestamps of observations" } - StudyScope.launch { - val copiedMap = timeStampMutex.withLock { - val copiedMap = observationCollectionTimestamp.toMap() - observationCollectionTimestamp = mutableMapOf() - copiedMap - } - copiedMap.forEach { - observationRepository.lastCollection(it.key, it.value) - } - } - } } fun removeDataPointCount(scheduleId: String) { Napier.d(tag = "ObservationDataManager::removeDataPointCount") { "Removing datapoint count for schedule ID: $scheduleId" } scheduleCount.remove(scheduleId) - observationCollectionTimestamp.remove(scheduleId) } abstract fun sendData(onCompletion: (Boolean) -> Unit = {}) fun listenToDatapointCountChanges() { - Napier.d(tag = "ObservationDataManager::listenToDatapointCountChanges") { "Starting to listen for changes in datapoint counts" } - if (countJob == null || Scope.isActive(countJob!!)) { + if (countJob == null) { + Napier.d(tag = "ObservationDataManager::listenToDatapointCountChanges") { "Starting to listen for changes in datapoint counts" } countJob = Scope.repeatedLaunch(10000) { - observationDataRepository.count().cancellable().firstOrNull()?.let { - if (it > 0) { - Napier.d(tag = "ObservationDataManager::listenToDatapointCountChanges") { "Observation data count: $it! Sending data..." } - sendData() + if (konnection.isConnected() && (uploadJob == null || uploadJob?.isActive == false)) { + observationDataRepository.count().firstOrNull()?.let { + if (it > 0) { + Napier.d(tag = "ObservationDataManager::listenToDatapointCountChanges") { "Observation data count: $it! Sending data..." } + uploadJob = Scope.launch(Dispatchers.IO) { + sendData() + }.second + uploadJob?.invokeOnCompletion { + uploadJob = null + } + } } + } else { + Napier.d(tag = "ObservationDataManager::listenToDatapointCountChanges") { "No conncetion" } + uploadJob?.cancel() } - }.first - } - if (scheduleCountJob == null || Scope.isActive(scheduleCountJob!!)) { - scheduleCountJob = Scope.repeatedLaunch(5000) { - storeCount() - }.first - } - if (observationTimestampJob == null || Scope.isActive(observationTimestampJob!!)) { - observationTimestampJob = Scope.repeatedLaunch(15000) { - storeTimestamps() - }.first + }.second + countJob?.invokeOnCompletion { + countJob = null + } } } fun stopListeningToCountChanges() { Napier.d(tag = "ObservationDataManager::stopListeningToCountChanges") { "Stopped listening for changes in datapoint counts" } - Scope.cancel(setOf(countJob, scheduleCountJob, observationTimestampJob).filterNotNull()) + countJob?.cancel() countJob = null - scheduleCountJob = null - observationTimestampJob = null } private fun deleteAll(idSet: Set) { diff --git a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationManager.kt b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationManager.kt index 177ee448..152dd99e 100644 --- a/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationManager.kt +++ b/shared/src/commonMain/kotlin/io/redlink/more/more_app_mutliplatform/observations/ObservationManager.kt @@ -244,7 +244,7 @@ class ObservationManager( onCompletion(true) } } - } ?: kotlin.run { + } ?: run { if (++counter == runningObservations.size) { onCompletion(true) } diff --git a/shared/src/iosMain/kotlin/io/redlink/more/more_app_mutliplatform/services/network/HttpClientReceiver.kt b/shared/src/iosMain/kotlin/io/redlink/more/more_app_mutliplatform/services/network/HttpClientReceiver.kt index b5b5d7d3..48e00bf4 100644 --- a/shared/src/iosMain/kotlin/io/redlink/more/more_app_mutliplatform/services/network/HttpClientReceiver.kt +++ b/shared/src/iosMain/kotlin/io/redlink/more/more_app_mutliplatform/services/network/HttpClientReceiver.kt @@ -10,16 +10,20 @@ */ package io.redlink.more.more_app_mutliplatform.services.network -import io.ktor.client.* -import io.ktor.client.engine.darwin.* -import io.ktor.client.plugins.* -import io.ktor.client.plugins.auth.* -import io.ktor.client.plugins.auth.providers.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.plugins.logging.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* +import io.ktor.client.HttpClient +import io.ktor.client.engine.darwin.Darwin +import io.ktor.client.plugins.auth.Auth +import io.ktor.client.plugins.auth.providers.basic +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.plugins.retry +import io.ktor.client.request.request +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json actual fun getHttpClient(customLogger: Logger): HttpClient = HttpClient(Darwin) { install(ContentNegotiation) { @@ -29,6 +33,9 @@ actual fun getHttpClient(customLogger: Logger): HttpClient = HttpClient(Darwin) } request { contentType(ContentType.Application.Json) + retry { + maxRetries = 3 + } } Logging { logger = customLogger