Fix sharing media inside the app (#7016)

When a `MediaSource` is disposed, the local temp file associated to it will be deleted from disk.

When we try sharing a media from the media viewer screen to another room, this will dismiss the screen, which in turn will dispose of the media source and remove the file, which then fails to be shared.

If we copy it first and then make sure we delete it after trying to send it, it works fine.
This commit is contained in:
Jorge Martin Espinosa
2026-06-16 16:42:59 +02:00
committed by GitHub
parent 710f501689
commit 8e9c0bedf2
2 changed files with 21 additions and 5 deletions
@@ -7,6 +7,7 @@
package io.element.android.features.share.impl
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -15,13 +16,14 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.share.api.OnSharedData
import io.element.android.features.share.api.ShareIntentData
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.annotations.ApplicationContext
import timber.log.Timber
import kotlin.collections.forEach
@ContributesBinding(AppScope::class)
class DefaultOnSharedData(
@ApplicationContext private val context: Context,
private val buildMeta: BuildMeta,
) : OnSharedData {
override fun invoke(data: ShareIntentData) {
when (data) {
@@ -29,6 +31,13 @@ class DefaultOnSharedData(
// No-op, there is nothing to do for plain text intents.
}
is ShareIntentData.Uris -> {
val fileProvider = "${buildMeta.applicationId}.fileprovider"
for (sharedUri in data.uris) {
// Remove the local copy of the shared file, as it is not needed anymore
if (sharedUri.uri.scheme == ContentResolver.SCHEME_CONTENT && sharedUri.uri.host == fileProvider) {
context.contentResolver.delete(sharedUri.uri, null)
}
}
revokeUriPermissions(data.uris.map { it.uri })
}
}
@@ -100,7 +100,11 @@ class AndroidLocalMediaActions(
override suspend fun share(localMedia: LocalMedia): Result<Unit> = withContext(coroutineDispatchers.io) {
require(localMedia.uri.scheme == ContentResolver.SCHEME_FILE)
runCatchingExceptions {
val shareableUri = localMedia.toShareableUri()
// Make a copy of the shared file in the cache directory, otherwise the original file will be gone once this screen is dismissed
// and will prevent sharing the media to another room inside the app.
val copiedFile = localMedia.uri.toFile()
.copyTo(File(context.cacheDir, "temp/media/" + (localMedia.uri.lastPathSegment ?: "shared_file")), true)
val shareableUri = copiedFile.toShareableUri()
val shareMediaIntent = Intent(Intent.ACTION_SEND)
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, shareableUri)
@@ -157,10 +161,13 @@ class AndroidLocalMediaActions(
}
}
private fun LocalMedia.toShareableUri(): Uri {
val mediaAsFile = this.toFile()
private fun File.toShareableUri(): Uri {
val authority = "${buildMeta.applicationId}.fileprovider"
return FileProvider.getUriForFile(context, authority, mediaAsFile).normalizeScheme()
return FileProvider.getUriForFile(context, authority, this).normalizeScheme()
}
private fun LocalMedia.toShareableUri(): Uri {
return this.toFile().toShareableUri()
}
@RequiresApi(Build.VERSION_CODES.Q)