Attachment¶
The attachment allows you to select files or capture images and video, enabling the application to access them for post-processing, such as uploading to a server.
The attachment has two modes of operation: edit mode and view mode.
Edit mode allows you to remove existing attachments or add new attachments to the attachment. Existing attachments are displayed in a list view and different UI elements, such as Add and Remove icon buttons, are provided for updating the attachments. Tapping the Add button shows a variety of preconfigured methods for adding files.

In view mode (also known as "non-edit" mode), you can only browse through the existing attachments and cannot add or remove attachments from the attachment. View mode provides the option to display attachments in either list or grid view. It also allows you to open the files using different applications.

Using the Attachment¶
The attachment can be used in your app like any traditional composable function:
var editable by rememberSaveable { mutableStateOf(false) }
val actions = mutableListOf<FioriAttachmentAction>()
actions.add(FioriAttachmentActionSelectPicture(processor = singleFilePickActionProcessor))
actions.add(
FioriAttachmentActionSelectFile(
processor = FioriAttachmentActionProcessorBuilder.buildSelectMultiFilesActionProcessor()
)
)
actions.add(
FioriAttachmentActionSelectDocuments(
processor = singleFilePickActionProcessor,
appendDivider = true
)
)
actions.add(
FioriAttachmentActionTakeVideo(
processor = FioriAttachmentActionProcessorBuilder.buildCaptureVideoActionProcessor(
uri = {
FileProvider.getUriForFile(
context,
LocalContext.current.packageName + ".attachment.provider",
File(
(context.getExternalFilesDir("Videos") ?: context.cacheDir).path.plus("/VID_${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))}.mp4"
)
)
)
}
)
)
)
actions.add(
FioriAttachmentActionTakePicture(
processor = FioriAttachmentCapturePictureActionProcessor(
uri = {
FileProvider.getUriForFile(
context,
LocalContext.current.packageName + ".attachment.provider",
File(
(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES
) ?: context.cacheDir).path.plus("/JPEG_${LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))
}.jpg"
)
)
)
},
operator = defaultFioriAttachmentActionCapturePictureOperator
))
)
// Sample to use saveable list to keep the attachments
val attachments = rememberSaveableFioriAttachmentItemList(attachmentItems = mutableListOf())
FioriAttachment(
editable = editable,
actions = actions,
attachments = attachments,
onAttachmentsAdd = { items ->
attachments.addAll(items)
},
onAttachmentItemDelete = {
attachments.remove(it)
},
)
// Sample code to use ViewModel to keep all attachments
val viewModel = viewModel<FioriAttachmentViewModel>(
factory = FioriAttachmentViewModelFactory(
actionRepository = DefaultFioriAttachmentActionRepository(
actions.toMutableList()
),
attachmentRepository = DefaultFioriAttachmentItemRepository(
rememberSaveableFioriAttachmentItemList(attachmentItems = listOf())
)
)
)
FioriAttachment(
editable = editable,
viewModel = viewModel,
onAttachmentsAdd = { items ->
viewModel.addAttachments(items)
},
onAttachmentItemDelete = { _, item ->
viewModel.removeAttachment(item)
}
)
Listening To Changes¶
The attachment supports five callbacks, onAttachmentsChange, onAttachmentsAdd, onAttachmentItemDelete, onAttachmentItemClick and onAttachmentItemLongClick, to notify the application about updates in the attachments.
- The
onAttachmentsChangecallback notifies you when a file resource was added or removed from the attachment. You don't need to maintain the list ofFioriAttachmentItemin the callback. This callback will be invoked when maintaining additions and deletions of attachments in the correspondingonAttachmentsAddandonAttachmentItemDeletecallback. - The
onAttachmentsAddcallback notifies you when an attached file resource is added. You should maintain a mutable list ofFioriAttachmentItemin this callback to keep track of added attachments. - The
onAttachmentItemDeletecallback notifies you when a file resource is removed from the attachment. Similarly to theonAttachmentsAddcallback, you should maintain a mutable list ofFioriAttachmentItemwithin this callback to track deletions of attachments. - The
onAttachmentItemClickcallback notifies you when an attached file resource has been clicked. By default, the attachment will open the file resource using a suitable third-party application. However, you can override the behavior to returnfalsein the callback if, for example, for security, you don't want to open the attached file resource. Then, you can returnfalsein the callback. - The
onAttachmentItemLongClickcallback notifies you when an attached file resource has been long-clicked.
var editable by rememberSaveable { mutableStateOf(false) }
val context = LocalContext.current
val multiFileProcessor = FioriAttachmentActionProcessorBuilder.buildSelectMultiFilesActionProcessor()
val actions = listOf(
FioriAttachmentActionSelectFile(
processor = multiFileProcessor
)
)
// Sample code to use saveble mutable-list to keep all attachments
val attachments = rememberSaveableFioriAttachmentItemList(attachmentItems = listOf())
FioriAttachment(
editable = editable,
actions = actions,
attachments = attachments,
onAttachmentsChange = {},
onAttachmentsAdd = { items ->
attachments.addAll(items)
},
onAttachmentItemDelete = {
attachments.remove(it)
},
onAttachmentItemClick = { _, item ->
Toast.makeText(
context,
"Opening file '${item.getFileName(context)}'", Toast.LENGTH_SHORT).show()
true
},
onAttachmentItemLongClick = { _, item ->
Toast.makeText(
context,
"Long Clicking file '${item.getFileName(context)}'", Toast.LENGTH_SHORT).show()
}
)
// Sample code to use ViewModel to keep all attachments
val viewModel = viewModel<FioriAttachmentViewModel>(
factory = FioriAttachmentViewModelFactory(
actionRepository = DefaultFioriAttachmentActionRepository(
actions.toMutableList()
),
attachmentRepository = DefaultFioriAttachmentItemRepository(
rememberSaveableFioriAttachmentItemList(attachmentItems = listOf())
)
)
)
FioriAttachment(
editable = editable,
viewModel = viewModel,
onAttachmentsChange = {},
onAttachmentsAdd = { items ->
viewModel.addAttachments(items)
},
onAttachmentItemDelete = { _, item ->
viewModel.removeAttachment(item)
},
onAttachmentItemClick = { _, item ->
Toast.makeText(
context,
"Opening file '${item.getFileName(context)}'", Toast.LENGTH_SHORT).show()
true
},
onAttachmentItemLongClick = { _, item ->
Toast.makeText(
context,
"Long Clicking file '${item.getFileName(context)}'", Toast.LENGTH_SHORT).show()
}
)
Attachment Action¶
In order to attach files to the attachment, you should be able to:
- Pick a variety of files, such as PDFs, documents, images, or zip files
- Pick the files from a variety of sources, such as Google Photos, Google Drive, Dropbox, or local storage
- Pick files using different mechanisms, such as an attachment from the Camera or storage
As an app developer you may want to configure the attachment to allow only specific types of attachments. To fulfill these kinds of requirements, attachment depends
on the FioriAttachmentAction class.
FioriAttachmentAction defines the type of file and method to pick those files.
For example, FioriAttachmentActionTakeVideo allows you to capture video using the camera and attach that to the attachment. Similarly FioriAttachmentActionSelectDocuments allows you to pick PDF documents. There are multiple different predefined AttachmentActions provided with the library:
| Attachment Action | Description |
|---|---|
FioriAttachmentActionTakeVideo |
Opens the camera and allows the user to capture video and attach it to the attachment |
FioriAttachmentActionTakePicture |
Opens the camera and allows the user to capture an image and attach it to the attachment |
FioriAttachmentActionSelectVideo |
Allows the user to select video from different sources and attach it to the attachment |
FioriAttachmentActionSelectPicture |
Allows the user to select pictures from different sources and attach them to the attachment |
FioriAttachmentActionSelectMedia |
Allows the user to select media files (images, videos, and audio files) from different sources and attach them to the attachment |
FioriAttachmentActionSelectDocuments |
Allows the user to select PDF files from different sources and attach them to the attachment |
FioriAttachmentActionSelectFile |
Allows the user to select any file format from different sources and attach it to the attachment |
FioriAttachmentActionProcessor¶
The FioriAttachmentActionProcessor is mandatory for every FioriAttachmentAction. This open class was designed to encapsulate the logic for selecting the file resources to be attached. There are four predefined FioriAttachmentActionProcessor classes: FioriAttachmentSelectSingleFileActionProcessor, FioriAttachmentSelectMultiFilesActionProcessor, FioriAttachmentCapturePictureActionProcessor, and FioriAttachmentCaptureVideoActionProcessor.
However, you have the option to either implement a concrete class of FioriAttachmentActionProcessor and encapsulate your own logic within the runAction method, or initialize a FioriAttachmentActionProcessor with the operator parameter. This parameter should be a composable callback function that implements your specific logic, especially if your logic can only run within a composable function.
FioriAttachmentSelectSingleFileActionProcessorallows you to select one single file resource and should be used by predefinedFioriAttachmentActionSelectVideo,FioriAttachmentActionSelectPicture,FioriAttachmentActionSelectMedia,FioriAttachmentActionSelectDocuments, andFioriAttachmentActionSelectFile.FioriAttachmentSelectMultiFilesActionProcessorallows you to select multiple file resources and should be used by predefinedFioriAttachmentActionSelectVideo,FioriAttachmentActionSelectPicture,FioriAttachmentActionSelectMedia,FioriAttachmentActionSelectDocuments, andFioriAttachmentActionSelectFile.FioriAttachmentCapturePictureActionProcessorallows you to take a picture and should be used byFioriAttachmentActionTakePicture.FioriAttachmentCaptureVideoActionProcessorallows you to take a video and should be used byFioriAttachmentActionTakeVideo.
FioriAttachmentActionTakePicture¶
FioriAttachmentActionTakePicture is a special Attachment action. It allows you to capture an image and attach it to the attachment. You can find the details about how to capture an image using the camera here.
In a very simplified version, to capture an image you need to trigger an implicit intent using an Intent action MediaStore.ACTION_IMAGE_CAPTURE. The triggered Intent opens the Camera activity, which saves the captured image to local storage and returns the control back to the calling Activity.
In order to store the captured image to the local storage, the application requires access to the specific location in local storage. You can provide access by creating a FileProvider. Each FileProvider exposes a specific location and an authority string.
Apps that have access to the authority string can access the location associated with the FileProvider.
FioriAttachmentActionTakePicture is designed to hide these complexities in capturing images and simplifies the process of attaching the captured image to the attachment. However, in order to store the captured image, the attachment also needs access to storage. In other words, it requires the authority of the FileProvider. See How to create a FileProvider for more information.
Below is an example of how to declare a FileProvider that provides access to the Pictures and Videos directory and exposes an authority, com.sap.cloud.mobile.fiori.horizon.composedemo.attachment.provider.
In the AndroidManifest.xml file:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.sap.cloud.mobile.fiori.horizon.composedemo.attachment.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/demo_capture_path"/>
</provider>
Resource file: demo_capture_path, used in the FileProvider declaration above.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="fiori_attachment_capture_image_file_path"
path="Android/data/com.sap.cloud.mobile.fiori.horizon.composedemo/files/Pictures" />
<external-path
name="fiori_attachment_capture_video_file_path"
path="Android/data/com.sap.cloud.mobile.fiori.horizon.composedemo/files/Videos" />
</paths>
Once you have correctly configured the FileProvider, you can instantiate the FioriAttachmentActionTakePicture object using the FioriAttachmentCapturePictureActionProcessor object.
To create a FioriAttachmentCapturePictureActionProcessor object, you can initialize it with a composable callback that returns a file path where the captured picture is stored, and defaultFioriAttachmentActionCapturePictureOperator as the operator. Alternatively, you can use the FioriAttachmentActionProcessorBuilder object, which offers the buildCapturePictureActionProcessor method. See the following code snippet:
FioriAttachmentActionTakePicture(
// Get the FioriAttachmentCapturePictureActionProcessor via the FioriAttachmentActionProcessorBuilder.buildCapturePictureActionProcessor method
processor = FioriAttachmentActionProcessorBuilder.buildCapturePictureActionProcessor(
uri = {
FileProvider.getUriForFile(
context,
LocalContext.current.packageName + ".attachment.provider",
File(
(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES
) ?: context.cacheDir).path.plus("/JPEG_${LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))
}.jpg"
)
))
}
)
)
FioriAttachmentActionTakePicture(
// Initialize the FioriAttachmentCapturePictureActionProcessor with defaultFioriAttachmentActionCapturePictureOperator
processor = FioriAttachmentCapturePictureActionProcessor(
uri = {
FileProvider.getUriForFile(
context,
LocalContext.current.packageName + ".attachment.provider",
File(
(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES
) ?: context.cacheDir).path.plus("/JPEG_${LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))
}.jpg"
)
))
},
operator = defaultFioriAttachmentActionCapturePictureOperator
)
)
FioriAttachmentActionTakeVideo¶
Similar to FioriAttachmentActionTakePicture, FioriAttachmentActionTakeVideo allows you to capture a video and attach it to the attachment. The FileProvider is required here for the FioriAttachmentActionTakeVideo. You can initialize it with a composable callback that returns a file path where the captured picture is stored, and defaultFioriAttachmentActionCaptureVideoOperator as the operator. Alternatively, you can use the FioriAttachmentActionProcessorBuilder.buildCaptureVideoActionProcessor method to create the FioriAttachmentCaptureVideoActionProcessor object for FioriAttachmentActionTakeVideo. See the following code snippet:
FioriAttachmentActionTakeVideo(
// Get the FioriAttachmentCaptureVideoActionProcessor via the FioriAttachmentActionProcessorBuilder.buildCaptureVideoActionProcessor method
processor = FioriAttachmentActionProcessorBuilder.buildCaptureVideoActionProcessor(
uri = {
FileProvider.getUriForFile(
context,
LocalContext.current.packageName + ".attachment.provider",
File(
(context.getExternalFilesDir("Videos") ?: context.cacheDir).path.plus("/VID_${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))}.mp4"
)
)
)
}
)
)
FioriAttachmentActionTakeVideo(
// Initialize the FioriAttachmentCaptureVideoActionProcessor with defaultFioriAttachmentActionCaptureVideoOperator
processor = FioriAttachmentCaptureVideoActionProcessor(
uri = {
FileProvider.getUriForFile(
context,
LocalContext.current.packageName + ".attachment.provider",
File(
(context.getExternalFilesDir("Videos") ?: context.cacheDir).path.plus("/VID_${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))}.mp4"
)
)
)
},
operator = defaultFioriAttachmentActionCaptureVideoOperator
)
)
FioriAttachmentProgrammaticAction¶
The FioriAttachmentProgrammaticAction allows you to set attachments programmatically. You can either implement a concrete class of FioriAttachmentActionProcessor and encapsulate your logic within the runAction method, or you can initialize a FioriAttachmentActionProcessor with the operator parameter. This parameter should be a composable callback function that implements your specific logic. See the following code snippet:
private fun lookupCapturedMediaFiles(context: Context, action: FioriAttachmentAction) {
val directory = context.getExternalFilesDir("/")
directory?.let {
val files = directory.listFiles()
files?.let {
val attachments = mutableListOf<FioriAttachmentItem>()
for (file in files) {
if (file.isDirectory && file.endsWith(Environment.DIRECTORY_PICTURES)) {
val pictures = file.listFiles()
pictures?.let {
for (pic in pictures) {
val uri = FileProvider.getUriForFile(
context,
context.packageName + ".attachment.provider",
pic,
)
attachments.add(FioriAttachmentItem(uri))
}
}
} else if (file.isDirectory && file.endsWith("Videos")) {
val videos = file.listFiles()
videos?.let {
for (video in videos) {
val uri = FileProvider.getUriForFile(
context,
context.packageName + ".attachment.provider",
video,
)
attachments.add(FioriAttachmentItem(uri))
}
}
}
}
if (attachments.isNotEmpty()) resultHandler.onResultAttach(attachments)
}
}
}
class DemoAttachmentProgrammaticActionProcessor : FioriAttachmentActionProcessor() {
override fun runAction(context: Context, action: FioriAttachmentAction) {
lookupCapturedMediaFiles(context = context, action = action)
}
}
// initialize FioriAttachmentProgrammaticAction with concrete class of FioriAttachmentActionProcessor
FioriAttachmentProgrammaticAction(
processor = DemoAttachmentProgrammaticActionProcessor(),
requiredPermissions = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
listOf(
Manifest.permission.READ_EXTERNAL_STORAGE
)
else
listOf()
)
// initialize FioriAttachmentProgrammaticAction with an instance of FioriAttachmentActionProcessor
FioriAttachmentProgrammaticAction(
processor = FioriAttachmentActionProcessor(
operator = { action, finish ->
val context = LocalContext.current
lookupCapturedMediaFiles(context = context, action = action)
finish()
},
),
requiredPermissions = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
listOf(
Manifest.permission.READ_EXTERNAL_STORAGE
)
else
listOf()
)
To demonstrate this feature, we implement a concrete class, DemoAttachmentProgrammaticActionProcessor, of FioriAttachmentActionProcessor. It will select all image and video files from the path declared in the preceding demo_capture_path.xml and attach them to the attachment using the resultHandler.onResultAttach(attachments) method. We also assume that the application has read permission for Manifest.permission.READ_EXTERNAL_STORAGE. The permission is set using the requiredPermissions parameter of the FioriAttachmentProgrammaticAction.
Customization¶
The attachment offers several methods for customization using FioriAttachmentDefaults. You can use it to customize the attachment's colors, text styles, height and width, etc.
Action¶
Also, you may want to customize the predefined FioriAttachmentAction. If, for example, you want the FioriAttachmentActionSelectMedia to work with audio, image, and video files, you can set the mimeType parameter as arrayOf("image/*", "video/*", "audio/*") for the FioriAttachmentActionSelectMedia. You may also customize the icon and label for FioriAttachmentActionSelectMedia See the following code snippet:
FioriAttachmentActionSelectMedia(
processor = FioriAttachmentActionProcessorBuilder.buildSelectMultiFilesActionProcessor(),
icon = FioriIcon(resId = R.drawable.ic_sap_icon_notes),
label = "Attache Medias",
mimeType = arrayOf("image/*", "video/*", "audio/*")
)
Additionally, you may want to design your own AttachmentActions. If, for example, you want to create a FioriAttachmentAction named PhotoPickerAttachmentAction that can use Android Photo picker to select the image and video files, you would need to create a subtype of FioriAttachmentAction class and FioriAttachmentActionProcessor class. See the following code snippet:
class PhotoPickerAttachmentAction(processor: PhotoPickerAttachmentActionProcessor) :
FioriAttachmentAction(
icon = FioriIcon(resId = R.drawable.ic_photo_camera_black_24dp),
label = "Photo Picker",
processor = processor
)
class PhotoPickerAttachmentActionProcessor(
resultHandler: FioriAttachmentActionResultHandler,
val launcher: ManagedActivityResultLauncher<PickVisualMediaRequest, List<Uri>>
) : FioriAttachmentActionProcessor(
resultHandler = resultHandler
) {
override fun runAction(context: Context, action: FioriAttachmentAction) =
launcher.launch(PickVisualMediaRequest())
}
fun buildPhotoPickerAttachmentActionProcessor(
resultHandler: FioriAttachmentActionResultHandler = FioriAttachmentActionResultHandler()
): PhotoPickerAttachmentActionProcessor =
PhotoPickerAttachmentActionProcessor(
resultHandler = resultHandler,
launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickMultipleVisualMedia()
) {
if (it.isNotEmpty()) {
val items = mutableListOf<FioriAttachmentItem>()
for (uri in it) {
items.add(FioriAttachmentItem(uri))
}
resultHandler.onResultAttach(items)
}
}
)
PhotoPickerAttachmentAction(processor = buildPhotoPickerAttachmentActionProcessor())
Attachment Item¶
The FioriAttachmentItem is designed to represent an attachment. It provides a set of properties and a range of methods that allow for easy customization.
In terms of display format, by default only the file resource name is displayed. However, you can customize this to show additional information about the file, such as its size, creation date, or any other relevant details. To do this, you can use the FioriAttachmentActionResultHandler class. You will need to create a concrete class based on FioriAttachmentActionResultHandler, which will allow you to customize the FioriAttachmentItem. Once this is complete, set it to the corresponding FioriAttachmentActionProcessor. Furthermore, you can tailor the FioriAttachmentActionResultHandler to verify the file size and format before attaching, set a progress loading indicator for attaching resources, or even replace the default thumbnail icon for the attachment resource. See the following code snippet:
private class CustomerAttachmentResultHandler : FioriAttachmentActionResultHandler() {
override fun onResultFormat(uri: Uri, context: Context): FioriAttachmentItem? {
val item = super.onResultFormat(uri, context)
item?.let {
item.secondaryInfo = if (item.file.mimeType().contains("pdf"))
item.file.extension()
.plus(", ${item.file.formattedFileSize()}") else item.file.formattedFileSize() // set the file size as the tertiary information
tertiaryInfo = itemFile.formattedFileDate() // set the creation data of file as the tertiary information
if (item.file.extension().contains("zip") && item.file.fileSize() > (4 * 1024 * 1024)) verify the file size and format for attaching
return null
item.updateDisplayLoadingIndicator(true) // set progress loading indicator
}
return item
}
}
private class CustomThumbnailAttachmentResultHandler : FioriAttachmentActionResultHandler() {
override fun onResultFormat(uri: Uri, context: Context): FioriAttachmentItem? {
val itemFile = DefaultFioriAttachmentItemFile(uri)
itemFile.parse(context)
return FioriAttachmentItem(file = itemFile, thumbnailHandler = object :
DefaultFioriAttachmentItemThumbnailHandler() {
@Composable
override fun getThumbnail(
itemFile: FioriAttachmentItemFile,
viewModeType: FioriAttachmentViewModeType,
colors: FioriAttachmentColors
): FioriImage = if (itemFile.mimeType().contains(documentMimeType))
FioriImage(
resId = R.drawable.ic_sap_icon_measurement_document,
color = colors.attachmentItemThumbnailTintColor().value,
contentDescription = getThumbnailContentDescription(itemFile)
)
else super.getThumbnail(itemFile, viewModeType, colors)
})
}
}

