diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 90ff8709d7..bcc0359a3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -55,6 +55,10 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.animation.DepthPageTransformer; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener; +import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart; +import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardBottomSheet; +import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment; +import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs; import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader; @@ -388,10 +392,12 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity MediaItem mediaItem = getCurrentMediaItem(); if (mediaItem != null) { - Intent composeIntent = new Intent(this, ShareActivity.class); - composeIntent.putExtra(Intent.EXTRA_STREAM, mediaItem.uri); - composeIntent.setType(mediaItem.type); - startActivity(composeIntent); + MultiselectForwardFragmentArgs.create( + this, + mediaItem.uri, + mediaItem.type, + args -> MultiselectForwardFragment.showBottomSheet(getSupportFragmentManager(), args) + ); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/Multiselect.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/Multiselect.kt index 2d84bb952b..cfe59cfc96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/Multiselect.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/Multiselect.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.conversation.mutiselect import android.Manifest import android.content.Context import android.content.pm.PackageManager +import android.net.Uri import androidx.core.content.ContextCompat import org.thoughtcrime.securesms.TransportOption import org.thoughtcrime.securesms.TransportOptions @@ -74,6 +75,22 @@ object Multiselect { } } + /** + * Helper function to determine whether a given attachment can be sent via MMS. + */ + fun isMmsSupported(context: Context, mediaUri: Uri, mediaType: String, mediaSize: Long): Boolean { + val canReadPhoneState = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED + if (!Util.isDefaultSmsProvider(context) || !canReadPhoneState || !Util.isMmsCapable(context)) { + return false + } + + val options = TransportOptions(context, true) + options.setDefaultTransport(TransportOption.Type.SMS) + + val mmsConstraints = MediaConstraints.getMmsMediaConstraints(options.selectedTransport.simSubscriptionId.orElse(-1)) + return mmsConstraints.isSatisfied(context, mediaUri, mediaType, mediaSize) || mmsConstraints.canResize(mediaType) + } + private fun canSendAllAttachmentsToNonPush(context: Context, messageRecord: MessageRecord): Boolean { return if (messageRecord is MmsMessageRecord) { messageRecord.slideDeck.asAttachments().all { isMmsSupported(context, it) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt index ec3196d222..c19485cc36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.conversation.mutiselect.forward import android.content.Context +import android.net.Uri import androidx.annotation.StringRes import androidx.annotation.WorkerThread import org.signal.core.util.StreamUtil @@ -15,6 +16,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.sharing.MultiShareArgs +import org.thoughtcrime.securesms.util.MediaUtil import java.util.Optional import java.util.function.Consumer @@ -32,6 +34,27 @@ class MultiselectForwardFragmentArgs( ) { companion object { + @JvmStatic + fun create(context: Context, mediaUri: Uri, mediaType: String, consumer: Consumer) { + SignalExecutors.BOUNDED.execute { + val mediaSize = MediaUtil.getMediaSize(context, mediaUri) + val isMmsSupported = Multiselect.isMmsSupported(context, mediaUri, mediaType, mediaSize) + val multiShareArgs = MultiShareArgs.Builder(setOf()) + .withDataUri(mediaUri) + .withDataType(mediaType) + .build() + + ThreadUtil.runOnMain { + consumer.accept( + MultiselectForwardFragmentArgs( + isMmsSupported, + listOf(multiShareArgs) + ) + ) + } + } + } + @JvmStatic fun create(context: Context, selectedParts: Set, consumer: Consumer) { SignalExecutors.BOUNDED.execute { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java index 088058151e..9490ec05b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java @@ -77,6 +77,19 @@ public abstract class MediaConstraints { } } + public boolean isSatisfied(@NonNull Context context, @NonNull Uri uri, @NonNull String contentType, long size) { + try { + return (MediaUtil.isGif(contentType) && size <= getGifMaxSize(context) && isWithinBounds(context, uri)) || + (MediaUtil.isImageType(contentType) && size <= getImageMaxSize(context) && isWithinBounds(context, uri)) || + (MediaUtil.isAudioType(contentType) && size <= getAudioMaxSize(context)) || + (MediaUtil.isVideoType(contentType) && size <= getVideoMaxSize(context)) || + size <= getDocumentMaxSize(context); + } catch (IOException ioe) { + Log.w(TAG, "Failed to determine if media's constraints are satisfied.", ioe); + return false; + } + } + private boolean isWithinBounds(Context context, Uri uri) throws IOException { try { InputStream is = PartAuthority.getAttachmentStream(context, uri); @@ -93,6 +106,11 @@ public abstract class MediaConstraints { MediaUtil.isVideo(attachment) && isVideoTranscodeAvailable(); } + public boolean canResize(@NonNull String mediaType) { + return MediaUtil.isImageType(mediaType) && !MediaUtil.isGif(mediaType) || + MediaUtil.isVideoType(mediaType) && isVideoTranscodeAvailable(); + } + public static boolean isVideoTranscodeAvailable() { return Build.VERSION.SDK_INT >= 26 && (FeatureFlags.useStreamingVideoMuxer() || MemoryFileDescriptor.supported()); }