Fix share intent handling for multiple images with captions.

This commit is contained in:
jeffrey-signal
2025-12-05 09:50:55 -06:00
committed by GitHub
parent 109fc7f1fa
commit da9c5edcc6
5 changed files with 35 additions and 26 deletions

View File

@@ -198,6 +198,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
<data android:mimeType="text/*" />
</intent-filter>
<meta-data

View File

@@ -25,10 +25,14 @@ sealed class ResolvedShareData {
}
data class Media(
val media: List<org.thoughtcrime.securesms.mediasend.Media>
val media: List<org.thoughtcrime.securesms.mediasend.Media>,
val text: CharSequence?
) : ResolvedShareData() {
override fun toMultiShareArgs(): MultiShareArgs {
return MultiShareArgs.Builder(setOf()).withMedia(media).build()
return MultiShareArgs.Builder(setOf())
.withMedia(media)
.withDraftText(text?.toString())
.build()
}
}

View File

@@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
@@ -13,6 +12,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.text.buildSpannedString
import com.google.android.material.appbar.MaterialToolbar
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.kotlin.subscribeBy
@@ -193,29 +193,22 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
override fun getDialogBackgroundColor(): Int = ContextCompat.getColor(this, R.color.signal_background_primary)
private fun getUnresolvedShareData(): Result<UnresolvedShareData, IntentError> {
return when {
intent.action == Intent.ACTION_SEND_MULTIPLE && intent.hasExtra(Intent.EXTRA_TEXT) -> {
intent.getCharSequenceArrayListExtra(Intent.EXTRA_TEXT)?.let { list ->
val stringBuilder = SpannableStringBuilder()
list.forEachIndexed { index, text ->
stringBuilder.append(text)
if (index != list.lastIndex) {
stringBuilder.append("\n")
}
}
Result.success(UnresolvedShareData.ExternalPrimitiveShare(stringBuilder))
} ?: Result.failure(IntentError.SEND_MULTIPLE_TEXT)
}
intent.action == Intent.ACTION_SEND_MULTIPLE && intent.hasExtra(Intent.EXTRA_STREAM) -> {
intent.getParcelableArrayListExtraCompat(Intent.EXTRA_STREAM, Uri::class.java)?.let {
Result.success(UnresolvedShareData.ExternalMultiShare(it))
return when (intent.action) {
Intent.ACTION_SEND_MULTIPLE if intent.hasExtra(Intent.EXTRA_STREAM) -> {
intent.getParcelableArrayListExtraCompat(Intent.EXTRA_STREAM, Uri::class.java)?.let { uris ->
val text: CharSequence? = intent.getCharSequenceArrayListExtra(Intent.EXTRA_TEXT)
?.let { textExtras -> combineTextExtras(textExtras) }
Result.success(UnresolvedShareData.ExternalMultiShare(uris, text))
} ?: Result.failure(IntentError.SEND_MULTIPLE_STREAM)
}
intent.action == Intent.ACTION_SEND && intent.hasExtra(Intent.EXTRA_STREAM) -> {
Intent.ACTION_SEND_MULTIPLE if intent.hasExtra(Intent.EXTRA_TEXT) -> {
intent.getCharSequenceArrayListExtra(Intent.EXTRA_TEXT)
?.let { textExtras -> Result.success(UnresolvedShareData.ExternalPrimitiveShare(text = combineTextExtras(textExtras))) }
?: Result.failure(IntentError.SEND_MULTIPLE_TEXT)
}
Intent.ACTION_SEND if intent.hasExtra(Intent.EXTRA_STREAM) -> {
val uri: Uri? = intent.getParcelableExtraCompat(Intent.EXTRA_STREAM, Uri::class.java)
if (uri == null) {
extractSingleExtraTextFromIntent(IntentError.SEND_STREAM)
@@ -225,7 +218,7 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
}
}
intent.action == Intent.ACTION_SEND && intent.hasExtra(Intent.EXTRA_TEXT) -> {
Intent.ACTION_SEND if intent.hasExtra(Intent.EXTRA_TEXT) -> {
extractSingleExtraTextFromIntent()
}
@@ -243,6 +236,17 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
}
}
private fun combineTextExtras(textExtras: List<CharSequence>): CharSequence {
return buildSpannedString {
textExtras.forEachIndexed { index, textItem ->
append(textItem)
if (index != textExtras.lastIndex) {
append("\n")
}
}
}
}
private fun ensureFragment(resolvedShareData: ResolvedShareData) {
if (!supportFragmentManager.isStateSaved && supportFragmentManager.fragments.none { it is MultiselectForwardFullScreenDialogFragment }) {
supportFragmentManager.beginTransaction()

View File

@@ -121,7 +121,7 @@ class ShareRepository(context: Context) {
}.filterNotNull()
return if (media.isNotEmpty()) {
ResolvedShareData.Media(media)
ResolvedShareData.Media(media, externalMultiShare.text)
} else {
ResolvedShareData.Failure
}

View File

@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.sharing.v2
import android.net.Uri
sealed class UnresolvedShareData {
data class ExternalMultiShare(val uris: List<Uri>) : UnresolvedShareData()
data class ExternalMultiShare(val uris: List<Uri>, val text: CharSequence?) : UnresolvedShareData()
data class ExternalSingleShare(val uri: Uri, val mimeType: String?, val text: CharSequence?) : UnresolvedShareData()
data class ExternalPrimitiveShare(val text: CharSequence) : UnresolvedShareData()
}