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.
+
+
+
+## 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
+
+