Skip to content

Commit 7cc63c1

Browse files
authored
add attachment size restrictions for loading (#518)
1 parent 4b5606e commit 7cc63c1

File tree

3 files changed

+59
-14
lines changed

3 files changed

+59
-14
lines changed

toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentElementState.kt

+27-6
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ internal class FormAttachmentState(
302302
*/
303303
val thumbnail: State<Bitmap?> = _thumbnail
304304

305+
/**
306+
* The maximum attachment size in bytes that can be loaded. If [size] is greater than this limit,
307+
* then the attachment will fail to load with an [AttachmentSizeLimitExceededException] when
308+
* [load] is called.
309+
*/
310+
val maxAttachmentSize = 50_000_000L
311+
305312
/**
306313
* The size of the thumbnail image.
307314
*/
@@ -327,12 +334,11 @@ internal class FormAttachmentState(
327334
_loadStatus.value = LoadStatus.Loading
328335
var result = Result.success(Unit)
329336
try {
330-
if (formAttachment == null) {
331-
result = Result.failure(Exception("Form attachment is null"))
332-
} else {
333-
formAttachment.retryLoad().onFailure {
334-
result = Result.failure(it)
335-
}.onSuccess {
337+
result = when {
338+
formAttachment == null -> Result.failure(IllegalStateException("Form attachment is null"))
339+
formAttachment.size == 0L -> Result.failure(EmptyAttachmentException())
340+
formAttachment.size > maxAttachmentSize -> Result.failure(AttachmentSizeLimitExceededException(maxAttachmentSize))
341+
else -> formAttachment.retryLoad().onSuccess {
336342
createThumbnail()
337343
}
338344
}
@@ -403,6 +409,9 @@ internal class FormAttachmentState(
403409
else -> null
404410
}
405411
} catch (ex: Exception) {
412+
if (ex is CancellationException) {
413+
throw ex
414+
}
406415
null
407416
}
408417
}
@@ -521,3 +530,15 @@ internal fun AttachmentElementState.getNewAttachmentNameForContentType(
521530
}
522531
return "${prefix}$count.$extension"
523532
}
533+
534+
/**
535+
* Exception indicating that the attachment size exceeds the maximum limit.
536+
*
537+
* @param limit The maximum attachment size limit in bytes.
538+
*/
539+
internal class AttachmentSizeLimitExceededException(val limit : Long) : Exception("Attachment size exceeds the maximum limit of $limit MB")
540+
541+
/**
542+
* Exception indicating that the attachment size is 0.
543+
*/
544+
internal class EmptyAttachmentException : Exception("Attachment size is 0")

toolkit/featureforms/src/main/java/com/arcgismaps/toolkit/featureforms/internal/components/attachment/AttachmentTile.kt

+31-8
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ internal fun AttachmentTile(
184184
)
185185
)
186186
}
187-
})
187+
},
188+
enabled = state.size <= state.maxAttachmentSize
189+
)
188190
DropdownMenuItem(
189191
text = { Text(text = stringResource(R.string.delete)) },
190192
leadingIcon = {
@@ -215,8 +217,11 @@ internal fun AttachmentTile(
215217
delay(configuration.longPressTimeoutMillis)
216218
wasALongPress = true
217219
// handle long press
218-
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
219-
showContextMenu = true
220+
if (state.size > 0) {
221+
// show context menu only if the attachment is not empty
222+
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
223+
showContextMenu = true
224+
}
220225
}
221226

222227
is PressInteraction.Release -> {
@@ -226,11 +231,6 @@ internal fun AttachmentTile(
226231
if (loadStatus is LoadStatus.NotLoaded || loadStatus is LoadStatus.FailedToLoad) {
227232
// load attachment
228233
state.loadWithParentScope()
229-
if (state.size == 0L) {
230-
// show an error toast if the attachment is empty since the load
231-
// will likely fail
232-
Toast.makeText(context, context.getString(R.string.download_empty_file), Toast.LENGTH_SHORT).show()
233-
}
234234
} else if (loadStatus is LoadStatus.Loaded) {
235235
// open attachment
236236
val intent = Intent()
@@ -257,6 +257,29 @@ internal fun AttachmentTile(
257257
}
258258
}
259259
}
260+
LaunchedEffect(Unit) {
261+
state.loadStatus.collectLatest {
262+
// show an error toast if the attachment failed to load
263+
if (it is LoadStatus.FailedToLoad) {
264+
val message = when(it.error) {
265+
is AttachmentSizeLimitExceededException -> {
266+
val limit = (it.error as AttachmentSizeLimitExceededException).limit
267+
val limitFormatted = Formatter.formatFileSize(context, limit)
268+
context.getString(R.string.attachment_size_limit_exceeded, limitFormatted)
269+
}
270+
271+
is EmptyAttachmentException ->{
272+
context.getString(R.string.download_empty_file)
273+
}
274+
275+
else ->{
276+
it.error.localizedMessage
277+
}
278+
}
279+
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
280+
}
281+
}
282+
}
260283
}
261284

262285
@Composable

toolkit/featureforms/src/main/res/values/strings.xml

+1
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,5 @@
116116
<string name="attachment_is_empty">Empty files are not supported</string>
117117
<string name="download_empty_file">Empty files cannot be downloaded</string>
118118
<string name="attachment_deleted">%1s was deleted successfully</string>
119+
<string name="attachment_size_limit_exceeded">Attachments larger than %1$s cannot be downloaded</string>
119120
</resources>

0 commit comments

Comments
 (0)