Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
run: ./gradlew checkCode

- name: Run unit tests
run: ./gradlew testLocalDebugUnitTest jvmTest
run: ./gradlew testLocalDebugUnitTest jvmTest testAndroidHostTest

- name: Generate codecov report
run: ./gradlew jacocoLocalDebugUnitTestReport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.scan_this_qr_to_download_geojson
import org.jetbrains.compose.resources.stringResource as multiplatformStringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
Expand Down Expand Up @@ -154,7 +157,7 @@ private fun ShareableContent(modifier: Modifier = Modifier, loiReport: LoiReport
GroundQrCode(
modifier = Modifier.align(Alignment.Center),
title = loiReport.loiName,
footer = stringResource(R.string.scan_this_qr_to_download_geojson),
footer = multiplatformStringResource(Res.string.scan_this_qr_to_download_geojson),
content = loiReport.geoJson.toString(),
contentDescription = "QR code with LOI Geometry",
centerLogoPainter = painterResource(R.drawable.ground_logo),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.groundplatform.android.R
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.other
import org.groundplatform.android.common.Constants
import org.jetbrains.compose.resources.stringResource
import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport
import org.groundplatform.domain.model.task.MultipleChoice
import org.groundplatform.domain.model.task.Option
Expand Down Expand Up @@ -148,7 +149,7 @@ private fun OtherTextField(

@Composable
private fun MultipleChoiceItem.toTextLabel() =
AnnotatedString(if (isOtherOption) stringResource(id = R.string.other) else option.label)
AnnotatedString(if (isOtherOption) stringResource(Res.string.other) else option.label)

@Preview(backgroundColor = 0xFFFFFFFF, showBackground = true)
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.scan_this_qr_to_download_geojson
import org.jetbrains.compose.resources.stringResource as multiplatformStringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -85,7 +88,7 @@ fun ShareLocationModal(loiReport: LoiReport, onDismiss: () -> Unit) {
GroundQrCode(
modifier = Modifier.align(Alignment.Center),
title = loiReport.loiName,
footer = stringResource(R.string.scan_this_qr_to_download_geojson),
footer = multiplatformStringResource(Res.string.scan_this_qr_to_download_geojson),
content = loiReport.geoJson.toString(),
contentDescription = "QR code with LOI Geometry",
centerLogoPainter = painterResource(R.drawable.ground_logo),
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@
<string name="read_only_data_collection_hint">Encuesta de solo lectura</string>
<string name="no_submissions">Sin presentaciones</string>
<string name="dismiss_info_popup">Descartar</string>
<string name="other">Otro</string>
<plurals name="submission_count">
<item quantity="one">%d presentación</item>
<item quantity="other">%d presentaciones</item>
Expand Down Expand Up @@ -236,7 +235,6 @@
<string name="photo_save_failed_title">Error al guardar</string>
<string name="photo_save_failed_desc">No se pudo guardar la foto capturada. Por favor, inténtalo de nuevo.</string>
<string name="share_location">Compartir ubicación</string>
<string name="scan_this_qr_to_download_geojson">Escanea este código QR para ver el GeoJSON</string>

<string name="join_survey">Unirse a una encuesta</string>
<string name="invalid_survey_qr_code">Código QR de encuesta no reconocido</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@
<string name="read_only_data_collection_hint">L&#8217;enquête est en lecture seule</string>
<string name="no_submissions">Aucune soumission</string>
<string name="dismiss_info_popup">Rejeter</string>
<string name="other">Autres</string>
<plurals name="submission_count">
<item quantity="one">%d soumission</item>
<item quantity="other">%d soumissions</item>
Expand Down Expand Up @@ -217,7 +216,6 @@
<string name="photo_save_failed_title">Erreur d&#8217;enregistrement</string>
<string name="photo_save_failed_desc">Impossible d&#8217;enregistrer la photo capturée. Veuillez réessayer.</string>
<string name="share_location">Partager l&#8217;emplacement</string>
<string name="scan_this_qr_to_download_geojson">Scannez ce code QR pour afficher le GeoJson</string>

<string name="join_survey">Rejoindre une enquête</string>
<string name="invalid_survey_qr_code">Code QR d&#8217;enquête non reconnu</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-lo/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@
<string name="read_only_data_collection_hint">ແບບສຳຫຼວດເປັນແບບເບິ່ງຢ່າງດຽວ</string>
<string name="no_submissions">ບໍ່ມີການສົ່ງຂໍ້ມູນ</string>
<string name="dismiss_info_popup">ປິດແຈ້ງເຕືອນ</string>
<string name="other">ອື່ນໆ</string>
<plurals name="submission_count">
<item quantity="other">%d ການສົ່ງຂໍ້ມູນ</item>
</plurals>
Expand Down Expand Up @@ -206,7 +205,6 @@
<string name="photo_save_failed_title">ຂໍ້ຜິດພາດໃນການບັນທຶກ</string>
<string name="photo_save_failed_desc">ບໍ່ສາມາດບັນທຶກຮູບພາບທີ່ຖ່າຍໄດ້. ກະລຸນາລອງໃໝ່ອີກຄັ້ງ.</string>
<string name="share_location">ແບ່ງປັນຕຳແໜ່ງ</string>
<string name="scan_this_qr_to_download_geojson">ສະແກນ QR ນີ້ເພື່ອເບິ່ງ GeoJSON</string>

<string name="join_survey">ເຂົ້າຮ່ວມແບບສຳຫຼວດ</string>
<string name="invalid_survey_qr_code">ບໍ່ຮັບຮູ້ລະຫັດ QR ແບບສຳຫຼວດນີ້</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-pt/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@
<string name="read_only_data_collection_hint">Inquérito apenas para leitura</string>
<string name="no_submissions">Nenhuma submissão</string>
<string name="dismiss_info_popup">Ignorar</string>
<string name="other">Outro</string>
<plurals name="submission_count">
<item quantity="one">%d submissão</item>
<item quantity="other">%d submissões</item>
Expand Down Expand Up @@ -238,7 +237,6 @@
<string name="photo_save_failed_title">Erro ao salvar</string>
<string name="photo_save_failed_desc">Falha ao salvar a foto capturada. Por favor, tente novamente.</string>
<string name="share_location">Partilhar localização</string>
<string name="scan_this_qr_to_download_geojson">Leia este código QR para visualizar o GeoJson</string>

<string name="join_survey">Aderir ao inquérito</string>
<string name="invalid_survey_qr_code">Código QR de inquérito não reconhecido</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-th/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@
<string name="read_only_data_collection_hint">แบบสำรวจเป็นแบบอ่านอย่างเดียว</string>
<string name="no_submissions">ไม่มีการส่งข้อมูล</string>
<string name="dismiss_info_popup">ปิดข้อความ</string>
<string name="other">อื่น ๆ</string>
<plurals name="submission_count">
<item quantity="other">การส่งข้อมูล %d รายการ</item>
</plurals>
Expand Down Expand Up @@ -208,7 +207,6 @@
<string name="photo_save_failed_title">ข้อผิดพลาดในการบันทึก</string>
<string name="photo_save_failed_desc">ไม่สามารถบันทึกรูปภาพที่ถ่ายได้ โปรดลองอีกครั้ง</string>
<string name="share_location">แชร์ตำแหน่ง</string>
<string name="scan_this_qr_to_download_geojson">สแกนคิวอาร์โค้ดนี้เพื่อดู GeoJSON</string>

<string name="join_survey">เข้าร่วมแบบสำรวจ</string>
<string name="invalid_survey_qr_code">ไม่รู้จักรหัสคิวอาร์ของแบบสำรวจนี้</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-vi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@
<string name="read_only_data_collection_hint">Khảo sát chỉ được phép xem</string>
<string name="no_submissions">Chưa có bài gửi nào</string>
<string name="dismiss_info_popup">Đóng lại</string>
<string name="other">Khác</string>
<plurals name="submission_count">
<item quantity="other">%d bài gửi</item>
</plurals>
Expand Down Expand Up @@ -210,7 +209,6 @@
<string name="photo_save_failed_title">Lỗi lưu</string>
<string name="photo_save_failed_desc">Không thể lưu ảnh đã chụp. Vui lòng thử lại.</string>
<string name="share_location">Chia sẻ vị trí</string>
<string name="scan_this_qr_to_download_geojson">Quét mã QR này để xem GeoJSON</string>

<string name="join_survey">Tham gia khảo sát</string>
<string name="invalid_survey_qr_code">Không nhận diện được mã QR khảo sát</string>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@
<string name="read_only_data_collection_hint">Survey is read-only</string>
<string name="no_submissions">No submissions</string>
<string name="dismiss_info_popup">Dismiss</string>
<string name="other">Other</string>
<plurals name="submission_count">
<item quantity="one">%d submission</item>
<item quantity="other">%d submissions</item>
Expand Down Expand Up @@ -234,7 +233,6 @@

<string name="recenter">Re-center</string>
<string name="share_location">Share location</string>
<string name="scan_this_qr_to_download_geojson">Scan this QR code to view the GeoJson</string>

<string name="join_survey">Join survey</string>
<string name="invalid_survey_qr_code">Unrecognized survey QR code</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.scan_this_qr_to_download_geojson
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
Expand Down Expand Up @@ -55,7 +57,7 @@ class DataSubmissionConfirmationScreenTest {
composeTestRule.onNodeWithText(LOI_REPORT.loiName).assertIsDisplayed()
composeTestRule.onNodeWithTag(TEST_TAG_GROUND_QR_CODE).assertIsDisplayed()
composeTestRule
.onNodeWithText(getString(R.string.scan_this_qr_to_download_geojson))
.onNodeWithText(getString(Res.string.scan_this_qr_to_download_geojson))
.performScrollTo()
.assertIsDisplayed()
composeTestRule.onNodeWithText(getString(R.string.close)).performScrollTo().assertIsDisplayed()
Expand All @@ -79,7 +81,7 @@ class DataSubmissionConfirmationScreenTest {
composeTestRule.onNodeWithText(LOI_REPORT.loiName).assertIsDisplayed()
composeTestRule.onNodeWithTag(TEST_TAG_GROUND_QR_CODE).assertIsDisplayed()
composeTestRule
.onNodeWithText(getString(R.string.scan_this_qr_to_download_geojson))
.onNodeWithText(getString(Res.string.scan_this_qr_to_download_geojson))
.assertIsDisplayed()
composeTestRule.onNodeWithText(getString(R.string.close)).assertIsDisplayed()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import ground_android.core.ui.generated.resources.Res
import ground_android.core.ui.generated.resources.scan_this_qr_to_download_geojson
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
Expand Down Expand Up @@ -49,7 +51,7 @@ class ShareLocationModalTest {
composeTestRule.onNodeWithText(LOI_NAME).assertIsDisplayed()
composeTestRule.onNodeWithTag(TEST_TAG_GROUND_QR_CODE).assertIsDisplayed()
composeTestRule
.onNodeWithText(getString(R.string.scan_this_qr_to_download_geojson))
.onNodeWithText(getString(Res.string.scan_this_qr_to_download_geojson))
.performScrollTo()
.assertIsDisplayed()
composeTestRule.onNodeWithText(getString(R.string.close)).performScrollTo().assertIsDisplayed()
Expand Down
2 changes: 1 addition & 1 deletion config/detekt/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ naming:
minimumFunctionNameLength: 3
FunctionNaming:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ]
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/androidHostTest/**' ]
functionPattern: '[a-zA-Z][a-zA-Z0-9]*'
excludeClassPattern: '$^'
FunctionParameterNaming:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,7 @@ data class Task(
CAPTURE_LOCATION,
INSTRUCTIONS,
}

fun isOmittedFromDocExport(): Boolean =
type == Type.DROP_PIN || type == Type.DRAW_AREA || type == Type.INSTRUCTIONS
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
*/
package org.groundplatform.domain.usecases

import kotlin.math.absoluteValue
import kotlin.math.round
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
Expand All @@ -35,6 +33,7 @@ import org.groundplatform.domain.model.locationofinterest.LoiReport
import org.groundplatform.domain.repository.LocationOfInterestRepositoryInterface
import org.groundplatform.domain.repository.SurveyRepositoryInterface
import org.groundplatform.domain.repository.UserRepositoryInterface
import org.groundplatform.domain.util.toFixedDecimals

/**
* Use case that generates a [LoiReport] containing the LOI geometry and properties as a GeoJSON.
Expand Down Expand Up @@ -128,12 +127,8 @@ class GetLoiReportUseCase(

/** Renders this [Double] as a JSON number with exactly 6 decimal digits. */
private fun Double.roundTo6Decimals(): JsonPrimitive {
val scaled = round(this * DECIMAL_SCALE).toLong()
val sign = if (scaled < 0) "-" else ""
val absScaled = scaled.absoluteValue
val intPart = absScaled / DECIMAL_SCALE
val fracPart = (absScaled % DECIMAL_SCALE).toString().padStart(DECIMAL_DIGITS, '0')
return JsonUnquotedLiteral("$sign$intPart.$fracPart")
val value = toFixedDecimals(DECIMAL_DIGITS)
return JsonUnquotedLiteral(value)
}

private companion object {
Expand All @@ -147,6 +142,5 @@ class GetLoiReportUseCase(
const val TYPE_POLYGON = "Polygon"
const val TYPE_MULTI_POLYGON = "MultiPolygon"
const val DECIMAL_DIGITS = 6
const val DECIMAL_SCALE = 1_000_000L
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.groundplatform.domain.util

import kotlin.math.absoluteValue
import kotlin.math.round

fun Double.toFixedDecimals(decimals: Int): String {
if (decimals <= 0) return round(this).toLong().toString()
var scale = 1L
repeat(decimals) { scale *= 10 }
val scaled = round(this * scale).toLong()
val sign = if (scaled < 0) "-" else ""
val absScaled = scaled.absoluteValue
val intPart = absScaled / scale
val fracPart = (absScaled % scale).toString().padStart(decimals, '0')
return "$sign$intPart.$fracPart"
}
22 changes: 20 additions & 2 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ kotlin {
compileSdk = libs.versions.androidCompileSdk.get().toInt()
minSdk = libs.versions.androidMinSdk.get().toInt()
androidResources.enable = true

withHostTest { isIncludeAndroidResources = true }
}

val xcfName = "GroundUiKit"
jvm()

val xcfName = "GroundUiKit"
listOf(iosArm64(), iosSimulatorArm64()).forEach {
it.binaries.framework {
baseName = xcfName
Expand All @@ -44,20 +47,35 @@ kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":core:domain"))
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.material3)
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.components.resources)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.kotlinx.collections.immutable)
}
}

commonTest { dependencies { implementation(libs.kotlin.test) } }
commonTest {
dependencies {
implementation(project(":core:testing"))
implementation(libs.kotlin.test)
implementation(libs.kotlinx.coroutines.test)
}
}

androidMain { dependencies { implementation(libs.google.zxing) } }

val androidHostTest by getting {
dependencies {
implementation(libs.junit)
implementation(libs.robolectric)
}
}

iosMain { dependencies {} }
}
}
Expand Down
Loading
Loading