diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt index 8016fd76dd..6f20a68a24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoEditorFragment.kt @@ -23,24 +23,19 @@ import org.thoughtcrime.securesms.util.Throttler import org.thoughtcrime.securesms.video.VideoPlayer import org.thoughtcrime.securesms.video.VideoPlayer.PlayerCallback import org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView -import org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView.OnRangeChangeListener -import org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView.Thumb +import org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView.PositionDragListener import java.io.IOException -import java.util.concurrent.TimeUnit -import kotlin.math.max -class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFragment { +class VideoEditorFragment : Fragment(), PositionDragListener, MediaSendPageFragment { private val sharedViewModel: MediaSelectionViewModel by viewModels(ownerProducer = { requireActivity() }) private val videoScanThrottle = Throttler(150) private val handler = Handler(Looper.getMainLooper()) - private var data = VideoTrimData() private var canEdit = false private var isVideoGif = false private var isInEdit = false private var isFocused = false private var wasPlayingBeforeEdit = false - private var maxVideoDurationUs: Long = 0 private var maxSend: Long = 0 private lateinit var uri: Uri private lateinit var controller: Controller @@ -86,7 +81,6 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag uri = requireArguments().getParcelable(KEY_URI)!! isVideoGif = requireArguments().getBoolean(KEY_IS_VIDEO_GIF) maxSend = requireArguments().getLong(KEY_MAX_SEND) - maxVideoDurationUs = TimeUnit.MILLISECONDS.toMicros(requireArguments().getLong(KEY_MAX_DURATION)) val state = sharedViewModel.state.value!! val slide = VideoSlide(requireContext(), uri, 0, isVideoGif) @@ -136,15 +130,24 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag }) } - sharedViewModel.state.observe(viewLifecycleOwner) { state -> - val focusedMedia = state.focusedMedia - val currentlyFocused = focusedMedia?.uri != null && focusedMedia.uri == uri - if (MediaConstraints.isVideoTranscodeAvailable() && canEdit && !isFocused && currentlyFocused) { - bindVideoTimeline(state) - } - - if (!currentlyFocused) { - stopPositionUpdates() + sharedViewModel.state.observe(viewLifecycleOwner) { incomingState -> + val focusedUri = incomingState.focusedMedia?.uri + val currentlyFocused = focusedUri != null && focusedUri == uri + if (MediaConstraints.isVideoTranscodeAvailable() && canEdit) { + if (currentlyFocused) { + if (!isFocused) { + bindVideoTimeline(incomingState) + } else { + val videoTrimData = if (focusedUri != null) { + incomingState.getOrCreateVideoTrimData(focusedUri) + } else { + VideoTrimData() + } + onEditVideoDuration(videoTrimData, incomingState.isTouchEnabled) + } + } else { + stopPositionUpdates() + } } isFocused = currentlyFocused } @@ -160,13 +163,15 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag val autoplay = isVideoGif val slide = VideoSlide(requireContext(), uri, 0, autoplay) + val data = state.getOrCreateVideoTrimData(uri) if (data.isDurationEdited) { player.clip(data.startTimeUs, data.endTimeUs, autoplay) } + if (slide.hasVideo()) { canEdit = true try { - videoTimeLine.setOnRangeChangeListener(this) + videoTimeLine.registerPlayerOnRangeChangeListener(this) hud.visibility = View.VISIBLE startPositionUpdates() @@ -184,16 +189,6 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag onSeek(position, true) } - @RequiresApi(23) - override fun onRangeDrag(minValueUs: Long, maxValueUs: Long, durationUs: Long, thumb: Thumb) { - onEditVideoDuration(durationUs, minValueUs, maxValueUs, thumb == Thumb.MIN, false) - } - - @RequiresApi(23) - override fun onRangeDragEnd(minValueUs: Long, maxValueUs: Long, durationUs: Long, thumb: Thumb) { - onEditVideoDuration(durationUs, minValueUs, maxValueUs, thumb == Thumb.MIN, true) - } - override fun onDestroyView() { super.onDestroyView() @@ -241,17 +236,9 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag return uri } - override fun saveState(): Any { - return data - } + override fun saveState(): Any = Unit - override fun restoreState(state: Any) { - if (state is VideoTrimData) { - data = state - } else { - Log.w(TAG, "Received a bad saved state. Received class: " + state.javaClass.name) - } - } + override fun restoreState(state: Any) = Unit override fun notifyHidden() { pausePlayback() @@ -263,19 +250,9 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag } @RequiresApi(23) - private fun onEditVideoDuration(totalDurationUs: Long, startTimeUs: Long, endTimeUs: Long, fromEdited: Boolean, editingComplete: Boolean) { - controller.onTouchEventsNeeded(!editingComplete) - + private fun onEditVideoDuration(data: VideoTrimData, editingComplete: Boolean) { hud.hidePlayButton() - val clampedStartTime = max(startTimeUs.toDouble(), 0.0).toLong() - - val wasEdited = data.isDurationEdited - val durationEdited = clampedStartTime > 0 || endTimeUs < totalDurationUs - val endMoved = data.endTimeUs != endTimeUs - - val updatedData = MediaSelectionViewModel.clampToMaxClipDuration(VideoTrimData(durationEdited, totalDurationUs, clampedStartTime, endTimeUs), maxVideoDurationUs, !endMoved) - if (editingComplete) { isInEdit = false videoScanThrottle.clear() @@ -289,10 +266,10 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag if (!editingComplete) { player.removeClip(false) } - player.playbackPosition = if (fromEdited || editingComplete) clampedStartTime / 1000 else endTimeUs / 1000 + player.playbackPosition = if (editingComplete) data.startTimeUs / 1000 else data.endTimeUs / 1000 if (editingComplete) { - if (durationEdited) { - player.clip(clampedStartTime, endTimeUs, wasPlayingBeforeEdit) + if (data.isDurationEdited) { + player.clip(data.startTimeUs, data.endTimeUs, wasPlayingBeforeEdit) } else { player.removeClip(wasPlayingBeforeEdit) } @@ -302,18 +279,6 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag } } } - - if (!wasEdited && durationEdited) { - controller.onVideoBeginEdit(uri) - } - - if (editingComplete) { - controller.onVideoEndEdit(uri) - } - - uri.let { - sharedViewModel.setEditorState(it, updatedData) - } } private fun onSeek(position: Long, dragComplete: Boolean) { @@ -333,10 +298,6 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag fun onPlayerError() fun onTouchEventsNeeded(needed: Boolean) - - fun onVideoBeginEdit(uri: Uri) - - fun onVideoEndEdit(uri: Uri) } companion object { @@ -345,14 +306,12 @@ class VideoEditorFragment : Fragment(), OnRangeChangeListener, MediaSendPageFrag private const val KEY_URI = "uri" private const val KEY_MAX_SEND = "max_send_size" private const val KEY_IS_VIDEO_GIF = "is_video_gif" - private const val KEY_MAX_DURATION = "max_duration" - fun newInstance(uri: Uri, maxAttachmentSize: Long, isVideoGif: Boolean, maxVideoDuration: Long): VideoEditorFragment { + fun newInstance(uri: Uri, maxAttachmentSize: Long, isVideoGif: Boolean): VideoEditorFragment { val args = Bundle() args.putParcelable(KEY_URI, uri) args.putLong(KEY_MAX_SEND, maxAttachmentSize) args.putBoolean(KEY_IS_VIDEO_GIF, isVideoGif) - args.putLong(KEY_MAX_DURATION, maxVideoDuration) val fragment = VideoEditorFragment() fragment.arguments = args diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionState.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionState.kt index 6ed0269cad..556e30daa9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionState.kt @@ -45,8 +45,8 @@ data class MediaSelectionState( val canSend = !isSent && selectedMedia.isNotEmpty() - fun getVideoTrimData(uri: Uri): VideoTrimData? { - return editorStateMap[uri] as? VideoTrimData + fun getOrCreateVideoTrimData(uri: Uri): VideoTrimData { + return editorStateMap[uri] as? VideoTrimData ?: VideoTrimData() } enum class ViewOnceToggleState(val code: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt index 045d3f82a3..2b6abed974 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt @@ -44,7 +44,9 @@ import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.livedata.Store import java.util.Collections +import kotlin.math.max import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds /** * ViewModel which maintains the list of selected media and other shared values. @@ -342,6 +344,31 @@ class MediaSelectionViewModel( store.update { it.copy(viewOnceToggleState = it.viewOnceToggleState.next()) } } + fun onEditVideoDuration(context: Context, totalDurationUs: Long, startTimeUs: Long, endTimeUs: Long, touchEnabled: Boolean) { + store.update { + val uri = it.focusedMedia?.uri ?: return@update it + val data = it.getOrCreateVideoTrimData(uri) + val clampedStartTime = max(startTimeUs.toDouble(), 0.0).toLong() + + val alreadyEdited = data.isDurationEdited + val durationEdited = clampedStartTime > 0 || endTimeUs < totalDurationUs + val endMoved = data.endTimeUs != endTimeUs + val maxVideoDurationUs: Long = if (it.isStory && !MediaConstraints.isVideoTranscodeAvailable()) { + Stories.MAX_VIDEO_DURATION_MILLIS + } else { + it.transcodingPreset.calculateMaxVideoUploadDurationInSeconds(getMediaConstraints().getVideoMaxSize(context)).seconds.inWholeMicroseconds + } + val updatedData = clampToMaxClipDuration(VideoTrimData(durationEdited, totalDurationUs, clampedStartTime, endTimeUs), maxVideoDurationUs, !alreadyEdited || !endMoved) + if (!alreadyEdited && durationEdited) { + cancelUpload(MediaBuilder.buildMedia(uri)) + } + it.copy( + isTouchEnabled = touchEnabled, + editorStateMap = it.editorStateMap + (uri to updatedData) + ) + } + } + fun getEditorState(uri: Uri): Any? { return store.state.editorStateMap[uri] } @@ -352,10 +379,6 @@ class MediaSelectionViewModel( } } - fun onVideoBeginEdit(uri: Uri) { - cancelUpload(MediaBuilder.buildMedia(uri)) - } - fun send( selectedContacts: List = emptyList(), scheduledDate: Long? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt index 1f4cc3fb34..32d9b2cbe3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/review/MediaReviewFragment.kt @@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.conversation.ScheduleMessageTimePickerBottomSh import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardActivity import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.media.DecryptableUriMediaInput import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult import org.thoughtcrime.securesms.mediasend.v2.HudCommand @@ -77,7 +76,7 @@ import kotlin.math.roundToInt /** * Allows the user to view and edit selected media. */ -class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), ScheduleMessageTimePickerBottomSheet.ScheduleCallback { +class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), ScheduleMessageTimePickerBottomSheet.ScheduleCallback, VideoThumbnailsRangeSelectorView.RangeDragListener { private val sharedViewModel: MediaSelectionViewModel by viewModels( ownerProducer = { requireActivity() } @@ -297,6 +296,10 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul } }) + if (MediaConstraints.isVideoTranscodeAvailable()) { + videoTimeLine.registerEditorOnRangeChangeListener(this) + } + val selectionAdapter = MappingAdapter(false) MediaReviewAddItem.register(selectionAdapter) { launchGallery() @@ -527,7 +530,8 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul return } val uri = mediaItem.uri - videoTimeLine.setInput(DecryptableUriMediaInput.createForUri(requireContext(), uri)) + videoTimeLine.unregisterPlayerOnRangeChangeListener() + videoTimeLine.setInput(uri) val size: Long = tryGetUriSize(requireContext(), uri, Long.MAX_VALUE) val maxSend = sharedViewModel.getMediaConstraints().getVideoMaxSize(requireContext()) if (size > maxSend) { @@ -535,7 +539,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul } if (state.isTouchEnabled) { - val data = state.getVideoTrimData(uri) ?: return + val data = state.getOrCreateVideoTrimData(uri) if (data.totalInputDurationUs > 0) { videoTimeLine.setRange(data.startTimeUs, data.endTimeUs) @@ -545,9 +549,9 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul private fun presentVideoSizeHint(state: MediaSelectionState) { val focusedMedia = state.focusedMedia ?: return - val trimData = state.getVideoTrimData(focusedMedia.uri) + val trimData = state.getOrCreateVideoTrimData(focusedMedia.uri) - videoSizeHint.text = if (state.isVideoTrimmingVisible && trimData != null) { + videoSizeHint.text = if (state.isVideoTrimmingVisible) { val seconds = trimData.getDuration().inWholeSeconds val bytes = TranscodingQuality.createFromPreset(state.transcodingPreset, trimData.getDuration().inWholeMilliseconds).byteCountEstimate String.format(Locale.getDefault(), "%d:%02d • %s", seconds / 60, seconds % 60, MemoryUnitFormat.formatBytes(bytes, MemoryUnitFormat.MEGA_BYTES, true)) @@ -769,4 +773,8 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul scheduledSendTime = scheduledTime sendButton.performClick() } + + override fun onRangeDrag(minValue: Long, maxValue: Long, duration: Long, end: Boolean) { + sharedViewModel.onEditVideoDuration(context = requireContext(), totalDurationUs = duration, startTimeUs = minValue, endTimeUs = maxValue, touchEnabled = end) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/videos/MediaReviewVideoPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/videos/MediaReviewVideoPageFragment.kt index 3669e95b64..0bcad54282 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/videos/MediaReviewVideoPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/videos/MediaReviewVideoPageFragment.kt @@ -10,8 +10,6 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.mediasend.VideoEditorFragment import org.thoughtcrime.securesms.mediasend.v2.HudCommand import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel -import org.thoughtcrime.securesms.mms.MediaConstraints -import org.thoughtcrime.securesms.stories.Stories private const val VIDEO_EDITOR_TAG = "video.editor.fragment" @@ -34,16 +32,6 @@ class MediaReviewVideoPageFragment : Fragment(R.layout.fragment_container), Vide restoreVideoEditorState() } - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - saveEditorState() - } - - private fun saveEditorState() { - val saveState = videoEditorFragment.saveState() - sharedViewModel.setEditorState(requireUri(), saveState) - } - override fun onPlayerReady() { sharedViewModel.sendCommand(HudCommand.ResumeEntryTransition) } @@ -55,15 +43,6 @@ class MediaReviewVideoPageFragment : Fragment(R.layout.fragment_container), Vide override fun onTouchEventsNeeded(needed: Boolean) { sharedViewModel.setTouchEnabled(!needed) } - - override fun onVideoBeginEdit(uri: Uri) { - sharedViewModel.onVideoBeginEdit(uri) - } - - override fun onVideoEndEdit(uri: Uri) { - saveEditorState() - } - private fun restoreVideoEditorState() { val data = sharedViewModel.getEditorState(requireUri()) as? VideoTrimData @@ -81,8 +60,7 @@ class MediaReviewVideoPageFragment : Fragment(R.layout.fragment_container), Vide val videoEditorFragment = VideoEditorFragment.newInstance( requireUri(), requireMaxAttachmentSize(), - requireIsVideoGif(), - requireMaxVideoDuration() + requireIsVideoGif() ) childFragmentManager.beginTransaction() @@ -100,7 +78,6 @@ class MediaReviewVideoPageFragment : Fragment(R.layout.fragment_container), Vide private fun requireUri(): Uri = requireNotNull(requireArguments().getParcelableCompat(ARG_URI, Uri::class.java)) private fun requireMaxAttachmentSize(): Long = sharedViewModel.getMediaConstraints().getVideoMaxSize(requireContext()) private fun requireIsVideoGif(): Boolean = requireNotNull(requireArguments().getBoolean(ARG_IS_VIDEO_GIF)) - private fun requireMaxVideoDuration(): Long = if (sharedViewModel.isStory() && !MediaConstraints.isVideoTranscodeAvailable()) Stories.MAX_VIDEO_DURATION_MILLIS else Long.MAX_VALUE companion object { private const val ARG_URI = "arg.uri" diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsRangeSelectorView.java b/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsRangeSelectorView.java index 72b35c91c5..8a8163c861 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsRangeSelectorView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsRangeSelectorView.java @@ -51,7 +51,8 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView private long downMax; private Thumb dragThumb; private Thumb lastDragThumb; - private OnRangeChangeListener onRangeChangeListener; + private PositionDragListener playerOnRangeChangeListener; + private RangeDragListener editorOnRangeChangeListener; @Px private int thumbSizePixels; @Px private int thumbTouchRadius; @ColorInt private int thumbColor; @@ -109,15 +110,7 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView @Override protected void afterDurationChange(long duration) { - super.afterDurationChange(duration); - - if (maxValue != null && duration < maxValue) { - maxValue = duration; - } - - if (minValue != null && duration < minValue) { - minValue = duration; - } + maxValue = duration; if (duration > 0) { if (externalMaxValue != null) { @@ -131,15 +124,21 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView } } - if (onRangeChangeListener != null) { - onRangeChangeListener.onRangeDragEnd(getMinValue(), getMaxValue(), getDuration(), Thumb.MIN); - } + onRangeDrag(getMinValue(), getMaxValue(), duration, true); invalidate(); } - public void setOnRangeChangeListener(OnRangeChangeListener onRangeChangeListener) { - this.onRangeChangeListener = onRangeChangeListener; + public void registerPlayerOnRangeChangeListener(PositionDragListener playerOnRangeChangeListener) { + this.playerOnRangeChangeListener = playerOnRangeChangeListener; + } + + public void registerEditorOnRangeChangeListener(RangeDragListener editorOnRangeChangeListener) { + this.editorOnRangeChangeListener = editorOnRangeChangeListener; + } + + public void unregisterPlayerOnRangeChangeListener() { + this.playerOnRangeChangeListener = null; } public void setActualPosition(long position) { @@ -349,22 +348,22 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView changed = setMaxValue(downMax + delta); break; } - if (changed && onRangeChangeListener != null) { + if (changed) { if (dragThumb == Thumb.POSITION) { - onRangeChangeListener.onPositionDrag(dragPosition); + onPositionDrag(dragPosition); } else { - onRangeChangeListener.onRangeDrag(getMinValue(), getMaxValue(), getDuration(), dragThumb); + onRangeDrag(getMinValue(), getMaxValue(), getDuration(), false); } } return true; } if (actionMasked == MotionEvent.ACTION_UP) { - if (onRangeChangeListener != null) { + if (editorOnRangeChangeListener != null) { if (dragThumb == Thumb.POSITION) { - onRangeChangeListener.onEndPositionDrag(dragPosition); + onEndPositionDrag(dragPosition); } else { - onRangeChangeListener.onRangeDragEnd(getMinValue(), getMaxValue(), getDuration(), dragThumb); + onRangeDrag(getMinValue(), getMaxValue(), getDuration(), true); } lastDragThumb = dragThumb; dragEndTimeMs = System.currentTimeMillis(); @@ -418,20 +417,36 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView maximumSelectableRangeMicros = timeUnit.toMicros(t); } + private void onPositionDrag(long position) { + if (playerOnRangeChangeListener != null) { + playerOnRangeChangeListener.onPositionDrag(position); + } + } + + private void onEndPositionDrag(long position) { + if (playerOnRangeChangeListener != null) { + playerOnRangeChangeListener.onEndPositionDrag(position); + } + } + + private void onRangeDrag(long minValue, long maxValue, long duration, boolean end) { + if (editorOnRangeChangeListener != null) { + editorOnRangeChangeListener.onRangeDrag(minValue, maxValue, duration, end); + } + } + public enum Thumb { MIN, MAX, POSITION } - public interface OnRangeChangeListener { - + public interface PositionDragListener { void onPositionDrag(long position); - void onEndPositionDrag(long position); + } - void onRangeDrag(long minValue, long maxValue, long duration, Thumb thumb); - - void onRangeDragEnd(long minValue, long maxValue, long duration, Thumb thumb); + public interface RangeDragListener { + void onRangeDrag(long minValue, long maxValue, long duration, boolean start); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsView.java b/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsView.java index ca6e2429b5..242c8ef2ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsView.java @@ -7,6 +7,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; +import android.net.Uri; import android.os.AsyncTask; import android.util.AttributeSet; import android.view.View; @@ -16,6 +17,7 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.media.DecryptableUriMediaInput; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.video.interfaces.MediaInput; @@ -26,11 +28,13 @@ import java.util.Arrays; import java.util.List; @RequiresApi(api = 23) -public class VideoThumbnailsView extends View { +abstract public class VideoThumbnailsView extends View { private static final String TAG = Log.tag(VideoThumbnailsView.class); private static final int CORNER_RADIUS = ViewUtil.dpToPx(8); + protected Uri currentUri; + private MediaInput input; private volatile ArrayList thumbnails; private AsyncTask thumbnailsTask; @@ -55,12 +59,13 @@ public class VideoThumbnailsView extends View { super(context, attrs, defStyleAttr); } - public void setInput(@NonNull MediaInput input) { - if (this.input != null && input.hasSameInput(this.input)) { + public void setInput(@NonNull Uri uri) throws IOException { + if (uri.equals(this.currentUri)) { return; } - this.input = input; + this.currentUri = uri; + this.input = DecryptableUriMediaInput.createForUri(getContext(), uri); this.thumbnails = null; if (thumbnailsTask != null) { thumbnailsTask.cancel(true); @@ -163,15 +168,14 @@ public class VideoThumbnailsView extends View { } } - public void setDuration(long duration) { + private void setDuration(long duration) { if (this.duration != duration) { this.duration = duration; afterDurationChange(duration); } } - protected void afterDurationChange(long duration) { - } + abstract void afterDurationChange(long duration); public long getDuration() { return duration; @@ -241,14 +245,10 @@ public class VideoThumbnailsView extends View { VideoThumbnailsView view = viewReference.get(); List thumbnails = view != null ? view.thumbnails : null; if (view != null) { - view.setDuration(duration); + view.setDuration(ThumbnailsTask.this.duration); view.invalidate(); Log.i(TAG, "onPostExecute, we have " + (thumbnails != null ? thumbnails.size() : "null") + " thumbs"); } } } - - public interface OnDurationListener { - void onDurationKnown(long duration); - } }