diff --git a/identify-layer-features/.gitignore b/identify-layer-features/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/identify-layer-features/.gitignore @@ -0,0 +1 @@ +/build diff --git a/identify-layer-features/README.md b/identify-layer-features/README.md new file mode 100644 index 000000000..816e71b8d --- /dev/null +++ b/identify-layer-features/README.md @@ -0,0 +1,33 @@ +# Identify layer features + +Identify features in all layers in a map. + +![Image of identify layers](identify-layer-features.png) + +## Use case + +"Identify layers" operation allows users to tap on a map, returning features at that location across multiple layers. Because some layer types have sublayers, the sample recursively counts results for sublayers within each layer. + +## How to use the sample + +Tap to identify features. A bottom text banner will show all layers with features under the tapped location, as well as the number of features. + +## How it works + +1. The tapped position is passed to `MapView.identifyLayers(...)` method. +2. For each `IdentifyLayerResult` in the results, features are counted. + * Note: there is one identify result per layer with matching features; if the feature count is 0, that means a sublayer contains the matching features. + +## Relevant API + +* IdentifyLayerResult +* IdentifyLayerResult.sublayerResults +* LayerContent + +## Additional information + +The GeoView supports two methods of identify: `identifyLayer`, which identifies features within a specific layer and `identifyLayers`, which identifies features for all layers in the current view. + +## Tags + +identify, recursion, recursive, sublayers diff --git a/identify-layer-features/README.metadata.json b/identify-layer-features/README.metadata.json new file mode 100644 index 000000000..8a0b8d3a1 --- /dev/null +++ b/identify-layer-features/README.metadata.json @@ -0,0 +1,32 @@ +{ + "category": "Search and Query", + "description": "Identify features in all layers in a map.", + "formal_name": "IdentifyLayerFeatures", + "ignore": false, + "images": [ + "identify-layer-features.png" + ], + "keywords": [ + "identify", + "recursion", + "recursive", + "sublayers", + "IdentifyLayerResult", + "IdentifyLayerResult.sublayerResults", + "LayerContent" + ], + "language": "kotlin", + "redirect_from": "", + "relevant_apis": [ + "IdentifyLayerResult", + "IdentifyLayerResult.sublayerResults", + "LayerContent" + ], + "snippets": [ + "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt", + "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt", + "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt", + "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt" + ], + "title": "Identify layer features" +} diff --git a/identify-layer-features/build.gradle b/identify-layer-features/build.gradle new file mode 100644 index 000000000..2a368331a --- /dev/null +++ b/identify-layer-features/build.gradle @@ -0,0 +1,48 @@ +apply plugin: 'com.android.application' +apply plugin: 'org.jetbrains.kotlin.android' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + applicationId "com.esri.arcgismaps.sample.identifylayerfeatures" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + buildConfigField("String", "API_KEY", API_KEY) + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + buildFeatures { + compose = true + buildConfig = true + } + composeOptions { + kotlinCompilerExtensionVersion = "$kotlinCompilerExt" + } + + namespace 'com.esri.arcgismaps.sample.identifylayerfeatures' +} + +dependencies { + // lib dependencies from rootProject build.gradle + implementation "androidx.core:core-ktx:$ktxAndroidCore" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:$ktxLifecycle" + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$ktxLifecycle" + implementation "androidx.activity:activity-compose:$composeActivityVersion" + // Jetpack Compose Bill of Materials + implementation platform("androidx.compose:compose-bom:$composeBOM") + // Jetpack Compose dependencies + implementation "androidx.compose.ui:ui" + implementation "androidx.compose.material3:material3" + implementation "androidx.compose.ui:ui-tooling" + implementation "androidx.compose.ui:ui-tooling-preview" + implementation project(path: ':samples-lib') +} diff --git a/identify-layer-features/identify-layer-features.png b/identify-layer-features/identify-layer-features.png new file mode 100644 index 000000000..bb990c388 Binary files /dev/null and b/identify-layer-features/identify-layer-features.png differ diff --git a/identify-layer-features/proguard-rules.pro b/identify-layer-features/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/identify-layer-features/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/identify-layer-features/src/main/AndroidManifest.xml b/identify-layer-features/src/main/AndroidManifest.xml new file mode 100644 index 000000000..c6647b46d --- /dev/null +++ b/identify-layer-features/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt new file mode 100644 index 000000000..8b43d63dc --- /dev/null +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt @@ -0,0 +1,56 @@ +/* Copyright 2023 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.sample.identifylayerfeatures + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import com.arcgismaps.ApiKey +import com.arcgismaps.ArcGISEnvironment +import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme +import com.esri.arcgismaps.sample.identifylayerfeatures.screens.MainScreen + +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // authentication with an API key or named user is + // required to access basemaps and other location services + ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY) + + setContent { + SampleAppTheme { + IdentifyLayerFeaturesApp() + } + } + } + + @Composable + private fun IdentifyLayerFeaturesApp() { + Surface( + color = MaterialTheme.colorScheme.background + ) { + MainScreen( + sampleName = getString(R.string.app_name), + application = application + ) + } + } +} diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt new file mode 100644 index 000000000..7bec430bb --- /dev/null +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt @@ -0,0 +1,89 @@ +/* Copyright 2023 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.sample.identifylayerfeatures.components + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.LifecycleOwner +import com.arcgismaps.mapping.view.MapView + +/** + * Wraps the MapView in a Composable function. + */ +@Composable +fun ComposeMapView( + modifier: Modifier = Modifier, + mapViewModel: MapViewModel +) { + // get an instance of the current lifecycle owner + val lifecycleOwner = LocalLifecycleOwner.current + // collect the latest state of the MapViewState + val mapViewState by mapViewModel.mapViewState.collectAsState() + // create and add MapView to the activity lifecycle + val mapView = createMapViewInstance(lifecycleOwner) + + // wrap the MapView as an AndroidView + AndroidView( + modifier = modifier, + factory = { mapView }, + // recomposes the MapView on changes in the MapViewState + update = { mapView -> + mapView.apply { + map = mapViewState.arcGISMap + setViewpoint(mapViewState.viewpoint) + } + } + ) + + // launch coroutine functions in the composition's CoroutineContext + LaunchedEffect(Unit) { + mapView.onSingleTapConfirmed.collect { + // call identifyLayers when a tap event occurs + val identifyResult = mapView.identifyLayers( + screenCoordinate = it.screenCoordinate, + tolerance = 12.0, + returnPopupsOnly = false, + maximumResults = 10 + ) + mapViewModel.handleIdentifyResult(identifyResult) + } + } +} + +/** + * Create the MapView instance and add it to the Activity lifecycle + */ +@Composable +fun createMapViewInstance(lifecycleOwner: LifecycleOwner): MapView { + // create the MapView + val mapView = MapView(LocalContext.current) + // add the side effects for MapView composition + DisposableEffect(lifecycleOwner) { + lifecycleOwner.lifecycle.addObserver(mapView) + onDispose { + lifecycleOwner.lifecycle.removeObserver(mapView) + } + } + return mapView +} diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt new file mode 100644 index 000000000..e1b216712 --- /dev/null +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt @@ -0,0 +1,147 @@ +/* Copyright 2023 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.sample.identifylayerfeatures.components + +import android.app.Application +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.AndroidViewModel +import com.arcgismaps.data.ServiceFeatureTable +import com.arcgismaps.geometry.Point +import com.arcgismaps.geometry.SpatialReference +import com.arcgismaps.mapping.ArcGISMap +import com.arcgismaps.mapping.BasemapStyle +import com.arcgismaps.mapping.Viewpoint +import com.arcgismaps.mapping.layers.ArcGISMapImageLayer +import com.arcgismaps.mapping.layers.FeatureLayer.Companion.createWithFeatureTable +import com.arcgismaps.mapping.view.IdentifyLayerResult +import com.esri.arcgismaps.sample.identifylayerfeatures.R +import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +class MapViewModel( + application: Application, + private val sampleCoroutineScope: CoroutineScope +) : AndroidViewModel(application) { + // set the MapView mutable stateflow + val mapViewState = MutableStateFlow(MapViewState()) + + // create a ViewModel to handle dialog interactions + val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel() + + // string text to display the identify layer results + val bottomTextBanner = mutableStateOf("Tap on the map to identify feature layers") + + init { + // create a feature layer of damaged property data + val featureTable = ServiceFeatureTable(application.getString(R.string.damage_assessment)) + val featureLayer = createWithFeatureTable(featureTable) + + // create a layer with world cities data + val mapImageLayer = ArcGISMapImageLayer(application.getString(R.string.world_cities)) + sampleCoroutineScope.launch { + mapImageLayer.load().onSuccess { + mapImageLayer.apply { + subLayerContents.value[1].isVisible = false + subLayerContents.value[2].isVisible = false + } + }.onFailure { error -> + // show the message dialog and pass the error message to be displayed in the dialog + messageDialogVM.showMessageDialog(error.message.toString(), error.cause.toString()) + } + } + + // create a topographic map + val map = ArcGISMap(BasemapStyle.ArcGISTopographic).apply { + // add world cities layer + operationalLayers.add(mapImageLayer) + // add damaged property data + operationalLayers.add(featureLayer) + } + // assign the map to the map view + mapViewState.value.arcGISMap = map + } + + /** + * Identify the feature layer results and display the resulting information + */ + fun handleIdentifyResult(result: Result>) { + sampleCoroutineScope.launch { + result.onSuccess { identifyResultList -> + val message = StringBuilder() + var totalCount = 0 + identifyResultList.forEach { identifyLayerResult -> + val geoElementsCount = geoElementsCountFromResult(identifyLayerResult) + val layerName = identifyLayerResult.layerContent.name + message.append(layerName).append(": ").append(geoElementsCount) + + // add new line character if not the final element in array + if (identifyLayerResult != identifyResultList[identifyResultList.size - 1]) { + message.append("\n") + } + totalCount += geoElementsCount + } + // if any elements were found show the results, else notify user that no elements were found + if (totalCount > 0) { + bottomTextBanner.value = "Number of elements found:\n${message}" + } else { + bottomTextBanner.value = "Number of elements found: N/A" + messageDialogVM.showMessageDialog( + title = "No element found", + description = "Tap an area on the map with visible features" + ) + } + }.onFailure { error -> + messageDialogVM.showMessageDialog( + title = "Error identifying results: ${error.message.toString()}", + description = error.cause.toString() + ) + } + } + } + + /** + * Gets a count of the GeoElements in the passed result layer. + * This method recursively calls itself to descend into sublayers and count their results. + * @param result from a single layer. + * @return the total count of GeoElements. + */ + private fun geoElementsCountFromResult(result: IdentifyLayerResult): Int { + var subLayerGeoElementCount = 0 + for (sublayerResult in result.sublayerResults) { + // recursively call this function to accumulate elements from all sublayers + subLayerGeoElementCount += geoElementsCountFromResult(sublayerResult) + } + return subLayerGeoElementCount + result.geoElements.size + } +} + +/** + * Data class that represents the MapView state + */ +data class MapViewState( + var arcGISMap: ArcGISMap = ArcGISMap(BasemapStyle.ArcGISNavigationNight), + var viewpoint: Viewpoint = Viewpoint( + center = Point( + x = -10977012.785807, + y = 4514257.550369, + spatialReference = SpatialReference(wkid = 3857) + ), + scale = 68015210.0 + ) +) diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt new file mode 100644 index 000000000..e81ad166d --- /dev/null +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt @@ -0,0 +1,86 @@ +/* Copyright 2023 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.sample.identifylayerfeatures.screens + +import android.app.Application +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.esri.arcgismaps.sample.identifylayerfeatures.components.ComposeMapView +import com.esri.arcgismaps.sample.identifylayerfeatures.components.MapViewModel +import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog +import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar + +/** + * Main screen layout for the sample app + */ +@Composable +fun MainScreen(sampleName: String, application: Application) { + // coroutineScope that will be cancelled when this call leaves the composition + val sampleCoroutineScope = rememberCoroutineScope() + // create a ViewModel to handle MapView interactions + val mapViewModel = remember { MapViewModel(application, sampleCoroutineScope) } + + Scaffold( + topBar = { SampleTopAppBar(title = sampleName) }, + content = { + Column( + modifier = Modifier + .fillMaxSize() + .padding(it) + ) { + // composable function that wraps the MapView + ComposeMapView( + modifier = Modifier + .fillMaxSize() + .weight(1f) + .animateContentSize(), + mapViewModel = mapViewModel + ) + // Bottom text to display the identify results + Row( + modifier = Modifier + .padding(12.dp) + .fillMaxWidth() + .animateContentSize() + ) { + Text(text = mapViewModel.bottomTextBanner.value) + } + // display a dialog if the sample encounters an error + mapViewModel.messageDialogVM.apply { + if (dialogStatus) { + MessageDialog( + title = messageTitle, + description = messageDescription, + onDismissRequest = ::dismissDialog + ) + } + } + } + } + ) +} diff --git a/identify-layer-features/src/main/res/drawable-v24/ic_launcher_foreground.xml b/identify-layer-features/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..c7bd21dbd --- /dev/null +++ b/identify-layer-features/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/identify-layer-features/src/main/res/drawable/ic_launcher_background.xml b/identify-layer-features/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..6d8cae103 --- /dev/null +++ b/identify-layer-features/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..6b78462d6 --- /dev/null +++ b/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..6b78462d6 --- /dev/null +++ b/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..a2f590828 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..1b5239980 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..ff10afd6e Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..115a4c768 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..dcd3cd808 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..459ca609d Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..8ca12fe02 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..8e19b410a Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..b824ebdd4 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..4c19a13c2 Binary files /dev/null and b/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/identify-layer-features/src/main/res/values/strings.xml b/identify-layer-features/src/main/res/values/strings.xml new file mode 100644 index 000000000..5a6b8c777 --- /dev/null +++ b/identify-layer-features/src/main/res/values/strings.xml @@ -0,0 +1,9 @@ + + Identify layer features + + https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0 + + + https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer + +