From ac958dbcfb6f49c5f477d7fa4f3b4cc9d18c031f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Santos?= Date: Wed, 24 Jul 2024 17:02:53 +0100 Subject: [PATCH 1/2] Setup Android lint --- .github/actions/setup/action.yml | 13 +++++ .github/workflows/validate.yml | 48 +++++++++++++++++++ composeApp/build.gradle.kts | 11 +++++ .../ic_launcher_foreground.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 1 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 1 + iosApp/Podfile | 2 +- iosApp/Podfile.lock | 2 +- 8 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/validate.yml rename composeApp/src/androidMain/res/{drawable-v24 => drawable}/ic_launcher_foreground.xml (100%) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 00000000..664c03dc --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,13 @@ +name: 'Setup' +description: 'Setup Java, checkout and setup gradle' +runs: + using: "composite" + steps: + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Gradle cache + uses: gradle/actions/setup-gradle@v3 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 00000000..f26496d4 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,48 @@ +name: Validate +on: + push: + branches: + - master + pull_request: + +jobs: + build: + name: Build + runs-on: macos-latest + + strategy: + matrix: + type: [Debug, Release] + + steps: + - uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build Android + run: ./gradlew assemble${{ matrix.type }} + + - name: Build iOS + run: ./gradlew link${{ matrix.type }}FrameworkIosSimulatorArm64 + + android-lint: + name: Android Lint + runs-on: macos-latest + needs: [ build ] + + steps: + - uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + + - name: Run lint + run: ./gradlew lint + + - name: Uploads test reports + uses: actions/upload-artifact@v4 + if: failure() + with: + name: android-lint-report + path: composeApp/build/reports/ diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 7df336d5..e78648a3 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -32,6 +32,7 @@ kotlin { framework { baseName = "composeApp" isStatic = true + binaryOption("bundleId", "composeApp") } podfile = project.file("../iosApp/Podfile") @@ -53,6 +54,10 @@ kotlin { implementation(compose.components.uiToolingPreview) implementation(libs.kotlin.serialization) } + + all { + languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + } } @OptIn(ExperimentalKotlinGradlePluginApi::class) @@ -96,4 +101,10 @@ android { dependencies { debugImplementation(libs.compose.ui.tooling) } + android { + lint { + warningsAsErrors = true + disable += "AndroidGradlePluginVersion" + } + } } diff --git a/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/composeApp/src/androidMain/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml rename to composeApp/src/androidMain/res/drawable/ic_launcher_foreground.xml diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cfe..6f3b755b 100644 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe..6f3b755b 100644 --- a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/iosApp/Podfile b/iosApp/Podfile index f3cd2a5a..c38ddd2b 100644 --- a/iosApp/Podfile +++ b/iosApp/Podfile @@ -1,4 +1,4 @@ -platform :ios, '9.0' +platform :ios, '12.0' use_frameworks! target 'iosApp' do diff --git a/iosApp/Podfile.lock b/iosApp/Podfile.lock index 2c88a8c9..b16758ea 100644 --- a/iosApp/Podfile.lock +++ b/iosApp/Podfile.lock @@ -41,6 +41,6 @@ SPEC CHECKSUMS: libz: 83658eb2a0db785623ffdf9ce13407e6b8b5c8f9 oonimkall: 9768ce9dad18265d45d2ea972c84fb0bd5237cc3 -PODFILE CHECKSUM: 9f3463701e9fb15f3dded022f7afabede102208e +PODFILE CHECKSUM: 04b498ba1984c25e9816234b0551222184c25492 COCOAPODS: 1.15.2 From 017a04c678857599424d5c561403c5b20ae71a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Santos?= Date: Wed, 24 Jul 2024 18:20:32 +0100 Subject: [PATCH 2/2] Apply kotlin lint --- .editorconfig | 14 ++ .github/workflows/validate.yml | 21 +++ build.gradle.kts | 1 + composeApp/build.gradle.kts | 11 +- .../org/ooni/engine/AndroidOonimkallBridge.kt | 2 +- .../org/ooni/probe/AndroidApplication.kt | 2 +- .../kotlin/org/ooni/engine/Engine.kt | 63 ++++---- .../kotlin/org/ooni/engine/EventResult.kt | 55 +++++-- .../kotlin/org/ooni/engine/OonimkallBridge.kt | 4 +- .../kotlin/org/ooni/engine/TaskEvent.kt | 16 +- .../kotlin/org/ooni/engine/TaskSettings.kt | 6 +- .../commonMain/kotlin/org/ooni/probe/App.kt | 12 +- .../kotlin/org/ooni/probe/di/Dependencies.kt | 4 +- .../kotlin/org/ooni/probe/ui/Theme.kt | 141 +++++++----------- .../org/ooni/probe/ui/main/MainScreen.kt | 13 +- .../org/ooni/probe/ui/main/MainViewModel.kt | 15 +- .../org/ooni/probe/MainViewController.kt | 6 +- gradle/libs.versions.toml | 5 +- iosApp/iosApp.xcodeproj/project.pbxproj | 18 --- iosApp/iosApp/ContentView.swift | 2 +- 20 files changed, 226 insertions(+), 185 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..0503f08f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] + +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f26496d4..066aa74e 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -46,3 +46,24 @@ jobs: with: name: android-lint-report path: composeApp/build/reports/ + + kotlin-lint: + name: Kotlin Lint + runs-on: macos-latest + needs: [ build ] + + steps: + - uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + + - name: Run lint + run: ./gradlew ktlintCheck + + - name: Uploads test reports + uses: actions/upload-artifact@v4 + if: failure() + with: + name: android-lint-report + path: composeApp/build/reports/ktlint/ diff --git a/build.gradle.kts b/build.gradle.kts index f49eedf2..ee4a7838 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,4 +7,5 @@ plugins { alias(libs.plugins.jetbrainsComposeCompiler) apply false alias(libs.plugins.kotlinMultiplatform) apply false alias(libs.plugins.cocoapods) apply false + alias(libs.plugins.ktlint) apply false } \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index e78648a3..cb16a249 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -8,6 +8,7 @@ plugins { alias(libs.plugins.jetbrainsComposeCompiler) alias(libs.plugins.cocoapods) alias(libs.plugins.kotlinSerialization) + alias(libs.plugins.ktlint) } kotlin { @@ -37,7 +38,7 @@ kotlin { podfile = project.file("../iosApp/Podfile") } - + sourceSets { androidMain.dependencies { implementation(libs.compose.ui.tooling.preview) @@ -108,3 +109,11 @@ android { } } } + +ktlint { + filter { + exclude("**/generated/**") + include("**/kotlin/**") + } + additionalEditorconfig.put("ktlint_function_naming_ignore_when_annotated_with", "Composable") +} diff --git a/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidOonimkallBridge.kt b/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidOonimkallBridge.kt index 1d3f9d41..0729a380 100644 --- a/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidOonimkallBridge.kt +++ b/composeApp/src/androidMain/kotlin/org/ooni/engine/AndroidOonimkallBridge.kt @@ -13,4 +13,4 @@ class AndroidOonimkallBridge : OonimkallBridge { override fun waitForNextEvent() = task.waitForNextEvent() } } -} \ No newline at end of file +} diff --git a/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt b/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt index e1a28d8b..0802c08b 100644 --- a/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt +++ b/composeApp/src/androidMain/kotlin/org/ooni/probe/AndroidApplication.kt @@ -2,4 +2,4 @@ package org.ooni.probe import android.app.Application -class AndroidApplication : Application() \ No newline at end of file +class AndroidApplication : Application() diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt index e3acc575..76c2f90d 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/Engine.kt @@ -10,31 +10,32 @@ import kotlin.math.roundToInt class Engine( private val bridge: OonimkallBridge, private val json: Json, - private val baseFilePath: String + private val baseFilePath: String, ) { + fun startTask(taskSettings: TaskSettings): Flow = + channelFlow { + val finalSettings = + taskSettings.copy( + stateDir = baseFilePath, + tunnelDir = baseFilePath, + tempDir = baseFilePath, + assetsDir = baseFilePath, + ) - fun startTask(taskSettings: TaskSettings): Flow = channelFlow { - val finalSettings = taskSettings.copy( - stateDir = baseFilePath, - tunnelDir = baseFilePath, - tempDir = baseFilePath, - assetsDir = baseFilePath, - ) - - val task = bridge.startTask(json.encodeToString(finalSettings)) + val task = bridge.startTask(json.encodeToString(finalSettings)) - while (!task.isDone()) { - val eventJson = task.waitForNextEvent() - val eventResult = json.decodeFromString(eventJson) - eventResult.toTaskEvent()?.let { send(it) } - } + while (!task.isDone()) { + val eventJson = task.waitForNextEvent() + val eventResult = json.decodeFromString(eventJson) + eventResult.toTaskEvent()?.let { send(it) } + } - invokeOnClose { - if (it is CancellationException) { - task.interrupt() + invokeOnClose { + if (it is CancellationException) { + task.interrupt() + } } } - } private fun EventResult.toTaskEvent(): TaskEvent? = when (key) { @@ -46,20 +47,22 @@ class Engine( value?.percentage?.let { percentageValue -> TaskEvent.Progress( percentage = (percentageValue * 100.0).roundToInt(), - message = value?.message + message = value?.message, ) } - "log" -> value?.message?.let { message -> - TaskEvent.Log( - level = value?.log_level, - message = message - ) - } + "log" -> + value?.message?.let { message -> + TaskEvent.Log( + level = value?.logLevel, + message = message, + ) + } - "status.report_create" -> value?.report_id?.let { - TaskEvent.ReportCreate(reportId = it) - } + "status.report_create" -> + value?.reportId?.let { + TaskEvent.ReportCreate(reportId = it) + } "task_terminated" -> TaskEvent.TaskTerminated @@ -67,4 +70,4 @@ class Engine( else -> null } -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/EventResult.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/EventResult.kt index e48ca6a6..d81e235f 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/EventResult.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/EventResult.kt @@ -1,29 +1,64 @@ package org.ooni.engine +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable class EventResult { + @SerialName("key") var key: String? = null + + @SerialName("value") var value: Value? = null @Serializable class Value { + @SerialName("key") var key: Double = 0.0 - var log_level: String? = null + + @SerialName("log_level") + var logLevel: String? = null + + @SerialName("message") var message: String? = null + + @SerialName("percentage") var percentage: Double = 0.0 - var json_str: String? = null + + @SerialName("json_str") + var jsonStr: String? = null + + @SerialName("idx") var idx: Int = 0 - var report_id: String? = null - var probe_ip: String? = null - var probe_asn: String? = null - var probe_cc: String? = null - var probe_network_name: String? = null - var downloaded_kb: Double = 0.0 - var uploaded_kb: Double = 0.0 + + @SerialName("report_id") + var reportId: String? = null + + @SerialName("probe_ip") + var probeIp: String? = null + + @SerialName("probe_asn") + var probeAsn: String? = null + + @SerialName("probe_cc") + var probeCc: String? = null + + @SerialName("probe_network_name") + var probeNetworkName: String? = null + + @SerialName("downloaded_kb") + var downloadedKb: Double = 0.0 + + @SerialName("uploaded_kb") + var uploadedKb: Double = 0.0 + + @SerialName("input") var input: String? = null + + @SerialName("failure") var failure: String? = null - var orig_key: String? = null + + @SerialName("orig_key") + var origKey: String? = null } } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/OonimkallBridge.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/OonimkallBridge.kt index c498c54f..fb401cf6 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/OonimkallBridge.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/OonimkallBridge.kt @@ -5,7 +5,9 @@ interface OonimkallBridge { interface Task { fun interrupt() + fun isDone(): Boolean + fun waitForNextEvent(): String } -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskEvent.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskEvent.kt index b4a8c210..5a75a01f 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskEvent.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskEvent.kt @@ -3,25 +3,25 @@ package org.ooni.engine sealed interface TaskEvent { data class Log( val level: String?, - val message: String - ): TaskEvent + val message: String, + ) : TaskEvent data object Started : TaskEvent data class ReportCreate( - val reportId: String + val reportId: String, ) : TaskEvent data class Progress( val percentage: Int, - val message: String? - ): TaskEvent + val message: String?, + ) : TaskEvent data object StatusEnd : TaskEvent data object TaskTerminated : TaskEvent data class FailureStartup( - val message: String? - ): TaskEvent -} \ No newline at end of file + val message: String?, + ) : TaskEvent +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskSettings.kt b/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskSettings.kt index 5b250811..4bb25915 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskSettings.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/engine/TaskSettings.kt @@ -13,12 +13,12 @@ data class TaskSettings( @SerialName("temp_dir") val tempDir: String? = null, @SerialName("tunnel_dir") val tunnelDir: String? = null, @SerialName("assets_dir") val assetsDir: String? = null, - @SerialName("options") val options: Options = Options() + @SerialName("options") val options: Options = Options(), ) { @Serializable data class Options( @SerialName("no_collector") val noCollector: Boolean = true, @SerialName("software_name") val softwareName: String = "Probe Multiplatform", - @SerialName("software_version") val softwareVersion: String = "1.0" + @SerialName("software_version") val softwareVersion: String = "1.0", ) -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt index d28fc18c..86cd973c 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt @@ -3,26 +3,24 @@ package org.ooni.probe import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import org.ooni.probe.di.Dependencies import org.jetbrains.compose.ui.tooling.preview.Preview +import org.ooni.probe.di.Dependencies import org.ooni.probe.ui.Theme import org.ooni.probe.ui.main.MainScreen @Composable @Preview -fun App( - dependencies: Dependencies -) { +fun App(dependencies: Dependencies) { Theme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background, ) { MainScreen( - dependencies.mainViewModel + dependencies.mainViewModel, ) } } -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt index 65759bca..8309f950 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt @@ -9,7 +9,6 @@ class Dependencies( private val oonimkallBridge: OonimkallBridge, private val baseFileDir: String, ) { - private val json by lazy { Json { encodeDefaults = true @@ -20,5 +19,4 @@ class Dependencies( private val engine by lazy { Engine(oonimkallBridge, json, baseFileDir) } val mainViewModel by lazy { MainViewModel(engine) } - -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/Theme.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/Theme.kt index aabcb76d..ca9a42a8 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/Theme.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/Theme.kt @@ -10,84 +10,58 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -val PrimaryColor = Color(0xff0588cb) -val PrimaryLightColor = PrimaryColor.copy(alpha = 0.75f) - -val SecondaryColor = Color(0xff5db8fe) -val SecondaryLightColor = SecondaryColor.copy(alpha = 0.75f) - -// TODO: fix the color theme - -val PrimaryTextColor = Color(0xffffffff) -val SecondaryTextColor = Color(0xff000000) - -// val SurfaceDark = Color(0xFF1f1f1f) -// val SurfaceDark = Color(0xFF121212) -val SurfaceDark = Color(0xFF161616) - -val SurfaceLight = Color(0xFFFFFFFF) - -val BackgroundLightColor = Color(0xffF1F0F5) - -val BackgroundDarkColor = Color(0xff010100) - -val ErrorColor = Color(0xFFFF8989) -val OnErrorColor = Color(0xFF000000) - -val SuccessColor = Color(0xFF34b233) - -const val SessionColor = 0xFfBA4949 -const val ShortBreakColor = 0xFf38858A -const val LongBreakColor = 0xFf397097 - -const val Red = 0xFFFF0000 -const val Orange = 0xFFFFA500 -const val Blue = 0xFF0000FF -const val Green = 0xFF00FF00 - -const val LightGreen = 0xFF90EE90 -const val Yellow = 0xFFFFFF00 -const val LightBlue = 0xFFADD8E6 -const val Pink = 0xFFFFC0CB - -private val LightColors = lightColorScheme( - primary = PrimaryColor, - onPrimary = PrimaryTextColor, - secondary = SecondaryColor, - onSecondary = SecondaryTextColor, - tertiary = PrimaryLightColor, - onTertiary = PrimaryTextColor, - background = BackgroundLightColor, - onBackground = Color.Black, - surface = SurfaceLight, - onSurface = Color.Black, - surfaceVariant = SurfaceLight, - onSurfaceVariant = Color.Black, - secondaryContainer = PrimaryColor, - onSecondaryContainer = Color.White, - error = ErrorColor, - onError = OnErrorColor, -) - -private val DarkColors = darkColorScheme( - primary = PrimaryColor, - onPrimary = PrimaryTextColor, - secondary = SecondaryLightColor, - onSecondary = SecondaryTextColor, - tertiary = PrimaryLightColor, - onTertiary = PrimaryTextColor, - background = BackgroundDarkColor, - onBackground = Color.White, - surface = SurfaceDark, - onSurface = Color.White, - surfaceVariant = SurfaceDark, - onSurfaceVariant = Color.White, - secondaryContainer = PrimaryColor, - onSecondaryContainer = Color.White, - error = ErrorColor, - onError = OnErrorColor, -) +private val primaryColor = Color(0xff0588cb) +private val primaryLightColor = primaryColor.copy(alpha = 0.75f) +private val secondaryColor = Color(0xff5db8fe) +private val secondaryLightColor = secondaryColor.copy(alpha = 0.75f) +private val primaryTextColor = Color(0xffffffff) +private val secondaryTextColor = Color(0xff000000) +private val surfaceDark = Color(0xFF161616) +private val surfaceLight = Color(0xFFFFFFFF) +private val backgroundLightColor = Color(0xffF1F0F5) +private val backgroundDarkColor = Color(0xff010100) +private val errorColor = Color(0xFFFF8989) +private val onErrorColor = Color(0xFF000000) + +private val LightColors = + lightColorScheme( + primary = primaryColor, + onPrimary = primaryTextColor, + secondary = secondaryColor, + onSecondary = secondaryTextColor, + tertiary = primaryLightColor, + onTertiary = primaryTextColor, + background = backgroundLightColor, + onBackground = Color.Black, + surface = surfaceLight, + onSurface = Color.Black, + surfaceVariant = surfaceLight, + onSurfaceVariant = Color.Black, + secondaryContainer = primaryColor, + onSecondaryContainer = Color.White, + error = errorColor, + onError = onErrorColor, + ) +private val DarkColors = + darkColorScheme( + primary = primaryColor, + onPrimary = primaryTextColor, + secondary = secondaryLightColor, + onSecondary = secondaryTextColor, + tertiary = primaryLightColor, + onTertiary = primaryTextColor, + background = backgroundDarkColor, + onBackground = Color.White, + surface = surfaceDark, + onSurface = Color.White, + surfaceVariant = surfaceDark, + onSurfaceVariant = Color.White, + secondaryContainer = primaryColor, + onSecondaryContainer = Color.White, + error = errorColor, + onError = onErrorColor, + ) @Composable internal fun Theme( @@ -95,11 +69,12 @@ internal fun Theme( content: @Composable () -> Unit, ) { val colorScheme = if (useDarkTheme) DarkColors else LightColors - val shapes = Shapes( - small = RoundedCornerShape(4.dp), - medium = RoundedCornerShape(8.dp), - large = RoundedCornerShape(12.dp), - ) + val shapes = + Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(8.dp), + large = RoundedCornerShape(12.dp), + ) val typography = MaterialTheme.typography MaterialTheme( @@ -108,4 +83,4 @@ internal fun Theme( typography, content, ) -} \ No newline at end of file +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainScreen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainScreen.kt index 753f7a87..548804d7 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainScreen.kt @@ -12,24 +12,23 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @Composable -fun MainScreen( - viewModel: MainViewModel -) { +fun MainScreen(viewModel: MainViewModel) { val state by viewModel.state.collectAsState() Column { Button( onClick = { viewModel.onEvent(MainViewModel.Event.StartClick) }, - enabled = !state.isRunning + enabled = !state.isRunning, ) { Text("Run Test") } Text( text = state.log, - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) + modifier = + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), ) } } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainViewModel.kt index 643dfb32..c19eeef1 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/main/MainViewModel.kt @@ -16,7 +16,7 @@ import org.ooni.engine.Engine import org.ooni.engine.TaskSettings class MainViewModel( - private val engine: Engine + private val engine: Engine, ) { private val events = MutableSharedFlow(extraBufferCapacity = 1) @@ -53,7 +53,7 @@ class MainViewModel( data class State( val isRunning: Boolean = false, - val log: String = "" + val log: String = "", ) sealed interface Event { @@ -61,10 +61,11 @@ class MainViewModel( } companion object { - val TASK_SETTINGS = TaskSettings( - name = "web_connectivity", - inputs = listOf("https://ooni.org"), - logLevel = "DEBUG2" - ) + val TASK_SETTINGS = + TaskSettings( + name = "web_connectivity", + inputs = listOf("https://ooni.org"), + logLevel = "DEBUG2", + ) } } diff --git a/composeApp/src/iosMain/kotlin/org/ooni/probe/MainViewController.kt b/composeApp/src/iosMain/kotlin/org/ooni/probe/MainViewController.kt index f9477233..c41e982a 100644 --- a/composeApp/src/iosMain/kotlin/org/ooni/probe/MainViewController.kt +++ b/composeApp/src/iosMain/kotlin/org/ooni/probe/MainViewController.kt @@ -5,5 +5,7 @@ import org.ooni.engine.OonimkallBridge import org.ooni.probe.di.Dependencies import platform.Foundation.NSTemporaryDirectory -fun MainViewController(bridge: OonimkallBridge) = - ComposeUIViewController { App(Dependencies(bridge, NSTemporaryDirectory())) } \ No newline at end of file +fun mainViewController(bridge: OonimkallBridge) = + ComposeUIViewController { + App(Dependencies(bridge, NSTemporaryDirectory())) + } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3a3514b..34f548b2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ android-compileSdk = "34" android-minSdk = "24" android-targetSdk = "34" -androidx-activityCompose = "1.9.0" +androidx-activityCompose = "1.9.1" compose = "1.6.8" compose-plugin = "1.6.11" kotlin = "2.0.0" @@ -24,4 +24,5 @@ jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -cocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } \ No newline at end of file +cocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "12.1.1" } \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 2562ec72..123f806b 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -127,7 +127,6 @@ B92378962B6B1156000C7307 /* Frameworks */, 7555FF79242A565900829871 /* Resources */, 93E977732C4FE022009CCABC /* ShellScript */, - 3793390471A4D6FCCF24C27E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -206,23 +205,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 3793390471A4D6FCCF24C27E /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; 93E977732C4FE022009CCABC /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index 52eb65b6..daa9a9a7 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -4,7 +4,7 @@ import composeApp struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { - MainViewControllerKt.MainViewController(bridge: IosOonimkallBridge()) + MainViewControllerKt.mainViewController(bridge: IosOonimkallBridge()) } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}