Skip to content

Commit

Permalink
Add video trimming time indication pill.
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-signal authored and greyson-signal committed Mar 5, 2020
1 parent 2152b4a commit 51603be
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ private AttachmentCompressionJob(@NonNull Parameters parameters,
public void onRun() throws Exception {
Log.d(TAG, "Running for: " + attachmentId);

AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);
AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);

if (databaseAttachment == null) {
throw new UndeliverableMessageException("Cannot find the specified attachment.");
Expand Down Expand Up @@ -141,7 +141,7 @@ private void scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
{
try {
if (MediaUtil.isVideo(attachment)) {
transcodeVideoIfNeededToDatabase(context, attachmentDatabase, attachment, constraints, EventBus.getDefault(), this::isCanceled);
attachment = transcodeVideoIfNeededToDatabase(context, attachmentDatabase, attachment, constraints, EventBus.getDefault(), this::isCanceled);
if (!constraints.isSatisfied(context, attachment)) {
throw new UndeliverableMessageException("Size constraints could not be met on video!");
}
Expand All @@ -163,12 +163,12 @@ private void scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
}
}

private static void transcodeVideoIfNeededToDatabase(@NonNull Context context,
@NonNull AttachmentDatabase attachmentDatabase,
@NonNull DatabaseAttachment attachment,
@NonNull MediaConstraints constraints,
@NonNull EventBus eventBus,
@NonNull InMemoryTranscoder.CancelationSignal cancelationSignal)
private static @NonNull DatabaseAttachment transcodeVideoIfNeededToDatabase(@NonNull Context context,
@NonNull AttachmentDatabase attachmentDatabase,
@NonNull DatabaseAttachment attachment,
@NonNull MediaConstraints constraints,
@NonNull EventBus eventBus,
@NonNull InMemoryTranscoder.CancelationSignal cancelationSignal)
throws UndeliverableMessageException
{
AttachmentDatabase.TransformProperties transformProperties = attachment.getTransformProperties();
Expand All @@ -179,7 +179,7 @@ private static void transcodeVideoIfNeededToDatabase(@NonNull Context context,
if (transformProperties.isVideoEdited()) {
throw new UndeliverableMessageException("Video edited, but transcode is not available");
}
return;
return attachment;
}

try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.AttachmentUploadJob_compressing_video_start))) {
Expand Down Expand Up @@ -210,6 +210,11 @@ private static void transcodeVideoIfNeededToDatabase(@NonNull Context context,

attachmentDatabase.updateAttachmentData(attachment, mediaStream, transformProperties.isVideoEdited());
attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId());
DatabaseAttachment updatedAttachment = attachmentDatabase.getAttachment(attachment.getAttachmentId());
if (updatedAttachment == null) {
throw new AssertionError();
}
return updatedAttachment;
}
}
}
Expand All @@ -226,6 +231,7 @@ private static void transcodeVideoIfNeededToDatabase(@NonNull Context context,
} catch (IOException | MmsException | VideoSizeException e) {
throw new UndeliverableMessageException("Failed to transcode", e);
}
return attachment;
}

