diff --git a/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentElementState.kt b/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentElementState.kt index 53b44cfd7..9fb0e10b7 100644 --- a/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentElementState.kt +++ b/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentElementState.kt @@ -302,6 +302,13 @@ internal class FormAttachmentState( */ val thumbnail: State = _thumbnail + /** + * The maximum attachment size in bytes that can be loaded. If [size] is greater than this limit, + * then the attachment will fail to load with an [AttachmentSizeLimitExceededException] when + * [load] is called. + */ + val maxAttachmentSize = 50_000_000L + /** * The size of the thumbnail image. */ @@ -327,12 +334,11 @@ internal class FormAttachmentState( _loadStatus.value = LoadStatus.Loading var result = Result.success(Unit) try { - if (formAttachment == null) { - result = Result.failure(Exception("Form attachment is null")) - } else { - formAttachment.retryLoad().onFailure { - result = Result.failure(it) - }.onSuccess { + result = when { + formAttachment == null -> Result.failure(IllegalStateException("Form attachment is null")) + formAttachment.size == 0L -> Result.failure(EmptyAttachmentException()) + formAttachment.size > maxAttachmentSize -> Result.failure(AttachmentSizeLimitExceededException(maxAttachmentSize)) + else -> formAttachment.retryLoad().onSuccess { createThumbnail() } } @@ -403,6 +409,9 @@ internal class FormAttachmentState( else -> null } } catch (ex: Exception) { + if (ex is CancellationException) { + throw ex + } null } } @@ -521,3 +530,15 @@ internal fun AttachmentElementState.getNewAttachmentNameForContentType( } return "${prefix}$count.$extension" } + +/** + * Exception indicating that the attachment size exceeds the maximum limit. + * + * @param limit The maximum attachment size limit in bytes. + */ +internal class AttachmentSizeLimitExceededException(val limit : Long) : Exception("Attachment size exceeds the maximum limit of $limit MB") + +/** + * Exception indicating that the attachment size is 0. + */ +internal class EmptyAttachmentException : Exception("Attachment size is 0") diff --git a/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentTile.kt b/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentTile.kt index 8c0c377eb..8b6a88c29 100644 --- a/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentTile.kt +++ b/toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentTile.kt @@ -184,7 +184,9 @@ internal fun AttachmentTile( ) ) } - }) + }, + enabled = state.size <= state.maxAttachmentSize + ) DropdownMenuItem( text = { Text(text = stringResource(R.string.delete)) }, leadingIcon = { @@ -215,8 +217,11 @@ internal fun AttachmentTile( delay(configuration.longPressTimeoutMillis) wasALongPress = true // handle long press - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - showContextMenu = true + if (state.size > 0) { + // show context menu only if the attachment is not empty + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + showContextMenu = true + } } is PressInteraction.Release -> { @@ -226,11 +231,6 @@ internal fun AttachmentTile( if (loadStatus is LoadStatus.NotLoaded || loadStatus is LoadStatus.FailedToLoad) { // load attachment state.loadWithParentScope() - if (state.size == 0L) { - // show an error toast if the attachment is empty since the load - // will likely fail - Toast.makeText(context, context.getString(R.string.download_empty_file), Toast.LENGTH_SHORT).show() - } } else if (loadStatus is LoadStatus.Loaded) { // open attachment val intent = Intent() @@ -257,6 +257,29 @@ internal fun AttachmentTile( } } } + LaunchedEffect(Unit) { + state.loadStatus.collectLatest { + // show an error toast if the attachment failed to load + if (it is LoadStatus.FailedToLoad) { + val message = when(it.error) { + is AttachmentSizeLimitExceededException -> { + val limit = (it.error as AttachmentSizeLimitExceededException).limit + val limitFormatted = Formatter.formatFileSize(context, limit) + context.getString(R.string.attachment_size_limit_exceeded, limitFormatted) + } + + is EmptyAttachmentException ->{ + context.getString(R.string.download_empty_file) + } + + else ->{ + it.error.localizedMessage + } + } + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + } + } } @Composable diff --git a/toolkit/featureforms/src/main/res/values/strings.xml b/toolkit/featureforms/src/main/res/values/strings.xml index 2c358987d..6452a8fef 100644 --- a/toolkit/featureforms/src/main/res/values/strings.xml +++ b/toolkit/featureforms/src/main/res/values/strings.xml @@ -116,4 +116,5 @@ Empty files are not supported Empty files cannot be downloaded %1s was deleted successfully + Attachments larger than %1$s cannot be downloaded