Add multi-photo bulk upload to the Android app
Pick or share several photos at once and post them in one batch, still against the existing single-image /upload endpoint — no server change. - Unify the post screen on a list model (PickedItem): a single-photo post or share is just a batch of one, so there is one code path. - Multi-select via PickMultipleVisualMedia and multi-share via ACTION_SEND_MULTIPLE (manifest filter added). - Per-photo caption and EXIF-prefilled date; each card shows its own upload status, with a summary and retry-only-failures on re-post. - Enqueue one WorkManager worker per photo, reusing the durable retry queue. UploadQueue now appends a per-item suffix to the slug so a burst enqueued in the same second cannot collide and be dropped by enqueueUniqueWork.
This commit is contained in:
@@ -39,8 +39,8 @@ class MainActivity : ComponentActivity() {
|
||||
var showSettings by rememberSaveable { mutableStateOf(!viewModel.isConfigured) }
|
||||
|
||||
val picker = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.PickVisualMedia(),
|
||||
) { uri -> uri?.let(viewModel::selectImage) }
|
||||
ActivityResultContracts.PickMultipleVisualMedia(),
|
||||
) { uris -> if (uris.isNotEmpty()) viewModel.selectImages(uris) }
|
||||
|
||||
val notificationPermission = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
@@ -73,7 +73,7 @@ class MainActivity : ComponentActivity() {
|
||||
} else {
|
||||
PostScreen(
|
||||
state = viewModel.post,
|
||||
onPickImage = {
|
||||
onPickImages = {
|
||||
picker.launch(
|
||||
PickVisualMediaRequest(
|
||||
ActivityResultContracts.PickVisualMedia.ImageOnly,
|
||||
@@ -82,6 +82,7 @@ class MainActivity : ComponentActivity() {
|
||||
},
|
||||
onCaptionChange = viewModel::setCaption,
|
||||
onDateChange = viewModel::setDate,
|
||||
onRemove = viewModel::removeItem,
|
||||
onSubmit = viewModel::submit,
|
||||
onReset = viewModel::clear,
|
||||
onOpenSettings = { showSettings = true },
|
||||
@@ -98,16 +99,31 @@ class MainActivity : ComponentActivity() {
|
||||
consumeShareIntent(intent)
|
||||
}
|
||||
|
||||
/** Picks up a photo handed over by the share sheet. */
|
||||
/** Picks up one or several photos handed over by the share sheet. */
|
||||
private fun consumeShareIntent(intent: Intent?) {
|
||||
if (intent?.action != Intent.ACTION_SEND) return
|
||||
val uri: Uri? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
val uris: List<Uri> = when (intent?.action) {
|
||||
Intent.ACTION_SEND -> {
|
||||
val uri: Uri? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
}
|
||||
listOfNotNull(uri)
|
||||
}
|
||||
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
}.orEmpty()
|
||||
}
|
||||
|
||||
else -> return
|
||||
}
|
||||
uri?.let(viewModel::selectImage)
|
||||
if (uris.isNotEmpty()) viewModel.selectImages(uris)
|
||||
}
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
|
||||
Reference in New Issue
Block a user