private static MediaStream getResizedMedia(@NonNull Context context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
Expand All @@ -17,16 +18,21 @@

import org.thoughtcrime.securesms.R;

import java.util.Locale;
import java.util.concurrent.TimeUnit;

@RequiresApi(api = 23)
public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView {

private static final long MINIMUM_SELECTABLE_RANGE = TimeUnit.MILLISECONDS.toMicros(500);

private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint paintGrey = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect tempDrawRect = new Rect();
private static final int ANIMATION_DURATION_MS = 100;

private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint paintGrey = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint thumbTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint thumbTimeBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect tempDrawRect = new Rect();
private final RectF timePillRect = new RectF();
private Drawable chevronLeft;
private Drawable chevronRight;

Expand All @@ -42,8 +48,8 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
private long downMin;
private long downMax;
private Thumb dragThumb;
private Thumb lastDragThumb;
private OnRangeChangeListener onRangeChangeListener;

@Px private int thumbSizePixels;
@Px private int thumbTouchRadius;
@Px private int cursorPixels;
Expand All @@ -52,6 +58,11 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
@ColorInt private int thumbColorEdited;
private long actualPosition;
private long dragPosition;
@Px private int thumbHintTextSize;
@ColorInt private int thumbHintTextColor;
@ColorInt private int thumbHintBackgroundColor;
private long dragStartTimeMs;
private long dragEndTimeMs;

public VideoThumbnailsRangeSelectorView(final Context context) {
super(context);
Expand All @@ -71,13 +82,19 @@ public VideoThumbnailsRangeSelectorView(final Context context, final @Nullable A
private void init(final @Nullable AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.VideoThumbnailsRangeSelectorView, 0, 0);

thumbSizePixels = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_thumbWidth, 1);
cursorPixels = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_cursorWidth, 1);
thumbColor = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_thumbColor, 0xffff0000);
thumbColorEdited = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_thumbColorEdited, thumbColor);
cursorColor = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_cursorColor, thumbColor);
thumbTouchRadius = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_thumbTouchRadius, 50);
try {
thumbSizePixels = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_thumbWidth, 1);
cursorPixels = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_cursorWidth, 1);
thumbColor = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_thumbColor, 0xffff0000);
thumbColorEdited = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_thumbColorEdited, thumbColor);
cursorColor = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_cursorColor, thumbColor);
thumbTouchRadius = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_thumbTouchRadius, 50);
thumbHintTextSize = typedArray.getDimensionPixelSize(R.styleable.VideoThumbnailsRangeSelectorView_thumbHintTextSize, 0);
thumbHintTextColor = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_thumbHintTextColor, 0xffff0000);
thumbHintBackgroundColor = typedArray.getColor(R.styleable.VideoThumbnailsRangeSelectorView_thumbHintBackgroundColor, 0xff00ff00);
} finally {
typedArray.recycle();
}
}

chevronLeft = VectorDrawableCompat.create(getResources(), R.drawable.ic_chevron_left_black_8dp, null);
Expand All @@ -88,6 +105,12 @@ private void init(final @Nullable AttributeSet attrs) {
paintGrey.setStrokeWidth(1);

paint.setStrokeWidth(2);

thumbTimeTextPaint.setTextSize(thumbHintTextSize);
thumbTimeTextPaint.setColor(thumbHintTextColor);

thumbTimeBackgroundPaint.setStyle(Paint.Style.FILL_AND_STROKE);
thumbTimeBackgroundPaint.setColor(thumbHintBackgroundColor);
}

