From 2e24d3ea5518ee2ab614e66c6394d865c6539e56 Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Thu, 4 Dec 2025 13:59:25 -0800 Subject: [PATCH 1/6] Update project dependencies --- app/build.gradle.kts | 1 + .../java/AndroidLibraryConventionPlugin.kt | 1 + gradle/libs.versions.toml | 47 ++++++++++--------- .../sampleslib/components/DropDownMenuBox.kt | 4 +- .../AddFeaturesWithContingentValuesScreen.kt | 4 +- .../AnimateImagesWithImageOverlayScreen.kt | 4 +- .../ApplyHillshadeRendererToRasterScreen.kt | 4 +- .../BrowseOGCAPIFeatureServiceScreen.kt | 4 +- .../configureclusters/screens/MainScreen.kt | 6 +-- .../createandsavemap/screens/MainScreen.kt | 8 ++-- .../screens/CreateKMLMultiTrackScreen.kt | 4 +- .../components/ShowPopupViewModel.kt | 19 ++++---- .../showpopup/screens/ShowPopupScreen.kt | 10 ++-- .../screens/TraceUtilityNetworkScreen.kt | 4 +- 14 files changed, 65 insertions(+), 55 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 81230c1a3..b52903651 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -86,6 +86,7 @@ dependencies { implementation(libs.androidx.room.ktx) implementation(libs.coil.compose) implementation(libs.coil.network.http) + implementation(libs.androidx.compose.material.icons.extended) annotationProcessor(libs.androidx.room.compiler) ksp(libs.androidx.room.compiler) } diff --git a/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt index d661944fc..f370d3f0c 100644 --- a/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt @@ -52,6 +52,7 @@ class AndroidLibraryConventionPlugin : Plugin { implementation(libs.findLibrary("androidx-constraintlayout").get()) implementation(libs.findLibrary("androidx-appcompat").get()) implementation(libs.findLibrary("android-material").get()) + implementation(libs.findLibrary("androidx-compose-material-icons-extended").get()) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5ed6a3671..724c1b5ba 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,32 +1,33 @@ [versions] # ArcGIS Maps SDK for Kotlin version -arcgisMapsKotlinVersion = "300.0.0-4757" +arcgisMapsKotlinVersion = "300.0.0-4802" ### Android versions -androidGradlePlugin = "8.12.1" -lifecycle = "2.9.2" -androidTools = "31.12.1" +androidGradlePlugin = "8.13.1" +lifecycle = "2.10.0" +androidTools = "31.13.1" appcompat = "1.7.1" constraintLayoutVersion = "2.2.1" kotlinxSerializationJson = "1.9.0" accompanistSystemuicontroller = "0.36.0" -workVersion = "2.10.3" -datastorePreferences = "1.1.7" -roomVersion = "2.7.2" +workVersion = "2.11.0" +datastorePreferences = "1.2.0" +roomVersion = "2.8.4" ### Kotlin versions -kotlinVersion = "2.2.10" +kotlinVersion = "2.2.21" coreKtx = "1.17.0" -ksp = "2.2.10-2.0.2" -kotlinReflection = "2.2.20" -commonsIoVersion = "2.20.0" +ksp = "2.3.3" +kotlinReflection = "2.2.21" +commonsIoVersion = "2.21.0" ### Compose versions -composeBom = "2025.08.00" -activityCompose = "1.10.1" -material = "1.12.0" -navigationCompose = "2.9.3" +composeBom = "2025.12.00" +activityCompose = "1.12.1" +material = "1.13.0" +navigationCompose = "2.9.6" +materialIconsExt = "1.7.8" ### Testing versions junit = "4.13.2" @@ -34,16 +35,17 @@ junitVersion = "1.3.0" espressoCore = "3.7.0" ### Application Verions -versionCode = "2008000" -versionName = "200.8.0" +versionCode = "3000000" +versionName = "300.0.0" minSdk = "28" targetSdk = "36" ### Third party libraries -arcore = "1.50.0" +arcore = "1.51.0" playServicesLocation = "21.3.0" -navigationFragmentKtx = "2.9.3" -navigationUiKtx = "2.9.3" +navigationFragmentKtx = "2.9.6" +navigationUiKtx = "2.9.6" +coil = "3.3.0" [libraries] @@ -76,6 +78,7 @@ androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui- androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } +androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended", version.ref = "materialIconsExt"} ### ArcGIS Maps SDK for Kotlin libs arcgis-maps-kotlin = { group = "com.esri", name = "arcgis-maps-kotlin", version.ref = "arcgisMapsKotlinVersion" } @@ -88,8 +91,8 @@ arcgis-maps-kotlin-toolkit-popup = { group = "com.esri", name = "arcgis-maps-kot arcgis-maps-kotlin-toolkit-scalebar = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-scalebar" } ### Third party libraries -coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version = "3.0.0-rc01" } -coil-network-http = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version = "3.0.0-rc01" } +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } +coil-network-http = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" } ar-core = { group = "com.google.ar", name = "core", version.ref = "arcore" } play-services-location = { group = "com.google.android.gms", name = "play-services-location", version.ref = "playServicesLocation" } diff --git a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/DropDownMenuBox.kt b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/DropDownMenuBox.kt index 0ac6d09c5..9e01c4a1d 100644 --- a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/DropDownMenuBox.kt +++ b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/DropDownMenuBox.kt @@ -21,10 +21,10 @@ package com.esri.arcgismaps.sample.sampleslib.components import android.content.res.Configuration import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -59,7 +59,7 @@ fun DropDownMenuBox( onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable) + modifier = Modifier.menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable) ) ExposedDropdownMenu( expanded = expanded, diff --git a/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/screens/AddFeaturesWithContingentValuesScreen.kt b/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/screens/AddFeaturesWithContingentValuesScreen.kt index d46ca167c..1a5fe92d3 100644 --- a/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/screens/AddFeaturesWithContingentValuesScreen.kt +++ b/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/screens/AddFeaturesWithContingentValuesScreen.kt @@ -32,10 +32,10 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold @@ -269,7 +269,7 @@ fun AttributeDropdown( enabled = availableValues.isNotEmpty(), modifier = Modifier .fillMaxWidth() - .menuAnchor(type = MenuAnchorType.PrimaryNotEditable), + .menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable), value = textValue ?: "", onValueChange = {}, label = { Text(attributeName) }, diff --git a/samples/animate-images-with-image-overlay/src/main/java/com/esri/arcgismaps/sample/animateimageswithimageoverlay/screens/AnimateImagesWithImageOverlayScreen.kt b/samples/animate-images-with-image-overlay/src/main/java/com/esri/arcgismaps/sample/animateimageswithimageoverlay/screens/AnimateImagesWithImageOverlayScreen.kt index b565c8f01..7db163d20 100644 --- a/samples/animate-images-with-image-overlay/src/main/java/com/esri/arcgismaps/sample/animateimageswithimageoverlay/screens/AnimateImagesWithImageOverlayScreen.kt +++ b/samples/animate-images-with-image-overlay/src/main/java/com/esri/arcgismaps/sample/animateimageswithimageoverlay/screens/AnimateImagesWithImageOverlayScreen.kt @@ -30,13 +30,13 @@ import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Slider @@ -200,7 +200,7 @@ private fun ImageOverlayMenu( onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable) + modifier = Modifier.menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable) ) ExposedDropdownMenu( expanded = expanded, diff --git a/samples/apply-hillshade-renderer-to-raster/src/main/java/com/esri/arcgismaps/sample/applyhillshaderenderertoraster/screens/ApplyHillshadeRendererToRasterScreen.kt b/samples/apply-hillshade-renderer-to-raster/src/main/java/com/esri/arcgismaps/sample/applyhillshaderenderertoraster/screens/ApplyHillshadeRendererToRasterScreen.kt index 555ca2959..b7b1ee854 100644 --- a/samples/apply-hillshade-renderer-to-raster/src/main/java/com/esri/arcgismaps/sample/applyhillshaderenderertoraster/screens/ApplyHillshadeRendererToRasterScreen.kt +++ b/samples/apply-hillshade-renderer-to-raster/src/main/java/com/esri/arcgismaps/sample/applyhillshaderenderertoraster/screens/ApplyHillshadeRendererToRasterScreen.kt @@ -30,13 +30,13 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Slider @@ -207,7 +207,7 @@ fun HillshadeRendererOptions( trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, modifier = Modifier .fillMaxWidth() - .menuAnchor(type = MenuAnchorType.PrimaryNotEditable) + .menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable) ) ExposedDropdownMenu( expanded = expanded, diff --git a/samples/browse-ogc-api-feature-service/src/main/java/com/esri/arcgismaps/sample/browseogcapifeatureservice/screens/BrowseOGCAPIFeatureServiceScreen.kt b/samples/browse-ogc-api-feature-service/src/main/java/com/esri/arcgismaps/sample/browseogcapifeatureservice/screens/BrowseOGCAPIFeatureServiceScreen.kt index 9a1577684..843902e7f 100644 --- a/samples/browse-ogc-api-feature-service/src/main/java/com/esri/arcgismaps/sample/browseogcapifeatureservice/screens/BrowseOGCAPIFeatureServiceScreen.kt +++ b/samples/browse-ogc-api-feature-service/src/main/java/com/esri/arcgismaps/sample/browseogcapifeatureservice/screens/BrowseOGCAPIFeatureServiceScreen.kt @@ -28,10 +28,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface @@ -146,7 +146,7 @@ private fun LayerPickerBar( label = { Text("Layers") }, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) }, modifier = Modifier - .menuAnchor(MenuAnchorType.PrimaryNotEditable) + .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable) .fillMaxWidth() ) ExposedDropdownMenu( diff --git a/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt index e5b516c22..02f2a2333 100644 --- a/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt +++ b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt @@ -34,6 +34,7 @@ import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FloatingActionButton @@ -41,7 +42,6 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.SheetState @@ -268,7 +268,7 @@ private fun ClusterRadiusControls( onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable) + modifier = Modifier.menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable) ) ExposedDropdownMenu( expanded = expanded, @@ -322,7 +322,7 @@ private fun ClusterMaxScaleControls( onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable) + modifier = Modifier.menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable) ) ExposedDropdownMenu( expanded = expanded, diff --git a/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt index 35bd75098..1f2637084 100644 --- a/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt +++ b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt @@ -37,13 +37,13 @@ import androidx.compose.material3.Button import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost @@ -275,7 +275,7 @@ fun FolderDropdown( OutlinedTextField( modifier = Modifier .fillMaxWidth() - .menuAnchor(type = MenuAnchorType.PrimaryNotEditable), + .menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable), value = label, onValueChange = { newDescription -> label = newDescription }, label = { Text(text = "Folder:") }, @@ -340,7 +340,7 @@ fun BasemapDropdown( OutlinedTextField( modifier = Modifier .fillMaxWidth() - .menuAnchor(type = MenuAnchorType.PrimaryNotEditable), + .menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable), value = basemapStyle, onValueChange = {}, label = { Text(text = "Basemap Style:") }, @@ -391,7 +391,7 @@ fun LayersDropdown( OutlinedTextField( modifier = Modifier .fillMaxWidth() - .menuAnchor(type = MenuAnchorType.PrimaryNotEditable), + .menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable), value = "Select...", onValueChange = {}, label = { Text(text = "Operational Layers:") }, diff --git a/samples/create-kml-multi-track/src/main/java/com/esri/arcgismaps/sample/createkmlmultitrack/screens/CreateKMLMultiTrackScreen.kt b/samples/create-kml-multi-track/src/main/java/com/esri/arcgismaps/sample/createkmlmultitrack/screens/CreateKMLMultiTrackScreen.kt index 278edbb31..792bccc5f 100644 --- a/samples/create-kml-multi-track/src/main/java/com/esri/arcgismaps/sample/createkmlmultitrack/screens/CreateKMLMultiTrackScreen.kt +++ b/samples/create-kml-multi-track/src/main/java/com/esri/arcgismaps/sample/createkmlmultitrack/screens/CreateKMLMultiTrackScreen.kt @@ -32,13 +32,13 @@ import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -285,7 +285,7 @@ fun TrackBrowseOptions( onValueChange = {}, readOnly = true, trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, - modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable) + modifier = Modifier.menuAnchor(type = ExposedDropdownMenuAnchorType.PrimaryNotEditable) ) ExposedDropdownMenu( expanded = expanded, diff --git a/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/components/ShowPopupViewModel.kt b/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/components/ShowPopupViewModel.kt index 30ff475f5..b75c98cf9 100644 --- a/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/components/ShowPopupViewModel.kt +++ b/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/components/ShowPopupViewModel.kt @@ -17,9 +17,6 @@ package com.esri.arcgismaps.sample.showpopup.components import android.app.Application -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.unit.dp import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope @@ -28,11 +25,13 @@ import com.arcgismaps.geometry.GeometryType import com.arcgismaps.mapping.ArcGISMap import com.arcgismaps.mapping.PortalItem import com.arcgismaps.mapping.layers.FeatureLayer -import com.arcgismaps.mapping.popup.Popup import com.arcgismaps.mapping.view.SingleTapConfirmedEvent import com.arcgismaps.portal.Portal import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy +import com.arcgismaps.toolkit.popup.PopupState import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch class ShowPopupViewModel(application: Application) : AndroidViewModel(application) { @@ -55,9 +54,10 @@ class ShowPopupViewModel(application: Application) : AndroidViewModel(applicatio } } - // Popup that gets passed to the main screen composable - var popup: Popup? by mutableStateOf(null) - private set + // PopupState flow that gets passed to the main screen composable + private var _popupState = MutableStateFlow(null) + val popupState = _popupState.asStateFlow() + // Keep track of the identified feature private var identifiedFeature: Feature? = null @@ -87,11 +87,12 @@ class ShowPopupViewModel(application: Application) : AndroidViewModel(applicatio tolerance = 12.dp, returnPopupsOnly = true ).onSuccess { result -> - popup = result.popups.first().also { popup -> + val popup = result.popups.first().also { popup -> identifiedFeature = (popup.geoElement as Feature).also { identifiedFeature -> featureLayer.selectFeature(identifiedFeature) } } + _popupState.value = PopupState(popup,viewModelScope) }.onFailure { error -> messageDialogVM.showMessageDialog( title = "Failed to identify: ${error.message}", @@ -105,7 +106,7 @@ class ShowPopupViewModel(application: Application) : AndroidViewModel(applicatio * Dismiss the popup and unselect the identified feature. */ fun onDismissRequest() { - popup = null + _popupState.value = null identifiedFeature?.let { featureLayer.unselectFeature(it) } } } diff --git a/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/screens/ShowPopupScreen.kt b/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/screens/ShowPopupScreen.kt index 93ad3726f..a004a5881 100644 --- a/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/screens/ShowPopupScreen.kt +++ b/samples/show-popup/src/main/java/com/esri/arcgismaps/sample/showpopup/screens/ShowPopupScreen.kt @@ -25,7 +25,9 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.arcgismaps.toolkit.geoviewcompose.MapView import com.arcgismaps.toolkit.popup.Popup @@ -42,6 +44,7 @@ fun ShowPopupScreen(sampleName: String) { val mapViewModel: ShowPopupViewModel = viewModel() val sheetState = rememberModalBottomSheetState() + val popupState by mapViewModel.popupState.collectAsStateWithLifecycle() Scaffold( topBar = { SampleTopAppBar(title = sampleName) }, @@ -59,14 +62,15 @@ fun ShowPopupScreen(sampleName: String) { mapViewProxy = mapViewModel.mapViewProxy, onSingleTapConfirmed = mapViewModel::identifyForPopup ) - mapViewModel.popup?.let { popup -> + popupState?.let { popupState -> ModalBottomSheet( modifier = Modifier.wrapContentSize(), - onDismissRequest = { mapViewModel.onDismissRequest() }, + onDismissRequest = mapViewModel::onDismissRequest, sheetState = sheetState ) { Popup( - popup = popup, + popupState = popupState, + onDismiss = mapViewModel::onDismissRequest, modifier = Modifier.fillMaxSize() ) } diff --git a/samples/trace-utility-network/src/main/java/com/esri/arcgismaps/sample/traceutilitynetwork/screens/TraceUtilityNetworkScreen.kt b/samples/trace-utility-network/src/main/java/com/esri/arcgismaps/sample/traceutilitynetwork/screens/TraceUtilityNetworkScreen.kt index 4b3798936..b703f86fd 100644 --- a/samples/trace-utility-network/src/main/java/com/esri/arcgismaps/sample/traceutilitynetwork/screens/TraceUtilityNetworkScreen.kt +++ b/samples/trace-utility-network/src/main/java/com/esri/arcgismaps/sample/traceutilitynetwork/screens/TraceUtilityNetworkScreen.kt @@ -37,13 +37,13 @@ import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.SegmentedButton @@ -318,7 +318,7 @@ fun ExposedDropdownMenuBoxWithTraceTypes( ) { TextField( modifier = Modifier - .menuAnchor(MenuAnchorType.PrimaryNotEditable) + .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable) .fillMaxWidth(), label = { Text("Trace Type") }, value = selectedTraceName, From c2488f820cee3a4249d682dfea37a6bb42de08c8 Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Fri, 12 Dec 2025 12:59:00 -0800 Subject: [PATCH 2/6] Use newer dependency versions --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 724c1b5ba..955da147e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,12 +1,12 @@ [versions] # ArcGIS Maps SDK for Kotlin version -arcgisMapsKotlinVersion = "300.0.0-4802" +arcgisMapsKotlinVersion = "300.0.0-4810" ### Android versions -androidGradlePlugin = "8.13.1" +androidGradlePlugin = "8.13.2" lifecycle = "2.10.0" -androidTools = "31.13.1" +androidTools = "31.13.2" appcompat = "1.7.1" constraintLayoutVersion = "2.2.1" kotlinxSerializationJson = "1.9.0" From 58145b7145c7fa3450a3d4fac31d3fc98ccbcc9a Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Tue, 16 Dec 2025 18:33:00 -0800 Subject: [PATCH 3/6] Update project configuration and add concept tests --- app/build.gradle.kts | 2 +- .../ScenarioRuleScreenshotTest.kt | 50 +++++++++++ .../sampleviewer/UIAutomatorScreenshotTest.kt | 84 +++++++++++++++++++ .../model/DefaultSampleInfoRepository.kt | 7 ++ .../model/SampleInfoRepository.kt | 2 + ...droidApplicationComposeConventionPlugin.kt | 2 + .../java/AndroidLibraryConventionPlugin.kt | 4 - .../build_logic/convention/AndroidCompose.kt | 23 ++++- gradle/libs.versions.toml | 11 ++- 9 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt create mode 100644 app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b52903651..de541cc44 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,7 +51,7 @@ android { abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64") } isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") // If signing.properties file not found, gradle will build an unsigned APK. // For release builds, provide the required "signingPropsFilePath" for a signed APK, using: // ./gradlew assembleRelease -PsigningPropsFilePath=absolute-file-path/signing.properties diff --git a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt new file mode 100644 index 000000000..7324edd8e --- /dev/null +++ b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt @@ -0,0 +1,50 @@ +/* Copyright 2025 Esri + * + * 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 + * + * http://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 com.esri.arcgismaps.kotlin.sampleviewer + +import android.graphics.Bitmap +import androidx.test.core.graphics.writeToTestStorage +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.captureToBitmap +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.ext.junit.rules.activityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestName +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ScenarioRuleScreenshotTest { + + @get:Rule + val scenarioRule = activityScenarioRule() + + @get:Rule + val testName = TestName() + + @Test + fun launch_idle_and_save_screenshot() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + onView(isRoot()).perform( + captureToBitmap { bmp: Bitmap -> + bmp.writeToTestStorage("Launcher_${testName.methodName}") + } + ) + } +} diff --git a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt new file mode 100644 index 000000000..e31e903b0 --- /dev/null +++ b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt @@ -0,0 +1,84 @@ +/* Copyright 2025 Esri + * + * 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 + * + * http://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 com.esri.arcgismaps.kotlin.sampleviewer + +import android.content.Context +import android.content.Intent +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File + +/** + * Run with: ./gradlew connectedAndroidTest + */ +@RunWith(AndroidJUnit4::class) +class UIAutomatorScreenshotTest { + + private lateinit var device: UiDevice + + @Before + fun setup() { + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + device.pressHome() + device.waitForIdle() + + // Launch the app + val context = ApplicationProvider.getApplicationContext() + val intent = context.packageManager.getLaunchIntentForPackage( + "com.esri.arcgismaps.kotlin.sampleviewer" + )?.apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + + context.startActivity(intent) + + // Wait for app to launch + device.wait( + Until.hasObject(By.pkg("com.esri.arcgismaps.kotlin.sampleviewer")), + 10000 + ) + device.waitForIdle() + } + + + @Test + fun testHomeScreenScreenshot() = runBlocking { + val screenshotFile = getScreenshotFile("HomeScreen").apply { + parentFile?.mkdirs() + } + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + assertTrue(device.takeScreenshot(screenshotFile)) + } + + private fun getScreenshotFile(fileName: String): File { + val picturesDir = android.os.Environment.getExternalStoragePublicDirectory( + android.os.Environment.DIRECTORY_PICTURES + ) + val screenshotDir = File(picturesDir, "SampleViewerScreenshots") + screenshotDir.mkdirs() + return File(screenshotDir, "${fileName}.png") + } +} diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt index 6ca44f0cc..db25d88f2 100644 --- a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt +++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt @@ -126,4 +126,11 @@ object DefaultSampleInfoRepository : SampleInfoRepository { override fun getSamplesInCategory(sampleCategory: SampleCategory): Flow> { return sampleData.map { it.filter { sample -> sample.metadata.sampleCategory == sampleCategory } } } + + /** + * Get all samples from the repository. + */ + override fun getAllSamples(): Flow> { + return sampleData + } } diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt index d7ca8e15a..d5c9e1a2a 100644 --- a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt +++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt @@ -26,4 +26,6 @@ interface SampleInfoRepository { fun getSamplesInCategory(sampleCategory: SampleCategory): Flow> fun getSampleByName(sampleName: String): Flow + + fun getAllSamples(): Flow> } diff --git a/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt index 06dc52b38..b06260bca 100644 --- a/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt @@ -1,5 +1,6 @@ import com.android.build.api.dsl.ApplicationExtension import com.esri.arcgismaps.kotlin.build_logic.convention.configureAndroidCompose +import com.esri.arcgismaps.kotlin.build_logic.convention.configureAndroidComposeTests import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.getByType @@ -14,6 +15,7 @@ class AndroidApplicationComposeConventionPlugin : Plugin { } val extension = extensions.getByType() configureAndroidCompose(extension) + configureAndroidComposeTests(extension) } } } diff --git a/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt index f370d3f0c..ccb4bd0c9 100644 --- a/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt @@ -2,12 +2,10 @@ import com.android.build.gradle.LibraryExtension import com.esri.arcgismaps.kotlin.build_logic.convention.configureKotlinAndroid import com.esri.arcgismaps.kotlin.build_logic.convention.implementation import com.esri.arcgismaps.kotlin.build_logic.convention.libs -import com.esri.arcgismaps.kotlin.build_logic.convention.testImplementation import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.kotlin class AndroidLibraryConventionPlugin : Plugin { override fun apply(target: Project) { @@ -21,7 +19,6 @@ class AndroidLibraryConventionPlugin : Plugin { configureKotlinAndroid(this) compileSdk = libs.findVersion("targetSdk").get().toString().toInt() defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true } @@ -47,7 +44,6 @@ class AndroidLibraryConventionPlugin : Plugin { } dependencies { - testImplementation(kotlin("test")) // External libraries implementation(libs.findLibrary("androidx-constraintlayout").get()) implementation(libs.findLibrary("androidx-appcompat").get()) diff --git a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt index be21e4159..d5ecf021f 100644 --- a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt +++ b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt @@ -22,16 +22,31 @@ internal fun Project.configureAndroidCompose( dependencies { val composeBom = libs.findLibrary("androidx-compose-bom").get() implementation(platform(composeBom)) - androidTestImplementation(platform(composeBom)) implementation(libs.findLibrary("androidx-activity-compose").get()) implementation(libs.findLibrary("androidx-compose-material3").get()) implementation(libs.findLibrary("androidx-lifecycle-viewmodel-compose").get()) implementation(libs.findLibrary("androidx-compose-ui-tooling-preview").get()) debugImplementation(libs.findLibrary("androidx-compose-ui-tooling").get()) - debugImplementation(libs.findLibrary("androidx-compose-ui-test-manifest").get()) - androidTestImplementation(libs.findLibrary("androidx-compose-ui-test").get()) - androidTestImplementation(libs.findLibrary("androidx-compose-ui-test-junit4").get()) + } + } +} +internal fun Project.configureAndroidComposeTests( + commonExtension: CommonExtension<*, *, *, *, *, *>, +) { + commonExtension.apply { + dependencies { + val composeBom = libs.findLibrary("androidx-compose-bom").get() + androidTestImplementation(platform(composeBom)) + androidTestImplementation(libs.findLibrary("androidx-compose-ui-test-junit4").get()) + androidTestImplementation(libs.findLibrary("androidx-test-uiautomator").get()) + androidTestImplementation(libs.findLibrary("androidx-test-runner").get()) + androidTestImplementation(libs.findLibrary("androidx-test-rules").get()) + androidTestImplementation(libs.findLibrary("androidx-test-ext-junit-ktx").get()) + androidTestImplementation(libs.findLibrary("androidx-junit").get()) + androidTestImplementation(libs.findLibrary("androidx-espresso-core").get()) + androidTestImplementation(libs.findLibrary("kotlinx-coroutines-test").get()) + debugImplementation(libs.findLibrary("androidx-compose-ui-test-manifest").get()) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 955da147e..40fadaf5d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,6 +33,11 @@ materialIconsExt = "1.7.8" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" +uiautomator = "2.3.0" +testRunner = "1.7.0" +testRules = "1.7.0" +testExtJunit = "1.3.0" +coroutinesTest = "1.10.2" ### Application Verions versionCode = "3000000" @@ -73,7 +78,6 @@ androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graph androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } -androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test" } androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } @@ -100,6 +104,11 @@ play-services-location = { group = "com.google.android.gms", name = "play-servic 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-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } +androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "testRunner" } +androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "testRules" } +androidx-test-ext-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "testExtJunit" } +kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutinesTest" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } From e2ab9667b894756132e7a8d02bbf2610c56e22bd Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Wed, 17 Dec 2025 11:06:16 -0800 Subject: [PATCH 4/6] Add concurrent futures support --- .../kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt | 8 +++----- .../kotlin/sampleviewer/UIAutomatorScreenshotTest.kt | 2 +- .../kotlin/build_logic/convention/AndroidCompose.kt | 2 ++ gradle/libs.versions.toml | 4 ++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt index 7324edd8e..bf8b6afa5 100644 --- a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt +++ b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/ScenarioRuleScreenshotTest.kt @@ -16,7 +16,6 @@ package com.esri.arcgismaps.kotlin.sampleviewer -import android.graphics.Bitmap import androidx.test.core.graphics.writeToTestStorage import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.captureToBitmap @@ -41,10 +40,9 @@ class ScenarioRuleScreenshotTest { @Test fun launch_idle_and_save_screenshot() { InstrumentationRegistry.getInstrumentation().waitForIdleSync() - onView(isRoot()).perform( - captureToBitmap { bmp: Bitmap -> + onView(isRoot()) + .perform(captureToBitmap { bmp -> bmp.writeToTestStorage("Launcher_${testName.methodName}") - } - ) + }) } } diff --git a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt index e31e903b0..bbcac5a94 100644 --- a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt +++ b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/UIAutomatorScreenshotTest.kt @@ -79,6 +79,6 @@ class UIAutomatorScreenshotTest { ) val screenshotDir = File(picturesDir, "SampleViewerScreenshots") screenshotDir.mkdirs() - return File(screenshotDir, "${fileName}.png") + return File(screenshotDir, "${System.currentTimeMillis()}_${fileName}.png") } } diff --git a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt index d5ecf021f..ec7f055ac 100644 --- a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt +++ b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt @@ -26,6 +26,8 @@ internal fun Project.configureAndroidCompose( implementation(libs.findLibrary("androidx-compose-material3").get()) implementation(libs.findLibrary("androidx-lifecycle-viewmodel-compose").get()) implementation(libs.findLibrary("androidx-compose-ui-tooling-preview").get()) + implementation(libs.findLibrary("androidx-concurrent-futures").get()) + implementation(libs.findLibrary("androidx-concurrent-futures-ktx").get()) debugImplementation(libs.findLibrary("androidx-compose-ui-tooling").get()) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40fadaf5d..a0c53f8fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,6 +37,8 @@ uiautomator = "2.3.0" testRunner = "1.7.0" testRules = "1.7.0" testExtJunit = "1.3.0" +androidxConcurrent = "1.3.0" +androidxConcurrentKtx = "1.3.0" coroutinesTest = "1.10.2" ### Application Verions @@ -108,6 +110,8 @@ androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiaut androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "testRunner" } androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "testRules" } androidx-test-ext-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "testExtJunit" } +androidx-concurrent-futures = { group = "androidx.concurrent", name = "concurrent-futures", version.ref = "androidxConcurrent" } +androidx-concurrent-futures-ktx = { group = "androidx.concurrent", name = "concurrent-futures-ktx", version.ref = "androidxConcurrentKtx" } kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutinesTest" } # Dependencies of the included build-logic From fd7e533a4418913ee734a928bd855a4f9b559a55 Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Wed, 17 Dec 2025 16:13:26 -0800 Subject: [PATCH 5/6] Add test dependencies --- gradle/libs.versions.toml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 062b9051b..bcf591ccb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,6 +33,13 @@ materialIconsExt = "1.7.8" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" +uiautomator = "2.3.0" +testRunner = "1.7.0" +testRules = "1.7.0" +testExtJunit = "1.3.0" +androidxConcurrent = "1.3.0" +androidxConcurrentKtx = "1.3.0" +coroutinesTest = "1.10.2" ### Application Verions versionCode = "3000000" @@ -73,7 +80,6 @@ androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graph androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } -androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test" } androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } @@ -100,6 +106,13 @@ play-services-location = { group = "com.google.android.gms", name = "play-servic 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-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } +androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "testRunner" } +androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "testRules" } +androidx-test-ext-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "testExtJunit" } +androidx-concurrent-futures = { group = "androidx.concurrent", name = "concurrent-futures", version.ref = "androidxConcurrent" } +androidx-concurrent-futures-ktx = { group = "androidx.concurrent", name = "concurrent-futures-ktx", version.ref = "androidxConcurrentKtx" } +kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutinesTest" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } From 30f19cb4f210fad5debd04bf538b66179ce9336d Mon Sep 17 00:00:00 2001 From: Shubham Sharma Date: Thu, 18 Dec 2025 17:46:06 -0800 Subject: [PATCH 6/6] Add verifySampleCount unit test support --- .../DefaultSampleInfoRepositoryAndroidTest.kt | 63 +++++++++++++++++++ .../build_logic/convention/AndroidCompose.kt | 1 + 2 files changed, 64 insertions(+) create mode 100644 app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepositoryAndroidTest.kt diff --git a/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepositoryAndroidTest.kt b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepositoryAndroidTest.kt new file mode 100644 index 000000000..bef0dceed --- /dev/null +++ b/app/src/androidTest/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepositoryAndroidTest.kt @@ -0,0 +1,63 @@ +/* Copyright 2025 Esri + * + * 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 + * + * http://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 com.esri.arcgismaps.kotlin.sampleviewer.model + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import kotlinx.serialization.json.Json +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SampleInfoRepositoryTest { + + private val json = Json { ignoreUnknownKeys = true } + + /** + * Given the generated assets in 'samples/samples.json', + * when the DefaultSampleInfoRepository is loaded, + * then the count of samples emitted by getAllSamples() + * should match the JSON sample count. + */ + @Test + fun verifySampleCount() = runBlocking { + val context: Context = InstrumentationRegistry.getInstrumentation().targetContext + + // Read the generated asset directly and count top-level entries. + val samplesJsonString = context.assets.open("samples/samples.json").bufferedReader().use { it.readText() } + val parsed = json.decodeFromString>>(samplesJsonString) + val jsonCount = parsed.size + + // Load the repository from the generated assets. + DefaultSampleInfoRepository.load(context) + + // Wait for the repository flow to be populated. + // The StateFlow emits an initial empty list, so we wait until it emits the expected count. + val sampleViewerCount = withTimeout(30_000) { + DefaultSampleInfoRepository.getAllSamples() + .first { samples -> samples.size >= jsonCount } + .size + } + + assertEquals(jsonCount, sampleViewerCount) + } +} diff --git a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt index ec7f055ac..a218a3dc9 100644 --- a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt +++ b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt @@ -49,6 +49,7 @@ internal fun Project.configureAndroidComposeTests( androidTestImplementation(libs.findLibrary("androidx-espresso-core").get()) androidTestImplementation(libs.findLibrary("kotlinx-coroutines-test").get()) debugImplementation(libs.findLibrary("androidx-compose-ui-test-manifest").get()) + debugImplementation(libs.findLibrary("junit").get()) } } }