Implement video length enforcement for Stories.

This commit is contained in:
Alex Hart
2022-06-21 16:05:52 -03:00
committed by Cody Henthorne
parent 2c3d8337c3
commit 6a385c7a22
26 changed files with 597 additions and 108 deletions

View File

@@ -12,7 +12,6 @@ import com.annimon.stream.Stream;
import org.signal.core.util.BreakIteratorCompat;
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mediasend.Media;
@@ -27,7 +26,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public final class MultiShareArgs implements Parcelable {
@@ -152,12 +150,8 @@ public final class MultiShareArgs implements Parcelable {
public boolean isValidForStories() {
return isTextStory ||
!media.isEmpty() && media.stream().allMatch(
m -> MediaUtil.isImageOrVideoType(m.getMimeType()) &&
isValidStoryDuration(m)
) ||
MediaUtil.isImageType(dataType) ||
MediaUtil.isVideoType(dataType) ||
(!media.isEmpty() && media.stream().allMatch(m -> MediaUtil.isStorySupportedType(m.getMimeType()))) ||
MediaUtil.isStorySupportedType(dataType) ||
isValidForTextStoryGeneration();
}
@@ -165,25 +159,6 @@ public final class MultiShareArgs implements Parcelable {
return !isTextStory;
}
public static boolean isValidStoryDuration(@NonNull Media media) {
if (MediaUtil.isVideoType(media.getMimeType())) {
if (media.getDuration() > 0 && media.getDuration() <= Stories.MAX_VIDEO_DURATION_MILLIS) {
return true;
} else if (media.getTransformProperties().isPresent()) {
AttachmentDatabase.TransformProperties transformProperties = media.getTransformProperties().get();
if (transformProperties.isVideoTrim()) {
return transformProperties.getVideoTrimEndTimeUs() - transformProperties.getVideoTrimStartTimeUs() <= TimeUnit.MILLISECONDS.toMicros(Stories.MAX_VIDEO_DURATION_MILLIS);
} else {
return false;
}
} else {
return false;
}
} else {
return true;
}
}
public boolean isValidForTextStoryGeneration() {
if (isTextStory || !media.isEmpty()) {
return false;

View File

@@ -16,6 +16,8 @@ import org.signal.core.util.BreakIteratorCompat;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SimpleTask;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey;
import org.thoughtcrime.securesms.conversation.MessageSendType;
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
@@ -32,10 +34,12 @@ import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryBackgroundColors;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.SlideFactory;
import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
@@ -48,9 +52,11 @@ import org.thoughtcrime.securesms.util.MessageUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -257,9 +263,18 @@ public final class MultiShareSender {
} else {
List<Slide> storySupportedSlides = slideDeck.getSlides()
.stream()
.flatMap(slide -> {
if (slide instanceof VideoSlide) {
return expandToClips(context, (VideoSlide) slide).stream();
} else {
return java.util.stream.Stream.of(slide);
}
})
.filter(it -> MediaUtil.isStorySupportedType(it.getContentType()))
.collect(Collectors.toList());
// For each video slide, we want to convert it into a media, then clip it, and then transform it BACK into a slide.
for (final Slide slide : storySupportedSlides) {
SlideDeck singletonDeck = new SlideDeck();
singletonDeck.addSlide(slide);
@@ -319,6 +334,20 @@ public final class MultiShareSender {
}
}
private static Collection<Slide> expandToClips(@NonNull Context context, @NonNull VideoSlide videoSlide) {
long duration = Stories.MediaTransform.getVideoDuration(Objects.requireNonNull(videoSlide.getUri()));
if (duration > Stories.MAX_VIDEO_DURATION_MILLIS) {
return Stories.MediaTransform.clipMediaToStoryDuration(Stories.MediaTransform.videoSlideToMedia(videoSlide, duration))
.stream()
.map(media -> Stories.MediaTransform.mediaToVideoSlide(context, media))
.collect(Collectors.toList());
} else if (duration == 0L) {
return Collections.emptyList();
} else {
return Collections.singletonList(videoSlide);
}
}
private static void sendTextMessage(@NonNull Context context,
@NonNull MultiShareArgs multiShareArgs,
@NonNull Recipient recipient,