@Override
Expand Down Expand Up @@ -194,6 +217,16 @@ protected void onDraw(final Canvas canvas) {
chevronRight.draw(canvas);
canvas.restore();

// draw time hint pill
if (thumbHintTextSize > 0) {
if (dragStartTimeMs > 0 && (dragThumb == Thumb.MIN || dragThumb == Thumb.MAX)) {
drawTimeHint(canvas, drawableWidth, drawableHeight, dragThumb, false);
}
if (dragEndTimeMs > 0 && (lastDragThumb == Thumb.MIN || lastDragThumb == Thumb.MAX)) {
drawTimeHint(canvas, drawableWidth, drawableHeight, lastDragThumb, true);
}
}

// draw current position marker
if (left <= cursor && cursor <= right && dragThumb != Thumb.MIN && dragThumb != Thumb.MAX) {
canvas.translate(cursorPixels / 2, 0);
Expand All @@ -203,6 +236,50 @@ protected void onDraw(final Canvas canvas) {
}
}

private void drawTimeHint(Canvas canvas, int drawableWidth, int drawableHeight, Thumb dragThumb, boolean fadeOut) {
canvas.save();
long microsecondValue = dragThumb == Thumb.MIN ? getMinValue() : getMaxValue();
long seconds = TimeUnit.MICROSECONDS.toSeconds(microsecondValue);
String timeString = String.format(Locale.getDefault(), "%d:%02d", seconds / 60, seconds % 60);
float topBottomPadding = thumbHintTextSize * 0.5f;
float leftRightPadding = thumbHintTextSize * 0.75f;

thumbTimeTextPaint.getTextBounds(timeString, 0, timeString.length(), tempDrawRect);

timePillRect.set(tempDrawRect.left - leftRightPadding, tempDrawRect.top - topBottomPadding, tempDrawRect.right + leftRightPadding, tempDrawRect.bottom + topBottomPadding);

float halfPillWidth = timePillRect.width() / 2f;
float halfPillHeight = timePillRect.height() / 2f;

long animationTime = fadeOut ? ANIMATION_DURATION_MS - Math.min(ANIMATION_DURATION_MS, System.currentTimeMillis() - dragEndTimeMs)
: Math.min(ANIMATION_DURATION_MS, System.currentTimeMillis() - dragStartTimeMs);
float animationPosition = animationTime / (float) ANIMATION_DURATION_MS;
float scaleIn = 0.2f * animationPosition + 0.8f;
int alpha = (int) (255 * animationPosition);

if (dragThumb == Thumb.MAX) {
canvas.translate(Math.min(right, drawableWidth - halfPillWidth), 0);
} else {
canvas.translate(Math.max(left, halfPillWidth), 0);
}
canvas.translate(0, drawableHeight + halfPillHeight);
canvas.scale(scaleIn, scaleIn);
thumbTimeBackgroundPaint.setAlpha(alpha);
thumbTimeTextPaint.setAlpha(alpha);
canvas.translate(leftRightPadding - halfPillWidth, halfPillHeight);
canvas.drawRoundRect(timePillRect, halfPillHeight, halfPillHeight, thumbTimeBackgroundPaint);
canvas.drawText(timeString, 0, 0, thumbTimeTextPaint);
canvas.restore();

if (fadeOut && animationTime > 0 || !fadeOut && animationTime < ANIMATION_DURATION_MS) {
invalidate();
} else {
if (fadeOut) {
lastDragThumb = null;
}
}
}

public long getMinValue() {
return minValue == null ? 0 : minValue;
}
Expand Down Expand Up @@ -257,11 +334,13 @@ private static long clamp(long value, long min, long max) {
public boolean onTouchEvent(MotionEvent event) {
int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
xDown = event.getX();
downCursor = actualPosition;
downMin = getMinValue();
downMax = getMaxValue();
dragThumb = closestThumb(event.getX());
xDown = event.getX();
downCursor = actualPosition;
downMin = getMinValue();
downMax = getMaxValue();
dragThumb = closestThumb(event.getX());
dragStartTimeMs = System.currentTimeMillis();
invalidate();
return dragThumb != null;
}

Expand Down Expand Up @@ -297,7 +376,9 @@ public boolean onTouchEvent(MotionEvent event) {
} else {
onRangeChangeListener.onRangeDragEnd(getMinValue(), getMaxValue(), getDuration(), dragThumb);
}
dragThumb = null;
lastDragThumb = dragThumb;
dragEndTimeMs = System.currentTimeMillis();
dragThumb = null;
invalidate();
}
return true;
Expand Down
8 changes: 6 additions & 2 deletions app/src/main/res/layout/video_editor_hud.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:clipChildren="false">

<org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView
android:id="@+id/video_timeline"
Expand All @@ -23,8 +24,11 @@
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:thumbColor="#fff"
app:thumbColor="@color/core_white"
app:thumbColorEdited="#ff0"
app:thumbHintBackgroundColor="@color/core_grey_90"
app:thumbHintTextColor="@color/core_white"
app:thumbHintTextSize="14sp"
app:thumbTouchRadius="24dp"
app:thumbWidth="16dp" />

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@
<attr name="cursorWidth" format="dimension" />
<attr name="cursorColor" format="color" />
<attr name="thumbTouchRadius" format="dimension" />
<attr name="thumbHintTextSize" format="dimension" />
<attr name="thumbHintTextColor" format="color" />
<attr name="thumbHintBackgroundColor" format="color" />
</declare-styleable>

<declare-styleable name="GroupMemberListView">
Expand Down

0 comments on commit 51603be

Please sign in to comment.