Add video trimming time indication pill.
This commit is contained in:
parent
2152b4a2cd
commit
51603be5ec
4 changed files with 123 additions and 29 deletions
|
@ -108,8 +108,8 @@ public final class AttachmentCompressionJob extends BaseJob {
|
|||
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.");
|
||||
|
@ -141,7 +141,7 @@ public final class AttachmentCompressionJob extends BaseJob {
|
|||
{
|
||||
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!");
|
||||
}
|
||||
|
@ -163,12 +163,12 @@ public final class AttachmentCompressionJob extends BaseJob {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -179,7 +179,7 @@ public final class AttachmentCompressionJob extends BaseJob {
|
|||
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))) {
|
||||
|
@ -210,6 +210,11 @@ public final class AttachmentCompressionJob extends BaseJob {
|
|||
|
||||
attachmentDatabase.updateAttachmentData(attachment, mediaStream, transformProperties.isVideoEdited());
|
||||
attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId());
|
||||
DatabaseAttachment updatedAttachment = attachmentDatabase.getAttachment(attachment.getAttachmentId());
|
||||
if (updatedAttachment == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return updatedAttachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,6 +231,7 @@ public final class AttachmentCompressionJob extends BaseJob {
|
|||
} catch (IOException | MmsException | VideoSizeException e) {
|
||||
throw new UndeliverableMessageException("Failed to transcode", e);
|
||||
}
|
||||
return attachment;
|
||||
}
|
||||
|
||||
private static MediaStream getResizedMedia(@NonNull Context context,
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.res.TypedArray;
|
|||
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;
|
||||
|
@ -17,16 +18,21 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
|||
|
||||
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 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 Rect tempDrawRect = new Rect();
|
||||
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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -71,13 +82,19 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
|
|||
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);
|
||||
|
@ -88,6 +105,12 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
|
|||
paintGrey.setStrokeWidth(1);
|
||||
|
||||
paint.setStrokeWidth(2);
|
||||
|
||||
thumbTimeTextPaint.setTextSize(thumbHintTextSize);
|
||||
thumbTimeTextPaint.setColor(thumbHintTextColor);
|
||||
|
||||
thumbTimeBackgroundPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
thumbTimeBackgroundPaint.setColor(thumbHintBackgroundColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -194,6 +217,16 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
|
|||
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);
|
||||
|
@ -203,6 +236,50 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -257,11 +334,13 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -297,7 +376,9 @@ public final class VideoThumbnailsRangeSelectorView extends VideoThumbnailsView
|
|||
} else {
|
||||
onRangeChangeListener.onRangeDragEnd(getMinValue(), getMaxValue(), getDuration(), dragThumb);
|
||||
}
|
||||
dragThumb = null;
|
||||
lastDragThumb = dragThumb;
|
||||
dragEndTimeMs = System.currentTimeMillis();
|
||||
dragThumb = null;
|
||||
invalidate();
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -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"
|
||||
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Add table
Reference in a new issue