Additionally, you can control the display of a loading indicator in an attachment by setting the displayLoadingIndicator property of the FioriAttachmentItem during initialization. Alternatively, you can use the updateDisplayLoadingIndicator method to update the status of the loading indicator.
Similarly, the enableDeleteAction property controls the enablement of the delete icon button for an attachment. You can also use the updateEnableDeleteAction method to change its status. The delete icon supports additional customizations: you can configure a custom delete icon for all attachments by setting the deleteActionIcon parameter via the FioriAttachmentDefaults.styles method. For individual attachments, you can specify the deleteIcon property of the FioriAttachmentItem to customize the icon.
The annotatedSecondaryInfo property stores an instance of FioriAttachmentItemAnnotationInfo, which presents styled information for an attachment. It takes precedence over the secondaryInfo property. For example, if both annotatedSecondaryInfo and secondaryInfo are set, the value of annotatedSecondaryInfo will be displayed. You can control when to display the value of annotatedSecondaryInfo by setting the corresponding values for displayOnlyInEditMode, displayInViewListLayout, and displayInViewGridLayout properties of the FioriAttachmentItemAnnotationInfo.
Limit Attachment¶
The attachment control allows you to limit the maximum number of attachments. You can set this limit using the maxAttachmentsNumber parameter of the FioriAttachmentDefaults.styles method. Once the maximum is reached, the Add button will be disabled in edit mode. It’s important to ensure that the number of attachments does not exceed this limit, as doing so may trigger an IllegalArgumentException and potentially crash the app.
You can display a subset of attachments by setting the visibleConfiguration parameter of the attachment control. This parameter accepts an instance of FioriAttachmentVisibleConfiguration, allowing you to specify the number of attachments to display. Only the specified number of attachments will be displayed, and a button will be provided to expand or collapse the attachments list. See the following code snippet:
val maxAttachmentsNumber = 6
val multiFileProcessor = FioriAttachmentActionProcessorBuilder.buildSelectMultiFilesActionProcessor()
val actions = listOf(FioriAttachmentActionSelectFile(processor = multiFileProcessor))
val attachments = rememberSaveableFioriAttachmentItemList(listOf())
val label = rememberSaveable { mutableStateOf("") }
label.value = "${attachments.size} out of $maxAttachmentsNumber attachments uploaded." +
"You can add up to $maxAttachmentsNumber attachments."
FioriAttachment(
verticalScrollable = false,
headerLabelMsg = label.value,
actions = actions,
attachments = attachments,
visibleConfiguration = (3),
onAttachmentsAdd = { items ->
val addedResults = items.reversed().subList(
0,
min(maxAttachmentsNumber.minus(attachments.size), items.size)
)
if (addedResults.isNotEmpty()) attachments.addAll(0, addedResults)
},
onAttachmentItemDelete = {
attachments.remove(it)
},
styles = FioriAttachmentDefaults.styles(
maxAttachmentsNumber = maxAttachmentsNumber
)
)
