diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 7f4f9dc2..7d48b923 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -31,7 +31,7 @@ jobs:
uses: actions/upload-artifact@v4.5.0
with:
name: unsigned-release-apk
- path: app/build/outputs/apk/fdroid/release/app-fdroid-release-unsigned.apk
+ path: androidApp/build/outputs/apk/fdroid/release/androidApp-fdroid-release-unsigned.apk
if-no-files-found: error
- uses: noriban/sign-android-release@v5
name: Sign app APK
@@ -39,7 +39,7 @@ jobs:
# ID used to access action output
id: sign_app
with:
- releaseDirectory: app/build/outputs/apk/fdroid/release
+ releaseDirectory: androidApp/build/outputs/apk/fdroid/release
signingKeyBase64: ${{ secrets.CI_KEYSTORE_BASE64 }}
alias: ${{ secrets.CI_ALIAS }}
keyStorePassword: ${{ secrets.CI_KEY_STORE_PASSWORD }}
diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
index dcf11f97..cee607e2 100644
--- a/.github/workflows/create-release.yml
+++ b/.github/workflows/create-release.yml
@@ -85,18 +85,18 @@ jobs:
# ID used to access action output
id: sign_app
with:
- releaseDirectory: app/build/outputs/apk/fdroid/release
+ releaseDirectory: androidApp/build/outputs/apk/fdroid/release
signingKeyBase64: ${{ secrets.RELEASE_KEY_STORE_BASE64 }}
alias: ${{ secrets.RELEASE_ALIAS }}
keyStorePassword: ${{ secrets.RELEASE_KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.RELEASE_KEY_PASSWORD }}
- name: Rename signed APK
- run: mv ${{steps.sign_app.outputs.signedReleaseFile}} app/build/outputs/apk/fdroid/release/ScanBridge${{ needs.prepare.outputs.artifact_version_suffix }}.apk
+ run: mv ${{steps.sign_app.outputs.signedReleaseFile}} androidApp/build/outputs/apk/fdroid/release/ScanBridge${{ needs.prepare.outputs.artifact_version_suffix }}.apk
- name: Upload signed APK
uses: actions/upload-artifact@v4.5.0
with:
name: signed-release-apk
- path: app/build/outputs/apk/fdroid/release/ScanBridge${{ needs.prepare.outputs.artifact_version_suffix }}.apk
+ path: androidApp/build/outputs/apk/fdroid/release/ScanBridge${{ needs.prepare.outputs.artifact_version_suffix }}.apk
if-no-files-found: error
publish-result:
diff --git a/.gitignore b/.gitignore
index 802c8c92..91250319 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,5 @@ app/src/androidTest/native-libs
/app/debug
/app/release
/app/play
-/fastlane/report.xml
\ No newline at end of file
+/fastlane/report.xml
+/http-requests/http-client.private.env.json
diff --git a/README.md b/README.md
index a01bd621..679da54f 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-
-
-
-
-
+
+
+
## Features
@@ -104,7 +104,7 @@ If you want to chat with me or other users, you can join the Matrix room
## Contributions
Contributions are welcome, and it would be amazing if you want to help. Refer to
-the [Contribution Guidelines](CONTRIBUTING.md) for more information.
+the [Contribution Guidelines](androidApp/CONTRIBUTING.md) for more information.
## License
diff --git a/app/.gitignore b/androidApp/.gitignore
similarity index 100%
rename from app/.gitignore
rename to androidApp/.gitignore
diff --git a/CHANGELOG.md b/androidApp/CHANGELOG.md
similarity index 100%
rename from CHANGELOG.md
rename to androidApp/CHANGELOG.md
diff --git a/CONTRIBUTING.md b/androidApp/CONTRIBUTING.md
similarity index 100%
rename from CONTRIBUTING.md
rename to androidApp/CONTRIBUTING.md
diff --git a/COPYING b/androidApp/COPYING
similarity index 100%
rename from COPYING
rename to androidApp/COPYING
diff --git a/Gemfile b/androidApp/Gemfile
similarity index 100%
rename from Gemfile
rename to androidApp/Gemfile
diff --git a/Gemfile.lock b/androidApp/Gemfile.lock
similarity index 100%
rename from Gemfile.lock
rename to androidApp/Gemfile.lock
diff --git a/adb+.sh b/androidApp/adb+.sh
similarity index 100%
rename from adb+.sh
rename to androidApp/adb+.sh
diff --git a/assets/GetItOnGooglePlay_Badge_Web_color_English.svg b/androidApp/assets/GetItOnGooglePlay_Badge_Web_color_English.svg
similarity index 100%
rename from assets/GetItOnGooglePlay_Badge_Web_color_English.svg
rename to androidApp/assets/GetItOnGooglePlay_Badge_Web_color_English.svg
diff --git a/assets/get-it-on-fdroid.svg b/androidApp/assets/get-it-on-fdroid.svg
similarity index 100%
rename from assets/get-it-on-fdroid.svg
rename to androidApp/assets/get-it-on-fdroid.svg
diff --git a/assets/icon.svg b/androidApp/assets/icon.svg
similarity index 100%
rename from assets/icon.svg
rename to androidApp/assets/icon.svg
diff --git a/app/build.gradle.kts b/androidApp/build.gradle.kts
similarity index 77%
rename from app/build.gradle.kts
rename to androidApp/build.gradle.kts
index 01f50bcc..3aa83677 100644
--- a/app/build.gradle.kts
+++ b/androidApp/build.gradle.kts
@@ -14,30 +14,14 @@ plugins {
alias(libs.plugins.koin)
alias(libs.plugins.protobuf)
alias(libs.plugins.room)
- id("com.google.devtools.ksp") version "2.3.6"
- id("app.cash.paraphrase") version "0.4.1"
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.paraphrase)
}
-fun getGitCommitHash(): String {
- return try {
- val command = "git rev-parse --short HEAD"
- val process = ProcessBuilder()
- .command(command.split(" "))
- .directory(rootProject.projectDir)
- .redirectError(ProcessBuilder.Redirect.INHERIT)
- .start()
- val wait = process.waitFor(60, TimeUnit.SECONDS)
- if (!wait) {
- return "unknown"
- }
-
- val result = process.inputStream.bufferedReader().readText()
-
- result.trim()
- } catch (_: Exception) {
- "unknown" // Fallback
- }
-}
+val gitHashProvider = providers.exec {
+ commandLine("git", "rev-parse", "--short", "HEAD")
+ isIgnoreExitValue = true
+}.standardOutput.asText.map { it.trim() }
tasks.withType().all {
compilerOptions {
@@ -53,10 +37,6 @@ android {
generateLocaleConfig = true
}
- room {
- schemaDirectory("$projectDir/schemas")
- }
-
defaultConfig {
applicationId = "io.github.chrisimx.scanbridge"
minSdk = 28
@@ -76,10 +56,10 @@ android {
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
- buildConfigField("String", "GIT_COMMIT_HASH", "\"${getGitCommitHash()}\"")
+ buildConfigField("String", "GIT_COMMIT_HASH", "\"${gitHashProvider.get()}\"")
}
debug {
- buildConfigField("String", "GIT_COMMIT_HASH", "\"${getGitCommitHash()}\"")
+ buildConfigField("String", "GIT_COMMIT_HASH", "\"${gitHashProvider.get()}\"")
}
}
flavorDimensions += "edition"
@@ -96,8 +76,8 @@ android {
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
compose = true
@@ -107,11 +87,16 @@ android {
kotlin {
compilerOptions {
- jvmTarget = JvmTarget.JVM_11
+ jvmTarget = JvmTarget.JVM_17
optIn.add("kotlin.uuid.ExperimentalUuidApi")
+ freeCompilerArgs.add("-Xnon-local-break-continue")
}
}
+room {
+ schemaDirectory("$projectDir/schemas")
+}
+
dependencies {
implementation(libs.koin.core)
implementation(libs.koin.android)
@@ -119,17 +104,19 @@ dependencies {
implementation(libs.koin.annotations)
implementation(libs.koin.compose.viewmodel)
implementation(libs.koin.androix.navigation)
- implementation(libs.esclkt)
implementation(libs.zoomable)
implementation(libs.kotlin.reflect)
implementation(libs.coil.compose)
implementation(libs.timber)
implementation(libs.androidx.navigation.compose)
implementation(libs.kotlinx.serialization.json)
+ implementation(project(":core"))
+ implementation(project(":composeUI"))
+ implementation(libs.androidx.concurrent.futures)
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.runtime)
- implementation(libs.androidx.room.ktx)
+ implementation(libs.androidx.sqlite.bundled)
implementation(libs.androidx.datastore)
implementation(libs.protobuf.kotlin.lite)
@@ -146,12 +133,12 @@ dependencies {
implementation(libs.androidx.material.icons.core)
implementation(libs.androidx.constraintlayout.compose)
implementation(libs.itext7.core)
- implementation(libs.ktor.okhttp)
+ implementation(libs.ktor.client.okhttp)
implementation(libs.ktor.logging)
"playImplementation"(project(":lvl_library"))
- "playImplementation"("com.squareup.retrofit2:retrofit:3.0.0")
- "playImplementation"("com.squareup.retrofit2:converter-gson:2.9.0")
- "playImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
+ "playImplementation"(libs.retrofit)
+ "playImplementation"(libs.retrofit.converter.gson)
+ "playImplementation"(libs.kotlinx.coroutines.android)
testImplementation(libs.junit)
@@ -168,7 +155,9 @@ dependencies {
protobuf {
protoc {
- artifact = "com.google.protobuf:protoc:4.33.5"
+ val protoc = libs.protoc.get()
+
+ artifact = "${protoc.module}:${protoc.version}"
}
generateProtoTasks {
all().forEach { task ->
@@ -182,6 +171,10 @@ protobuf {
}
}
+koinCompiler {
+ compileSafety = true
+}
+
afterEvaluate {
tasks.named("clean") {
doLast {
diff --git a/fastlane/Appfile b/androidApp/fastlane/Appfile
similarity index 100%
rename from fastlane/Appfile
rename to androidApp/fastlane/Appfile
diff --git a/fastlane/Fastfile b/androidApp/fastlane/Fastfile
similarity index 91%
rename from fastlane/Fastfile
rename to androidApp/fastlane/Fastfile
index 9d68fe38..eb1f6e5e 100644
--- a/fastlane/Fastfile
+++ b/androidApp/fastlane/Fastfile
@@ -100,8 +100,8 @@ platform :android do
sh("adb shell wm user-rotation lock #{rotation}")
screengrab(
- tests_apk_path: "app/build/outputs/apk/androidTest/play/debug/app-play-debug-androidTest.apk",
- app_apk_path: "app/build/outputs/apk/play/debug/app-play-debug.apk",
+ tests_apk_path: "androidApp/build/outputs/apk/androidTest/play/debug/androidApp-play-debug-androidTest.apk",
+ app_apk_path: "androidApp/build/outputs/apk/play/debug/androidApp-play-debug.apk",
output_directory: $playMetadata,
device_type: device_type,
tests_package_name: "io.github.chrisimx.scanbridge.play.test",
@@ -111,8 +111,8 @@ platform :android do
)
screengrab(
- tests_apk_path: "app/build/outputs/apk/androidTest/fdroid/debug/app-fdroid-debug-androidTest.apk",
- app_apk_path: "app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk",
+ tests_apk_path: "androidApp/build/outputs/apk/androidTest/fdroid/debug/androidApp-fdroid-debug-androidTest.apk",
+ app_apk_path: "androidApp/build/outputs/apk/fdroid/debug/androidApp-fdroid-debug.apk",
output_directory: "fastlane/metadata/android",
device_type: device_type,
tests_package_name: "io.github.chrisimx.scanbridge.test",
diff --git a/fastlane/README.md b/androidApp/fastlane/README.md
similarity index 100%
rename from fastlane/README.md
rename to androidApp/fastlane/README.md
diff --git a/fastlane/Screengrabfile b/androidApp/fastlane/Screengrabfile
similarity index 100%
rename from fastlane/Screengrabfile
rename to androidApp/fastlane/Screengrabfile
diff --git a/fastlane/metadata/android/de-DE/changelogs/1000000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1000000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1000000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1000000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1000001.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1000001.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1000001.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1000001.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1001000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1001000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1001000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1001000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1002000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1002000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1002000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1002000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1003000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1003000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1003000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1003000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1004000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1004000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1004000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1004000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1005000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1005000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1005000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1005000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1006001.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1006001.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1006001.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1006001.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/1006002.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/1006002.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/1006002.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/1006002.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/2000000.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/2000000.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/2000000.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/2000000.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/2000001.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/2000001.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/2000001.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/2000001.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/2001002.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/2001002.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/2001002.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/2001002.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/2001003.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/2001003.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/2001003.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/2001003.txt
diff --git a/fastlane/metadata/android/de-DE/changelogs/2001004.txt b/androidApp/fastlane/metadata/android/de-DE/changelogs/2001004.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/changelogs/2001004.txt
rename to androidApp/fastlane/metadata/android/de-DE/changelogs/2001004.txt
diff --git a/fastlane/metadata/android/de-DE/full_description.txt b/androidApp/fastlane/metadata/android/de-DE/full_description.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/full_description.txt
rename to androidApp/fastlane/metadata/android/de-DE/full_description.txt
diff --git a/fastlane/metadata/android/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391883115.png b/androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391883115.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391883115.png
rename to androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391883115.png
diff --git a/fastlane/metadata/android/de-DE/images/phoneScreenshots/02-scanSettings_1773391879801.png b/androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/02-scanSettings_1773391879801.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/phoneScreenshots/02-scanSettings_1773391879801.png
rename to androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/02-scanSettings_1773391879801.png
diff --git a/fastlane/metadata/android/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391880499.png b/androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391880499.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391880499.png
rename to androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391880499.png
diff --git a/fastlane/metadata/android/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391882198.png b/androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391882198.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391882198.png
rename to androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391882198.png
diff --git a/fastlane/metadata/android/de-DE/images/phoneScreenshots/05-settingsScreen_1773391884031.png b/androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/05-settingsScreen_1773391884031.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/phoneScreenshots/05-settingsScreen_1773391884031.png
rename to androidApp/fastlane/metadata/android/de-DE/images/phoneScreenshots/05-settingsScreen_1773391884031.png
diff --git a/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392085232.png b/androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392085232.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392085232.png
rename to androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392085232.png
diff --git a/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392081788.png b/androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392081788.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392081788.png
rename to androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392081788.png
diff --git a/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392082774.png b/androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392082774.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392082774.png
rename to androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392082774.png
diff --git a/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392084316.png b/androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392084316.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392084316.png
rename to androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392084316.png
diff --git a/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392086217.png b/androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392086217.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392086217.png
rename to androidApp/fastlane/metadata/android/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392086217.png
diff --git a/fastlane/metadata/android/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541896730.png b/androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541896730.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541896730.png
rename to androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541896730.png
diff --git a/fastlane/metadata/android/de-DE/images/tenInchScreenshots/02-scanSettings_1772541894045.png b/androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/02-scanSettings_1772541894045.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/tenInchScreenshots/02-scanSettings_1772541894045.png
rename to androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/02-scanSettings_1772541894045.png
diff --git a/fastlane/metadata/android/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541894498.png b/androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541894498.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541894498.png
rename to androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541894498.png
diff --git a/fastlane/metadata/android/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541895682.png b/androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541895682.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541895682.png
rename to androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541895682.png
diff --git a/fastlane/metadata/android/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541897397.png b/androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541897397.png
similarity index 100%
rename from fastlane/metadata/android/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541897397.png
rename to androidApp/fastlane/metadata/android/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541897397.png
diff --git a/fastlane/metadata/android/de-DE/short_description.txt b/androidApp/fastlane/metadata/android/de-DE/short_description.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/short_description.txt
rename to androidApp/fastlane/metadata/android/de-DE/short_description.txt
diff --git a/fastlane/metadata/android/de-DE/title.txt b/androidApp/fastlane/metadata/android/de-DE/title.txt
similarity index 100%
rename from fastlane/metadata/android/de-DE/title.txt
rename to androidApp/fastlane/metadata/android/de-DE/title.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1000000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1000000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1000000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1000000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1000001.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1000001.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1000001.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1000001.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1001000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1001000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1001000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1001000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1002000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1002000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1002000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1002000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1003000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1003000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1003000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1003000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1004000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1004000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1004000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1004000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1005000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1005000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1005000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1005000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1006001.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1006001.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1006001.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1006001.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/1006002.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/1006002.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/1006002.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/1006002.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/2000000.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/2000000.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/2000000.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/2000000.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/2000001.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/2000001.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/2000001.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/2000001.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/2001002.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/2001002.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/2001002.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/2001002.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/2001003.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/2001003.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/2001003.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/2001003.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/2001004.txt b/androidApp/fastlane/metadata/android/en-US/changelogs/2001004.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/changelogs/2001004.txt
rename to androidApp/fastlane/metadata/android/en-US/changelogs/2001004.txt
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/androidApp/fastlane/metadata/android/en-US/full_description.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/full_description.txt
rename to androidApp/fastlane/metadata/android/en-US/full_description.txt
diff --git a/fastlane/metadata/android/en-US/images/icon.png b/androidApp/fastlane/metadata/android/en-US/images/icon.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/icon.png
rename to androidApp/fastlane/metadata/android/en-US/images/icon.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391873581.png b/androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391873581.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391873581.png
rename to androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391873581.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/02-scanSettings_1773391870896.png b/androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/02-scanSettings_1773391870896.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/02-scanSettings_1773391870896.png
rename to androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/02-scanSettings_1773391870896.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/03-discoveryScreen_1773391871298.png b/androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/03-discoveryScreen_1773391871298.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/03-discoveryScreen_1773391871298.png
rename to androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/03-discoveryScreen_1773391871298.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391872597.png b/androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391872597.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391872597.png
rename to androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391872597.png
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/05-settingsScreen_1773391874196.png b/androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/05-settingsScreen_1773391874196.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/phoneScreenshots/05-settingsScreen_1773391874196.png
rename to androidApp/fastlane/metadata/android/en-US/images/phoneScreenshots/05-settingsScreen_1773391874196.png
diff --git a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392074370.png b/androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392074370.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392074370.png
rename to androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392074370.png
diff --git a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/02-scanSettings_1773392071595.png b/androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/02-scanSettings_1773392071595.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/sevenInchScreenshots/02-scanSettings_1773392071595.png
rename to androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/02-scanSettings_1773392071595.png
diff --git a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392071982.png b/androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392071982.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392071982.png
rename to androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392071982.png
diff --git a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392073386.png b/androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392073386.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392073386.png
rename to androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392073386.png
diff --git a/fastlane/metadata/android/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392074964.png b/androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392074964.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392074964.png
rename to androidApp/fastlane/metadata/android/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392074964.png
diff --git a/fastlane/metadata/android/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541886403.png b/androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541886403.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541886403.png
rename to androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541886403.png
diff --git a/fastlane/metadata/android/en-US/images/tenInchScreenshots/02-scanSettings_1772541881705.png b/androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/02-scanSettings_1772541881705.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/tenInchScreenshots/02-scanSettings_1772541881705.png
rename to androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/02-scanSettings_1772541881705.png
diff --git a/fastlane/metadata/android/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541884121.png b/androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541884121.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541884121.png
rename to androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541884121.png
diff --git a/fastlane/metadata/android/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541885597.png b/androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541885597.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541885597.png
rename to androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541885597.png
diff --git a/fastlane/metadata/android/en-US/images/tenInchScreenshots/05-settingsScreen_1772541889078.png b/androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/05-settingsScreen_1772541889078.png
similarity index 100%
rename from fastlane/metadata/android/en-US/images/tenInchScreenshots/05-settingsScreen_1772541889078.png
rename to androidApp/fastlane/metadata/android/en-US/images/tenInchScreenshots/05-settingsScreen_1772541889078.png
diff --git a/fastlane/metadata/android/en-US/short_description.txt b/androidApp/fastlane/metadata/android/en-US/short_description.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/short_description.txt
rename to androidApp/fastlane/metadata/android/en-US/short_description.txt
diff --git a/fastlane/metadata/android/en-US/title.txt b/androidApp/fastlane/metadata/android/en-US/title.txt
similarity index 100%
rename from fastlane/metadata/android/en-US/title.txt
rename to androidApp/fastlane/metadata/android/en-US/title.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1000000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1000000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1000000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1000000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1000001.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1000001.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1000001.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1000001.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1001000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1001000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1001000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1001000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1002000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1002000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1002000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1002000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1003000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1003000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1003000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1003000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1004000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1004000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1004000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1004000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1005000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1005000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1005000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1005000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1006001.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1006001.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1006001.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1006001.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/1006002.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/1006002.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/1006002.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/1006002.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/2000000.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/2000000.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/2000000.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/2000000.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/2000001.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/2000001.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/2000001.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/2000001.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/2001002.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/2001002.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/2001002.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/2001002.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/2001003.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/2001003.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/2001003.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/2001003.txt
diff --git a/fastlane/metadata/android/it-IT/changelogs/2001004.txt b/androidApp/fastlane/metadata/android/it-IT/changelogs/2001004.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/changelogs/2001004.txt
rename to androidApp/fastlane/metadata/android/it-IT/changelogs/2001004.txt
diff --git a/fastlane/metadata/android/it-IT/full_description.txt b/androidApp/fastlane/metadata/android/it-IT/full_description.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/full_description.txt
rename to androidApp/fastlane/metadata/android/it-IT/full_description.txt
diff --git a/fastlane/metadata/android/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391892623.png b/androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391892623.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391892623.png
rename to androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391892623.png
diff --git a/fastlane/metadata/android/it-IT/images/phoneScreenshots/02-scanSettings_1773391889434.png b/androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/02-scanSettings_1773391889434.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/phoneScreenshots/02-scanSettings_1773391889434.png
rename to androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/02-scanSettings_1773391889434.png
diff --git a/fastlane/metadata/android/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391890147.png b/androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391890147.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391890147.png
rename to androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391890147.png
diff --git a/fastlane/metadata/android/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391891679.png b/androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391891679.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391891679.png
rename to androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391891679.png
diff --git a/fastlane/metadata/android/it-IT/images/phoneScreenshots/05-settingsScreen_1773391893582.png b/androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/05-settingsScreen_1773391893582.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/phoneScreenshots/05-settingsScreen_1773391893582.png
rename to androidApp/fastlane/metadata/android/it-IT/images/phoneScreenshots/05-settingsScreen_1773391893582.png
diff --git a/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392097014.png b/androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392097014.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392097014.png
rename to androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392097014.png
diff --git a/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392093573.png b/androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392093573.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392093573.png
rename to androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392093573.png
diff --git a/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392094636.png b/androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392094636.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392094636.png
rename to androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392094636.png
diff --git a/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392096232.png b/androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392096232.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392096232.png
rename to androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392096232.png
diff --git a/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392098119.png b/androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392098119.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392098119.png
rename to androidApp/fastlane/metadata/android/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392098119.png
diff --git a/fastlane/metadata/android/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541911340.png b/androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541911340.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541911340.png
rename to androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541911340.png
diff --git a/fastlane/metadata/android/it-IT/images/tenInchScreenshots/02-scanSettings_1772541904718.png b/androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/02-scanSettings_1772541904718.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/tenInchScreenshots/02-scanSettings_1772541904718.png
rename to androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/02-scanSettings_1772541904718.png
diff --git a/fastlane/metadata/android/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541907169.png b/androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541907169.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541907169.png
rename to androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541907169.png
diff --git a/fastlane/metadata/android/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541910309.png b/androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541910309.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541910309.png
rename to androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541910309.png
diff --git a/fastlane/metadata/android/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541914017.png b/androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541914017.png
similarity index 100%
rename from fastlane/metadata/android/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541914017.png
rename to androidApp/fastlane/metadata/android/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541914017.png
diff --git a/fastlane/metadata/android/it-IT/short_description.txt b/androidApp/fastlane/metadata/android/it-IT/short_description.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/short_description.txt
rename to androidApp/fastlane/metadata/android/it-IT/short_description.txt
diff --git a/fastlane/metadata/android/it-IT/title.txt b/androidApp/fastlane/metadata/android/it-IT/title.txt
similarity index 100%
rename from fastlane/metadata/android/it-IT/title.txt
rename to androidApp/fastlane/metadata/android/it-IT/title.txt
diff --git a/fastlane/metadata/android/screenshots.html b/androidApp/fastlane/metadata/android/screenshots.html
similarity index 100%
rename from fastlane/metadata/android/screenshots.html
rename to androidApp/fastlane/metadata/android/screenshots.html
diff --git a/fastlane/playMetadata/de-DE/changelogs/1006002.txt b/androidApp/fastlane/playMetadata/de-DE/changelogs/1006002.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/changelogs/1006002.txt
rename to androidApp/fastlane/playMetadata/de-DE/changelogs/1006002.txt
diff --git a/fastlane/playMetadata/de-DE/changelogs/2000000.txt b/androidApp/fastlane/playMetadata/de-DE/changelogs/2000000.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/changelogs/2000000.txt
rename to androidApp/fastlane/playMetadata/de-DE/changelogs/2000000.txt
diff --git a/fastlane/playMetadata/de-DE/changelogs/2000001.txt b/androidApp/fastlane/playMetadata/de-DE/changelogs/2000001.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/changelogs/2000001.txt
rename to androidApp/fastlane/playMetadata/de-DE/changelogs/2000001.txt
diff --git a/fastlane/playMetadata/de-DE/changelogs/2001002.txt b/androidApp/fastlane/playMetadata/de-DE/changelogs/2001002.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/changelogs/2001002.txt
rename to androidApp/fastlane/playMetadata/de-DE/changelogs/2001002.txt
diff --git a/fastlane/playMetadata/de-DE/changelogs/2001003.txt b/androidApp/fastlane/playMetadata/de-DE/changelogs/2001003.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/changelogs/2001003.txt
rename to androidApp/fastlane/playMetadata/de-DE/changelogs/2001003.txt
diff --git a/fastlane/playMetadata/de-DE/changelogs/2001004.txt b/androidApp/fastlane/playMetadata/de-DE/changelogs/2001004.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/changelogs/2001004.txt
rename to androidApp/fastlane/playMetadata/de-DE/changelogs/2001004.txt
diff --git a/fastlane/playMetadata/de-DE/full_description.txt b/androidApp/fastlane/playMetadata/de-DE/full_description.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/full_description.txt
rename to androidApp/fastlane/playMetadata/de-DE/full_description.txt
diff --git a/fastlane/playMetadata/de-DE/images/chromeScreenshots/01-scannedPageScreen_1773392137705.png b/androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/01-scannedPageScreen_1773392137705.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/chromeScreenshots/01-scannedPageScreen_1773392137705.png
rename to androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/01-scannedPageScreen_1773392137705.png
diff --git a/fastlane/playMetadata/de-DE/images/chromeScreenshots/02-scanSettings_1773392134051.png b/androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/02-scanSettings_1773392134051.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/chromeScreenshots/02-scanSettings_1773392134051.png
rename to androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/02-scanSettings_1773392134051.png
diff --git a/fastlane/playMetadata/de-DE/images/chromeScreenshots/03-discoveryScreen_1773392134837.png b/androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/03-discoveryScreen_1773392134837.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/chromeScreenshots/03-discoveryScreen_1773392134837.png
rename to androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/03-discoveryScreen_1773392134837.png
diff --git a/fastlane/playMetadata/de-DE/images/chromeScreenshots/04-emptyScanScreen_1773392136515.png b/androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/04-emptyScanScreen_1773392136515.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/chromeScreenshots/04-emptyScanScreen_1773392136515.png
rename to androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/04-emptyScanScreen_1773392136515.png
diff --git a/fastlane/playMetadata/de-DE/images/chromeScreenshots/05-settingsScreen_1773392138645.png b/androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/05-settingsScreen_1773392138645.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/chromeScreenshots/05-settingsScreen_1773392138645.png
rename to androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/05-settingsScreen_1773392138645.png
diff --git a/fastlane/playMetadata/de-DE/images/chromeScreenshots/06-supportScreen_1773392131057.png b/androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/06-supportScreen_1773392131057.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/chromeScreenshots/06-supportScreen_1773392131057.png
rename to androidApp/fastlane/playMetadata/de-DE/images/chromeScreenshots/06-supportScreen_1773392131057.png
diff --git a/fastlane/playMetadata/de-DE/images/featureGraphic.png b/androidApp/fastlane/playMetadata/de-DE/images/featureGraphic.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/featureGraphic.png
rename to androidApp/fastlane/playMetadata/de-DE/images/featureGraphic.png
diff --git a/fastlane/playMetadata/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391854599.png b/androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391854599.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391854599.png
rename to androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/01-scannedPageScreen_1773391854599.png
diff --git a/fastlane/playMetadata/de-DE/images/phoneScreenshots/02-scanSettings_1773391851373.png b/androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/02-scanSettings_1773391851373.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/phoneScreenshots/02-scanSettings_1773391851373.png
rename to androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/02-scanSettings_1773391851373.png
diff --git a/fastlane/playMetadata/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391852033.png b/androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391852033.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391852033.png
rename to androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/03-discoveryScreen_1773391852033.png
diff --git a/fastlane/playMetadata/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391853682.png b/androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391853682.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391853682.png
rename to androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/04-emptyScanScreen_1773391853682.png
diff --git a/fastlane/playMetadata/de-DE/images/phoneScreenshots/05-settingsScreen_1773391855601.png b/androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/05-settingsScreen_1773391855601.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/phoneScreenshots/05-settingsScreen_1773391855601.png
rename to androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/05-settingsScreen_1773391855601.png
diff --git a/fastlane/playMetadata/de-DE/images/phoneScreenshots/06-supportScreen_1773391848067.png b/androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/06-supportScreen_1773391848067.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/phoneScreenshots/06-supportScreen_1773391848067.png
rename to androidApp/fastlane/playMetadata/de-DE/images/phoneScreenshots/06-supportScreen_1773391848067.png
diff --git a/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392051915.png b/androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392051915.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392051915.png
rename to androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/01-scannedPageScreen_1773392051915.png
diff --git a/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392048636.png b/androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392048636.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392048636.png
rename to androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/02-scanSettings_1773392048636.png
diff --git a/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392049419.png b/androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392049419.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392049419.png
rename to androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/03-discoveryScreen_1773392049419.png
diff --git a/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392051098.png b/androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392051098.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392051098.png
rename to androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/04-emptyScanScreen_1773392051098.png
diff --git a/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392052967.png b/androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392052967.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392052967.png
rename to androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/05-settingsScreen_1773392052967.png
diff --git a/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/06-supportScreen_1773392044965.png b/androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/06-supportScreen_1773392044965.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/sevenInchScreenshots/06-supportScreen_1773392044965.png
rename to androidApp/fastlane/playMetadata/de-DE/images/sevenInchScreenshots/06-supportScreen_1773392044965.png
diff --git a/fastlane/playMetadata/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541856637.png b/androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541856637.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541856637.png
rename to androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/01-scannedPageScreen_1772541856637.png
diff --git a/fastlane/playMetadata/de-DE/images/tenInchScreenshots/02-scanSettings_1772541853898.png b/androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/02-scanSettings_1772541853898.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/tenInchScreenshots/02-scanSettings_1772541853898.png
rename to androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/02-scanSettings_1772541853898.png
diff --git a/fastlane/playMetadata/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541854300.png b/androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541854300.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541854300.png
rename to androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/03-discoveryScreen_1772541854300.png
diff --git a/fastlane/playMetadata/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541855615.png b/androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541855615.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541855615.png
rename to androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/04-emptyScanScreen_1772541855615.png
diff --git a/fastlane/playMetadata/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541857282.png b/androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541857282.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541857282.png
rename to androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/05-settingsScreen_1772541857282.png
diff --git a/fastlane/playMetadata/de-DE/images/tenInchScreenshots/06-supportScreen_1772541851562.png b/androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/06-supportScreen_1772541851562.png
similarity index 100%
rename from fastlane/playMetadata/de-DE/images/tenInchScreenshots/06-supportScreen_1772541851562.png
rename to androidApp/fastlane/playMetadata/de-DE/images/tenInchScreenshots/06-supportScreen_1772541851562.png
diff --git a/fastlane/playMetadata/de-DE/short_description.txt b/androidApp/fastlane/playMetadata/de-DE/short_description.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/short_description.txt
rename to androidApp/fastlane/playMetadata/de-DE/short_description.txt
diff --git a/fastlane/playMetadata/de-DE/title.txt b/androidApp/fastlane/playMetadata/de-DE/title.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/title.txt
rename to androidApp/fastlane/playMetadata/de-DE/title.txt
diff --git a/fastlane/playMetadata/de-DE/video.txt b/androidApp/fastlane/playMetadata/de-DE/video.txt
similarity index 100%
rename from fastlane/playMetadata/de-DE/video.txt
rename to androidApp/fastlane/playMetadata/de-DE/video.txt
diff --git a/fastlane/playMetadata/en-US/changelogs/1006002.txt b/androidApp/fastlane/playMetadata/en-US/changelogs/1006002.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/changelogs/1006002.txt
rename to androidApp/fastlane/playMetadata/en-US/changelogs/1006002.txt
diff --git a/fastlane/playMetadata/en-US/changelogs/2000000.txt b/androidApp/fastlane/playMetadata/en-US/changelogs/2000000.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/changelogs/2000000.txt
rename to androidApp/fastlane/playMetadata/en-US/changelogs/2000000.txt
diff --git a/fastlane/playMetadata/en-US/changelogs/2000001.txt b/androidApp/fastlane/playMetadata/en-US/changelogs/2000001.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/changelogs/2000001.txt
rename to androidApp/fastlane/playMetadata/en-US/changelogs/2000001.txt
diff --git a/fastlane/playMetadata/en-US/changelogs/2001002.txt b/androidApp/fastlane/playMetadata/en-US/changelogs/2001002.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/changelogs/2001002.txt
rename to androidApp/fastlane/playMetadata/en-US/changelogs/2001002.txt
diff --git a/fastlane/playMetadata/en-US/changelogs/2001003.txt b/androidApp/fastlane/playMetadata/en-US/changelogs/2001003.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/changelogs/2001003.txt
rename to androidApp/fastlane/playMetadata/en-US/changelogs/2001003.txt
diff --git a/fastlane/playMetadata/en-US/changelogs/2001004.txt b/androidApp/fastlane/playMetadata/en-US/changelogs/2001004.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/changelogs/2001004.txt
rename to androidApp/fastlane/playMetadata/en-US/changelogs/2001004.txt
diff --git a/fastlane/playMetadata/en-US/full_description.txt b/androidApp/fastlane/playMetadata/en-US/full_description.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/full_description.txt
rename to androidApp/fastlane/playMetadata/en-US/full_description.txt
diff --git a/fastlane/playMetadata/en-US/images/chromeScreenshots/01-scannedPageScreen_1773392127266.png b/androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/01-scannedPageScreen_1773392127266.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/chromeScreenshots/01-scannedPageScreen_1773392127266.png
rename to androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/01-scannedPageScreen_1773392127266.png
diff --git a/fastlane/playMetadata/en-US/images/chromeScreenshots/02-scanSettings_1773392124079.png b/androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/02-scanSettings_1773392124079.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/chromeScreenshots/02-scanSettings_1773392124079.png
rename to androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/02-scanSettings_1773392124079.png
diff --git a/fastlane/playMetadata/en-US/images/chromeScreenshots/03-discoveryScreen_1773392124691.png b/androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/03-discoveryScreen_1773392124691.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/chromeScreenshots/03-discoveryScreen_1773392124691.png
rename to androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/03-discoveryScreen_1773392124691.png
diff --git a/fastlane/playMetadata/en-US/images/chromeScreenshots/04-emptyScanScreen_1773392126076.png b/androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/04-emptyScanScreen_1773392126076.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/chromeScreenshots/04-emptyScanScreen_1773392126076.png
rename to androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/04-emptyScanScreen_1773392126076.png
diff --git a/fastlane/playMetadata/en-US/images/chromeScreenshots/05-settingsScreen_1773392128027.png b/androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/05-settingsScreen_1773392128027.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/chromeScreenshots/05-settingsScreen_1773392128027.png
rename to androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/05-settingsScreen_1773392128027.png
diff --git a/fastlane/playMetadata/en-US/images/chromeScreenshots/06-supportScreen_1773392121020.png b/androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/06-supportScreen_1773392121020.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/chromeScreenshots/06-supportScreen_1773392121020.png
rename to androidApp/fastlane/playMetadata/en-US/images/chromeScreenshots/06-supportScreen_1773392121020.png
diff --git a/fastlane/playMetadata/en-US/images/featureGraphic.png b/androidApp/fastlane/playMetadata/en-US/images/featureGraphic.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/featureGraphic.png
rename to androidApp/fastlane/playMetadata/en-US/images/featureGraphic.png
diff --git a/fastlane/playMetadata/en-US/images/icon.png b/androidApp/fastlane/playMetadata/en-US/images/icon.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/icon.png
rename to androidApp/fastlane/playMetadata/en-US/images/icon.png
diff --git a/fastlane/playMetadata/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391844501.png b/androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391844501.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391844501.png
rename to androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/01-scannedPageScreen_1773391844501.png
diff --git a/fastlane/playMetadata/en-US/images/phoneScreenshots/02-scanSettings_1773391841799.png b/androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/02-scanSettings_1773391841799.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/phoneScreenshots/02-scanSettings_1773391841799.png
rename to androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/02-scanSettings_1773391841799.png
diff --git a/fastlane/playMetadata/en-US/images/phoneScreenshots/03-discoveryScreen_1773391842230.png b/androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/03-discoveryScreen_1773391842230.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/phoneScreenshots/03-discoveryScreen_1773391842230.png
rename to androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/03-discoveryScreen_1773391842230.png
diff --git a/fastlane/playMetadata/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391843467.png b/androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391843467.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391843467.png
rename to androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/04-emptyScanScreen_1773391843467.png
diff --git a/fastlane/playMetadata/en-US/images/phoneScreenshots/05-settingsScreen_1773391845183.png b/androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/05-settingsScreen_1773391845183.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/phoneScreenshots/05-settingsScreen_1773391845183.png
rename to androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/05-settingsScreen_1773391845183.png
diff --git a/fastlane/playMetadata/en-US/images/phoneScreenshots/06-supportScreen_1773391839216.png b/androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/06-supportScreen_1773391839216.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/phoneScreenshots/06-supportScreen_1773391839216.png
rename to androidApp/fastlane/playMetadata/en-US/images/phoneScreenshots/06-supportScreen_1773391839216.png
diff --git a/fastlane/playMetadata/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392041217.png b/androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392041217.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392041217.png
rename to androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/01-scannedPageScreen_1773392041217.png
diff --git a/fastlane/playMetadata/en-US/images/sevenInchScreenshots/02-scanSettings_1773392038538.png b/androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/02-scanSettings_1773392038538.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/sevenInchScreenshots/02-scanSettings_1773392038538.png
rename to androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/02-scanSettings_1773392038538.png
diff --git a/fastlane/playMetadata/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392039016.png b/androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392039016.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392039016.png
rename to androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/03-discoveryScreen_1773392039016.png
diff --git a/fastlane/playMetadata/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392040264.png b/androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392040264.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392040264.png
rename to androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/04-emptyScanScreen_1773392040264.png
diff --git a/fastlane/playMetadata/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392041800.png b/androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392041800.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392041800.png
rename to androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/05-settingsScreen_1773392041800.png
diff --git a/fastlane/playMetadata/en-US/images/sevenInchScreenshots/06-supportScreen_1773392036000.png b/androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/06-supportScreen_1773392036000.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/sevenInchScreenshots/06-supportScreen_1773392036000.png
rename to androidApp/fastlane/playMetadata/en-US/images/sevenInchScreenshots/06-supportScreen_1773392036000.png
diff --git a/fastlane/playMetadata/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541846501.png b/androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541846501.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541846501.png
rename to androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/01-scannedPageScreen_1772541846501.png
diff --git a/fastlane/playMetadata/en-US/images/tenInchScreenshots/02-scanSettings_1772541839135.png b/androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/02-scanSettings_1772541839135.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/tenInchScreenshots/02-scanSettings_1772541839135.png
rename to androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/02-scanSettings_1772541839135.png
diff --git a/fastlane/playMetadata/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541841584.png b/androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541841584.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541841584.png
rename to androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/03-discoveryScreen_1772541841584.png
diff --git a/fastlane/playMetadata/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541845528.png b/androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541845528.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541845528.png
rename to androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/04-emptyScanScreen_1772541845528.png
diff --git a/fastlane/playMetadata/en-US/images/tenInchScreenshots/05-settingsScreen_1772541849182.png b/androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/05-settingsScreen_1772541849182.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/tenInchScreenshots/05-settingsScreen_1772541849182.png
rename to androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/05-settingsScreen_1772541849182.png
diff --git a/fastlane/playMetadata/en-US/images/tenInchScreenshots/06-supportScreen_1772541835912.png b/androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/06-supportScreen_1772541835912.png
similarity index 100%
rename from fastlane/playMetadata/en-US/images/tenInchScreenshots/06-supportScreen_1772541835912.png
rename to androidApp/fastlane/playMetadata/en-US/images/tenInchScreenshots/06-supportScreen_1772541835912.png
diff --git a/fastlane/playMetadata/en-US/short_description.txt b/androidApp/fastlane/playMetadata/en-US/short_description.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/short_description.txt
rename to androidApp/fastlane/playMetadata/en-US/short_description.txt
diff --git a/fastlane/playMetadata/en-US/title.txt b/androidApp/fastlane/playMetadata/en-US/title.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/title.txt
rename to androidApp/fastlane/playMetadata/en-US/title.txt
diff --git a/fastlane/playMetadata/en-US/video.txt b/androidApp/fastlane/playMetadata/en-US/video.txt
similarity index 100%
rename from fastlane/playMetadata/en-US/video.txt
rename to androidApp/fastlane/playMetadata/en-US/video.txt
diff --git a/fastlane/playMetadata/it-IT/changelogs/1006002.txt b/androidApp/fastlane/playMetadata/it-IT/changelogs/1006002.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/changelogs/1006002.txt
rename to androidApp/fastlane/playMetadata/it-IT/changelogs/1006002.txt
diff --git a/fastlane/playMetadata/it-IT/changelogs/2000000.txt b/androidApp/fastlane/playMetadata/it-IT/changelogs/2000000.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/changelogs/2000000.txt
rename to androidApp/fastlane/playMetadata/it-IT/changelogs/2000000.txt
diff --git a/fastlane/playMetadata/it-IT/changelogs/2000001.txt b/androidApp/fastlane/playMetadata/it-IT/changelogs/2000001.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/changelogs/2000001.txt
rename to androidApp/fastlane/playMetadata/it-IT/changelogs/2000001.txt
diff --git a/fastlane/playMetadata/it-IT/changelogs/2001002.txt b/androidApp/fastlane/playMetadata/it-IT/changelogs/2001002.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/changelogs/2001002.txt
rename to androidApp/fastlane/playMetadata/it-IT/changelogs/2001002.txt
diff --git a/fastlane/playMetadata/it-IT/changelogs/2001003.txt b/androidApp/fastlane/playMetadata/it-IT/changelogs/2001003.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/changelogs/2001003.txt
rename to androidApp/fastlane/playMetadata/it-IT/changelogs/2001003.txt
diff --git a/fastlane/playMetadata/it-IT/changelogs/2001004.txt b/androidApp/fastlane/playMetadata/it-IT/changelogs/2001004.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/changelogs/2001004.txt
rename to androidApp/fastlane/playMetadata/it-IT/changelogs/2001004.txt
diff --git a/fastlane/playMetadata/it-IT/full_description.txt b/androidApp/fastlane/playMetadata/it-IT/full_description.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/full_description.txt
rename to androidApp/fastlane/playMetadata/it-IT/full_description.txt
diff --git a/fastlane/playMetadata/it-IT/images/chromeScreenshots/01-scannedPageScreen_1773392147446.png b/androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/01-scannedPageScreen_1773392147446.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/chromeScreenshots/01-scannedPageScreen_1773392147446.png
rename to androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/01-scannedPageScreen_1773392147446.png
diff --git a/fastlane/playMetadata/it-IT/images/chromeScreenshots/02-scanSettings_1773392144173.png b/androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/02-scanSettings_1773392144173.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/chromeScreenshots/02-scanSettings_1773392144173.png
rename to androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/02-scanSettings_1773392144173.png
diff --git a/fastlane/playMetadata/it-IT/images/chromeScreenshots/03-discoveryScreen_1773392144879.png b/androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/03-discoveryScreen_1773392144879.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/chromeScreenshots/03-discoveryScreen_1773392144879.png
rename to androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/03-discoveryScreen_1773392144879.png
diff --git a/fastlane/playMetadata/it-IT/images/chromeScreenshots/04-emptyScanScreen_1773392146388.png b/androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/04-emptyScanScreen_1773392146388.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/chromeScreenshots/04-emptyScanScreen_1773392146388.png
rename to androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/04-emptyScanScreen_1773392146388.png
diff --git a/fastlane/playMetadata/it-IT/images/chromeScreenshots/05-settingsScreen_1773392148380.png b/androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/05-settingsScreen_1773392148380.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/chromeScreenshots/05-settingsScreen_1773392148380.png
rename to androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/05-settingsScreen_1773392148380.png
diff --git a/fastlane/playMetadata/it-IT/images/chromeScreenshots/06-supportScreen_1773392140988.png b/androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/06-supportScreen_1773392140988.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/chromeScreenshots/06-supportScreen_1773392140988.png
rename to androidApp/fastlane/playMetadata/it-IT/images/chromeScreenshots/06-supportScreen_1773392140988.png
diff --git a/fastlane/playMetadata/it-IT/images/featureGraphic.png b/androidApp/fastlane/playMetadata/it-IT/images/featureGraphic.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/featureGraphic.png
rename to androidApp/fastlane/playMetadata/it-IT/images/featureGraphic.png
diff --git a/fastlane/playMetadata/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391864832.png b/androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391864832.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391864832.png
rename to androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/01-scannedPageScreen_1773391864832.png
diff --git a/fastlane/playMetadata/it-IT/images/phoneScreenshots/02-scanSettings_1773391861554.png b/androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/02-scanSettings_1773391861554.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/phoneScreenshots/02-scanSettings_1773391861554.png
rename to androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/02-scanSettings_1773391861554.png
diff --git a/fastlane/playMetadata/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391862266.png b/androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391862266.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391862266.png
rename to androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/03-discoveryScreen_1773391862266.png
diff --git a/fastlane/playMetadata/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391863833.png b/androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391863833.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391863833.png
rename to androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/04-emptyScanScreen_1773391863833.png
diff --git a/fastlane/playMetadata/it-IT/images/phoneScreenshots/05-settingsScreen_1773391865784.png b/androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/05-settingsScreen_1773391865784.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/phoneScreenshots/05-settingsScreen_1773391865784.png
rename to androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/05-settingsScreen_1773391865784.png
diff --git a/fastlane/playMetadata/it-IT/images/phoneScreenshots/06-supportScreen_1773391858142.png b/androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/06-supportScreen_1773391858142.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/phoneScreenshots/06-supportScreen_1773391858142.png
rename to androidApp/fastlane/playMetadata/it-IT/images/phoneScreenshots/06-supportScreen_1773391858142.png
diff --git a/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392064698.png b/androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392064698.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392064698.png
rename to androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/01-scannedPageScreen_1773392064698.png
diff --git a/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392060668.png b/androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392060668.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392060668.png
rename to androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/02-scanSettings_1773392060668.png
diff --git a/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392062139.png b/androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392062139.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392062139.png
rename to androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/03-discoveryScreen_1773392062139.png
diff --git a/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392063851.png b/androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392063851.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392063851.png
rename to androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/04-emptyScanScreen_1773392063851.png
diff --git a/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392065789.png b/androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392065789.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392065789.png
rename to androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/05-settingsScreen_1773392065789.png
diff --git a/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/06-supportScreen_1773392056702.png b/androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/06-supportScreen_1773392056702.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/sevenInchScreenshots/06-supportScreen_1773392056702.png
rename to androidApp/fastlane/playMetadata/it-IT/images/sevenInchScreenshots/06-supportScreen_1773392056702.png
diff --git a/fastlane/playMetadata/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541871263.png b/androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541871263.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541871263.png
rename to androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/01-scannedPageScreen_1772541871263.png
diff --git a/fastlane/playMetadata/it-IT/images/tenInchScreenshots/02-scanSettings_1772541864073.png b/androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/02-scanSettings_1772541864073.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/tenInchScreenshots/02-scanSettings_1772541864073.png
rename to androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/02-scanSettings_1772541864073.png
diff --git a/fastlane/playMetadata/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541866487.png b/androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541866487.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541866487.png
rename to androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/03-discoveryScreen_1772541866487.png
diff --git a/fastlane/playMetadata/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541870383.png b/androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541870383.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541870383.png
rename to androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/04-emptyScanScreen_1772541870383.png
diff --git a/fastlane/playMetadata/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541873928.png b/androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541873928.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541873928.png
rename to androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/05-settingsScreen_1772541873928.png
diff --git a/fastlane/playMetadata/it-IT/images/tenInchScreenshots/06-supportScreen_1772541861315.png b/androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/06-supportScreen_1772541861315.png
similarity index 100%
rename from fastlane/playMetadata/it-IT/images/tenInchScreenshots/06-supportScreen_1772541861315.png
rename to androidApp/fastlane/playMetadata/it-IT/images/tenInchScreenshots/06-supportScreen_1772541861315.png
diff --git a/fastlane/playMetadata/it-IT/short_description.txt b/androidApp/fastlane/playMetadata/it-IT/short_description.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/short_description.txt
rename to androidApp/fastlane/playMetadata/it-IT/short_description.txt
diff --git a/fastlane/playMetadata/it-IT/title.txt b/androidApp/fastlane/playMetadata/it-IT/title.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/title.txt
rename to androidApp/fastlane/playMetadata/it-IT/title.txt
diff --git a/fastlane/playMetadata/it-IT/video.txt b/androidApp/fastlane/playMetadata/it-IT/video.txt
similarity index 100%
rename from fastlane/playMetadata/it-IT/video.txt
rename to androidApp/fastlane/playMetadata/it-IT/video.txt
diff --git a/fastlane/playMetadata/screenshots.html b/androidApp/fastlane/playMetadata/screenshots.html
similarity index 100%
rename from fastlane/playMetadata/screenshots.html
rename to androidApp/fastlane/playMetadata/screenshots.html
diff --git a/androidApp/gradle/gradle-daemon-jvm.properties b/androidApp/gradle/gradle-daemon-jvm.properties
new file mode 100644
index 00000000..c4e7acbd
--- /dev/null
+++ b/androidApp/gradle/gradle-daemon-jvm.properties
@@ -0,0 +1,13 @@
+#This file is generated by updateDaemonJvm
+toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
+toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
+toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect
+toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
+toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/0b98aec810298c2c1d7fdac5dac37910/redirect
+toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/658299a896470fbb3103ba3a430ee227/redirect
+toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
+toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
+toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/248ffb1098f61659502d0c09aa348294/redirect
+toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/ac151d55def6b6a9a159dc4cb4642851/redirect
+toolchainVendor=JETBRAINS
+toolchainVersion=21
diff --git a/androidApp/gradle/libs.versions.toml b/androidApp/gradle/libs.versions.toml
new file mode 100644
index 00000000..e546184f
--- /dev/null
+++ b/androidApp/gradle/libs.versions.toml
@@ -0,0 +1,100 @@
+[versions]
+agp = "9.1.0"
+coilCompose = "3.3.0"
+constraintlayoutCompose = "1.1.1"
+datastore = "1.2.0"
+esclkt = "2.0.6"
+escl-mock-server = "1.0.1"
+itextCore = "9.3.0"
+kotlin = "2.3.20-Beta1"
+coreKtx = "1.17.0"
+junit = "4.13.2"
+junitVersion = "1.3.0"
+espressoCore = "3.7.0"
+kotlinReflect = "2.1.20"
+kotlinxSerializationJson = "1.9.0"
+lifecycleRuntimeKtx = "2.9.4"
+activityCompose = "1.11.0"
+composeBom = "2025.10.00"
+room = "2.8.4"
+timber = "5.0.1"
+zoomable = "0.18.0"
+material3 = "1.5.0-alpha06"
+materialIcons = "1.7.8"
+navigationCompose = "2.9.5"
+versionsPlugin = "0.53.0"
+screengrab = "2.1.1"
+ktor = "3.4.0"
+koin = "4.2.0-RC1"
+koin-plugin = "0.3.0"
+protobuf-plugin = "0.9.6"
+protobuf-kotlin-lite = "4.33.5"
+rules = "1.7.0"
+
+[libraries]
+
+androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
+
+androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
+androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
+androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
+
+koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
+koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin" }
+koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" }
+koin-test-junit4 = { module = "io.insert-koin:koin-test-junit4", version.ref = "koin" }
+
+# Android
+koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
+koin-android-test = { module = "io.insert-koin:koin-android-test", version.ref = "koin" }
+
+koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin"}
+
+koin-androix-navigation = { module = "io.insert-koin:koin-androidx-compose-navigation", version.ref = "koin"}
+
+# Compose
+koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
+koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation", version.ref = "koin" }
+koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
+
+ktor-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor"}
+ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor"}
+androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayoutCompose" }
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
+androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core" , version.ref = "materialIcons"}
+androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
+coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }
+esclkt = { module = "io.github.chrisimx:esclkt", version.ref = "esclkt" }
+itext7-core = { module = "com.itextpdf:itext-core", version.ref = "itextCore" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
+kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" }
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+screengrab = { module = "tools.fastlane:screengrab", version.ref = "screengrab" }
+timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
+zoomable = { module = "me.saket.telephoto:zoomable", version.ref = "zoomable" }
+protobuf-kotlin-lite = { module = "com.google.protobuf:protobuf-kotlin-lite", version.ref = "protobuf-kotlin-lite"}
+androidx-rules = { group = "androidx.test", name = "rules", version.ref = "rules" }
+
+escl-mock-server = { module = "io.github.chrisimx:escl-mock-server", version.ref = "escl-mock-server"}
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
+versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" }
+koin = { id = "io.insert-koin.compiler.plugin", version.ref = "koin-plugin" }
+protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
+room = { id = "androidx.room", version.ref = "room"}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/androidApp/proguard-rules.pro
similarity index 100%
rename from app/proguard-rules.pro
rename to androidApp/proguard-rules.pro
diff --git a/app/src/androidTest/assets/scan-1.jpg b/androidApp/src/androidTest/assets/scan-1.jpg
similarity index 100%
rename from app/src/androidTest/assets/scan-1.jpg
rename to androidApp/src/androidTest/assets/scan-1.jpg
diff --git a/app/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt b/androidApp/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt
similarity index 100%
rename from app/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt
rename to androidApp/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt
diff --git a/app/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeTest.kt b/androidApp/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeTest.kt
similarity index 100%
rename from app/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeTest.kt
rename to androidApp/src/androidTest/java/io/github/chrisimx/scanbridge/ScanBridgeTest.kt
diff --git a/app/src/androidTest/java/io/github/chrisimx/scanbridge/screenshot/ScanBridgeScreenshotTest.kt b/androidApp/src/androidTest/java/io/github/chrisimx/scanbridge/screenshot/ScanBridgeScreenshotTest.kt
similarity index 100%
rename from app/src/androidTest/java/io/github/chrisimx/scanbridge/screenshot/ScanBridgeScreenshotTest.kt
rename to androidApp/src/androidTest/java/io/github/chrisimx/scanbridge/screenshot/ScanBridgeScreenshotTest.kt
diff --git a/app/src/debug/AndroidManifest.xml b/androidApp/src/debug/AndroidManifest.xml
similarity index 100%
rename from app/src/debug/AndroidManifest.xml
rename to androidApp/src/debug/AndroidManifest.xml
diff --git a/app/src/fdroid/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt b/androidApp/src/fdroid/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt
similarity index 100%
rename from app/src/fdroid/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt
rename to androidApp/src/fdroid/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt
diff --git a/app/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml
similarity index 100%
rename from app/src/main/AndroidManifest.xml
rename to androidApp/src/main/AndroidManifest.xml
diff --git a/app/src/main/ic_launcher-playstore.png b/androidApp/src/main/ic_launcher-playstore.png
similarity index 100%
rename from app/src/main/ic_launcher-playstore.png
rename to androidApp/src/main/ic_launcher-playstore.png
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/AppSettingsScreen.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/AppSettingsScreen.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/AppSettingsScreen.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/AppSettingsScreen.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/CrashActivity.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/CrashActivity.kt
similarity index 97%
rename from app/src/main/java/io/github/chrisimx/scanbridge/CrashActivity.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/CrashActivity.kt
index 981beed2..01a16386 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/CrashActivity.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/CrashActivity.kt
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -28,7 +27,6 @@ import io.github.chrisimx.scanbridge.theme.ScanBridgeTheme
import timber.log.Timber
class CrashActivity : ComponentActivity() {
- @OptIn(ExperimentalMaterial3ExpressiveApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/CrashHandler.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/CrashHandler.kt
similarity index 89%
rename from app/src/main/java/io/github/chrisimx/scanbridge/CrashHandler.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/CrashHandler.kt
index 8d0eae94..76f02afc 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/CrashHandler.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/CrashHandler.kt
@@ -21,15 +21,13 @@ package io.github.chrisimx.scanbridge
import android.content.Context
import android.content.Intent
-import io.github.chrisimx.scanbridge.datastore.lastRouteStore
-import io.github.chrisimx.scanbridge.proto.copy
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlinx.coroutines.runBlocking
import timber.log.Timber
-class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
+class CrashHandler(val context: Context, val lastRouteRepository: LastRouteRepository) : Thread.UncaughtExceptionHandler {
private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
@@ -71,10 +69,6 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
}
suspend fun deleteStoredRoute() {
- context.lastRouteStore.updateData {
- it.copy {
- clearLastRoute()
- }
- }
+ lastRouteRepository.setLastRoute(null)
}
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/CropScreen.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/CropScreen.kt
similarity index 96%
rename from app/src/main/java/io/github/chrisimx/scanbridge/CropScreen.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/CropScreen.kt
index 36597ab0..601e70f5 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/CropScreen.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/CropScreen.kt
@@ -6,7 +6,6 @@ import androidx.compose.animation.core.snap
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Icon
@@ -43,7 +42,6 @@ import kotlin.uuid.Uuid
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import me.saket.telephoto.ExperimentalTelephotoApi
import me.saket.telephoto.zoomable.EnabledZoomGestures
import me.saket.telephoto.zoomable.ZoomSpec
import me.saket.telephoto.zoomable.rememberZoomableState
@@ -69,7 +67,7 @@ suspend fun finishCrop(cropRect: Rect, file: String): File? = withContext(Dispat
return@withContext croppedFile
}
-@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalTelephotoApi::class)
+// TODO: @OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalTelephotoApi::class)
@Composable
fun CropScreen(scanId: Uuid, returnRoute: BaseRoute, navController: NavController) {
val context = LocalContext.current
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/MainActivity.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/MainActivity.kt
similarity index 96%
rename from app/src/main/java/io/github/chrisimx/scanbridge/MainActivity.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/MainActivity.kt
index decde6da..53ebbaed 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/MainActivity.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/MainActivity.kt
@@ -31,6 +31,7 @@ import java.io.File
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.android.scope.AndroidScopeComponent
import org.koin.androidx.scope.activityScope
@@ -41,6 +42,7 @@ class MainActivity :
ComponentActivity(),
AndroidScopeComponent {
private val localeProvider: AndroidLocaleProvider by inject()
+ private val crashHandler: Thread.UncaughtExceptionHandler by inject()
override val scope: Scope by activityScope()
@@ -52,7 +54,7 @@ class MainActivity :
}
override fun onCreate(savedInstanceState: Bundle?) {
- Thread.setDefaultUncaughtExceptionHandler(CrashHandler(this.applicationContext))
+ Thread.setDefaultUncaughtExceptionHandler(crashHandler)
enableEdgeToEdge()
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApp.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApp.kt
similarity index 76%
rename from app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApp.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApp.kt
index 50578717..c32b116a 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApp.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApp.kt
@@ -23,8 +23,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Favorite
import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@@ -44,35 +42,37 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
-import com.google.protobuf.StringValue
-import io.github.chrisimx.scanbridge.datastore.lastRouteStore
-import io.github.chrisimx.scanbridge.datastore.shownMessagesStore
-import io.github.chrisimx.scanbridge.proto.copy
-import io.github.chrisimx.scanbridge.proto.lastRouteOrNull
import io.github.chrisimx.scanbridge.theme.ScanBridgeTheme
import io.github.chrisimx.scanbridge.uicomponents.CrashFileHandler
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.firstOrNull
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
+import org.koin.compose.koinInject
+import org.koin.core.parameter.parametersOf
import timber.log.Timber
-@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ScanBridgeApp() {
ScanBridgeTheme {
val navController = rememberNavController()
var startDestination: Any? by remember { mutableStateOf(null) }
val context = LocalContext.current
+ val coroutineScope = rememberCoroutineScope()
val currentBackStackEntry by navController.currentBackStackEntryAsState()
val typedRoute = currentBackStackEntry?.toTypedRoute()
+ val lastRouteRepository = koinInject()
+ val shownMessagesRepository = koinInject {
+ parametersOf(coroutineScope)
+ }
+
+ val thanksForPurchaseAlreadyShown by shownMessagesRepository
+ .getWasShownFlow(UserInformationMessage.THANKS_FOR_PURCHASE)
+ .collectAsState(true)
+
LaunchedEffect(Unit) {
Timber.d("Loading last route from shared preferences")
- val lastRoute = context.lastRouteStore.data.firstOrNull()?.lastRouteOrNull?.value
+ val lastRoute = lastRouteRepository.getLastRoute()
if (lastRoute != null) {
try {
Timber.d("Last route found: $lastRoute")
@@ -95,35 +95,17 @@ fun ScanBridgeApp() {
val json = Json.encodeToString(route)
Timber.d("Route saved as: $json")
- context.lastRouteStore.updateData {
- it.copy {
- lastRoute = StringValue.of(json)
- }
- }
+ lastRouteRepository.setLastRoute(json)
}
}
- val playThankShouldBeShown by remember {
- context.shownMessagesStore.data.map {
- BuildConfig.FLAVOR == "play" && !it.thankPlayOne
- }
- }.collectAsState(false)
-
- val coroutineScope = rememberCoroutineScope()
-
val markThanksMessageAsRead = {
coroutineScope.launch {
- withContext(Dispatchers.IO) {
- context.shownMessagesStore.updateData {
- it.copy {
- thankPlayOne = true
- }
- }
- }
+ shownMessagesRepository.setShown(UserInformationMessage.THANKS_FOR_PURCHASE, true)
}
}
- if (playThankShouldBeShown) {
+ if (!thanksForPurchaseAlreadyShown) {
AlertDialog(
{
markThanksMessageAsRead()
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApplication.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApplication.kt
new file mode 100644
index 00000000..bd8e67da
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApplication.kt
@@ -0,0 +1,104 @@
+package io.github.chrisimx.scanbridge
+
+import AndroidHttpClientFactory
+import AndroidScanBridgeDbBuilderFactory
+import android.app.Application
+import android.content.Context
+import androidx.datastore.core.DataStore
+import io.github.chrisimx.scanbridge.data.ui.CustomScannerViewModel
+import io.github.chrisimx.scanbridge.data.ui.ScanSettingsComposableStateHolder
+import io.github.chrisimx.scanbridge.data.ui.ScanningScreenViewModel
+import io.github.chrisimx.scanbridge.datastore.appSettingsStore
+import io.github.chrisimx.scanbridge.datastore.shownMessagesStore
+import io.github.chrisimx.scanbridge.db.DefaultScanBridgeDbFactory
+import io.github.chrisimx.scanbridge.db.ScanBridgeDb
+import io.github.chrisimx.scanbridge.db.ScanBridgeDbBuilderFactory
+import io.github.chrisimx.scanbridge.db.ScanBridgeDbFactory
+import io.github.chrisimx.scanbridge.infrastructure.KmLogScanBridgeLoggerFactory
+import io.github.chrisimx.scanbridge.migrations.MigrationExecutor
+import io.github.chrisimx.scanbridge.migrations.RoomBackedMigrationExecutor
+import io.github.chrisimx.scanbridge.migrations.migrationsModule
+import io.github.chrisimx.scanbridge.ports.HttpClientFactory
+import io.github.chrisimx.scanbridge.ports.LocaleProvider
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLoggerFactory
+import io.github.chrisimx.scanbridge.proto.ScanBridgeSettings
+import io.github.chrisimx.scanbridge.proto.ShownMessages
+import io.github.chrisimx.scanbridge.repositories.DatastoreLastRouteRepository
+import io.github.chrisimx.scanbridge.repositories.DatastoreShownMessagesRepository
+import io.github.chrisimx.scanbridge.repositories.RoomLastRouteRepository
+import io.github.chrisimx.scanbridge.services.AndroidLocaleProvider
+import io.github.chrisimx.scanbridge.services.DebugLogService
+import io.github.chrisimx.scanbridge.services.FileDebugLogService
+import io.github.chrisimx.scanbridge.services.ScanJobRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.context.startKoin
+import org.koin.core.qualifier.named
+import org.koin.dsl.bind
+import org.koin.dsl.module
+import org.koin.mp.KoinPlatform.getKoin
+import org.koin.plugin.module.dsl.create
+import org.koin.plugin.module.dsl.factory
+import org.koin.plugin.module.dsl.single
+import org.koin.plugin.module.dsl.viewModel
+import timber.log.Timber
+
+fun createAppSettingsDataStore(context: Context) = context.appSettingsStore
+fun createShownMessagesDataStore(context: Context) = context.shownMessagesStore
+
+fun createScanBridgeDb(factory: ScanBridgeDbFactory): ScanBridgeDb = factory.createInstance()
+val appModule = module {
+ single>(named()) {
+ create(::createShownMessagesDataStore)
+ }
+ single>(named()) {
+ create(::createAppSettingsDataStore)
+ }
+ single() bind Thread.UncaughtExceptionHandler::class
+ single() bind LocaleProvider::class
+ single {
+ FileDebugLogService(get(named()), get())
+ } bind DebugLogService::class
+ single() bind HttpClientFactory::class
+ single() bind ScanBridgeLoggerFactory::class
+ single()
+ single() bind MigrationExecutor::class
+ includes(migrationsModule)
+ single() bind ScanBridgeDbBuilderFactory::class
+ single() bind ScanBridgeDbFactory::class
+ single {
+ create(::createScanBridgeDb)
+ }
+ single()
+ single() bind LastRouteRepository::class
+ single { (scope: CoroutineScope) ->
+ DatastoreShownMessagesRepository(get(named()), scope)
+ } bind ShownMessagesRepository::class
+ factory()
+ viewModel()
+ viewModel()
+}
+
+class ScanBridgeApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ startKoin {
+ androidContext(this@ScanBridgeApplication)
+ modules(appModule)
+ }
+
+ Timber.plant(Timber.DebugTree())
+
+ runMigrations()
+ }
+
+ fun runMigrations() {
+ val coroutineScope = CoroutineScope(Dispatchers.IO)
+ val migrationExecutor = getKoin().get()
+ coroutineScope.launch {
+ migrationExecutor.runMigrations()
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeNavHost.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScanSettings.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanSettings.kt
similarity index 99%
rename from app/src/main/java/io/github/chrisimx/scanbridge/ScanSettings.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanSettings.kt
index e891892d..fefdf13f 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/ScanSettings.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanSettings.kt
@@ -56,9 +56,9 @@ import androidx.compose.ui.unit.dp
import io.github.chrisimx.esclkt.DiscreteResolution
import io.github.chrisimx.esclkt.SupportedResolutions
import io.github.chrisimx.esclkt.equalsLength
-import io.github.chrisimx.scanbridge.data.ui.NumberValidationResult
import io.github.chrisimx.scanbridge.data.ui.ScanSettingsComposableStateHolder
import io.github.chrisimx.scanbridge.data.ui.ScanSettingsLengthUnit
+import io.github.chrisimx.scanbridge.model.NumberValidationResult
import io.github.chrisimx.scanbridge.uicomponents.SizeBasedConditionalView
import io.github.chrisimx.scanbridge.uicomponents.ValidatedDimensionsTextEdit
import io.github.chrisimx.scanbridge.util.localizedString
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScannerBrowser.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScannerBrowser.kt
similarity index 96%
rename from app/src/main/java/io/github/chrisimx/scanbridge/ScannerBrowser.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/ScannerBrowser.kt
index 7030942a..cfb26d07 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/ScannerBrowser.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScannerBrowser.kt
@@ -48,6 +48,7 @@ import androidx.navigation.NavController
import io.github.chrisimx.scanbridge.data.model.EditedCustomScanner
import io.github.chrisimx.scanbridge.data.ui.CustomScannerViewModel
import io.github.chrisimx.scanbridge.db.entities.CustomScanner
+import io.github.chrisimx.scanbridge.model.DiscoveredScanner
import io.github.chrisimx.scanbridge.uicomponents.FoundScannerItem
import io.github.chrisimx.scanbridge.uicomponents.FullScreenError
import io.github.chrisimx.scanbridge.uicomponents.dialog.CustomScannerDialog
@@ -63,14 +64,14 @@ fun startScannerDiscovery(
context: Context,
scannerMap: SnapshotStateMap,
scannerMapSecure: SnapshotStateMap
-): Optional>> {
+): Optional>> {
val service = getSystemService(context, NsdManager::class.java)
if (service == null) {
Timber.e("Couldn't get NsdManager service")
return Optional.empty()
}
- val listener = ScannerDiscovery(service, isSecure = false, scannerMap)
- val listenerSecure = ScannerDiscovery(service, isSecure = true, scannerMapSecure)
+ val listener = ScannerDiscoveryBackend(service, isSecure = false, scannerMap)
+ val listenerSecure = ScannerDiscoveryBackend(service, isSecure = true, scannerMapSecure)
service.discoverServices("_uscan._tcp", NsdManager.PROTOCOL_DNS_SD, listener)
service.discoverServices("_uscans._tcp", NsdManager.PROTOCOL_DNS_SD, listenerSecure)
Timber.i("Discovery started")
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScannerDiscoveryBackend.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScannerDiscoveryBackend.kt
similarity index 97%
rename from app/src/main/java/io/github/chrisimx/scanbridge/ScannerDiscoveryBackend.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/ScannerDiscoveryBackend.kt
index 256e5ef5..48b22c38 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/ScannerDiscoveryBackend.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScannerDiscoveryBackend.kt
@@ -24,6 +24,7 @@ import android.net.nsd.NsdServiceInfo
import android.os.Build
import android.os.ext.SdkExtensions
import androidx.compose.runtime.snapshots.SnapshotStateMap
+import io.github.chrisimx.scanbridge.model.DiscoveredScanner
import io.ktor.http.URLBuilder
import io.ktor.http.URLProtocol
import io.ktor.http.Url
@@ -35,9 +36,7 @@ import timber.log.Timber
private const val TAG = "ScannerDiscovery"
-data class DiscoveredScanner(val name: String, val addresses: List)
-
-class ScannerDiscovery(
+class ScannerDiscoveryBackend(
val nsdManager: NsdManager,
val isSecure: Boolean,
val statefulScannerMap: SnapshotStateMap
@@ -156,7 +155,7 @@ class ScannerDiscovery(
}
}
-private fun ScannerDiscovery.tryParseScannerUrl(address: InetAddress, serviceInfo: NsdServiceInfo, rs: String): Url? {
+private fun ScannerDiscoveryBackend.tryParseScannerUrl(address: InetAddress, serviceInfo: NsdServiceInfo, rs: String): Url? {
if (address.isLinkLocalAddress) {
Timber.tag(TAG).d("Ignoring link local address: ${address.hostAddress}")
return null
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScanningScreen.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanningScreen.kt
similarity index 99%
rename from app/src/main/java/io/github/chrisimx/scanbridge/ScanningScreen.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanningScreen.kt
index 0e695d25..d332932a 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/ScanningScreen.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/ScanningScreen.kt
@@ -115,6 +115,8 @@ import me.saket.telephoto.zoomable.rememberZoomableState
import me.saket.telephoto.zoomable.zoomable
import org.koin.androidx.compose.koinViewModel
import org.koin.core.parameter.parametersOf
+import scanbridge.composeui.generated.resources.Res
+import scanbridge.composeui.generated.resources.trying_to_retrieve_scannercapabilities
import timber.log.Timber
private const val TAG = "ScanningScreen"
@@ -354,7 +356,7 @@ fun ScanningScreen(
}
LoadingScreen(
- loadingText = R.string.trying_to_retrieve_scannercapabilities
+ loadingText = Res.string.trying_to_retrieve_scannercapabilities
)
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/StartupScreen.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/StartupScreen.kt
similarity index 99%
rename from app/src/main/java/io/github/chrisimx/scanbridge/StartupScreen.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/StartupScreen.kt
index 0323e206..76685def 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/StartupScreen.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/StartupScreen.kt
@@ -50,6 +50,7 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import io.github.chrisimx.scanbridge.data.model.EditedCustomScanner
+import io.github.chrisimx.scanbridge.model.DiscoveredScanner
import timber.log.Timber
@OptIn(ExperimentalMaterial3Api::class)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/androidservice/ScanJobForegroundService.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/androidservice/ScanJobForegroundService.kt
similarity index 93%
rename from app/src/main/java/io/github/chrisimx/scanbridge/androidservice/ScanJobForegroundService.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/androidservice/ScanJobForegroundService.kt
index 20567fbb..4f95b5c9 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/androidservice/ScanJobForegroundService.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/androidservice/ScanJobForegroundService.kt
@@ -12,18 +12,20 @@ import android.os.IBinder
import androidx.compose.ui.graphics.asImageBitmap
import androidx.core.app.NotificationCompat
import androidx.lifecycle.application
-import androidx.room.withTransaction
+import androidx.room.immediateTransaction
+import androidx.room.useWriterConnection
import io.github.chrisimx.esclkt.ESCLHttpCallResult
import io.github.chrisimx.esclkt.ESCLRequestClient
import io.github.chrisimx.esclkt.JobState
import io.github.chrisimx.esclkt.ScanSettings
import io.github.chrisimx.scanbridge.MainActivity
import io.github.chrisimx.scanbridge.R
-import io.github.chrisimx.scanbridge.data.model.ScanJob
-import io.github.chrisimx.scanbridge.data.ui.ScanRelativeRotation
import io.github.chrisimx.scanbridge.db.ScanBridgeDb
import io.github.chrisimx.scanbridge.db.daos.ScannedPageDao
import io.github.chrisimx.scanbridge.db.entities.ScannedPage
+import io.github.chrisimx.scanbridge.model.ScanJob
+import io.github.chrisimx.scanbridge.model.ScanRelativeRotation
+import io.github.chrisimx.scanbridge.ports.HttpClientFactory
import io.github.chrisimx.scanbridge.services.ScanJobRepository
import io.github.chrisimx.scanbridge.util.extractPdfImages
import io.github.chrisimx.scanbridge.util.toJobStateString
@@ -63,6 +65,9 @@ class ScanJobForegroundService : Service() {
}
private val scanJobs: ScanJobRepository by inject()
+
+ private val httpClientFactory: HttpClientFactory by inject()
+
private val db: ScanBridgeDb by inject()
private val scannedPageDao: ScannedPageDao = db.scannedPageDao()
@@ -165,7 +170,10 @@ class ScanJobForegroundService : Service() {
private suspend fun doScan(scanJob: ScanJob) {
val currentScanSettings = scanJob.scanSettings
- val esclRequestClient = scanJob.esclClient
+ val esclRequestClient = ESCLRequestClient(
+ scanJob.scannerBaseUrl,
+ httpClientFactory.create(scanJob.httpClientConfig)
+ )
scanJobs.notifyStarted(scanJob)
@@ -365,20 +373,22 @@ class ScanJobForegroundService : Service() {
suspend fun addScan(sessionID: Uuid, path: String, settings: ScanSettings, rotation: ScanRelativeRotation) {
Timber.d("Adding scan: $path, $rotation")
- db.withTransaction {
- val highestIdx = scannedPageDao.getHighestIdxForSession(sessionID) ?: -1
-
- Timber.d("Inserting scan with index ${highestIdx + 1}")
- scannedPageDao.insertAll(
- ScannedPage(
- Uuid.generateV4(),
- sessionID,
- path,
- settings,
- rotation,
- highestIdx + 1
+ db.useWriterConnection {
+ it.immediateTransaction {
+ val highestIdx = scannedPageDao.getHighestIdxForSession(sessionID) ?: -1
+
+ Timber.d("Inserting scan with index ${highestIdx + 1}")
+ scannedPageDao.insertAll(
+ ScannedPage(
+ Uuid.generateV4(),
+ sessionID,
+ path,
+ settings,
+ rotation,
+ highestIdx + 1
+ )
)
- )
+ }
}
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/EditedCustomScanner.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/EditedCustomScanner.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/model/EditedCustomScanner.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/EditedCustomScanner.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacyESCLScanSettings.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacyESCLScanSettings.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacyESCLScanSettings.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacyESCLScanSettings.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacySessionV2.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacySessionV2.kt
similarity index 97%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacySessionV2.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacySessionV2.kt
index e56f1db8..e1aa2c04 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacySessionV2.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/model/LegacySessionV2.kt
@@ -4,7 +4,7 @@ import io.github.chrisimx.esclkt.ScanSettings
import io.github.chrisimx.esclkt.ScannerCapabilities
import io.github.chrisimx.esclkt.getInputSourceCaps
import io.github.chrisimx.esclkt.getInputSourceOptions
-import io.github.chrisimx.scanbridge.data.ui.ScanRelativeRotation
+import io.github.chrisimx.scanbridge.model.ScanRelativeRotation
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import timber.log.Timber
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/CustomScannerViewModel.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/CustomScannerViewModel.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/ui/CustomScannerViewModel.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/CustomScannerViewModel.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsComposableStateHolder.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsComposableStateHolder.kt
similarity index 96%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsComposableStateHolder.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsComposableStateHolder.kt
index 1d122b68..259258c4 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsComposableStateHolder.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsComposableStateHolder.kt
@@ -40,11 +40,13 @@ import io.github.chrisimx.esclkt.millimeters
import io.github.chrisimx.esclkt.scanRegion
import io.github.chrisimx.esclkt.threeHundredthsOfInch
import io.github.chrisimx.scanbridge.R
-import io.github.chrisimx.scanbridge.services.LocaleProvider
+import io.github.chrisimx.scanbridge.model.Locale
+import io.github.chrisimx.scanbridge.model.NumberValidationResult
+import io.github.chrisimx.scanbridge.model.ScanSettingsEnterableData
+import io.github.chrisimx.scanbridge.ports.LocaleProvider
import io.github.chrisimx.scanbridge.util.derived
import io.github.chrisimx.scanbridge.util.getMaxResolution
import io.github.chrisimx.scanbridge.util.toDoubleLocalized
-import java.util.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -72,7 +74,7 @@ class ScanSettingsComposableStateHolder(
@InjectedParam
val scanSettings: StateFlow,
@InjectedParam
- private val initialScanSettingsData: ScanSettingsStateData,
+ private val initialScanSettingsData: ScanSettingsEnterableData,
@InjectedParam
private val updateSettings: suspend (ScanSettings.() -> ScanSettings) -> Unit,
@InjectedParam
@@ -82,7 +84,7 @@ class ScanSettingsComposableStateHolder(
) {
private val _uiState = MutableStateFlow(initialScanSettingsData)
- val uiState: StateFlow = _uiState.asStateFlow()
+ val uiState: StateFlow = _uiState.asStateFlow()
val inputSourceOptions: StateFlow> = _uiState.derived(coroutineScope) {
it.capabilities.getInputSourceOptions()
@@ -402,12 +404,11 @@ class ScanSettingsComposableStateHolder(
}
}
- private fun unitByLocale(locale: Locale = Locale.getDefault()): ScanSettingsLengthUnit =
- if (locale.country in setOf("US", "LR", "MM")) {
- ScanSettingsLengthUnit.INCH
- } else {
- ScanSettingsLengthUnit.MILLIMETER
- }
+ private fun unitByLocale(locale: Locale): ScanSettingsLengthUnit = if (locale.country in setOf("US", "LR", "MM")) {
+ ScanSettingsLengthUnit.INCH
+ } else {
+ ScanSettingsLengthUnit.MILLIMETER
+ }
fun selectMaxRegion() {
_uiState.update {
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenData.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenData.kt
similarity index 91%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenData.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenData.kt
index 656d2cb4..6b0c5320 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenData.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenData.kt
@@ -23,25 +23,13 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
-import io.github.chrisimx.esclkt.ESCLRequestClient
import io.github.chrisimx.esclkt.ScannerCapabilities
import java.io.File
import kotlin.uuid.Uuid
-enum class ScanRelativeRotation {
- Rotated,
- Original
-}
-
data class ErrorDescription(val pretext: Int?, val icon: Int?, val text: String?)
-fun ScanRelativeRotation.toggleRotation() = when (this) {
- ScanRelativeRotation.Rotated -> ScanRelativeRotation.Original
- ScanRelativeRotation.Original -> ScanRelativeRotation.Rotated
-}
-
data class ScanningScreenData(
- val esclClient: ESCLRequestClient,
val sessionID: Uuid,
val confirmDialogShown: MutableState = mutableStateOf(false),
val confirmPageDeleteDialogShown: MutableState = mutableStateOf(false),
@@ -58,7 +46,6 @@ data class ScanningScreenData(
val isRotating: MutableState = mutableStateOf(false)
) {
fun toImmutable() = ImmutableScanningScreenData(
- esclClient,
sessionID,
confirmDialogShown,
confirmPageDeleteDialogShown,
@@ -77,7 +64,6 @@ data class ScanningScreenData(
}
data class ImmutableScanningScreenData(
- val esclClient: ESCLRequestClient,
val sessionID: Uuid,
private val confirmDialogShownState: State,
private val confirmPageDeleteDialogShownState: State,
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenViewModel.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenViewModel.kt
similarity index 91%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenViewModel.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenViewModel.kt
index 3d88cc57..1d27c139 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenViewModel.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanningScreenViewModel.kt
@@ -29,14 +29,14 @@ import androidx.core.content.FileProvider
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.application
import androidx.lifecycle.viewModelScope
-import androidx.room.withTransaction
+import androidx.room.immediateTransaction
+import androidx.room.useWriterConnection
import com.itextpdf.io.image.ImageDataFactory
import com.itextpdf.kernel.geom.PageSize
import com.itextpdf.kernel.pdf.PdfDocument
import com.itextpdf.kernel.pdf.PdfWriter
import com.itextpdf.layout.Document
import com.itextpdf.layout.element.Image
-import getTrustAllTM
import io.github.chrisimx.esclkt.ESCLRequestClient
import io.github.chrisimx.esclkt.InputSource
import io.github.chrisimx.esclkt.ScanRegion
@@ -55,6 +55,12 @@ import io.github.chrisimx.scanbridge.db.ScanBridgeDb
import io.github.chrisimx.scanbridge.db.entities.ScannedPage
import io.github.chrisimx.scanbridge.db.entities.Session
import io.github.chrisimx.scanbridge.db.entities.TempFile
+import io.github.chrisimx.scanbridge.model.HttpClientConfig
+import io.github.chrisimx.scanbridge.model.ScanJob
+import io.github.chrisimx.scanbridge.model.ScanRelativeRotation
+import io.github.chrisimx.scanbridge.model.ScanSettingsEnterableData
+import io.github.chrisimx.scanbridge.model.toggleRotation
+import io.github.chrisimx.scanbridge.ports.HttpClientFactory
import io.github.chrisimx.scanbridge.proto.chunkSizePdfExportOrNull
import io.github.chrisimx.scanbridge.services.ScanJobRepository
import io.github.chrisimx.scanbridge.stores.DefaultScanSettingsStore
@@ -65,11 +71,6 @@ import io.github.chrisimx.scanbridge.util.rotateBy90
import io.github.chrisimx.scanbridge.util.saveAsJPEG
import io.github.chrisimx.scanbridge.util.snackbarErrorRetrievingPage
import io.github.chrisimx.scanbridge.util.zipFiles
-import io.ktor.client.HttpClient
-import io.ktor.client.engine.okhttp.OkHttp
-import io.ktor.client.plugins.HttpTimeout
-import io.ktor.client.plugins.logging.Logger
-import io.ktor.client.plugins.logging.Logging
import io.ktor.http.Url
import java.io.File
import java.nio.file.Files
@@ -103,49 +104,22 @@ enum class ScanningScreenEvent {
class ScanningScreenViewModel(
@InjectedParam
- address: Url,
+ val address: Url,
@InjectedParam
- timeout: UInt,
+ val timeout: UInt,
@InjectedParam
- withDebugInterceptor: Boolean,
+ val withDebugInterceptor: Boolean,
@InjectedParam
- certificateValidationDisabled: Boolean,
+ val certificateValidationDisabled: Boolean,
@InjectedParam
val sessionID: Uuid,
val db: ScanBridgeDb,
application: Application,
- val scanJobRepo: ScanJobRepository
+ val scanJobRepo: ScanJobRepository,
+ val httpClientFactory: HttpClientFactory
) : AndroidViewModel(application) {
private val _scanningScreenData =
ScanningScreenData(
- ESCLRequestClient(
- address,
- HttpClient(OkHttp) {
- install(HttpTimeout) {
- requestTimeoutMillis = timeout.toLong() * 1000
- connectTimeoutMillis = timeout.toLong() * 1000
- socketTimeoutMillis = timeout.toLong() * 1000
- }
- if (withDebugInterceptor) {
- install(Logging) {
- logger = object : Logger {
- override fun log(message: String) {
- Timber.tag("ESCLRequestClient").d(message)
- }
- }
- }
- }
- if (certificateValidationDisabled) {
- engine {
- config {
- val (socketFactory, trustManager) = getTrustAllTM()
- sslSocketFactory(socketFactory, trustManager)
- hostnameVerifier { _, _ -> true }
- }
- }
- }
- }
- ),
sessionID
)
val scanningScreenData: ImmutableScanningScreenData
@@ -309,7 +283,7 @@ class ScanningScreenViewModel(
}
}
- suspend fun saveUpdatedScanSettingsUiData(newData: ScanSettingsStateData?) {
+ suspend fun saveUpdatedScanSettingsUiData(newData: ScanSettingsEnterableData?) {
Timber.d("Settings ui data updated $newData")
sessionDao.updateScanSettingsUiData(sessionID, newData)
}
@@ -321,17 +295,19 @@ class ScanningScreenViewModel(
Timber.d("Stored session: $storedSession")
val updateSettings: suspend (ScanSettings.() -> ScanSettings) -> Unit = { lambda ->
- db.withTransaction {
- val oldSession = sessionDao.getSessionById(sessionID) ?: return@withTransaction
- val newSession = oldSession.copy(
- currentScanSettings = oldSession.currentScanSettings?.lambda()
- )
- Timber.d("Settings updated ${newSession.currentScanSettings}")
- sessionDao.update(newSession)
+ db.useWriterConnection {
+ it.immediateTransaction {
+ val oldSession = sessionDao.getSessionById(sessionID) ?: return@immediateTransaction
+ val newSession = oldSession.copy(
+ currentScanSettings = oldSession.currentScanSettings?.lambda()
+ )
+ Timber.d("Settings updated ${newSession.currentScanSettings}")
+ sessionDao.update(newSession)
+ }
}
}
- val defaultScanSettingsUIData = ScanSettingsStateData(
+ val defaultScanSettingsUIData = ScanSettingsEnterableData(
caps
)
@@ -510,11 +486,16 @@ class ScanningScreenViewModel(
return@launch
}
- val scanJob = io.github.chrisimx.scanbridge.data.model.ScanJob(
+ val scanJob = ScanJob(
Uuid.generateV4(),
sessionID,
currentSettings,
- _scanningScreenData.esclClient
+ address,
+ HttpClientConfig(
+ certificateValidationDisabled,
+ withDebugInterceptor,
+ timeout.toULong()
+ )
)
scanJobRepo.enqueue(scanJob)
ScanJobForegroundService.startService(application)
@@ -526,14 +507,16 @@ class ScanningScreenViewModel(
val filePaths = mutableListOf()
val tmpPaths = mutableListOf()
- db.withTransaction {
- val scannedPages = scannedPageDao.getAllForSession(sessionID)
- val tmpFiles = tmpFileDao.getFilesBySessionId(sessionID)
+ db.useWriterConnection {
+ it.immediateTransaction {
+ val scannedPages = scannedPageDao.getAllForSession(sessionID)
+ val tmpFiles = tmpFileDao.getFilesBySessionId(sessionID)
- filePaths += scannedPages.map { it.filePath }
- tmpPaths += tmpFiles.map { it.path }
+ filePaths += scannedPages.map { it.filePath }
+ tmpPaths += tmpFiles.map { it.path }
- sessionDao.deleteById(sessionID)
+ sessionDao.deleteById(sessionID)
+ }
}
withContext(Dispatchers.IO) {
@@ -769,8 +752,20 @@ class ScanningScreenViewModel(
}
}
+ fun createHttpClientConfig() = HttpClientConfig(
+ certificateValidationDisabled,
+ withDebugInterceptor,
+ timeout.toULong()
+ )
+
fun retrieveScannerCapabilities() = viewModelScope.launch {
- val esclClient = scanningScreenData.esclClient
+ val httpClient = httpClientFactory.create(
+ createHttpClientConfig()
+ )
+ val esclClient = ESCLRequestClient(
+ address,
+ httpClient
+ )
val scannerCapabilitiesResult = esclClient.getScannerCapabilities()
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/datastore/DataStores.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/DataStores.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/datastore/DataStores.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/DataStores.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/datastore/LastRouteStoreSerializer.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/LastRouteStoreSerializer.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/datastore/LastRouteStoreSerializer.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/LastRouteStoreSerializer.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanBridgeSettingsSerializer.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanBridgeSettingsSerializer.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanBridgeSettingsSerializer.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanBridgeSettingsSerializer.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanSettingsDataStoreHelpers.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanSettingsDataStoreHelpers.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanSettingsDataStoreHelpers.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/ScanSettingsDataStoreHelpers.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/datastore/ShownMessagesSerializer.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/ShownMessagesSerializer.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/datastore/ShownMessagesSerializer.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/datastore/ShownMessagesSerializer.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/logs/FileLogger.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/logs/FileLogger.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/logs/FileLogger.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/logs/FileLogger.kt
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/Migration.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/Migration.kt
new file mode 100644
index 00000000..6ec515de
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/Migration.kt
@@ -0,0 +1,16 @@
+package io.github.chrisimx.scanbridge.migrations
+
+import io.github.chrisimx.scanbridge.db.ScanBridgeDb
+
+interface Migration {
+ /**
+ * A unique identifier of the migration that will be used to determine if it has already run
+ */
+ val migrationId: String
+
+ /**
+ * Execute the migration
+ * @return Whether the migration was successful
+ */
+ suspend fun migrate(db: ScanBridgeDb): Boolean
+}
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/MigrationExecutor.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/MigrationExecutor.kt
new file mode 100644
index 00000000..f0968f49
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/MigrationExecutor.kt
@@ -0,0 +1,5 @@
+package io.github.chrisimx.scanbridge.migrations
+
+interface MigrationExecutor {
+ suspend fun runMigrations()
+}
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/MigrationRegistry.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/MigrationRegistry.kt
new file mode 100644
index 00000000..a4e1839e
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/MigrationRegistry.kt
@@ -0,0 +1,23 @@
+package io.github.chrisimx.scanbridge.migrations
+
+import io.github.chrisimx.scanbridge.repositories.DatastoreLastRouteRepository
+import io.github.chrisimx.scanbridge.repositories.LastRouteRepoAutoMigration
+import io.github.chrisimx.scanbridge.repositories.RoomLastRouteRepository
+import org.koin.dsl.bind
+import org.koin.dsl.module
+import org.koin.plugin.module.dsl.create
+
+fun createLastRouteRepoAutoMigration(
+ datastoreLastRouteRepository: DatastoreLastRouteRepository,
+ roomLastRouteRepository: RoomLastRouteRepository
+): LastRouteRepoAutoMigration = LastRouteRepoAutoMigration(
+ datastoreLastRouteRepository,
+ roomLastRouteRepository,
+ "LastRouteDataStoreToRoom"
+)
+
+val migrationsModule = module {
+ single {
+ create(::createLastRouteRepoAutoMigration)
+ } bind Migration::class
+}
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/RoomBackedMigrationExecutor.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/RoomBackedMigrationExecutor.kt
new file mode 100644
index 00000000..c6942117
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/migrations/RoomBackedMigrationExecutor.kt
@@ -0,0 +1,36 @@
+package io.github.chrisimx.scanbridge.migrations
+
+import io.github.chrisimx.scanbridge.db.ScanBridgeDb
+import io.github.chrisimx.scanbridge.db.entities.ExecutedMigrationToRoom
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLoggerFactory
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.koin.mp.KoinPlatform.getKoin
+
+class RoomBackedMigrationExecutor(val db: ScanBridgeDb, loggerFactory: ScanBridgeLoggerFactory) : MigrationExecutor {
+ val logger = loggerFactory.withClass(this::class)
+ val executedMigrationsDao = db.executedMigrationsDao()
+
+ override suspend fun runMigrations() {
+ val migrations = getKoin().getAll()
+
+ migrations.forEach {
+ withContext(Dispatchers.IO) {
+ val ranAlready = executedMigrationsDao.isAlreadyDone(it.migrationId)
+ if (!ranAlready) {
+ val success = it.migrate(db)
+ if (success) {
+ executedMigrationsDao.markAsExecuted(
+ ExecutedMigrationToRoom(it.migrationId)
+ )
+ logger.info { "Migration with id ${it.migrationId} has finished successfully." }
+ } else {
+ logger.error { "Migration with id ${it.migrationId} has failed" }
+ }
+ } else {
+ logger.debug { "Migration with id ${it.migrationId} has already run" }
+ }
+ }
+ }
+ }
+}
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/DatastoreLastRouteRepository.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/DatastoreLastRouteRepository.kt
new file mode 100644
index 00000000..2a6d61c2
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/DatastoreLastRouteRepository.kt
@@ -0,0 +1,32 @@
+package io.github.chrisimx.scanbridge.repositories
+
+import android.content.Context
+import com.google.protobuf.StringValue
+import io.github.chrisimx.scanbridge.LastRouteRepository
+import io.github.chrisimx.scanbridge.datastore.lastRouteStore
+import io.github.chrisimx.scanbridge.proto.copy
+import io.github.chrisimx.scanbridge.proto.lastRouteOrNull
+import kotlinx.coroutines.flow.firstOrNull
+
+class DatastoreLastRouteRepository(context: Context) : LastRouteRepository {
+ val lastRouteStore = context.lastRouteStore
+ override suspend fun getLastRoute(): String? = lastRouteStore.data.firstOrNull()?.lastRouteOrNull?.value
+
+ override suspend fun setLastRoute(route: String?) {
+ if (route != null) {
+ lastRouteStore.updateData {
+ it.copy {
+ lastRoute = StringValue.of(route)
+ }
+ }
+ } else {
+ lastRouteStore.updateData {
+ lastRouteStore.updateData {
+ it.copy {
+ clearLastRoute()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/DatastoreShownMessagesRepository.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/DatastoreShownMessagesRepository.kt
new file mode 100644
index 00000000..afc939d4
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/DatastoreShownMessagesRepository.kt
@@ -0,0 +1,36 @@
+package io.github.chrisimx.scanbridge.repositories
+
+import androidx.datastore.core.DataStore
+import io.github.chrisimx.scanbridge.BuildConfig
+import io.github.chrisimx.scanbridge.ShownMessagesRepository
+import io.github.chrisimx.scanbridge.UserInformationMessage
+import io.github.chrisimx.scanbridge.proto.ShownMessages
+import io.github.chrisimx.scanbridge.proto.copy
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import org.koin.core.annotation.InjectedParam
+
+class DatastoreShownMessagesRepository(
+ val shownMessagesDataStore: DataStore,
+ @InjectedParam
+ val coroutineScope: CoroutineScope
+) : ShownMessagesRepository {
+ override fun getWasShownFlow(message: UserInformationMessage): Flow = shownMessagesDataStore.data.map {
+ (BuildConfig.FLAVOR != "play" && message.playOnly) || !when (message) {
+ UserInformationMessage.THANKS_FOR_PURCHASE -> it.thankPlayOne
+ }
+ }
+
+ override suspend fun setShown(message: UserInformationMessage, shown: Boolean) {
+ shownMessagesDataStore.updateData {
+ it.copy {
+ when (message) {
+ UserInformationMessage.THANKS_FOR_PURCHASE -> {
+ thankPlayOne = shown
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/RoomLastRouteRepository.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/RoomLastRouteRepository.kt
new file mode 100644
index 00000000..413cc57b
--- /dev/null
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/repositories/RoomLastRouteRepository.kt
@@ -0,0 +1,27 @@
+package io.github.chrisimx.scanbridge.repositories
+
+import io.github.chrisimx.scanbridge.LastRouteRepository
+import io.github.chrisimx.scanbridge.db.ScanBridgeDb
+import io.github.chrisimx.scanbridge.db.entities.LastRoute
+import io.github.chrisimx.scanbridge.migrations.Migration
+
+class RoomLastRouteRepository(db: ScanBridgeDb) : LastRouteRepository {
+ val lastRouteDao = db.lastRouteDao()
+ override suspend fun getLastRoute(): String? = lastRouteDao.getLastRoute()?.route
+
+ override suspend fun setLastRoute(route: String?) {
+ if (route != null) {
+ lastRouteDao.setLastRoute(LastRoute(route))
+ } else {
+ lastRouteDao.clearLastRoute()
+ }
+ }
+}
+
+class LastRouteRepoAutoMigration(val a: LastRouteRepository, val b: LastRouteRepository, override val migrationId: String) : Migration {
+ override suspend fun migrate(db: ScanBridgeDb): Boolean {
+ val toBeMigrated = a.getLastRoute()
+ b.setLastRoute(toBeMigrated)
+ return true
+ }
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/services/AndroidLocaleProvider.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/AndroidLocaleProvider.kt
similarity index 51%
rename from app/src/main/java/io/github/chrisimx/scanbridge/services/AndroidLocaleProvider.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/services/AndroidLocaleProvider.kt
index db456fe2..7b262da6 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/services/AndroidLocaleProvider.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/AndroidLocaleProvider.kt
@@ -1,20 +1,22 @@
package io.github.chrisimx.scanbridge.services
import android.annotation.SuppressLint
-import java.util.Locale
+import io.github.chrisimx.scanbridge.model.Locale
+import io.github.chrisimx.scanbridge.ports.LocaleProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import timber.log.Timber
class AndroidLocaleProvider : LocaleProvider {
- @SuppressLint("ConstantLocale")
- private val _locale = MutableStateFlow(Locale.getDefault())
+ @SuppressLint("ConstantLocale") // Constant local is not correct because update is called on Activity recreation
+ private val _locale = MutableStateFlow(getCurrentLocale())
override val locale: StateFlow = _locale.asStateFlow()
+ private fun getCurrentLocale(): Locale = Locale(java.util.Locale.getDefault().country)
+
internal fun update() {
- val locale = Locale.getDefault()
Timber.d("Locale updated to $locale")
- _locale.value = locale
+ _locale.value = getCurrentLocale()
}
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/services/DebugLogService.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/DebugLogService.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/services/DebugLogService.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/services/DebugLogService.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/services/FileDebugLogService.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/FileDebugLogService.kt
similarity index 96%
rename from app/src/main/java/io/github/chrisimx/scanbridge/services/FileDebugLogService.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/services/FileDebugLogService.kt
index 780a1650..7fdd3017 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/services/FileDebugLogService.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/FileDebugLogService.kt
@@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
-class FileDebugLogService(val appSettings: DataStore, val application: Application) : DebugLogService {
+class FileDebugLogService(appSettings: DataStore, val application: Application) : DebugLogService {
val scope = CoroutineScope(Dispatchers.Main)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/services/ScanJobRepository.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/ScanJobRepository.kt
similarity index 98%
rename from app/src/main/java/io/github/chrisimx/scanbridge/services/ScanJobRepository.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/services/ScanJobRepository.kt
index d9962c2e..703127a2 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/services/ScanJobRepository.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/services/ScanJobRepository.kt
@@ -1,6 +1,6 @@
package io.github.chrisimx.scanbridge.services
-import io.github.chrisimx.scanbridge.data.model.ScanJob
+import io.github.chrisimx.scanbridge.model.ScanJob
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/stores/DefaultScanSettingsStore.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/DefaultScanSettingsStore.kt
similarity index 93%
rename from app/src/main/java/io/github/chrisimx/scanbridge/stores/DefaultScanSettingsStore.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/DefaultScanSettingsStore.kt
index a534ef49..606ce6b6 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/stores/DefaultScanSettingsStore.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/DefaultScanSettingsStore.kt
@@ -22,13 +22,13 @@ package io.github.chrisimx.scanbridge.stores
import android.content.Context
import com.google.protobuf.StringValue
import io.github.chrisimx.esclkt.ScanSettings
-import io.github.chrisimx.scanbridge.data.ui.ScanSettingsStateData
+import io.github.chrisimx.scanbridge.ScanSettingsJson
import io.github.chrisimx.scanbridge.datastore.appSettingsStore
import io.github.chrisimx.scanbridge.datastore.updateSettings
+import io.github.chrisimx.scanbridge.model.ScanSettingsEnterableData
import io.github.chrisimx.scanbridge.proto.lastUsedScanSettingsOrNull
import io.github.chrisimx.scanbridge.proto.lastUsedScanSettingsUiStateOrNull
import io.github.chrisimx.scanbridge.proto.rememberScanSettingsOrNull
-import io.github.chrisimx.scanbridge.util.ScanSettingsJson
import kotlinx.coroutines.flow.first
import timber.log.Timber
@@ -38,7 +38,7 @@ object DefaultScanSettingsStore {
return appPreferences.rememberScanSettingsOrNull?.value ?: true
}
- suspend fun save(context: Context, scanSettings: ScanSettings, uiStateData: ScanSettingsStateData?) {
+ suspend fun save(context: Context, scanSettings: ScanSettings, uiStateData: ScanSettingsEnterableData?) {
if (!isRememberSettingsEnabled(context)) {
Timber.d("Scan settings persistence is disabled, skipping save")
return
@@ -61,7 +61,7 @@ object DefaultScanSettingsStore {
}
}
- suspend fun load(context: Context): Pair {
+ suspend fun load(context: Context): Pair {
if (!isRememberSettingsEnabled(context)) {
Timber.d("Scan settings persistence is disabled, returning null")
return null to null
@@ -80,7 +80,7 @@ object DefaultScanSettingsStore {
val json = ScanSettingsJson.json
val lastUsedScanSettingsDecoded = json.decodeFromString(lastUsedScanSettings)
val lastUsedScanSettingsUIStateDecoded = lastUsedScanSettingsUiState?.let {
- json.decodeFromString(it)
+ json.decodeFromString(it)
}
Timber.d("Loaded default scan settings $lastUsedScanSettings, $lastUsedScanSettingsUIStateDecoded")
return lastUsedScanSettingsDecoded to lastUsedScanSettingsUIStateDecoded
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/stores/LegacyCustomScannerStore.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/LegacyCustomScannerStore.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/stores/LegacyCustomScannerStore.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/LegacyCustomScannerStore.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/stores/LegacySessionsStore.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/LegacySessionsStore.kt
similarity index 73%
rename from app/src/main/java/io/github/chrisimx/scanbridge/stores/LegacySessionsStore.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/LegacySessionsStore.kt
index 542f4970..027d52c1 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/stores/LegacySessionsStore.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/stores/LegacySessionsStore.kt
@@ -1,9 +1,10 @@
package io.github.chrisimx.scanbridge.stores
import android.content.Context
-import androidx.room.util.copy
-import androidx.room.withTransaction
+import androidx.room.immediateTransaction
+import androidx.room.useWriterConnection
import io.github.chrisimx.esclkt.ScannerCapabilities
+import io.github.chrisimx.scanbridge.ScanSettingsJson
import io.github.chrisimx.scanbridge.data.model.LegacySessionV2
import io.github.chrisimx.scanbridge.data.model.LegacySessionV2.Companion.fromString
import io.github.chrisimx.scanbridge.datastore.appSettingsStore
@@ -12,14 +13,11 @@ import io.github.chrisimx.scanbridge.db.entities.ScannedPage
import io.github.chrisimx.scanbridge.db.entities.Session
import io.github.chrisimx.scanbridge.db.entities.TempFile
import io.github.chrisimx.scanbridge.proto.copy
-import io.github.chrisimx.scanbridge.util.ScanSettingsJson
import java.io.File
import java.nio.file.Files
import kotlin.concurrent.atomics.AtomicBoolean
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.io.path.Path
-import kotlin.io.path.extension
-import kotlin.io.path.nameWithoutExtension
import kotlin.uuid.Uuid
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -84,29 +82,10 @@ object LegacySessionsStore {
val sessionId = Uuid.parse(legacySession.sessionID)
- db.withTransaction {
- sessionDao().insertAll(
- Session(sessionId, legacySession.scanSettings, null)
- )
-
- tmpFileDao().insertAllList(
- legacySession.tmpFiles.map {
- TempFile(ownerSessionId = sessionId, path = it)
- }
- )
-
- scannedPageDao().insertAllList(
- legacySession.scannedPages.mapIndexed { index, page ->
- ScannedPage(
- scanId = Uuid.generateV4(),
- ownerSessionId = sessionId,
- filePath = page.filePath,
- originalScanSettings = page.originalScanSettings,
- rotation = page.rotation,
- orderIndex = index
- )
- }
- )
+ db.useWriterConnection {
+ it.immediateTransaction {
+ db.insertLegacySessionData(sessionId, legacySession)
+ }
}
Files.deleteIfExists(sessionDir.resolve("$sessionIdString.session"))
@@ -124,4 +103,29 @@ object LegacySessionsStore {
return this
}
+
+ private suspend fun ScanBridgeDb.insertLegacySessionData(sessionId: Uuid, legacySession: LegacySessionV2) {
+ sessionDao().insertAll(
+ Session(sessionId, legacySession.scanSettings, null)
+ )
+
+ tmpFileDao().insertAllList(
+ legacySession.tmpFiles.map {
+ TempFile(ownerSessionId = sessionId, path = it)
+ }
+ )
+
+ scannedPageDao().insertAllList(
+ legacySession.scannedPages.mapIndexed { index, page ->
+ ScannedPage(
+ scanId = Uuid.generateV4(),
+ ownerSessionId = sessionId,
+ filePath = page.filePath,
+ originalScanSettings = page.originalScanSettings,
+ rotation = page.rotation,
+ orderIndex = index
+ )
+ }
+ )
+ }
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CrashFileHandler.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CrashFileHandler.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CrashFileHandler.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CrashFileHandler.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CroppableImage.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CroppableImage.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CroppableImage.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/CroppableImage.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ExportSettingsPopup.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ExportSettingsPopup.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ExportSettingsPopup.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ExportSettingsPopup.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FoundScannerItem.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FoundScannerItem.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FoundScannerItem.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FoundScannerItem.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FullScreenError.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FullScreenError.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FullScreenError.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/FullScreenError.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/SizeBasedConditionalView.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/SizeBasedConditionalView.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/SizeBasedConditionalView.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/SizeBasedConditionalView.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TemporaryFileHandlePrompt.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TemporaryFileHandlePrompt.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TemporaryFileHandlePrompt.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TemporaryFileHandlePrompt.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TitledCard.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TitledCard.kt
similarity index 98%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TitledCard.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TitledCard.kt
index b11e9198..8bfd0e24 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TitledCard.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/TitledCard.kt
@@ -47,7 +47,7 @@ fun TitledCard(title: String, color: Color = Teal1, content: @Composable () -> U
Text(
title,
modifier = Modifier.padding(bottom = 16.dp),
- fontFamily = Poppins,
+ fontFamily = Poppins(),
fontWeight = FontWeight.SemiBold,
fontSize = 20.sp,
color = color
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ValidatedTextField.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ValidatedTextField.kt
similarity index 97%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ValidatedTextField.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ValidatedTextField.kt
index 88ea65ba..89a869ca 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ValidatedTextField.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/ValidatedTextField.kt
@@ -28,7 +28,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import io.github.chrisimx.scanbridge.R
-import io.github.chrisimx.scanbridge.data.ui.NumberValidationResult
+import io.github.chrisimx.scanbridge.model.NumberValidationResult
+
fun NumberValidationResult.toHumanString(context: Context): String = when (this) {
is NumberValidationResult.OutOfRange -> context.getString(R.string.error_state_not_in_allowed_range)
NumberValidationResult.NotANumber -> context.getString(R.string.error_state_not_a_valid_number)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ConfirmCloseDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ConfirmCloseDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ConfirmCloseDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ConfirmCloseDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CrashFileDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CrashFileDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CrashFileDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CrashFileDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CustomScannerDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CustomScannerDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CustomScannerDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/CustomScannerDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/DeleteDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/DeleteDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/DeleteDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/DeleteDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ErrorDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ErrorDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ErrorDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/ErrorDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/LoadingDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/LoadingDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/LoadingDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/LoadingDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/SimpleTextDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/SimpleTextDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/SimpleTextDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/SimpleTextDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/TemporaryFileHandlingDialog.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/TemporaryFileHandlingDialog.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/TemporaryFileHandlingDialog.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/dialog/TemporaryFileHandlingDialog.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/CheckboxSetting.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/CheckboxSetting.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/CheckboxSetting.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/CheckboxSetting.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/MoreInformationButton.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/MoreInformationButton.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/MoreInformationButton.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/MoreInformationButton.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/UIntSetting.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/UIntSetting.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/UIntSetting.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/UIntSetting.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/VersionComposable.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/VersionComposable.kt
similarity index 97%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/VersionComposable.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/VersionComposable.kt
index 7424eb8e..58c7cfdd 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/VersionComposable.kt
+++ b/androidApp/src/main/java/io/github/chrisimx/scanbridge/uicomponents/settings/VersionComposable.kt
@@ -58,7 +58,7 @@ fun VersionComposable() {
Text(
stringResource(R.string.app_name),
modifier = Modifier.padding(PaddingValues(4.dp)),
- fontFamily = Poppins,
+ fontFamily = Poppins(),
fontSize = 24.sp,
fontWeight = FontWeight.ExtraBold,
style = MaterialTheme.typography.labelLarge.copy(
@@ -69,7 +69,7 @@ fun VersionComposable() {
Text(
"${BuildConfig.VERSION_NAME.removeSuffix("-play")} (${BuildConfig.VERSION_CODE}, ${BuildConfig.GIT_COMMIT_HASH})",
fontStyle = FontStyle.Normal,
- fontFamily = Poppins
+ fontFamily = Poppins()
)
val editionNotice = mutableListOf()
@@ -87,7 +87,7 @@ fun VersionComposable() {
Text(
editionNotice.joinToString(),
fontStyle = FontStyle.Italic,
- fontFamily = Poppins
+ fontFamily = Poppins()
)
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/ESCLKtExtensions.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ESCLKtExtensions.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/ESCLKtExtensions.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ESCLKtExtensions.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/ImageUtil.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ImageUtil.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/ImageUtil.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ImageUtil.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/NavControllerExtensions.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/NavControllerExtensions.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/NavControllerExtensions.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/NavControllerExtensions.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/PdfUtil.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/PdfUtil.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/PdfUtil.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/PdfUtil.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/ScanFileNameUtil.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ScanFileNameUtil.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/ScanFileNameUtil.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ScanFileNameUtil.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/StateFlowExtensions.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/StateFlowExtensions.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/StateFlowExtensions.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/StateFlowExtensions.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/TempFileUtil.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/TempFileUtil.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/TempFileUtil.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/TempFileUtil.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/UIUtils.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/UIUtils.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/UIUtils.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/UIUtils.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/ZipArchiveUtil.kt b/androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ZipArchiveUtil.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/ZipArchiveUtil.kt
rename to androidApp/src/main/java/io/github/chrisimx/scanbridge/util/ZipArchiveUtil.kt
diff --git a/app/src/main/proto/app_settings.proto b/androidApp/src/main/proto/app_settings.proto
similarity index 100%
rename from app/src/main/proto/app_settings.proto
rename to androidApp/src/main/proto/app_settings.proto
diff --git a/app/src/main/proto/route_store.proto b/androidApp/src/main/proto/route_store.proto
similarity index 100%
rename from app/src/main/proto/route_store.proto
rename to androidApp/src/main/proto/route_store.proto
diff --git a/app/src/main/proto/shown_messages.proto b/androidApp/src/main/proto/shown_messages.proto
similarity index 100%
rename from app/src/main/proto/shown_messages.proto
rename to androidApp/src/main/proto/shown_messages.proto
diff --git a/app/src/main/res/drawable/baseline_image_24.xml b/androidApp/src/main/res/drawable/baseline_image_24.xml
similarity index 100%
rename from app/src/main/res/drawable/baseline_image_24.xml
rename to androidApp/src/main/res/drawable/baseline_image_24.xml
diff --git a/app/src/main/res/drawable/baseline_picture_as_pdf_24.xml b/androidApp/src/main/res/drawable/baseline_picture_as_pdf_24.xml
similarity index 100%
rename from app/src/main/res/drawable/baseline_picture_as_pdf_24.xml
rename to androidApp/src/main/res/drawable/baseline_picture_as_pdf_24.xml
diff --git a/app/src/main/res/drawable/baseline_rotate_right_24.xml b/androidApp/src/main/res/drawable/baseline_rotate_right_24.xml
similarity index 100%
rename from app/src/main/res/drawable/baseline_rotate_right_24.xml
rename to androidApp/src/main/res/drawable/baseline_rotate_right_24.xml
diff --git a/app/src/main/res/drawable/favorite_24px.xml b/androidApp/src/main/res/drawable/favorite_24px.xml
similarity index 100%
rename from app/src/main/res/drawable/favorite_24px.xml
rename to androidApp/src/main/res/drawable/favorite_24px.xml
diff --git a/app/src/main/res/drawable/github_mark.xml b/androidApp/src/main/res/drawable/github_mark.xml
similarity index 100%
rename from app/src/main/res/drawable/github_mark.xml
rename to androidApp/src/main/res/drawable/github_mark.xml
diff --git a/app/src/main/res/drawable/icon_about_dialog.png b/androidApp/src/main/res/drawable/icon_about_dialog.png
similarity index 100%
rename from app/src/main/res/drawable/icon_about_dialog.png
rename to androidApp/src/main/res/drawable/icon_about_dialog.png
diff --git a/app/src/main/res/drawable/outline_crop_24.xml b/androidApp/src/main/res/drawable/outline_crop_24.xml
similarity index 100%
rename from app/src/main/res/drawable/outline_crop_24.xml
rename to androidApp/src/main/res/drawable/outline_crop_24.xml
diff --git a/app/src/main/res/drawable/outline_edit_24.xml b/androidApp/src/main/res/drawable/outline_edit_24.xml
similarity index 100%
rename from app/src/main/res/drawable/outline_edit_24.xml
rename to androidApp/src/main/res/drawable/outline_edit_24.xml
diff --git a/app/src/main/res/drawable/outline_error_24.xml b/androidApp/src/main/res/drawable/outline_error_24.xml
similarity index 100%
rename from app/src/main/res/drawable/outline_error_24.xml
rename to androidApp/src/main/res/drawable/outline_error_24.xml
diff --git a/app/src/main/res/drawable/outline_file_save_24.xml b/androidApp/src/main/res/drawable/outline_file_save_24.xml
similarity index 100%
rename from app/src/main/res/drawable/outline_file_save_24.xml
rename to androidApp/src/main/res/drawable/outline_file_save_24.xml
diff --git a/app/src/main/res/drawable/outline_pan_zoom_24.xml b/androidApp/src/main/res/drawable/outline_pan_zoom_24.xml
similarity index 100%
rename from app/src/main/res/drawable/outline_pan_zoom_24.xml
rename to androidApp/src/main/res/drawable/outline_pan_zoom_24.xml
diff --git a/app/src/main/res/drawable/outline_scan_24.xml b/androidApp/src/main/res/drawable/outline_scan_24.xml
similarity index 100%
rename from app/src/main/res/drawable/outline_scan_24.xml
rename to androidApp/src/main/res/drawable/outline_scan_24.xml
diff --git a/app/src/main/res/drawable/round_print_36.xml b/androidApp/src/main/res/drawable/round_print_36.xml
similarity index 100%
rename from app/src/main/res/drawable/round_print_36.xml
rename to androidApp/src/main/res/drawable/round_print_36.xml
diff --git a/app/src/main/res/drawable/rounded_adf_scanner_24.xml b/androidApp/src/main/res/drawable/rounded_adf_scanner_24.xml
similarity index 100%
rename from app/src/main/res/drawable/rounded_adf_scanner_24.xml
rename to androidApp/src/main/res/drawable/rounded_adf_scanner_24.xml
diff --git a/app/src/main/res/drawable/rounded_content_copy_24.xml b/androidApp/src/main/res/drawable/rounded_content_copy_24.xml
similarity index 100%
rename from app/src/main/res/drawable/rounded_content_copy_24.xml
rename to androidApp/src/main/res/drawable/rounded_content_copy_24.xml
diff --git a/app/src/main/res/drawable/rounded_document_scanner_24.xml b/androidApp/src/main/res/drawable/rounded_document_scanner_24.xml
similarity index 100%
rename from app/src/main/res/drawable/rounded_document_scanner_24.xml
rename to androidApp/src/main/res/drawable/rounded_document_scanner_24.xml
diff --git a/app/src/main/res/drawable/rounded_scanner_24.xml b/androidApp/src/main/res/drawable/rounded_scanner_24.xml
similarity index 100%
rename from app/src/main/res/drawable/rounded_scanner_24.xml
rename to androidApp/src/main/res/drawable/rounded_scanner_24.xml
diff --git a/app/src/main/res/drawable/rounded_warning_24.xml b/androidApp/src/main/res/drawable/rounded_warning_24.xml
similarity index 100%
rename from app/src/main/res/drawable/rounded_warning_24.xml
rename to androidApp/src/main/res/drawable/rounded_warning_24.xml
diff --git a/app/src/main/res/drawable/twotone_wifi_find_24.xml b/androidApp/src/main/res/drawable/twotone_wifi_find_24.xml
similarity index 100%
rename from app/src/main/res/drawable/twotone_wifi_find_24.xml
rename to androidApp/src/main/res/drawable/twotone_wifi_find_24.xml
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
rename from app/src/main/res/mipmap-hdpi/ic_launcher.webp
rename to androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_background.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_background.webp
similarity index 100%
rename from app/src/main/res/mipmap-hdpi/ic_launcher_background.webp
rename to androidApp/src/main/res/mipmap-hdpi/ic_launcher_background.webp
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
similarity index 100%
rename from app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
rename to androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp
similarity index 100%
rename from app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
rename to androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
rename from app/src/main/res/mipmap-mdpi/ic_launcher.webp
rename to androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_background.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_background.webp
similarity index 100%
rename from app/src/main/res/mipmap-mdpi/ic_launcher_background.webp
rename to androidApp/src/main/res/mipmap-mdpi/ic_launcher_background.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
similarity index 100%
rename from app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
rename to androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp
similarity index 100%
rename from app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
rename to androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
rename from app/src/main/res/mipmap-xhdpi/ic_launcher.webp
rename to androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_background.webp
similarity index 100%
rename from app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp
rename to androidApp/src/main/res/mipmap-xhdpi/ic_launcher_background.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
similarity index 100%
rename from app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
rename to androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
similarity index 100%
rename from app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
rename to androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
rename to androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp
rename to androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
rename to androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
rename to androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
rename to androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp
rename to androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
rename to androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
similarity index 100%
rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
rename to androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/raw/test_scanner_capabilities b/androidApp/src/main/res/raw/test_scanner_capabilities
similarity index 100%
rename from app/src/main/res/raw/test_scanner_capabilities
rename to androidApp/src/main/res/raw/test_scanner_capabilities
diff --git a/app/src/main/res/resources.properties b/androidApp/src/main/res/resources.properties
similarity index 100%
rename from app/src/main/res/resources.properties
rename to androidApp/src/main/res/resources.properties
diff --git a/app/src/main/res/values-de/strings.xml b/androidApp/src/main/res/values-de/strings.xml
similarity index 100%
rename from app/src/main/res/values-de/strings.xml
rename to androidApp/src/main/res/values-de/strings.xml
diff --git a/app/src/main/res/values-it/strings.xml b/androidApp/src/main/res/values-it/strings.xml
similarity index 100%
rename from app/src/main/res/values-it/strings.xml
rename to androidApp/src/main/res/values-it/strings.xml
diff --git a/app/src/main/res/values/colors.xml b/androidApp/src/main/res/values/colors.xml
similarity index 100%
rename from app/src/main/res/values/colors.xml
rename to androidApp/src/main/res/values/colors.xml
diff --git a/app/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml
similarity index 100%
rename from app/src/main/res/values/strings.xml
rename to androidApp/src/main/res/values/strings.xml
diff --git a/app/src/main/res/values/themes.xml b/androidApp/src/main/res/values/themes.xml
similarity index 100%
rename from app/src/main/res/values/themes.xml
rename to androidApp/src/main/res/values/themes.xml
diff --git a/app/src/main/res/xml/backup_rules.xml b/androidApp/src/main/res/xml/backup_rules.xml
similarity index 100%
rename from app/src/main/res/xml/backup_rules.xml
rename to androidApp/src/main/res/xml/backup_rules.xml
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/androidApp/src/main/res/xml/data_extraction_rules.xml
similarity index 100%
rename from app/src/main/res/xml/data_extraction_rules.xml
rename to androidApp/src/main/res/xml/data_extraction_rules.xml
diff --git a/app/src/main/res/xml/network_security_config.xml b/androidApp/src/main/res/xml/network_security_config.xml
similarity index 100%
rename from app/src/main/res/xml/network_security_config.xml
rename to androidApp/src/main/res/xml/network_security_config.xml
diff --git a/app/src/main/res/xml/provider_paths.xml b/androidApp/src/main/res/xml/provider_paths.xml
similarity index 100%
rename from app/src/main/res/xml/provider_paths.xml
rename to androidApp/src/main/res/xml/provider_paths.xml
diff --git a/app/src/play/AndroidManifest.xml b/androidApp/src/play/AndroidManifest.xml
similarity index 100%
rename from app/src/play/AndroidManifest.xml
rename to androidApp/src/play/AndroidManifest.xml
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/AccountSetupScreen.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/AccountSetupScreen.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/AccountSetupScreen.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/AccountSetupScreen.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/InternetConnectivityFlow.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/InternetConnectivityFlow.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/InternetConnectivityFlow.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/InternetConnectivityFlow.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/LicenseErrorStringMapping.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/LicenseErrorStringMapping.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/LicenseErrorStringMapping.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/LicenseErrorStringMapping.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/OwnershipProofService.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/OwnershipProofService.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/OwnershipProofService.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/OwnershipProofService.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/OwnershipTestScreen.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/OwnershipTestScreen.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/OwnershipTestScreen.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/OwnershipTestScreen.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/PlainTextObfuscator.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/PlainTextObfuscator.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/PlainTextObfuscator.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/PlainTextObfuscator.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/SignupScreen.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/SignupScreen.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/SignupScreen.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/SignupScreen.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/SimpleStrictLicensePolicy.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/SimpleStrictLicensePolicy.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/SimpleStrictLicensePolicy.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/SimpleStrictLicensePolicy.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/SpecialIcons.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/SpecialIcons.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/SpecialIcons.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/SpecialIcons.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/StartupTabDefinitions.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/SuccessSigningUpScreen.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/SuccessSigningUpScreen.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/SuccessSigningUpScreen.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/SuccessSigningUpScreen.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/SupportScreen.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/SupportScreen.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/SupportScreen.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/SupportScreen.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerificationClient.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerificationClient.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerificationClient.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerificationClient.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerifierApi.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerifierApi.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerifierApi.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/AccountVerifierApi.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/AccountCreationRequest.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/AccountCreationRequest.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/AccountCreationRequest.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/AccountCreationRequest.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/GoogleChallenge.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/GoogleChallenge.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/GoogleChallenge.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/GoogleChallenge.kt
diff --git a/app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/VIPCreationResult.kt b/androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/VIPCreationResult.kt
similarity index 100%
rename from app/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/VIPCreationResult.kt
rename to androidApp/src/play/java/io/github/chrisimx/scanbridge/zammadapi/models/VIPCreationResult.kt
diff --git a/app/src/play/res/drawable/fireamp_icon.xml b/androidApp/src/play/res/drawable/fireamp_icon.xml
similarity index 100%
rename from app/src/play/res/drawable/fireamp_icon.xml
rename to androidApp/src/play/res/drawable/fireamp_icon.xml
diff --git a/app/src/play/res/drawable/outline_globe_24.xml b/androidApp/src/play/res/drawable/outline_globe_24.xml
similarity index 100%
rename from app/src/play/res/drawable/outline_globe_24.xml
rename to androidApp/src/play/res/drawable/outline_globe_24.xml
diff --git a/app/src/play/res/values-de/strings.xml b/androidApp/src/play/res/values-de/strings.xml
similarity index 100%
rename from app/src/play/res/values-de/strings.xml
rename to androidApp/src/play/res/values-de/strings.xml
diff --git a/app/src/play/res/values-it/strings.xml b/androidApp/src/play/res/values-it/strings.xml
similarity index 100%
rename from app/src/play/res/values-it/strings.xml
rename to androidApp/src/play/res/values-it/strings.xml
diff --git a/app/src/play/res/values/strings.xml b/androidApp/src/play/res/values/strings.xml
similarity index 100%
rename from app/src/play/res/values/strings.xml
rename to androidApp/src/play/res/values/strings.xml
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApplication.kt b/app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApplication.kt
deleted file mode 100644
index 91667bb1..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/ScanBridgeApplication.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package io.github.chrisimx.scanbridge
-
-import android.app.Application
-import android.content.Context
-import androidx.datastore.core.DataStore
-import androidx.room.Room
-import io.github.chrisimx.scanbridge.data.ui.CustomScannerViewModel
-import io.github.chrisimx.scanbridge.data.ui.ScanSettingsComposableStateHolder
-import io.github.chrisimx.scanbridge.data.ui.ScanningScreenViewModel
-import io.github.chrisimx.scanbridge.datastore.appSettingsStore
-import io.github.chrisimx.scanbridge.db.ScanBridgeDb
-import io.github.chrisimx.scanbridge.proto.ScanBridgeSettings
-import io.github.chrisimx.scanbridge.services.AndroidLocaleProvider
-import io.github.chrisimx.scanbridge.services.DebugLogService
-import io.github.chrisimx.scanbridge.services.FileDebugLogService
-import io.github.chrisimx.scanbridge.services.LocaleProvider
-import io.github.chrisimx.scanbridge.services.ScanJobRepository
-import io.github.chrisimx.scanbridge.stores.LegacyCustomScannerStore.migrateLegacyCustomScanners
-import io.github.chrisimx.scanbridge.stores.LegacySessionsStore.migrateLegacySessions
-import java.util.concurrent.Executors
-import kotlinx.coroutines.flow.firstOrNull
-import kotlinx.coroutines.runBlocking
-import org.koin.android.ext.koin.androidContext
-import org.koin.core.context.startKoin
-import org.koin.dsl.bind
-import org.koin.dsl.module
-import org.koin.plugin.module.dsl.factory
-import org.koin.plugin.module.dsl.single
-import org.koin.plugin.module.dsl.viewModel
-import timber.log.Timber
-
-val appModule = module {
- single> {
- get().appSettingsStore
- }
- single() bind LocaleProvider::class
- single() bind DebugLogService::class
- single()
- single {
- val context = get()
- val writeDebug = runBlocking { context.appSettingsStore.data.firstOrNull()?.writeDebug ?: false }
- val builder = Room.databaseBuilder(
- get(),
- ScanBridgeDb::class.java,
- "scanbridge"
- )
- if (writeDebug) {
- builder.setQueryCallback(
- { sqlQuery, bindArgs ->
- Timber.tag("RoomDebug").d("SQL: $sqlQuery, args: $bindArgs")
- },
- Executors.newSingleThreadExecutor()
- )
- }
- builder.build()
- .migrateLegacyCustomScanners(get())
- .migrateLegacySessions(get())
- }
- factory()
- viewModel()
- viewModel()
-}
-
-class ScanBridgeApplication : Application() {
- override fun onCreate() {
- super.onCreate()
- startKoin {
- androidContext(this@ScanBridgeApplication)
- modules(appModule)
- }
-
- Timber.plant(Timber.DebugTree())
- }
-}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/ScanJob.kt b/app/src/main/java/io/github/chrisimx/scanbridge/data/model/ScanJob.kt
deleted file mode 100644
index 47f30b49..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/ScanJob.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package io.github.chrisimx.scanbridge.data.model
-
-import io.github.chrisimx.esclkt.ESCLRequestClient
-import io.github.chrisimx.esclkt.ScanSettings
-import kotlin.uuid.Uuid
-
-data class ScanJob(val jobID: Uuid, val ownerSessionId: Uuid, val scanSettings: ScanSettings, val esclClient: ESCLRequestClient)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsStateData.kt b/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsStateData.kt
deleted file mode 100644
index 62a322e6..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/ui/ScanSettingsStateData.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2024-2025 Christian Nagel and contributors
- *
- * This file is part of ScanBridge.
- *
- * ScanBridge is free software: you can redistribute it and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ScanBridge is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with eSCLKt.
- * If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-package io.github.chrisimx.scanbridge.data.ui
-
-import io.github.chrisimx.esclkt.ScannerCapabilities
-import io.github.chrisimx.scanbridge.data.model.PaperFormat
-import io.github.chrisimx.scanbridge.data.model.loadDefaultFormats
-import kotlinx.serialization.Serializable
-
-@Serializable
-sealed class NumberValidationResult {
- data class Success(val value: Double) : NumberValidationResult()
- data class OutOfRange(val min: Double, val max: Double) : NumberValidationResult()
- data object NotANumber : NumberValidationResult()
-}
-
-@Serializable
-data class ScanSettingsStateData(
- val capabilities: ScannerCapabilities,
- val paperFormats: List = loadDefaultFormats(),
- val customMenuEnabled: Boolean = false,
- val widthString: String = "",
- val heightString: String = "",
- val maximumSize: Boolean = true
-)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsUiDataTypeConverter.kt b/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsUiDataTypeConverter.kt
deleted file mode 100644
index 293b59a4..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsUiDataTypeConverter.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package io.github.chrisimx.scanbridge.db.typeconverters
-
-import androidx.room.TypeConverter
-import io.github.chrisimx.scanbridge.data.ui.ScanSettingsStateData
-import io.github.chrisimx.scanbridge.util.ScanSettingsJson
-
-class ScanSettingsUiDataTypeConverter {
-
- @TypeConverter
- fun fromScanSettingsString(scanSettings: String): ScanSettingsStateData? = if (scanSettings == "null") {
- null
- } else {
- ScanSettingsJson.json.decodeFromString(scanSettings)
- }
-
- @TypeConverter
- fun toScanSettingsString(scanSettings: ScanSettingsStateData?): String = if (scanSettings == null) {
- "null"
- } else {
- ScanSettingsJson.json.encodeToString(scanSettings)
- }
-}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/services/LocaleProvider.kt b/app/src/main/java/io/github/chrisimx/scanbridge/services/LocaleProvider.kt
deleted file mode 100644
index 8aef6fd8..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/services/LocaleProvider.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.chrisimx.scanbridge.services
-
-import java.util.Locale
-import kotlinx.coroutines.flow.StateFlow
-
-interface LocaleProvider {
- val locale: StateFlow
-}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Font.kt b/app/src/main/java/io/github/chrisimx/scanbridge/theme/Font.kt
deleted file mode 100644
index 8fdf3d94..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Font.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.github.chrisimx.scanbridge.theme
-
-import androidx.compose.ui.text.font.Font
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.text.font.FontWeight
-import io.github.chrisimx.scanbridge.R
-
-val Poppins = FontFamily(
- Font(R.font.poppins_thin, weight = FontWeight.Thin),
- Font(R.font.poppins_thinitalic, weight = FontWeight.Thin, style = FontStyle.Italic),
-
- Font(R.font.poppins_extralight, weight = FontWeight.ExtraLight),
- Font(R.font.poppins_extralightitalic, weight = FontWeight.ExtraLight, style = FontStyle.Italic),
-
- Font(R.font.poppins_light, weight = FontWeight.Light),
- Font(R.font.poppins_lightitalic, weight = FontWeight.Light, style = FontStyle.Italic),
-
- Font(R.font.poppins_regular, weight = FontWeight.Normal),
- Font(R.font.poppins_italic, weight = FontWeight.Normal, style = FontStyle.Italic),
-
- Font(R.font.poppins_medium, weight = FontWeight.Medium),
- Font(R.font.poppins_mediumitalic, weight = FontWeight.Medium, style = FontStyle.Italic),
-
- Font(R.font.poppins_semibold, weight = FontWeight.SemiBold),
- Font(R.font.poppins_semibolditalic, weight = FontWeight.SemiBold, style = FontStyle.Italic),
-
- Font(R.font.poppins_bold, weight = FontWeight.Bold),
- Font(R.font.poppins_bolditalic, weight = FontWeight.Bold, style = FontStyle.Italic),
-
- Font(R.font.poppins_extrabold, weight = FontWeight.ExtraBold),
- Font(R.font.poppins_extrabolditalic, weight = FontWeight.ExtraBold, style = FontStyle.Italic),
-
- Font(R.font.poppins_black, weight = FontWeight.Black),
- Font(R.font.poppins_blackitalic, weight = FontWeight.Black, style = FontStyle.Italic)
-)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Typography.kt b/app/src/main/java/io/github/chrisimx/scanbridge/theme/Typography.kt
deleted file mode 100644
index a99f7cf7..00000000
--- a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Typography.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2024-2025 Christian Nagel and contributors
- *
- * This file is part of ScanBridge.
- *
- * ScanBridge is free software: you can redistribute it and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ScanBridge is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with eSCLKt.
- * If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-package io.github.chrisimx.scanbridge.theme
-
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.Typography
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.sp
-
-// Set of Material typography styles to start with
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
-val Typography = Typography(
- titleSmall = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 20.sp,
- letterSpacing = 0.sp
- ),
- titleMedium = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 18.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.sp
- ),
- titleMediumEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- fontSize = 18.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.sp
- ),
- titleLarge = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 22.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp
- ),
- titleLargeEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- fontSize = 22.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp
- ),
- labelSmall = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- lineHeight = 14.sp,
- fontSize = 11.sp,
- letterSpacing = 0.sp
- ),
- labelMedium = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- lineHeight = 14.sp,
- fontSize = 14.sp,
- letterSpacing = 0.sp
- ),
- labelLarge = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- letterSpacing = 0.sp
- ),
- bodySmall = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- letterSpacing = 0.sp
- ),
- bodySmallEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- letterSpacing = 0.sp
- ),
- bodyMedium = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- letterSpacing = 0.sp
- ),
- bodyMediumEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- letterSpacing = 0.sp
- ),
- bodyLarge = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.5.sp
- ),
- bodyLargeEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.5.sp
- ),
- displaySmall = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- letterSpacing = 0.sp
- ),
- displayMedium = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- letterSpacing = 0.sp
- ),
- displayLarge = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- letterSpacing = 0.sp
- ),
- displaySmallEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- letterSpacing = 0.sp
- ),
- displayMediumEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- letterSpacing = 0.sp
- ),
- displayLargeEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- letterSpacing = 0.sp
- ),
- headlineLarge = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 24.sp,
- lineHeight = 32.sp,
- letterSpacing = 0.sp
- ),
- headlineMedium = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 20.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp
- ),
- headlineMediumEmphasized = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.ExtraBold,
- fontSize = 20.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp
- ),
- headlineSmall = TextStyle(
- fontFamily = Poppins,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.sp
- )
-)
diff --git a/app/src/test/java/org/github/chrisimx/scanbridge/ScanSettingsStoreTest.kt b/app/src/test/java/org/github/chrisimx/scanbridge/ScanSettingsStoreTest.kt
deleted file mode 100644
index d2320e3b..00000000
--- a/app/src/test/java/org/github/chrisimx/scanbridge/ScanSettingsStoreTest.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2024-2025 Christian Nagel and contributors
- *
- * This file is part of ScanBridge.
- *
- * ScanBridge is free software: you can redistribute it and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ScanBridge is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with eSCLKt.
- * If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-package org.github.chrisimx.scanbridge
-
-import io.github.chrisimx.esclkt.ColorMode
-import io.github.chrisimx.esclkt.EnumOrRaw
-import io.github.chrisimx.esclkt.InputSource
-import io.github.chrisimx.scanbridge.data.model.StatelessImmutableESCLScanSettingsState
-import io.github.chrisimx.scanbridge.data.model.StatelessImmutableScanRegion
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-class ScanSettingsStoreTest {
-
- @Test
- fun testScanSettingsPersistenceBehavior() {
- val testSettings = StatelessImmutableESCLScanSettingsState(
- version = "2.63",
- intent = null,
- scanRegions = StatelessImmutableScanRegion("297", "210", "0", "0"),
- documentFormatExt = "application/pdf",
- contentType = null,
- inputSource = InputSource.Feeder,
- xResolution = 300u,
- yResolution = 300u,
- colorMode = EnumOrRaw.Known(ColorMode.RGB24),
- colorSpace = null,
- mediaType = null,
- ccdChannel = null,
- binaryRendering = null,
- duplex = true,
- numberOfPages = null,
- brightness = null,
- compressionFactor = null,
- contrast = null,
- gamma = null,
- highlight = null,
- noiseRemoval = null,
- shadow = null,
- sharpen = null,
- threshold = null,
- contextID = null,
- blankPageDetection = null,
- feedDirection = null,
- blankPageDetectionAndRemoval = null
- )
-
- assertEquals(InputSource.Feeder, testSettings.inputSource)
- assertEquals(true, testSettings.duplex)
- assertEquals("210", testSettings.scanRegions?.width)
- assertEquals("297", testSettings.scanRegions?.height)
- assertEquals("application/pdf", testSettings.documentFormatExt)
-
- // Test conversion to mutable (for editing in UI)
- val mutableSettings = testSettings.toMutable()
- assertEquals(InputSource.Feeder, mutableSettings.inputSource)
- assertEquals(true, mutableSettings.duplex)
-
- // Test conversion back to stateless (for persistence)
- val statelessSettings = mutableSettings.toStateless()
- assertEquals(testSettings.inputSource, statelessSettings.inputSource)
- assertEquals(testSettings.duplex, statelessSettings.duplex)
- assertEquals(testSettings.documentFormatExt, statelessSettings.documentFormatExt)
- }
-
- @Test
- fun testScanSettingsStatelessDataStructure() {
- val testScanRegion = StatelessImmutableScanRegion(
- "297",
- "210",
- "0",
- "0"
- )
-
- val testSettings = StatelessImmutableESCLScanSettingsState(
- version = "2.63",
- intent = null,
- scanRegions = testScanRegion,
- documentFormatExt = "image/jpeg",
- contentType = null,
- inputSource = InputSource.Feeder,
- xResolution = 300u,
- yResolution = 300u,
- colorMode = EnumOrRaw.Known(ColorMode.RGB24),
- colorSpace = null,
- mediaType = null,
- ccdChannel = null,
- binaryRendering = null,
- duplex = true,
- numberOfPages = null,
- brightness = null,
- compressionFactor = null,
- contrast = null,
- gamma = null,
- highlight = null,
- noiseRemoval = null,
- shadow = null,
- sharpen = null,
- threshold = null,
- contextID = null,
- blankPageDetection = null,
- feedDirection = null,
- blankPageDetectionAndRemoval = null
- )
-
- assertEquals(InputSource.Feeder, testSettings.inputSource)
- assertEquals(true, testSettings.duplex)
- assertEquals("image/jpeg", testSettings.documentFormatExt)
- assertEquals(true, testSettings.scanRegions != null)
-
- // Test the toMutable conversion to ensure settings can be converted back
- val mutableSettings = testSettings.toMutable()
- assertEquals(InputSource.Feeder, mutableSettings.inputSource)
- assertEquals(true, mutableSettings.duplex)
- assertEquals("image/jpeg", mutableSettings.documentFormatExt)
-
- // Test that we can convert back to stateless
- val reconvertedSettings = mutableSettings.toStateless()
- assertEquals(testSettings.inputSource, reconvertedSettings.inputSource)
- assertEquals(testSettings.duplex, reconvertedSettings.duplex)
- assertEquals(testSettings.documentFormatExt, reconvertedSettings.documentFormatExt)
- }
-
- @Test
- fun testScanRegionDataStructure() {
- val a4Region = StatelessImmutableScanRegion("297", "210", "0", "0")
- val mutableA4 = a4Region.toMutable()
-
- assertEquals("210", mutableA4.width)
- assertEquals("297", mutableA4.height)
- assertEquals("0", mutableA4.xOffset)
- assertEquals("0", mutableA4.yOffset)
-
- // Test that we can modify mutable settings
- mutableA4.width = "148" // A5 width
- mutableA4.height = "210" // A5 height
-
- assertEquals("148", mutableA4.width)
- assertEquals("210", mutableA4.height)
-
- // Test conversion back to stateless
- val a5Region = mutableA4.toStateless()
- val reconvertedA5 = a5Region.toMutable()
- assertEquals("148", reconvertedA5.width)
- assertEquals("210", reconvertedA5.height)
- }
-}
diff --git a/build.gradle.kts b/build.gradle.kts
index d497f649..c70a45c6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,9 +1,13 @@
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- alias(libs.plugins.android.application) apply false
- alias(libs.plugins.kotlin.compose) apply false
+ alias(libs.plugins.kotlin.multiplatform).apply(false)
+ alias(libs.plugins.compose.compiler).apply(false)
+ alias(libs.plugins.compose.multiplatform).apply(false)
+ alias(libs.plugins.kotlin.android).apply(false)
+ alias(libs.plugins.android.application).apply(false)
+ alias(libs.plugins.android.kmp.library).apply(false)
+ alias(libs.plugins.kotlin.jvm).apply(false)
alias(libs.plugins.versions)
}
diff --git a/composeUI/.gitignore b/composeUI/.gitignore
new file mode 100644
index 00000000..92e0b0fb
--- /dev/null
+++ b/composeUI/.gitignore
@@ -0,0 +1,2 @@
+/build
+build
\ No newline at end of file
diff --git a/composeUI/build.gradle.kts b/composeUI/build.gradle.kts
new file mode 100644
index 00000000..a7dc95b6
--- /dev/null
+++ b/composeUI/build.gradle.kts
@@ -0,0 +1,74 @@
+import org.gradle.kotlin.dsl.withType
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
+
+plugins {
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.compose.compiler)
+ alias(libs.plugins.compose.multiplatform)
+ alias(libs.plugins.android.kmp.library)
+}
+
+kotlin {
+ jvm {
+ compilerOptions { jvmTarget = JvmTarget.JVM_17 }
+ }
+
+ android {
+ namespace = "io.github.chrisimx.scanbridge"
+ compileSdk = 36
+ minSdk = 23
+ androidResources.enable = true
+
+ compilerOptions {
+ jvmTarget.set(
+ org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
+ )
+ }
+ }
+
+ sourceSets {
+ commonMain.dependencies {
+ api(libs.compose.runtime)
+ api(libs.compose.ui)
+ api(libs.compose.foundation)
+ api(libs.compose.resources)
+ api(libs.compose.ui.tooling.preview)
+ api(libs.compose.material3)
+ }
+
+ commonTest.dependencies {
+ implementation(kotlin("test"))
+ implementation(libs.compose.ui.test)
+ }
+
+ androidMain.dependencies {
+ }
+
+ jvmMain.dependencies {
+ implementation(compose.desktop.currentOs)
+ }
+ }
+
+ targets
+ .withType()
+ .matching { it.konanTarget.family.isAppleFamily }
+ .configureEach {
+ binaries {
+ framework {
+ baseName = "ScanBridgeSharedUI"
+ isStatic = true
+ }
+ }
+ }
+}
+
+dependencies {
+ androidRuntimeClasspath("org.jetbrains.compose.ui:ui-tooling:1.10.0")
+}
+
+compose.resources {
+ publicResClass = true
+ // packageOfResClass = "me.sample.library.resources"
+ generateResClass = always
+}
diff --git a/composeUI/src/androidMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.android.kt b/composeUI/src/androidMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.android.kt
new file mode 100644
index 00000000..0c3ef625
--- /dev/null
+++ b/composeUI/src/androidMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.android.kt
@@ -0,0 +1,24 @@
+package io.github.chrisimx.scanbridge.theme
+
+import android.os.Build
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+@Composable
+actual fun platformDarkColorScheme(): ColorScheme? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val context = LocalContext.current
+ dynamicDarkColorScheme(context)
+} else {
+ null
+}
+
+@Composable
+actual fun platformLightColorScheme(): ColorScheme? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val context = LocalContext.current
+ dynamicLightColorScheme(context)
+} else {
+ null
+}
diff --git a/composeUI/src/appleMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.apple.kt b/composeUI/src/appleMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.apple.kt
new file mode 100644
index 00000000..52760201
--- /dev/null
+++ b/composeUI/src/appleMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.apple.kt
@@ -0,0 +1,9 @@
+package io.github.chrisimx.scanbridge.theme
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.runtime.Composable
+
+@Composable
+actual fun platformDarkColorScheme(): ColorScheme? {
+ TODO("Not yet implemented")
+}
diff --git a/composeUI/src/commonMain/composeResources/drawable/ic_cyclone.xml b/composeUI/src/commonMain/composeResources/drawable/ic_cyclone.xml
new file mode 100644
index 00000000..f1c45b5a
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/drawable/ic_cyclone.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/composeUI/src/commonMain/composeResources/drawable/ic_dark_mode.xml b/composeUI/src/commonMain/composeResources/drawable/ic_dark_mode.xml
new file mode 100644
index 00000000..0ce2444a
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/drawable/ic_dark_mode.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/composeUI/src/commonMain/composeResources/drawable/ic_light_mode.xml b/composeUI/src/commonMain/composeResources/drawable/ic_light_mode.xml
new file mode 100644
index 00000000..b7331d3e
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/drawable/ic_light_mode.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/composeUI/src/commonMain/composeResources/drawable/ic_rotate_right.xml b/composeUI/src/commonMain/composeResources/drawable/ic_rotate_right.xml
new file mode 100644
index 00000000..18106717
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/drawable/ic_rotate_right.xml
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/font/poppins_black.ttf b/composeUI/src/commonMain/composeResources/font/poppins_black.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_black.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_black.ttf
diff --git a/app/src/main/res/font/poppins_blackitalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_blackitalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_blackitalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_blackitalic.ttf
diff --git a/app/src/main/res/font/poppins_bold.ttf b/composeUI/src/commonMain/composeResources/font/poppins_bold.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_bold.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_bold.ttf
diff --git a/app/src/main/res/font/poppins_bolditalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_bolditalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_bolditalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_bolditalic.ttf
diff --git a/app/src/main/res/font/poppins_extrabold.ttf b/composeUI/src/commonMain/composeResources/font/poppins_extrabold.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_extrabold.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_extrabold.ttf
diff --git a/app/src/main/res/font/poppins_extrabolditalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_extrabolditalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_extrabolditalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_extrabolditalic.ttf
diff --git a/app/src/main/res/font/poppins_extralight.ttf b/composeUI/src/commonMain/composeResources/font/poppins_extralight.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_extralight.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_extralight.ttf
diff --git a/app/src/main/res/font/poppins_extralightitalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_extralightitalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_extralightitalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_extralightitalic.ttf
diff --git a/app/src/main/res/font/poppins_italic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_italic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_italic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_italic.ttf
diff --git a/app/src/main/res/font/poppins_light.ttf b/composeUI/src/commonMain/composeResources/font/poppins_light.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_light.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_light.ttf
diff --git a/app/src/main/res/font/poppins_lightitalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_lightitalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_lightitalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_lightitalic.ttf
diff --git a/app/src/main/res/font/poppins_medium.ttf b/composeUI/src/commonMain/composeResources/font/poppins_medium.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_medium.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_medium.ttf
diff --git a/app/src/main/res/font/poppins_mediumitalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_mediumitalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_mediumitalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_mediumitalic.ttf
diff --git a/app/src/main/res/font/poppins_regular.ttf b/composeUI/src/commonMain/composeResources/font/poppins_regular.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_regular.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_regular.ttf
diff --git a/app/src/main/res/font/poppins_semibold.ttf b/composeUI/src/commonMain/composeResources/font/poppins_semibold.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_semibold.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_semibold.ttf
diff --git a/app/src/main/res/font/poppins_semibolditalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_semibolditalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_semibolditalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_semibolditalic.ttf
diff --git a/app/src/main/res/font/poppins_thin.ttf b/composeUI/src/commonMain/composeResources/font/poppins_thin.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_thin.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_thin.ttf
diff --git a/app/src/main/res/font/poppins_thinitalic.ttf b/composeUI/src/commonMain/composeResources/font/poppins_thinitalic.ttf
similarity index 100%
rename from app/src/main/res/font/poppins_thinitalic.ttf
rename to composeUI/src/commonMain/composeResources/font/poppins_thinitalic.ttf
diff --git a/composeUI/src/commonMain/composeResources/values-de/strings.xml b/composeUI/src/commonMain/composeResources/values-de/strings.xml
new file mode 100644
index 00000000..a56588bb
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/values-de/strings.xml
@@ -0,0 +1,127 @@
+
+
+ Druckersymbol
+ Einstellungen
+ Es wurden keine Scanner gefunden. Bist du mit einem lokalen Netzwerk verbunden?
+ Warnungssymbol
+ Fehler
+ Fehler beim Abrufen der Eigenschaften des Scanners:\n\n%1$s
+ Okay
+ Die Eigenschaften des Scanners werden abgerufen
+ Scannen
+ Abbrechen
+ Bricht ab…
+ Eingescannte Seite
+ Der Scanauftrag wurde annulliert
+ Der Scanauftrag wurde aufgrund eines Fehlers abgebrochen
+ Der Scanauftrag wurde erfolgreich fertiggestellt
+ Der Scanauftrag ist derzeit noch unerledigt
+ Es werden derzeit noch Seiten verarbeitet
+ Der Status des Scanauftrags konnte nicht abrufen werden
+ Seite löschen
+ Nach links verschieben
+ Nach rechts verschieben
+ Seite %1$s von %2$s
+ Exportieren
+ Seite wird abgerufen
+ Wird exportiert…
+ Bisher keine Seiten. Klicke den Scanbutton :)
+ Flachbett
+ ADF
+ Verfügbare Scanner
+ Scannersuche
+ Einstellungen im eSCLKt-Format kopieren
+ Scaneinstellungen
+ Außerhalb des unterstützten Bereichs
+ Ungültige Nummer
+ Gültig
+ Quelle:
+ Auflösung (DPI):
+ Duplex
+ Standard
+ Breite (in %1$s)
+ Höhe (in %1$s)
+ Maximum
+ Benutzerdefiniert
+ Ein Scanauftrag läuft bereits
+ Fehler beim Abrufen einer Seite: %1$s
+ Kopieren
+ Fehler
+ Ungespeicherte Scans
+ Willst du wirklich die Scanoberfläche schließen? Alle Scans, die du nicht exportiert hast, gehen dabei verloren!
+ Abbrechen
+ Verlassen
+ Appsymbol
+ Debug
+ Informationen zu dieser Einstellung
+ Absturzprotokoll gefunden
+ Ein Absturzprotokoll wurde gefunden. Dies ist der Fehler der zum Absturz führte. Bitte füge ihn deinem Bugreport an:
+ OK, löschen
+ Kamera
+ Temporäre Dateien gefunden
+ Temporäre Dateien (Scans und/oder Exporte) wurden im App-Speicher gefunden. Diese Dateien verbleiben im App-Speicher, wenn die App aus der Appübersicht geschlossen oder von Android während einer Scansitzung beendet wurde. Der Hauptgrund für diese Nachfrage ist, dass eine Bestätigung für das Löschen in diesem Fall nicht möglich war.\n\nWas möchtest du mit den folgenden Scan- und Exportdateien machen?:\n%1$s
+ Löschen
+ Nichts machen
+ Nach rechts drehen
+ Wird gedreht…
+ Auf einen benutzdefinierten Scanner zugreifen
+ Verbinden
+ URL (eSCL-Ressource)
+ Verbinden mit benutzerdefinierten Scanner
+ Ungültige URL
+ Gib bitte eine URL ein
+ Benutzerdefinierter Scanner
+ Als PDF exportieren
+ Als JPEG-Archiv exportieren
+ Keine weiteren Seiten. %1$s
+ Fehler beim Herunterladen des empfangenen Bilds als Datei: %1$s
+ Empfangenes Bild konnte nicht dekodiert werden. %1$s
+ Scanpreset:
+ Scanbereich:
+ Willst du wirklich die derzeitig ausgewählte Seite löschen? Dieser Vorgang ist unwiderruflich.
+ Debugprotokoll aufzeichnen
+ Alle Debuglogs werden zu einer in den Einstellungen exportierbaren Datei geschrieben. Dies kann zum Erstellen von Fehlerberichten sehr nützlich sein.
+ Debugprotokoll löschen
+ Debugprotokoll exportieren
+ Diese Einstellung legt fest wie lange ScanBridge maximal auf eine Antwort vom Scanner wartet bevor es den Scanversuch abbricht.\n\nDiese Einstellung ist für langsame Scanner und große Scanbereiche nützlich.
+ Timeoutlänge für Scannerantwort (in Sekunden)
+ Erweiterte Einstellungen
+
+ Name
+ HP Deskjet
+ Entdeckte Scanner
+ Gespeicherte Scanner
+ Max. Seitenzahl für pro PDF
+ "Da der Arbeitsspeicher von Smartphones begrenzt ist kann es schwierig werden sehr große PDFs zu erzeugen. Um Abstürze zu verhindern, teilt ScanBridge einen PDF-Export auf mehrere PDFs auf, wenn die hier festgelegte Grenze überschritten würde. In diesem Fall werden die PDFs in einem ZIP-Archiv zusammengefasst. Falls gewünscht, können die PDFs dann im Nachhinein zusammengeführt werden. \n\nWarnung: Wenn diese Option zu weit erhöht wird und sehr viele Seiten als PDF exportiert werden sollen, kann es zu Abstürzen kommen."
+ HTTPS-Zertifikatsüberprüfung umgehen
+ Diese Einstellung deaktiviert alle Zertifikatsprüfungen bei HTTPS-Verbindungen. Jedes Zertifikat wird folglich akzeptiert, egal ob es ungültig oder selbstsigniert ist. Diese Einstellung sollte nicht in unsicheren Netwerken verwendet werden, denn durch sie wird ein Man-in-the-Middle-Angriff leicht durchführbar.\n\nIn den meisten Fällen werden Scanner in privaten Netzwerken oder über VPN-Verbindungen verwendet, wo Authentizität in der Regel keine große Rolle spielt. In solchen Umgebungen ist es unwahrscheinlich, dass das Deaktivieren der Zertifikatsprüfung ein echtes Sicherheitsrisiko darstellt. Und falls Vertraulichkeit oder Authentizität doch eine Rolle spielen, hilft selbst HTTPS nur begrenzt – denn eSCL bietet keinerlei Client-Authentifizierung. Jeder mit Netzwerkzugriff auf den Scanner kann potenziell auf gescannte Seiten zugreifen.
+ Speichern der zuletzt verwendeten Scaneinstellungen
+ Wenn diese Option aktiviert ist, werden die zuletzt verwendeten Scaneinstellungen (z.B. Quelle, Duplex usw.) beim Erstellen einer neuen Scansitzung wiederhergestellt.\n\nWenn sie deaktiviert ist, beginnt jede Sitzung mit den standardmäßigen Scaneinstellungen (höchste Auflösung, größtmöglicher Scanbereich).
+ Für ScanBridge spenden
+ Den Quellcode anschauen
+ In Datei speichern
+ Bild wird geladen…
+ Seite zuschneiden
+ Zuschneiden
+ Seite wird zugeschnitten…
+ Zoom-Gesten aktivieren
+ ScanBridge ist abgestürzt :(
+ Die Eigenschaften des Scanners sollten bereits abgerufen sein wurden jedoch nicht gefunden.
+ mm
+ Zoll
+ Es tut uns leid. ScanBridge ist abgestürzt. Bitte melde dieses Problem und füge die folgende Fehlermeldung bei (du kannst sie mit der Schaltfläche unten kopieren).
+ Vorherige Sitzung konnte nicht geladen werden… %1$s
+ Falls du Hilfe brauchst oder Feedback geben willst, kannst du uns gerne über die E-Mail support@fireamp.eu kontaktieren. Wir werden uns sobald wie möglich bei dir melden!
+ Danke für deinen Kauf von ScanBridge!
+ Scan läuft…
+ Gescannte Seiten werden im Hintergrund abgerufen.
+ Farbmodus:
+ Schwarz-Weiß
+ Farbe ({bitdepth} bit)
+ Automatische Erkennung
+ Graustufen ({bitdepth} bit)
+ Speichern
+ Benutzerdefinierten Scanner bearbeiten
+ Willst du den benutzerdefinierten Scanner wirklich löschen? Dieser Vorgang ist unwiderruflich.
+ Benutzerdefinierten Scanner löschen
+
\ No newline at end of file
diff --git a/composeUI/src/commonMain/composeResources/values-it/strings.xml b/composeUI/src/commonMain/composeResources/values-it/strings.xml
new file mode 100644
index 00000000..7ebb1aa4
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/values-it/strings.xml
@@ -0,0 +1,126 @@
+
+ Icona stampante
+ Impostazioni
+ Nessuno scanner disponibile. Sei connesso alla rete locale?
+ Icona di avviso
+ Errore
+ Si è verificato un errore durante il tentativo di recuperare le funzionalità dello scanner:\n\n%1$s
+ OK
+ Tentativo di recupero delle funzionalità dello scanner
+ Scansiona
+ Annullamento in corso…
+ Pagina scansionata
+ L\'attività è stata annullata
+ L\'attività è stata interrotta a causa di un errore
+ L\'attività è stata completata correttamente
+ L\'attività è ancora in sospeso
+ Le pagine sono ancora in elaborazione
+ Impossibile recuperare lo stato dell\'attivitÃ
+ Elimina la pagina corrente
+ Scambia con la pagina precedente
+ Scambia con la pagina successiva
+ Pagina %1$s di %2$s
+ Esporta
+ Recupero della pagina
+ Esportazione in corso…
+ Nessuna pagina è stata ancora scansionata. Fai clic sul pulsante Scansiona :)
+ Piana/Vetro
+ Alimentatore
+ Scanner disponibili
+ Analisi della rete
+ Copia le impostazioni di scansione correnti in formato eSCLKt
+ Impostazioni di scansione
+ Valore fuori dall\'intervallo consentito
+ Numero non valido
+ Valido
+ Sorgente di input:
+ Risoluzione utilizzata (dpi):
+ Duplex
+ Predefinito
+ Larghezza (in %1$s)
+ Altezza (in %1$s)
+ Dimensione massima
+ Personalizzato
+ Attività ancora in esecuzione
+ Errore durante il recupero della pagina: %1$s
+ Copia
+ Errore
+ Scansioni non salvate
+ Vuoi davvero chiudere l\'attività di scansione? Tutte le scansioni non esportate andranno perse!
+ Annulla
+ Esci
+ Icona applicazione
+ Debug
+ Ulteriori informazioni su questa impostazione
+ Registro degli arresti anomali trovato
+ Registro degli arresti anomali individuato. Questa è l\'eccezione che ha causato l\'arresto anomalo. Allega questo messaggio alla tua segnalazione di bug:
+ OK, eliminalo
+ Fotocamera
+ Trovati file temporanei
+ Sono stati trovati file temporanei (scansioni e/o esportazioni) nell\'archivio dell\'app. Si tratta di file che rimangono nell\'archivio dell\'app se l\'app viene chiusa dall\'anteprima o interrotta da Android durante una sessione di scansione. Il motivo principale di questo avviso è che, chiudendo l\'app in questo modo, non era possibile confermare l\'eliminazione.\n\nCosa vuoi fare con i seguenti file di scansione ed esportazione?:\n%1$s
+ Elimina
+ Non fare nulla
+ Ruota a destra
+ Rotazione in corso…
+ Accedi allo scanner personalizzato
+ Connettiti
+ URL (risorsa eSCL)
+ Connettiti a uno scanner personalizzato
+ URL non valido
+ Per favore inserisci un URL
+ Scanner personalizzato
+ Esporta come PDF
+ Esporta come archivio di file JPEG
+ Nessuna pagina aggiuntiva. %1$s
+ Errore durante la copia dell\'immagine ricevuta nel file: %1$s
+ Impossibile decodificare l\'immagine ricevuta. %1$s
+ Modalità di scansione:
+ Area di scansione:
+ Eliminare la pagina corrente? Questa azione non può essere annullata.
+ Log di debug
+ Tutti i dati di log disponibili verranno scritti in un file esportabile. Utile per inviare segnalazioni di bug.
+ Cancella registro di debug
+ Esporta registro di debug
+ Questo timeout indica per quanto tempo ScanBridge aspetta la risposta dello scanner prima di interrompere la scansione.\n\nÈ particolarmente utile quando si usano scanner lenti o si devono scansionare aree molto grandi, che richiedono più tempo per la scansione.
+ Timeout di risposta (in secondi)
+ Impostazioni avanzate
+
+ Nome
+ HP Deskjet
+ Scanner rilevati
+ Scanner salvati
+ Numero massimo di pagine per file PDF
+ Poiché uno smartphone ha una memoria limitata, può diventare difficile gestire PDF di grandi dimensioni. Per evitare arresti anomali, ScanBridge limita il numero massimo di pagine memorizzate in un file PDF quando si esportano pagine scansionate in PDF. Se ci sono troppe pagine, vengono creati più PDF e archiviati in un archivio ZIP. Se necessario, i file PDF possono essere uniti successivamente.\n\nAttenzione: un aumento eccessivo di questo valore può causare arresti anomali durante l\'esportazione di un numero elevato di pagine.
+ Disabilita controlli certificati
+ Questa opzione disabilita la convalida di tutti i certificati HTTPS. Verrà accettato qualsiasi certificato, inclusi quelli autofirmati, scaduti o non validi. Non utilizzare questa opzione su reti pubbliche o non sicure, poiché rende possibili attacchi man-in-the-middle (MitM). Nella maggior parte dei casi, gli scanner vengono utilizzati su reti private o tramite connessioni VPN, dove le criticità sull\'autenticazione sono minime. In tali ambienti, è improbabile che la disabilitazione dei controlli dei certificati rappresenti un rischio reale per la sicurezza. E nel caso in cui la riservatezza o l\'autenticità siano un problema, anche HTTPS non sarà di grande aiuto, poiché eSCL non dispone di autenticazione client. Chiunque abbia accesso alla rete può potenzialmente recuperare le pagine scansionate.
+ Ricorda le impostazioni di scansione
+ Se abilitata, quando si crea una nuova sessione di scansione verranno ripristinate le ultime impostazioni di scansione utilizzate (sorgente di input, duplex, ecc.). Se disabilitata, ogni sessione inizia con le impostazioni di scansione predefinite (massima risoluzione, area di scansione più ampia possibile).
+ Dona a ScanBridge
+ Codice sorgente
+ Annulla
+ Salva in file
+ Caricamento dell’immagine…
+ La sessione precedente non può essere caricata… %1$s
+ Le proprietà dello scanner dovrebbero essere già state recuperate, ma non sono state trovate.
+ mm
+ Ritaglia pagina
+ Ritaglia
+ La pagina viene ritagliata…
+ Attiva i gesti di zoom
+ ScanBridge si è bloccato :(
+ Ci dispiace. ScanBridge si è bloccato. Si prega di segnalare questo problema e di allegare il seguente messaggio di errore (puoi copiarlo con il pulsante in basso).
+ pollice
+ Se hai bisogno di aiuto o vuoi inviarci un feedback, puoi contattarci liberamente all’indirizzo support@fireamp.eu . Ti risponderemo il prima possibile!
+ Grazie per aver acquistato ScanBridge!
+ Scansione in corso…
+ Le pagine scansionate vengono recuperate in background.
+ Modalità colore:
+ Bianco e nero
+ Colore ({bitdepth} bit)
+ Rilevamento automatico
+ Scala di grigi ({bitdepth} bit)
+ Salva
+ Modifica scanner personalizzato
+ Vuoi davvero eliminare lo scanner personalizzato? Questa operazione è irreversibile.
+ Elimina scanner personalizzato
+
\ No newline at end of file
diff --git a/composeUI/src/commonMain/composeResources/values/strings.xml b/composeUI/src/commonMain/composeResources/values/strings.xml
new file mode 100644
index 00000000..1d72524c
--- /dev/null
+++ b/composeUI/src/commonMain/composeResources/values/strings.xml
@@ -0,0 +1,129 @@
+
+ ScanBridge
+ Printer icon
+ Settings
+ No scanners found. Are you connected to a local network?
+ Warning icon
+ Error
+ An error occurred while trying to retrieve scanner capabilities:\n\n%1$s
+ Okay
+ Trying to retrieve ScannerCapabilities
+ Scan
+ Cancel
+ Cancelling…
+ A scanned page
+ The job was canceled
+ The job was aborted because of an error
+ The job was completed successfully
+ The job is still pending
+ The pages are still processed
+ The job state can\'t be retrieved
+ Delete current page
+ Swap with previous
+ Swap with next
+ Page %1$s of %2$s
+ Export
+ Retrieving page
+ Exporting…
+ No pages were scanned yet. Click the scan button :)
+ Platen
+ ADF
+ Available scanners
+ Discovery
+ Copy current scan options in eSCLKt format
+ Scan settings
+ Not in allowed range
+ Not a valid number
+ Valid
+ Input source:
+ Used Resolution (dpi):
+ Duplex
+ Default
+ Width (in %1$s)
+ Height (in %1$s)
+ Maximum size
+ Custom
+ Job still running
+ Error while retrieving page: %1$s
+ Copy
+ Error
+ Scans unsaved
+ Do you really want to close the scanning interface? Any scans that you didn\'t export will be lost!
+ Cancel
+ Leave
+ App icon
+ Debug
+ Info button for the setting
+ Crash log file exists
+ A crash log was found. This is the exception which led to the crash. Please attach this to your bug report:
+ OK, delete it
+ Camera
+ Temporary files found
+ Temporary files (scans and/or exports) were found in the app storage. These are files which remain in the app storage if the app is closed from the overview or killed by Android while being in a scanning session. The main reason for this dialog is that asking for confirmation of deletion was impossible when the app was closed in this way.\n\nWhat do you want to do with the following scan and export files?:\n%1$s
+ Delete
+ Do nothing
+ Rotate right
+ Rotating…
+ Access custom scanner
+ Just connect
+ URL (eSCL resource)
+ Connect to a custom scanner
+ Invalid URL
+ Please enter an URL
+ Custom scanner
+ Export as PDF
+ Export as archive of JPEGs
+ No further pages. %1$s
+ Error while copying received image to file: %1$s
+ Couldn\'t decode received image. %1$s
+ Intent:
+ Scan Region:
+ Do you really want to delete the current page? This action can not be undone.
+ Debug log
+ This will write all available log data to an exportable file. This is useful for submitting bug repots.
+ Clear debug log
+ Export debug log
+ This timeout determines how long ScanBridge waits at maximum for the scanner to respond to requests until it aborts the scan job.\n\nThis is mostly useful for slow scanners and large scan areas which take longer to scan.
+ Response Timeout (in seconds)
+ Advanced Settings
+
+ Name
+ HP Deskjet
+ Discovered scanners
+ Saved scanners
+ Max. pages per PDF file
+ As a smartphone has limited memory, it can becomes difficult to handle large PDFs. To prevent crashes, ScanBridge limits the maximum count pages stored in one PDF file when exporting scanned pages as PDF. If there are too much pages, multiple PDFs are created and stored into a ZIP archive. These PDFs can be merged afterwards, if needed.\n\nWarning: Increasing this too far can lead to crashes while exporting if a large number of pages are to be exported.
+ Disable certificate checks
+ This option disables all HTTPS certificate validation. Any certificate will be accepted, including self-signed, expired, or otherwise invalid ones. Do not use this option on insecure or public networks, as it makes man-in-the-middle (MitM) attacks possible.\n\nIn most cases, scanners are used on private networks or over VPN connections, where concerns about authenticity are minimal. In such environments, disabling certificate checks is unlikely to pose a real security risk. And in case confidentiality or authenticity is a concern, even HTTPS won\'t help much, as eSCL lacks client authentication altogether. Anyone with access to the network can potentially retrieve scanned pages.
+ Remember scan settings
+ When enabled, your last used scan settings (input source, duplex, etc.) will be restored when you create a new scan session.\n\nWhen disabled, each session starts with default scan settings (highest resolution, largest possible scan area).
+ Support development
+ View source code
+ F-Droid
+ Play Store
+ Save to File
+ Loading image…
+ Crop current page
+ Crop
+ Cropping page…
+ Activate zoom gestures
+ ScanBridge crashed :(
+ "We are sorry. ScanBridge has crashed. Please report this issue and attach the following stacktrace (you can copy it with the button at the bottom)"
+ Loading previous session failed… %1$s
+ The scanner capabilities should be retrieved at this point, but they weren\'t.
+ mm
+ Inches
+ Scanned pages are received in the background.
+ If you need help or want to provide feedback, contact us anytime at support@fireamp.eu. We will get back to you as quickly as possible!
+ Thank you for purchasing ScanBridge!
+ Scan job running…
+ Color Mode:
+
+ Color ({bitdepth} bit)
+ Automatic detection
+ Grayscale ({bitdepth} bit)
+ Save
+ Edit custom scanner
+ Do you really want to delete this custom scanner? This action cannot be undone.
+ Delete custom scanner
+
\ No newline at end of file
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Color.kt b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Color.kt
similarity index 90%
rename from app/src/main/java/io/github/chrisimx/scanbridge/theme/Color.kt
rename to composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Color.kt
index 021f8553..6209286f 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Color.kt
+++ b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Color.kt
@@ -19,6 +19,7 @@
package io.github.chrisimx.scanbridge.theme
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
@@ -36,6 +37,6 @@ val Purple = Color(0xFF702CCC)
val gradientBrush = Brush.linearGradient(
colors = listOf(NiceBlue, Purple), // Colors of the gradient
- start = androidx.compose.ui.geometry.Offset.Zero, // Start position
- end = androidx.compose.ui.geometry.Offset.Infinite // End position
+ start = Offset.Zero, // Start position
+ end = Offset.Infinite // End position
)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Theme.kt b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.kt
similarity index 75%
rename from app/src/main/java/io/github/chrisimx/scanbridge/theme/Theme.kt
rename to composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.kt
index bb7cafce..aa346b1e 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/theme/Theme.kt
+++ b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.kt
@@ -19,23 +19,20 @@
package io.github.chrisimx.scanbridge.theme
-import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-private val DarkColorScheme = darkColorScheme(
+val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
-private val LightColorScheme = lightColorScheme(
+val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
@@ -51,6 +48,12 @@ private val LightColorScheme = lightColorScheme(
*/
)
+@Composable
+expect fun platformDarkColorScheme(): ColorScheme?
+
+@Composable
+expect fun platformLightColorScheme(): ColorScheme?
+
@Composable
fun ScanBridgeTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
@@ -59,19 +62,13 @@ fun ScanBridgeTheme(
content: @Composable () -> Unit
) {
val colorScheme = when {
- dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
- val context = LocalContext.current
- if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
- }
-
- darkTheme -> DarkColorScheme
-
- else -> LightColorScheme
+ darkTheme -> platformDarkColorScheme() ?: DarkColorScheme
+ else -> platformLightColorScheme() ?: LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
- typography = Typography,
+ typography = PoppinsTypography(),
content = content
)
}
diff --git a/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Typography.kt b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Typography.kt
new file mode 100644
index 00000000..ecff2280
--- /dev/null
+++ b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/theme/Typography.kt
@@ -0,0 +1,198 @@
+package io.github.chrisimx.scanbridge.theme
+
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Typography
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+import org.jetbrains.compose.resources.Font
+import scanbridge.composeui.generated.resources.*
+import scanbridge.composeui.generated.resources.Res
+
+@Composable
+fun Poppins(): FontFamily = FontFamily(
+ Font(Res.font.poppins_thin, weight = FontWeight.Thin),
+ Font(Res.font.poppins_thinitalic, weight = FontWeight.Thin, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_extralight, weight = FontWeight.ExtraLight),
+ Font(Res.font.poppins_extralightitalic, weight = FontWeight.ExtraLight, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_light, weight = FontWeight.Light),
+ Font(Res.font.poppins_lightitalic, weight = FontWeight.Light, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_regular, weight = FontWeight.Normal),
+ Font(Res.font.poppins_italic, weight = FontWeight.Normal, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_medium, weight = FontWeight.Medium),
+ Font(Res.font.poppins_mediumitalic, weight = FontWeight.Medium, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_semibold, weight = FontWeight.SemiBold),
+ Font(Res.font.poppins_semibolditalic, weight = FontWeight.SemiBold, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_bold, weight = FontWeight.Bold),
+ Font(Res.font.poppins_bolditalic, weight = FontWeight.Bold, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_extrabold, weight = FontWeight.ExtraBold),
+ Font(Res.font.poppins_extrabolditalic, weight = FontWeight.ExtraBold, style = FontStyle.Italic),
+
+ Font(Res.font.poppins_black, weight = FontWeight.Black),
+ Font(Res.font.poppins_blackitalic, weight = FontWeight.Black, style = FontStyle.Italic)
+)
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun PoppinsTypography(): Typography {
+ val poppinsFont = Poppins()
+
+ return Typography(
+ titleSmall = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 20.sp,
+ letterSpacing = 0.sp
+ ),
+ titleMedium = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 18.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.sp
+ ),
+ titleMediumEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ fontSize = 18.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.sp
+ ),
+ titleLarge = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ titleLargeEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ lineHeight = 14.sp,
+ fontSize = 11.sp,
+ letterSpacing = 0.sp
+ ),
+ labelMedium = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ lineHeight = 14.sp,
+ fontSize = 14.sp,
+ letterSpacing = 0.sp
+ ),
+ labelLarge = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ letterSpacing = 0.sp
+ ),
+ bodySmall = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ letterSpacing = 0.sp
+ ),
+ bodySmallEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ letterSpacing = 0.sp
+ ),
+ bodyMedium = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ letterSpacing = 0.sp
+ ),
+ bodyMediumEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ letterSpacing = 0.sp
+ ),
+ bodyLarge = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ ),
+ bodyLargeEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ ),
+ displaySmall = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ letterSpacing = 0.sp
+ ),
+ displayMedium = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ letterSpacing = 0.sp
+ ),
+ displayLarge = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ letterSpacing = 0.sp
+ ),
+ displaySmallEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ letterSpacing = 0.sp
+ ),
+ displayMediumEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ letterSpacing = 0.sp
+ ),
+ displayLargeEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ letterSpacing = 0.sp
+ ),
+ headlineLarge = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 24.sp,
+ lineHeight = 32.sp,
+ letterSpacing = 0.sp
+ ),
+ headlineMedium = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 20.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ headlineMediumEmphasized = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.ExtraBold,
+ fontSize = 20.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ headlineSmall = TextStyle(
+ fontFamily = poppinsFont,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.sp
+ )
+ )
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/LoadingScreen.kt b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/uicomponents/LoadingScreen.kt
similarity index 78%
rename from app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/LoadingScreen.kt
rename to composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/uicomponents/LoadingScreen.kt
index 92d16a13..f0c41190 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/uicomponents/LoadingScreen.kt
+++ b/composeUI/src/commonMain/kotlin/io/github/chrisimx/scanbridge/uicomponents/LoadingScreen.kt
@@ -28,18 +28,24 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import io.github.chrisimx.scanbridge.theme.ScanBridgeTheme
+import org.jetbrains.compose.resources.StringResource
+import org.jetbrains.compose.resources.stringResource
+import scanbridge.composeui.generated.resources.Res
+import scanbridge.composeui.generated.resources.trying_to_retrieve_scannercapabilities
@Composable
-fun LoadingScreen(loadingText: Int) {
+fun LoadingScreen(loadingText: StringResource) {
Column(
modifier = Modifier
.fillMaxSize()
@@ -60,3 +66,13 @@ fun LoadingScreen(loadingText: Int) {
)
}
}
+
+@Composable
+@Preview
+fun LoadingScreenPreview() {
+ ScanBridgeTheme {
+ Scaffold {
+ LoadingScreen(Res.string.trying_to_retrieve_scannercapabilities)
+ }
+ }
+}
diff --git a/composeUI/src/jvmMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.jvm.kt b/composeUI/src/jvmMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.jvm.kt
new file mode 100644
index 00000000..862a0dd4
--- /dev/null
+++ b/composeUI/src/jvmMain/kotlin/io/github/chrisimx/scanbridge/theme/Theme.jvm.kt
@@ -0,0 +1,10 @@
+package io.github.chrisimx.scanbridge.theme
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.runtime.Composable
+
+@Composable
+actual fun platformDarkColorScheme(): ColorScheme? = null
+
+@Composable
+actual fun platformLightColorScheme(): ColorScheme? = null
diff --git a/core/.gitignore b/core/.gitignore
new file mode 100644
index 00000000..92e0b0fb
--- /dev/null
+++ b/core/.gitignore
@@ -0,0 +1,2 @@
+/build
+build
\ No newline at end of file
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
new file mode 100644
index 00000000..329bec11
--- /dev/null
+++ b/core/build.gradle.kts
@@ -0,0 +1,91 @@
+import org.gradle.kotlin.dsl.withType
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
+
+plugins {
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.android.kmp.library)
+ alias(libs.plugins.koin)
+ alias(libs.plugins.kotlin.serialization)
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.room)
+}
+
+kotlin {
+ compilerOptions {
+ optIn.add("kotlin.uuid.ExperimentalUuidApi")
+ freeCompilerArgs.add("-Xnon-local-break-continue")
+ }
+}
+
+kotlin {
+ jvm {
+ compilerOptions { jvmTarget = JvmTarget.JVM_17 }
+ }
+
+ android {
+ namespace = "io.github.chrisimx.scanbridge"
+ compileSdk = 36
+ minSdk = 23
+
+ compilerOptions {
+ jvmTarget.set(
+ org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
+ )
+ }
+ }
+
+ iosArm64()
+ iosSimulatorArm64()
+
+ sourceSets {
+ commonMain.dependencies {
+ api("com.diamondedge:logging:2.1.0")
+ api(libs.koin.core)
+ api(libs.koin.annotations)
+ api(libs.kotlinx.coroutines)
+ api(libs.kotlinx.serialization.json)
+ api(libs.ktor.client.core)
+ api(libs.ktor.logging)
+ api(libs.esclkt)
+
+ // Room deps
+ implementation(libs.androidx.room.runtime)
+ implementation(libs.androidx.sqlite.bundled)
+ }
+
+ commonTest.dependencies {
+ implementation(kotlin("test"))
+ }
+
+ androidMain.dependencies {
+ api(libs.ktor.client.okhttp)
+ }
+
+ jvmMain.dependencies {
+ }
+ }
+
+ targets
+ .withType()
+ .matching { it.konanTarget.family.isAppleFamily }
+ .configureEach {
+ binaries {
+ framework {
+ baseName = "ScanBridgeCore"
+ isStatic = true
+ }
+ }
+ }
+}
+
+room {
+ schemaDirectory("$projectDir/schemas")
+}
+
+dependencies {
+ add("kspAndroid", libs.androidx.room.compiler)
+ add("kspJvm", libs.androidx.room.compiler)
+ add("kspIosSimulatorArm64", libs.androidx.room.compiler)
+ add("kspIosArm64", libs.androidx.room.compiler)
+}
diff --git a/app/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/1.json b/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/1.json
similarity index 100%
rename from app/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/1.json
rename to core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/1.json
diff --git a/app/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/2.json b/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/2.json
similarity index 100%
rename from app/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/2.json
rename to core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/2.json
diff --git a/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/3.json b/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/3.json
new file mode 100644
index 00000000..86a6e511
--- /dev/null
+++ b/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/3.json
@@ -0,0 +1,238 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 3,
+ "identityHash": "07cfa3c6a4549c1d0782b1afbe48d17e",
+ "entities": [
+ {
+ "tableName": "customscanners",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "scannedpages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scanId` TEXT NOT NULL, `ownerSessionId` TEXT NOT NULL, `filePath` TEXT NOT NULL, `originalScanSettings` TEXT NOT NULL, `rotation` TEXT NOT NULL, `orderIndex` INTEGER NOT NULL, PRIMARY KEY(`scanId`), FOREIGN KEY(`ownerSessionId`) REFERENCES `sessions`(`sessionId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "scanId",
+ "columnName": "scanId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ownerSessionId",
+ "columnName": "ownerSessionId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filePath",
+ "columnName": "filePath",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "originalScanSettings",
+ "columnName": "originalScanSettings",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rotation",
+ "columnName": "rotation",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "orderIndex",
+ "columnName": "orderIndex",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "scanId"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_scannedpages_ownerSessionId",
+ "unique": false,
+ "columnNames": [
+ "ownerSessionId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_scannedpages_ownerSessionId` ON `${TABLE_NAME}` (`ownerSessionId`)"
+ },
+ {
+ "name": "index_scannedpages_ownerSessionId_orderIndex",
+ "unique": true,
+ "columnNames": [
+ "ownerSessionId",
+ "orderIndex"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_scannedpages_ownerSessionId_orderIndex` ON `${TABLE_NAME}` (`ownerSessionId`, `orderIndex`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "sessions",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "ownerSessionId"
+ ],
+ "referencedColumns": [
+ "sessionId"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "sessions",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sessionId` TEXT NOT NULL, `currentScanSettings` TEXT, `currentSettingsUIData` TEXT DEFAULT null, `currentPage` INTEGER NOT NULL, PRIMARY KEY(`sessionId`))",
+ "fields": [
+ {
+ "fieldPath": "sessionId",
+ "columnName": "sessionId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "currentScanSettings",
+ "columnName": "currentScanSettings",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "currentSettingsUIData",
+ "columnName": "currentSettingsUIData",
+ "affinity": "TEXT",
+ "defaultValue": "null"
+ },
+ {
+ "fieldPath": "currentPage",
+ "columnName": "currentPage",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "sessionId"
+ ]
+ }
+ },
+ {
+ "tableName": "tempfiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tempFileId` TEXT NOT NULL, `ownerSessionId` TEXT NOT NULL, `path` TEXT NOT NULL, PRIMARY KEY(`tempFileId`), FOREIGN KEY(`ownerSessionId`) REFERENCES `sessions`(`sessionId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "tempFileId",
+ "columnName": "tempFileId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ownerSessionId",
+ "columnName": "ownerSessionId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "tempFileId"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_tempfiles_ownerSessionId",
+ "unique": false,
+ "columnNames": [
+ "ownerSessionId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_tempfiles_ownerSessionId` ON `${TABLE_NAME}` (`ownerSessionId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "sessions",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "ownerSessionId"
+ ],
+ "referencedColumns": [
+ "sessionId"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "lastroute",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`route` TEXT NOT NULL, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "route",
+ "columnName": "route",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '07cfa3c6a4549c1d0782b1afbe48d17e')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/4.json b/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/4.json
new file mode 100644
index 00000000..50c594c1
--- /dev/null
+++ b/core/schemas/io.github.chrisimx.scanbridge.db.ScanBridgeDb/4.json
@@ -0,0 +1,256 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 4,
+ "identityHash": "739608178e7b7634ce893a4594feea9a",
+ "entities": [
+ {
+ "tableName": "customscanners",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`uuid`))",
+ "fields": [
+ {
+ "fieldPath": "uuid",
+ "columnName": "uuid",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "uuid"
+ ]
+ }
+ },
+ {
+ "tableName": "scannedpages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scanId` TEXT NOT NULL, `ownerSessionId` TEXT NOT NULL, `filePath` TEXT NOT NULL, `originalScanSettings` TEXT NOT NULL, `rotation` TEXT NOT NULL, `orderIndex` INTEGER NOT NULL, PRIMARY KEY(`scanId`), FOREIGN KEY(`ownerSessionId`) REFERENCES `sessions`(`sessionId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "scanId",
+ "columnName": "scanId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ownerSessionId",
+ "columnName": "ownerSessionId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filePath",
+ "columnName": "filePath",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "originalScanSettings",
+ "columnName": "originalScanSettings",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rotation",
+ "columnName": "rotation",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "orderIndex",
+ "columnName": "orderIndex",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "scanId"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_scannedpages_ownerSessionId",
+ "unique": false,
+ "columnNames": [
+ "ownerSessionId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_scannedpages_ownerSessionId` ON `${TABLE_NAME}` (`ownerSessionId`)"
+ },
+ {
+ "name": "index_scannedpages_ownerSessionId_orderIndex",
+ "unique": true,
+ "columnNames": [
+ "ownerSessionId",
+ "orderIndex"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_scannedpages_ownerSessionId_orderIndex` ON `${TABLE_NAME}` (`ownerSessionId`, `orderIndex`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "sessions",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "ownerSessionId"
+ ],
+ "referencedColumns": [
+ "sessionId"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "sessions",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sessionId` TEXT NOT NULL, `currentScanSettings` TEXT, `currentSettingsUIData` TEXT DEFAULT null, `currentPage` INTEGER NOT NULL, PRIMARY KEY(`sessionId`))",
+ "fields": [
+ {
+ "fieldPath": "sessionId",
+ "columnName": "sessionId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "currentScanSettings",
+ "columnName": "currentScanSettings",
+ "affinity": "TEXT"
+ },
+ {
+ "fieldPath": "currentSettingsUIData",
+ "columnName": "currentSettingsUIData",
+ "affinity": "TEXT",
+ "defaultValue": "null"
+ },
+ {
+ "fieldPath": "currentPage",
+ "columnName": "currentPage",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "sessionId"
+ ]
+ }
+ },
+ {
+ "tableName": "tempfiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tempFileId` TEXT NOT NULL, `ownerSessionId` TEXT NOT NULL, `path` TEXT NOT NULL, PRIMARY KEY(`tempFileId`), FOREIGN KEY(`ownerSessionId`) REFERENCES `sessions`(`sessionId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "tempFileId",
+ "columnName": "tempFileId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "ownerSessionId",
+ "columnName": "ownerSessionId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "path",
+ "columnName": "path",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "tempFileId"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_tempfiles_ownerSessionId",
+ "unique": false,
+ "columnNames": [
+ "ownerSessionId"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_tempfiles_ownerSessionId` ON `${TABLE_NAME}` (`ownerSessionId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "sessions",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "ownerSessionId"
+ ],
+ "referencedColumns": [
+ "sessionId"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "lastroute",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`route` TEXT NOT NULL, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "route",
+ "columnName": "route",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ }
+ },
+ {
+ "tableName": "executedmigrationtoroom",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`migrationId` TEXT NOT NULL, PRIMARY KEY(`migrationId`))",
+ "fields": [
+ {
+ "fieldPath": "migrationId",
+ "columnName": "migrationId",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "migrationId"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '739608178e7b7634ce893a4594feea9a')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/core/src/androidMain/kotlin/AndroidHttpClientFactory.kt b/core/src/androidMain/kotlin/AndroidHttpClientFactory.kt
new file mode 100644
index 00000000..dca1f40f
--- /dev/null
+++ b/core/src/androidMain/kotlin/AndroidHttpClientFactory.kt
@@ -0,0 +1,38 @@
+import io.github.chrisimx.scanbridge.model.HttpClientConfig
+import io.github.chrisimx.scanbridge.ports.HttpClientFactory
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLoggerFactory
+import io.ktor.client.HttpClient
+import io.ktor.client.engine.okhttp.OkHttp
+import io.ktor.client.plugins.HttpTimeout
+import io.ktor.client.plugins.logging.Logger
+import io.ktor.client.plugins.logging.Logging
+
+class AndroidHttpClientFactory(loggerFactory: ScanBridgeLoggerFactory) : HttpClientFactory {
+ val httpClientLogger = loggerFactory.withTag("AndroidHttpClient")
+
+ override fun create(config: HttpClientConfig): HttpClient = HttpClient(OkHttp) {
+ install(HttpTimeout) {
+ requestTimeoutMillis = config.timeoutInSeconds.toLong() * 1000
+ connectTimeoutMillis = config.timeoutInSeconds.toLong() * 1000
+ socketTimeoutMillis = config.timeoutInSeconds.toLong() * 1000
+ }
+ if (config.debugLogging) {
+ install(Logging) {
+ logger = object : Logger {
+ override fun log(message: String) {
+ httpClientLogger.debug { message }
+ }
+ }
+ }
+ }
+ if (config.disableCertValidation) {
+ engine {
+ config {
+ val (socketFactory, trustManager) = getTrustAllTM()
+ sslSocketFactory(socketFactory, trustManager)
+ hostnameVerifier { _, _ -> true }
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/androidMain/kotlin/AndroidScanBridgeDbBuilderFactory.kt b/core/src/androidMain/kotlin/AndroidScanBridgeDbBuilderFactory.kt
new file mode 100644
index 00000000..8f522fea
--- /dev/null
+++ b/core/src/androidMain/kotlin/AndroidScanBridgeDbBuilderFactory.kt
@@ -0,0 +1,13 @@
+import android.content.Context
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import io.github.chrisimx.scanbridge.db.ScanBridgeDb
+import io.github.chrisimx.scanbridge.db.ScanBridgeDbBuilderFactory
+
+class AndroidScanBridgeDbBuilderFactory(private val context: Context) : ScanBridgeDbBuilderFactory {
+ override fun getBuilder(): RoomDatabase.Builder {
+ val databaseFile = context.getDatabasePath("scanbridge")
+ val builder = Room.databaseBuilder(context, databaseFile.absolutePath)
+ return builder
+ }
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/TrustAllTrustManager.kt b/core/src/androidMain/kotlin/TrustAllTrustManager.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/TrustAllTrustManager.kt
rename to core/src/androidMain/kotlin/TrustAllTrustManager.kt
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/LastRouteRepository.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/LastRouteRepository.kt
new file mode 100644
index 00000000..d7027afc
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/LastRouteRepository.kt
@@ -0,0 +1,6 @@
+package io.github.chrisimx.scanbridge
+
+interface LastRouteRepository {
+ suspend fun getLastRoute(): String?
+ suspend fun setLastRoute(route: String?)
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/PaperFormat.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/PaperFormat.kt
similarity index 97%
rename from app/src/main/java/io/github/chrisimx/scanbridge/data/model/PaperFormat.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/PaperFormat.kt
index b315c118..411cd7d4 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/data/model/PaperFormat.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/PaperFormat.kt
@@ -1,4 +1,4 @@
-package io.github.chrisimx.scanbridge.data.model
+package io.github.chrisimx.scanbridge
import io.github.chrisimx.esclkt.LengthUnit
import io.github.chrisimx.esclkt.Millimeters
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/util/ScanSettingsJson.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ScanSettingsJson.kt
similarity index 95%
rename from app/src/main/java/io/github/chrisimx/scanbridge/util/ScanSettingsJson.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ScanSettingsJson.kt
index 9b1ff20e..7a320eac 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/util/ScanSettingsJson.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ScanSettingsJson.kt
@@ -1,4 +1,4 @@
-package io.github.chrisimx.scanbridge.util
+package io.github.chrisimx.scanbridge
import io.github.chrisimx.esclkt.Inches
import io.github.chrisimx.esclkt.LengthUnit
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ShownMessagesRepository.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ShownMessagesRepository.kt
new file mode 100644
index 00000000..af6b8460
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ShownMessagesRepository.kt
@@ -0,0 +1,12 @@
+package io.github.chrisimx.scanbridge
+
+import kotlinx.coroutines.flow.Flow
+
+interface ShownMessagesRepository {
+ fun getWasShownFlow(message: UserInformationMessage): Flow
+ suspend fun setShown(message: UserInformationMessage, shown: Boolean)
+}
+
+enum class UserInformationMessage(val playOnly: Boolean) {
+ THANKS_FOR_PURCHASE(true)
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/ScanBridgeDb.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDb.kt
similarity index 68%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/ScanBridgeDb.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDb.kt
index 04734596..376e8f42 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/ScanBridgeDb.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDb.kt
@@ -5,10 +5,14 @@ import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import io.github.chrisimx.scanbridge.db.daos.CustomScannerDao
+import io.github.chrisimx.scanbridge.db.daos.ExecutedMigrationsDao
+import io.github.chrisimx.scanbridge.db.daos.LastRouteDao
import io.github.chrisimx.scanbridge.db.daos.ScannedPageDao
import io.github.chrisimx.scanbridge.db.daos.SessionDao
import io.github.chrisimx.scanbridge.db.daos.TempFileDao
import io.github.chrisimx.scanbridge.db.entities.CustomScanner
+import io.github.chrisimx.scanbridge.db.entities.ExecutedMigrationToRoom
+import io.github.chrisimx.scanbridge.db.entities.LastRoute
import io.github.chrisimx.scanbridge.db.entities.ScannedPage
import io.github.chrisimx.scanbridge.db.entities.Session
import io.github.chrisimx.scanbridge.db.entities.TempFile
@@ -18,12 +22,22 @@ import io.github.chrisimx.scanbridge.db.typeconverters.UrlTypeConverter
import io.github.chrisimx.scanbridge.db.typeconverters.UuidTypeConverter
@Database(
- entities = [CustomScanner::class, ScannedPage::class, Session::class, TempFile::class],
- version = 2,
+ entities = [
+ CustomScanner::class, ScannedPage::class, Session::class, TempFile::class, LastRoute::class, ExecutedMigrationToRoom::class
+ ],
+ version = 4,
autoMigrations = [
AutoMigration(
from = 1,
to = 2
+ ),
+ AutoMigration(
+ from = 2,
+ to = 3
+ ),
+ AutoMigration(
+ from = 3,
+ to = 4
)
]
)
@@ -38,4 +52,6 @@ abstract class ScanBridgeDb : RoomDatabase() {
abstract fun scannedPageDao(): ScannedPageDao
abstract fun sessionDao(): SessionDao
abstract fun tmpFileDao(): TempFileDao
+ abstract fun lastRouteDao(): LastRouteDao
+ abstract fun executedMigrationsDao(): ExecutedMigrationsDao
}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDbBuilderFactory.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDbBuilderFactory.kt
new file mode 100644
index 00000000..8f8866fb
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDbBuilderFactory.kt
@@ -0,0 +1,7 @@
+package io.github.chrisimx.scanbridge.db
+
+import androidx.room.RoomDatabase
+
+interface ScanBridgeDbBuilderFactory {
+ fun getBuilder(): RoomDatabase.Builder
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDbFactory.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDbFactory.kt
new file mode 100644
index 00000000..5980bea5
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/ScanBridgeDbFactory.kt
@@ -0,0 +1,19 @@
+package io.github.chrisimx.scanbridge.db
+
+import androidx.sqlite.driver.bundled.BundledSQLiteDriver
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.IO
+
+interface ScanBridgeDbFactory {
+ fun createInstance(): ScanBridgeDb
+}
+
+class DefaultScanBridgeDbFactory(val builderFactory: ScanBridgeDbBuilderFactory) : ScanBridgeDbFactory {
+ override fun createInstance(): ScanBridgeDb {
+ val dbBuilder = builderFactory.getBuilder()
+ return dbBuilder
+ .setDriver(BundledSQLiteDriver())
+ .setQueryCoroutineContext(Dispatchers.IO)
+ .build()
+ }
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/daos/CustomScannerDao.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/CustomScannerDao.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/daos/CustomScannerDao.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/CustomScannerDao.kt
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/ExecutedMigrationsDao.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/ExecutedMigrationsDao.kt
new file mode 100644
index 00000000..778ba47b
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/ExecutedMigrationsDao.kt
@@ -0,0 +1,18 @@
+package io.github.chrisimx.scanbridge.db.daos
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import io.github.chrisimx.scanbridge.db.entities.ExecutedMigrationToRoom
+
+@Dao
+interface ExecutedMigrationsDao {
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ fun markAsExecuted(migration: ExecutedMigrationToRoom)
+
+ @Query("SELECT * FROM executedmigrationtoroom WHERE migrationId = :id")
+ fun getExecutedMigration(id: String): ExecutedMigrationToRoom?
+
+ fun isAlreadyDone(id: String): Boolean = getExecutedMigration(id) != null
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/LastRouteDao.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/LastRouteDao.kt
new file mode 100644
index 00000000..807a0c24
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/LastRouteDao.kt
@@ -0,0 +1,19 @@
+package io.github.chrisimx.scanbridge.db.daos
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import io.github.chrisimx.scanbridge.db.entities.LastRoute
+
+@Dao
+interface LastRouteDao {
+ @Query("SELECT * FROM lastroute WHERE id = 1")
+ suspend fun getLastRoute(): LastRoute?
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun setLastRoute(lastRoute: LastRoute)
+
+ @Query("DELETE FROM lastroute WHERE id = 1")
+ suspend fun clearLastRoute()
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/daos/ScannedPageDao.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/ScannedPageDao.kt
similarity index 96%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/daos/ScannedPageDao.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/ScannedPageDao.kt
index d4ea1a32..c9786d7c 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/daos/ScannedPageDao.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/ScannedPageDao.kt
@@ -9,7 +9,6 @@ import androidx.room.Update
import io.github.chrisimx.scanbridge.db.entities.ScannedPage
import kotlin.uuid.Uuid
import kotlinx.coroutines.flow.Flow
-import timber.log.Timber
@Dao
interface ScannedPageDao {
@@ -40,7 +39,6 @@ interface ScannedPageDao {
val page2 = getByScanId(page2.scanId)
if (page1 == null || page2 == null) {
- Timber.e("Could not swap pages: $page1, $page2")
return
}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/daos/SessionDao.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/SessionDao.kt
similarity index 93%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/daos/SessionDao.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/SessionDao.kt
index 590e0361..9b642770 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/daos/SessionDao.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/SessionDao.kt
@@ -5,8 +5,8 @@ import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
-import io.github.chrisimx.scanbridge.data.ui.ScanSettingsStateData
import io.github.chrisimx.scanbridge.db.entities.Session
+import io.github.chrisimx.scanbridge.model.ScanSettingsEnterableData
import kotlin.uuid.Uuid
import kotlinx.coroutines.flow.Flow
@@ -37,7 +37,7 @@ interface SessionDao {
suspend fun updateCurrentPage(sessionId: Uuid, pageIdx: Int)
@Query("UPDATE sessions SET currentSettingsUIData = :uiData WHERE sessionId = :sessionId")
- suspend fun updateScanSettingsUiData(sessionId: Uuid, uiData: ScanSettingsStateData?)
+ suspend fun updateScanSettingsUiData(sessionId: Uuid, uiData: ScanSettingsEnterableData?)
@Delete
suspend fun delete(session: Session)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/daos/TempFileDao.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/TempFileDao.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/daos/TempFileDao.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/daos/TempFileDao.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/entities/CustomScanner.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/CustomScanner.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/entities/CustomScanner.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/CustomScanner.kt
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/ExecutedMigrationToRoom.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/ExecutedMigrationToRoom.kt
new file mode 100644
index 00000000..88d21686
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/ExecutedMigrationToRoom.kt
@@ -0,0 +1,10 @@
+package io.github.chrisimx.scanbridge.db.entities
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "executedmigrationtoroom")
+data class ExecutedMigrationToRoom(
+ @PrimaryKey
+ val migrationId: String
+)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/LastRoute.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/LastRoute.kt
new file mode 100644
index 00000000..7a233d28
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/LastRoute.kt
@@ -0,0 +1,7 @@
+package io.github.chrisimx.scanbridge.db.entities
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "lastroute")
+data class LastRoute(val route: String, @PrimaryKey val id: Int = 1)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/entities/ScannedPage.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/ScannedPage.kt
similarity index 93%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/entities/ScannedPage.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/ScannedPage.kt
index 2c1da1c8..77601bb3 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/entities/ScannedPage.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/ScannedPage.kt
@@ -5,7 +5,7 @@ import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import io.github.chrisimx.esclkt.ScanSettings
-import io.github.chrisimx.scanbridge.data.ui.ScanRelativeRotation
+import io.github.chrisimx.scanbridge.model.ScanRelativeRotation
import kotlin.uuid.Uuid
@Entity(
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/entities/Session.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/Session.kt
similarity index 76%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/entities/Session.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/Session.kt
index d2161d54..76d4024c 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/entities/Session.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/Session.kt
@@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.github.chrisimx.esclkt.ScanSettings
-import io.github.chrisimx.scanbridge.data.ui.ScanSettingsStateData
+import io.github.chrisimx.scanbridge.model.ScanSettingsEnterableData
import kotlin.uuid.Uuid
@Entity(tableName = "sessions")
@@ -13,6 +13,6 @@ data class Session(
val sessionId: Uuid,
val currentScanSettings: ScanSettings?,
@ColumnInfo(defaultValue = "null")
- val currentSettingsUIData: ScanSettingsStateData?,
+ val currentSettingsUIData: ScanSettingsEnterableData?,
val currentPage: Int = 0
)
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/entities/TempFile.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/TempFile.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/entities/TempFile.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/entities/TempFile.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsTypeConverter.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsTypeConverter.kt
similarity index 91%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsTypeConverter.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsTypeConverter.kt
index bb0dcc94..761d2840 100644
--- a/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsTypeConverter.kt
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsTypeConverter.kt
@@ -2,7 +2,7 @@ package io.github.chrisimx.scanbridge.db.typeconverters
import androidx.room.TypeConverter
import io.github.chrisimx.esclkt.ScanSettings
-import io.github.chrisimx.scanbridge.util.ScanSettingsJson
+import io.github.chrisimx.scanbridge.ScanSettingsJson
class ScanSettingsTypeConverter {
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsUiDataTypeConverter.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsUiDataTypeConverter.kt
new file mode 100644
index 00000000..0046b2fc
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/ScanSettingsUiDataTypeConverter.kt
@@ -0,0 +1,22 @@
+package io.github.chrisimx.scanbridge.db.typeconverters
+
+import androidx.room.TypeConverter
+import io.github.chrisimx.scanbridge.ScanSettingsJson
+import io.github.chrisimx.scanbridge.model.ScanSettingsEnterableData
+
+class ScanSettingsUiDataTypeConverter {
+
+ @TypeConverter
+ fun fromScanSettingsString(scanSettings: String): ScanSettingsEnterableData? = if (scanSettings == "null") {
+ null
+ } else {
+ ScanSettingsJson.json.decodeFromString(scanSettings)
+ }
+
+ @TypeConverter
+ fun toScanSettingsString(scanSettings: ScanSettingsEnterableData?): String = if (scanSettings == null) {
+ "null"
+ } else {
+ ScanSettingsJson.json.encodeToString(scanSettings)
+ }
+}
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/UrlTypeConverter.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/UrlTypeConverter.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/UrlTypeConverter.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/UrlTypeConverter.kt
diff --git a/app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/UuidTypeConverter.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/UuidTypeConverter.kt
similarity index 100%
rename from app/src/main/java/io/github/chrisimx/scanbridge/db/typeconverters/UuidTypeConverter.kt
rename to core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/db/typeconverters/UuidTypeConverter.kt
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/infrastructure/KmLogScanBridgeLogger.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/infrastructure/KmLogScanBridgeLogger.kt
new file mode 100644
index 00000000..5a4d04b2
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/infrastructure/KmLogScanBridgeLogger.kt
@@ -0,0 +1,33 @@
+package io.github.chrisimx.scanbridge.infrastructure
+
+import com.diamondedge.logging.KmLog
+import com.diamondedge.logging.logging
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLogger
+
+class KmLogScanBridgeLogger(tag: String) : ScanBridgeLogger {
+ private val logger: KmLog = logging(tag)
+
+ override fun verbose(tag: String?, msg: () -> String) {
+ logger.v(tag, msg)
+ }
+
+ override fun debug(tag: String?, msg: () -> String) {
+ logger.d(tag, msg)
+ }
+
+ override fun info(tag: String?, msg: () -> String) {
+ logger.i(tag, msg)
+ }
+
+ override fun warn(t: Throwable?, tag: String?, msg: () -> String) {
+ logger.w(t, tag, msg)
+ }
+
+ override fun error(t: Throwable?, tag: String?, msg: () -> String) {
+ logger.e(t, tag, msg)
+ }
+
+ override fun wtf(t: Throwable?, tag: String?, msg: () -> String) {
+ logger.e(t, tag, msg)
+ }
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/infrastructure/KmLogScanBridgeLoggerFactory.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/infrastructure/KmLogScanBridgeLoggerFactory.kt
new file mode 100644
index 00000000..0e562d33
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/infrastructure/KmLogScanBridgeLoggerFactory.kt
@@ -0,0 +1,17 @@
+package io.github.chrisimx.scanbridge.infrastructure
+
+import io.github.chrisimx.scanbridge.infrastructure.KmLogScanBridgeLogger
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLogger
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLoggerFactory
+import kotlin.reflect.KClass
+
+class KmLogScanBridgeLoggerFactory : ScanBridgeLoggerFactory {
+ override fun withClass(clazz: KClass): ScanBridgeLogger {
+ val tag = clazz.simpleName ?: "Unknown"
+ return KmLogScanBridgeLogger(
+ tag
+ )
+ }
+
+ override fun withTag(tag: String): ScanBridgeLogger = KmLogScanBridgeLogger(tag)
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/DiscoveredScanner.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/DiscoveredScanner.kt
new file mode 100644
index 00000000..4bdbfe85
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/DiscoveredScanner.kt
@@ -0,0 +1,3 @@
+package io.github.chrisimx.scanbridge.model
+
+data class DiscoveredScanner(val name: String, val addresses: List)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/HttpClientConfig.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/HttpClientConfig.kt
new file mode 100644
index 00000000..16484b2e
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/HttpClientConfig.kt
@@ -0,0 +1,3 @@
+package io.github.chrisimx.scanbridge.model
+
+data class HttpClientConfig(val disableCertValidation: Boolean, val debugLogging: Boolean, val timeoutInSeconds: ULong)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/Locale.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/Locale.kt
new file mode 100644
index 00000000..1e61000f
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/Locale.kt
@@ -0,0 +1,8 @@
+package io.github.chrisimx.scanbridge.model
+
+data class Locale(
+ /**
+ * The two character country code in uppercase
+ */
+ val country: String
+)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/NumberValidationResult.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/NumberValidationResult.kt
new file mode 100644
index 00000000..d8440620
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/NumberValidationResult.kt
@@ -0,0 +1,10 @@
+package io.github.chrisimx.scanbridge.model
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+sealed class NumberValidationResult {
+ data class Success(val value: Double) : NumberValidationResult()
+ data class OutOfRange(val min: Double, val max: Double) : NumberValidationResult()
+ data object NotANumber : NumberValidationResult()
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanBridgeFile.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanBridgeFile.kt
new file mode 100644
index 00000000..4fe327c1
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanBridgeFile.kt
@@ -0,0 +1,5 @@
+package io.github.chrisimx.scanbridge.model
+
+import kotlinx.io.files.Path
+
+data class ScanBridgeFile(val path: Path)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanJob.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanJob.kt
new file mode 100644
index 00000000..b4aa2563
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanJob.kt
@@ -0,0 +1,13 @@
+package io.github.chrisimx.scanbridge.model
+
+import io.github.chrisimx.esclkt.ScanSettings
+import io.ktor.http.Url
+import kotlin.uuid.Uuid
+
+data class ScanJob(
+ val jobID: Uuid,
+ val ownerSessionId: Uuid,
+ val scanSettings: ScanSettings,
+ val scannerBaseUrl: Url,
+ val httpClientConfig: HttpClientConfig
+)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanRelativeRotation.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanRelativeRotation.kt
new file mode 100644
index 00000000..559218e5
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanRelativeRotation.kt
@@ -0,0 +1,11 @@
+package io.github.chrisimx.scanbridge.model
+
+enum class ScanRelativeRotation {
+ Rotated,
+ Original
+}
+
+fun ScanRelativeRotation.toggleRotation() = when (this) {
+ ScanRelativeRotation.Rotated -> ScanRelativeRotation.Original
+ ScanRelativeRotation.Original -> ScanRelativeRotation.Rotated
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanSettingsEnterableData.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanSettingsEnterableData.kt
new file mode 100644
index 00000000..71806903
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/model/ScanSettingsEnterableData.kt
@@ -0,0 +1,16 @@
+package io.github.chrisimx.scanbridge.model
+
+import io.github.chrisimx.esclkt.ScannerCapabilities
+import io.github.chrisimx.scanbridge.PaperFormat
+import io.github.chrisimx.scanbridge.loadDefaultFormats
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ScanSettingsEnterableData(
+ val capabilities: ScannerCapabilities,
+ val paperFormats: List = loadDefaultFormats(),
+ val customMenuEnabled: Boolean = false,
+ val widthString: String = "",
+ val heightString: String = "",
+ val maximumSize: Boolean = true
+)
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/HttpClientFactory.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/HttpClientFactory.kt
new file mode 100644
index 00000000..6b74a1b4
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/HttpClientFactory.kt
@@ -0,0 +1,8 @@
+package io.github.chrisimx.scanbridge.ports
+
+import io.github.chrisimx.scanbridge.model.HttpClientConfig
+import io.ktor.client.HttpClient
+
+interface HttpClientFactory {
+ fun create(config: HttpClientConfig): HttpClient
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/LocaleProvider.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/LocaleProvider.kt
new file mode 100644
index 00000000..903c7a32
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/LocaleProvider.kt
@@ -0,0 +1,11 @@
+package io.github.chrisimx.scanbridge.ports
+
+import io.github.chrisimx.scanbridge.model.Locale
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Provides the currently selected [io.github.chrisimx.scanbridge.model.Locale] as a reactive stream.
+ */
+interface LocaleProvider {
+ val locale: StateFlow
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ScanBridgeLogger.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ScanBridgeLogger.kt
new file mode 100644
index 00000000..7c47afb8
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ScanBridgeLogger.kt
@@ -0,0 +1,21 @@
+package io.github.chrisimx.scanbridge.ports
+
+interface ScanBridgeLogger {
+ /** Log a verbose message */
+ fun verbose(tag: String? = null, msg: () -> String)
+
+ /** Log a debug message */
+ fun debug(tag: String? = null, msg: () -> String)
+
+ /** Log an info message */
+ fun info(tag: String? = null, msg: () -> String)
+
+ /** Log a warning message with optional throwable */
+ fun warn(t: Throwable? = null, tag: String? = null, msg: () -> String)
+
+ /** Log an error message with optional throwable */
+ fun error(t: Throwable? = null, tag: String? = null, msg: () -> String)
+
+ /** Log a critical/fatal message with optional throwable */
+ fun wtf(t: Throwable? = null, tag: String? = null, msg: () -> String)
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ScanBridgeLoggerFactory.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ScanBridgeLoggerFactory.kt
new file mode 100644
index 00000000..05f88004
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ScanBridgeLoggerFactory.kt
@@ -0,0 +1,9 @@
+package io.github.chrisimx.scanbridge.ports
+
+import io.github.chrisimx.scanbridge.ports.ScanBridgeLogger
+import kotlin.reflect.KClass
+
+interface ScanBridgeLoggerFactory {
+ fun withClass(clazz: KClass): ScanBridgeLogger
+ fun withTag(tag: String): ScanBridgeLogger
+}
diff --git a/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ZipService.kt b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ZipService.kt
new file mode 100644
index 00000000..e58c298e
--- /dev/null
+++ b/core/src/commonMain/kotlin/io/github/chrisimx/scanbridge/ports/ZipService.kt
@@ -0,0 +1,7 @@
+package io.github.chrisimx.scanbridge.ports
+
+import io.github.chrisimx.scanbridge.model.ScanBridgeFile
+
+interface ZipService {
+ fun zip(files: List, output: ScanBridgeFile, mapFileName: (ScanBridgeFile) -> String = { it.path.name })
+}
diff --git a/app/src/test/java/org/github/chrisimx/scanbridge/PaperFormatTest.kt b/core/src/commonTest/kotlin/PaperFormatTest.kt
similarity index 90%
rename from app/src/test/java/org/github/chrisimx/scanbridge/PaperFormatTest.kt
rename to core/src/commonTest/kotlin/PaperFormatTest.kt
index ac6ecd0e..24d98aeb 100644
--- a/app/src/test/java/org/github/chrisimx/scanbridge/PaperFormatTest.kt
+++ b/core/src/commonTest/kotlin/PaperFormatTest.kt
@@ -17,11 +17,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-package org.github.chrisimx.scanbridge
-
-import io.github.chrisimx.scanbridge.data.model.loadDefaultFormats
-import junit.framework.TestCase.assertEquals
-import org.junit.Test
+import io.github.chrisimx.scanbridge.loadDefaultFormats
+import kotlin.test.Test
+import kotlin.test.assertEquals
class PaperFormatTest {
diff --git a/core/src/iosMain/kotlin/IosScanBridgeDbBuilderFactory.kt b/core/src/iosMain/kotlin/IosScanBridgeDbBuilderFactory.kt
new file mode 100644
index 00000000..561cafe2
--- /dev/null
+++ b/core/src/iosMain/kotlin/IosScanBridgeDbBuilderFactory.kt
@@ -0,0 +1,29 @@
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import io.github.chrisimx.scanbridge.db.ScanBridgeDb
+import io.github.chrisimx.scanbridge.db.ScanBridgeDbBuilderFactory
+import kotlinx.cinterop.ExperimentalForeignApi
+import platform.Foundation.NSDocumentDirectory
+import platform.Foundation.NSFileManager
+import platform.Foundation.NSUserDomainMask
+
+class IosScanBridgeDbBuilderFactory : ScanBridgeDbBuilderFactory {
+ override fun getBuilder(): RoomDatabase.Builder {
+ val dbFilePath = documentDirectory() + "/scanbridge.db"
+ return Room.databaseBuilder(
+ name = dbFilePath
+ )
+ }
+
+ @OptIn(ExperimentalForeignApi::class)
+ private fun documentDirectory(): String {
+ val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
+ directory = NSDocumentDirectory,
+ inDomain = NSUserDomainMask,
+ appropriateForURL = null,
+ create = false,
+ error = null
+ )
+ return requireNotNull(documentDirectory?.path)
+ }
+}
diff --git a/desktopApp/.gitignore b/desktopApp/.gitignore
new file mode 100644
index 00000000..c795b054
--- /dev/null
+++ b/desktopApp/.gitignore
@@ -0,0 +1 @@
+build
\ No newline at end of file
diff --git a/desktopApp/appIcons/LinuxIcon.png b/desktopApp/appIcons/LinuxIcon.png
new file mode 100644
index 00000000..a2a45177
Binary files /dev/null and b/desktopApp/appIcons/LinuxIcon.png differ
diff --git a/desktopApp/appIcons/MacosIcon.icns b/desktopApp/appIcons/MacosIcon.icns
new file mode 100644
index 00000000..083def59
Binary files /dev/null and b/desktopApp/appIcons/MacosIcon.icns differ
diff --git a/desktopApp/appIcons/WindowsIcon.ico b/desktopApp/appIcons/WindowsIcon.ico
new file mode 100644
index 00000000..7e9ab754
Binary files /dev/null and b/desktopApp/appIcons/WindowsIcon.ico differ
diff --git a/desktopApp/build.gradle.kts b/desktopApp/build.gradle.kts
new file mode 100644
index 00000000..52c51045
--- /dev/null
+++ b/desktopApp/build.gradle.kts
@@ -0,0 +1,33 @@
+import org.jetbrains.compose.desktop.application.dsl.TargetFormat
+
+plugins {
+ alias(libs.plugins.compose.compiler)
+ alias(libs.plugins.compose.multiplatform)
+ alias(libs.plugins.kotlin.jvm)
+}
+dependencies {
+ implementation(project(":composeUI"))
+}
+
+compose.desktop {
+ application {
+ mainClass = "MainKt"
+
+ nativeDistributions {
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
+ packageName = "ScanBridge"
+ packageVersion = "1.0.0"
+
+ linux {
+ iconFile.set(project.file("appIcons/LinuxIcon.png"))
+ }
+ windows {
+ iconFile.set(project.file("appIcons/WindowsIcon.ico"))
+ }
+ macOS {
+ iconFile.set(project.file("appIcons/MacosIcon.icns"))
+ bundleID = "io.github.chrisimx.scanbridge.desktopApp"
+ }
+ }
+ }
+}
diff --git a/desktopApp/src/main/kotlin/Main.kt b/desktopApp/src/main/kotlin/Main.kt
new file mode 100644
index 00000000..be251960
--- /dev/null
+++ b/desktopApp/src/main/kotlin/Main.kt
@@ -0,0 +1,17 @@
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.application
+import androidx.compose.ui.window.rememberWindowState
+import io.github.chrisimx.scanbridge.App
+import java.awt.Dimension
+
+fun main() = application {
+ Window(
+ title = "ScanBridge",
+ state = rememberWindowState(width = 800.dp, height = 600.dp),
+ onCloseRequest = ::exitApplication
+ ) {
+ window.minimumSize = Dimension(350, 600)
+ App()
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 3ce09d6d..fa120c01 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,12 +1,28 @@
+#Gradle
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx4G -Dfile.encoding=UTF-8
+org.gradle.caching=true
+org.gradle.configuration-cache=true
+org.gradle.daemon=true
+org.gradle.parallel=true
+
+#Kotlin
+kotlin.daemon.jvmargs=-Xmx4G
+kotlin.native.binary.gc=cms
+kotlin.incremental.wasm=true
+
+#https://youtrack.jetbrains.com/issue/KT-82395
+kotlin.incremental.js=false
+kotlin.incremental.js.klib=false
+
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties
index c4e7acbd..5c34300f 100644
--- a/gradle/gradle-daemon-jvm.properties
+++ b/gradle/gradle-daemon-jvm.properties
@@ -1,13 +1,13 @@
#This file is generated by updateDaemonJvm
-toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
-toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
-toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect
-toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
-toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/0b98aec810298c2c1d7fdac5dac37910/redirect
-toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/658299a896470fbb3103ba3a430ee227/redirect
-toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
-toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
-toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/248ffb1098f61659502d0c09aa348294/redirect
-toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/ac151d55def6b6a9a159dc4cb4642851/redirect
+toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
+toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
+toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
+toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
+toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect
+toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/04e088f8677de3b384108493cc9481d0/redirect
+toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
+toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
+toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect
+toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect
toolchainVendor=JETBRAINS
toolchainVersion=21
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e546184f..5a5a28f9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,7 +1,13 @@
[versions]
+
+compose-multiplatform = "1.10.3"
+androidx-activityCompose = "1.12.0"
+
agp = "9.1.0"
coilCompose = "3.3.0"
+concurrentFutures = "1.2.0"
constraintlayoutCompose = "1.1.1"
+converterGson = "2.9.0"
datastore = "1.2.0"
esclkt = "2.0.6"
escl-mock-server = "1.0.1"
@@ -12,33 +18,54 @@ junit = "4.13.2"
junitVersion = "1.3.0"
espressoCore = "3.7.0"
kotlinReflect = "2.1.20"
+kotlinxCoroutinesAndroid = "1.7.3"
kotlinxSerializationJson = "1.9.0"
+kotlinxCoroutines = "1.10.2"
lifecycleRuntimeKtx = "2.9.4"
activityCompose = "1.11.0"
-composeBom = "2025.10.00"
+composeBom = "2026.03.01"
+retrofit = "3.0.0"
room = "2.8.4"
timber = "5.0.1"
zoomable = "0.18.0"
-material3 = "1.5.0-alpha06"
+material3 = "1.11.0-alpha05"
+androidx-material3 = "1.5.0-alpha16"
materialIcons = "1.7.8"
navigationCompose = "2.9.5"
versionsPlugin = "0.53.0"
screengrab = "2.1.1"
ktor = "3.4.0"
-koin = "4.2.0-RC1"
-koin-plugin = "0.3.0"
+koin = "4.2.0"
+koin-plugin = "0.6.2"
protobuf-plugin = "0.9.6"
protobuf-kotlin-lite = "4.33.5"
rules = "1.7.0"
+paraphrase-plugin = "0.4.1"
+ksp = "2.3.6"
+protoc = "4.33.5"
+androidx-sqlite = "2.6.2"
[libraries]
+androidx-concurrent-futures = { module = "androidx.concurrent:concurrent-futures", version.ref = "concurrentFutures" }
+compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "compose-multiplatform" }
+compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "compose-multiplatform" }
+compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "compose-multiplatform" }
+compose-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "compose-multiplatform" }
+compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "compose-multiplatform" }
+compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "compose-multiplatform" }
+compose-ui-test = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "compose-multiplatform" }
+compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
+androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidx-sqlite" }
+retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin" }
koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" }
@@ -57,7 +84,10 @@ koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", ver
koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation", version.ref = "koin" }
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
-ktor-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor"}
+kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
+ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor"}
+ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
+ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor"}
androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayoutCompose" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -71,7 +101,6 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
-androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
@@ -79,22 +108,35 @@ androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
-androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
+androidx-material3 = { module = "androidx.compose.material3:material3", version.ref= "androidx-material3" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinReflect" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+kotlinx-coroutines = { module = " org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines"}
+retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
screengrab = { module = "tools.fastlane:screengrab", version.ref = "screengrab" }
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
zoomable = { module = "me.saket.telephoto:zoomable", version.ref = "zoomable" }
protobuf-kotlin-lite = { module = "com.google.protobuf:protobuf-kotlin-lite", version.ref = "protobuf-kotlin-lite"}
androidx-rules = { group = "androidx.test", name = "rules", version.ref = "rules" }
+protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" }
escl-mock-server = { module = "io.github.chrisimx:escl-mock-server", version.ref = "escl-mock-server"}
[plugins]
+
+kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
+compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
android-application = { id = "com.android.application", version.ref = "agp" }
+android-kmp-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" }
koin = { id = "io.insert-koin.compiler.plugin", version.ref = "koin-plugin" }
protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
-room = { id = "androidx.room", version.ref = "room"}
\ No newline at end of file
+room = { id = "androidx.room", version.ref = "room"}
+paraphrase = { id = "app.cash.paraphrase", version.ref = "paraphrase-plugin"}
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
\ No newline at end of file
diff --git a/http-requests/eSCL.http b/http-requests/eSCL.http
new file mode 100644
index 00000000..88181234
--- /dev/null
+++ b/http-requests/eSCL.http
@@ -0,0 +1,4 @@
+### Retrieve the eSCL scanner capabilities
+GET http://{{server}}/eSCL/ScannerCapabilities
+
+###
\ No newline at end of file
diff --git a/http-requests/wsd.http b/http-requests/wsd.http
new file mode 100644
index 00000000..6ad54eb3
--- /dev/null
+++ b/http-requests/wsd.http
@@ -0,0 +1,164 @@
+### Retrieve scanner caps
+POST http://{{server}}/WebServices/ScannerService
+Content-Type: application/soap+xml
+User-Agent: WSDAPI
+
+
+
+
+ urn:uuid:9c8c2edb-3ffc-b823-28e8-b4339548f743
+ http://192.168.178.122:80/WebServices/ScannerService
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+
+ http://schemas.microsoft.com/windows/2006/08/wdp/scan/GetScannerElements
+
+
+
+
+ sca:ScannerConfiguration
+ sca:ScannerDescription
+ sca:DefaultScanTicket
+ sca:ScannerStatus
+
+
+
+
+
+
+### Get scanner status
+POST http://{{server}}/WebServices/ScannerService
+Content-Type: application/soap+xml
+User-Agent: WSDAPI
+
+
+
+
+ urn:uuid:8af74e6f-1f8b-4d40-c36f-f10eee60221e
+ http://192.168.178.122:80/WebServices/ScannerService
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+
+ http://schemas.microsoft.com/windows/2006/08/wdp/scan/GetScannerElements
+
+
+
+
+ sca:ScannerStatus
+
+
+
+
+
+
+### Create scanjob
+POST http://{{server}}/WebServices/ScannerService
+Content-Type: application/soap+xml
+User-Agent: WSDAPI
+
+
+
+
+ urn:uuid:e6d472bf-e3bd-a7a4-6066-cdea1f91e5fa
+ http://192.168.178.122:80/WebServices/ScannerService
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+
+ http://schemas.microsoft.com/windows/2006/08/wdp/scan/CreateScanJob
+
+
+
+
+
+ sane-airscan request
+ sane-airscan
+ sane-airscan
+
+
+ exif
+ 1
+
+
+ 8500
+ 11700
+
+
+ Platen
+
+
+ RGB24
+
+ 300
+ 300
+
+
+ 0
+ 0
+ 8500
+ 11700
+
+
+
+
+
+
+
+
+
+### Retrieve scanned image
+POST http://{{server}}/WebServices/ScannerService
+Content-Type: application/soap+xml
+User-Agent: WSDAPI
+
+
+
+
+ urn:uuid:dc1ff0f8-e4e2-b1ef-758f-b1e6186a5cc6
+ http://192.168.178.122:80/WebServices/ScannerService
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+
+ http://schemas.microsoft.com/windows/2006/08/wdp/scan/RetrieveImage
+
+
+
+
+ IMAGE000.JPG
+
+ 2
+ urn:uuid:81b7a2a1-f1ff-4504-84a2-3c2af46faf5d
+
+
+
+
+
+### Retrieve device info
+POST http://{{server}}/WebServices/Device
+Content-Type: application/soap+xml
+User-Agent: WSDAPI
+
+
+
+
+ http://schemas.xmlsoap.org/ws/2004/09/transfer/Get
+ urn:uuid:414bbe9a-7059-e7c3-a856-a38029a53c1c
+ urn:uuid:e3248000-80ce-11db-8000-3c2af46faf5d
+
+ http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
+
+
+
+
\ No newline at end of file
diff --git a/libraries/play-licensing/lvl_library/build.gradle b/libraries/play-licensing/lvl_library/build.gradle
index afaf0fa8..5f9aba84 100644
--- a/libraries/play-licensing/lvl_library/build.gradle
+++ b/libraries/play-licensing/lvl_library/build.gradle
@@ -5,6 +5,7 @@ android {
compileSdk 34
defaultConfig {
minSdk 4
+ targetSdk 34
}
buildTypes {
release {
@@ -15,10 +16,4 @@ android {
buildFeatures {
aidl true
}
- lint {
- targetSdk 34
- }
- testOptions {
- targetSdk 34
- }
}
diff --git a/settings-gradle.lockfile b/settings-gradle.lockfile
new file mode 100644
index 00000000..709a43f7
--- /dev/null
+++ b/settings-gradle.lockfile
@@ -0,0 +1,4 @@
+# This is a Gradle generated file for dependency locking.
+# Manual edits can break the build and are not advised.
+# This file is expected to be part of source control.
+empty=incomingCatalogForLibs0
diff --git a/settings.gradle.kts b/settings.gradle.kts
index ed52ce90..81296942 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,3 +1,5 @@
+rootProject.name = "ScanBridge"
+
pluginManagement {
repositories {
google {
@@ -30,7 +32,9 @@ dependencyResolutionManagement {
}
}
-rootProject.name = "ScanBridge"
-include(":app")
+include(":composeUI")
+include(":core")
+include(":androidApp")
+include(":desktopApp")
include(":lvl_library")
project(":lvl_library").projectDir = File(rootDir, "libraries/play-licensing/lvl_library/")