View on GitHub Sample viewer app

Add, delete, and download attachments for features from a service.

Image of edit feature attachments

Use case

Attachments provide a flexible way to manage additional information that is related to your features. Attachments allow you to add files to individual features, including: PDFs, text documents, or any other type of file. For example, if you have a feature representing a building, you could use attachments to add multiple photographs of the building taken from several angles, along with PDF files containing the building’s deed and tax information.

How to use the sample

Tap a feature on the map to open a bottom sheet displaying the number of attachments. Select an entry from the list to download and view the attachment in the gallery. Tap on the add attachment button to add an attachment or long press to delete.

How it works

  1. Create a ServiceFeatureTable from a URL.
  2. Create a FeatureLayer object from the service feature table.
  3. Select features from the feature layer with selectFeature.
  4. To fetch the feature’s attachments, cast to an ArcGISFeature and use ArcGISFeature.fetchAttachments().
  5. To add an attachment to the selected ArcGISFeature, create an attachment and use ArcGISFeature.addAttachment().
  6. To delete an attachment from the selected ArcGISFeature, use the ArcGISFeature.deleteAttachment().
  7. After a change, apply the changes to the server using ServiceFeatureTable.applyEdits().

Relevant API

  • ArcGISFeature.deleteAttachment
  • ArcGISFeature.fetchAttachments
  • Attachment.fetchData
  • FeatureLayer
  • ServiceFeatureTable
  • ServiceFeatureTable.applyEdits
  • ServiceFeatureTable.updateFeature

Additional information

Attachments can only be added to and accessed on service feature tables when their hasAttachments property is true.

Tags

edit and manage data, image, jpeg, pdf, picture, png, txt

Sample Code

AttachmentsBottomSheet.kt AttachmentsBottomSheet.kt MainActivity.kt
package com.esri.arcgismaps.sample.editfeatureattachments
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import com.arcgismaps.data.Attachment
import com.esri.arcgismaps.sample.editfeatureattachments.databinding.AttachmentEditSheetBinding
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import java.util.Locale
class AttachmentsBottomSheet(
context: MainActivity,
bottomSheetBinding: AttachmentEditSheetBinding,
attachments: List<Attachment>,
damageType: String,
) : BottomSheetDialog(context) {
init {
// clear and set bottom sheet content view to layout,
// to be able to set the content view on each bottom sheet draw
if (bottomSheetBinding.root.parent != null) {
(bottomSheetBinding.root.parent as ViewGroup).removeAllViews()
}
// set the state to be an expanded sheet
behavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheetBinding.apply {
// set the selected feature's information
damageStatus.text = String.format(Locale.getDefault(),"Damage type: %s", damageType)
numberOfAttachments.text = String.format(Locale.getDefault(),"Number of attachments: %d", attachments.size)
// get the adapter to display the list of attachments
listView.adapter = AttachmentsAdapter(context, attachments)
// listener to add a new attachment
addAttachmentButton.setOnClickListener {
context.selectAttachment()
}
// listener to download and open an attachment
listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
lifecycleScope.launch {
context.fetchAttachment(
attachments[position]
)
}
}
// listener to delete an attachment
listView.onItemLongClickListener =
AdapterView.OnItemLongClickListener { _, _, position, _ ->
// create a dialog to display the delete query
MaterialAlertDialogBuilder(context).apply {
setMessage(context.getString(R.string.delete_query))
setCancelable(true)
setPositiveButton(context.getString(R.string.yes)) { dialog, _ ->
// user confirmed, delete selected attachment
context.deleteAttachment(attachments[position])
dialog.dismiss()
}
setNegativeButton(context.getString(R.string.no)) { dialog, _ ->
dialog.cancel()
}
}.show()
true
}
// set apply button to validate and apply contingency feature on map
applyTv.setOnClickListener {
this@AttachmentsBottomSheet.dismiss()
}
}
}
class AttachmentsAdapter(
private val context: Activity,
private val attachmentName: List<Attachment>,
) : ArrayAdapter<Attachment>(context, R.layout.attachment_entry, attachmentName) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: context.layoutInflater.inflate(
R.layout.attachment_entry,
parent,
false
)
val attachmentTextView = view.findViewById<TextView>(R.id.AttachmentName)
attachmentTextView.text = attachmentName[position].name
return view
}
}
}