Improve video thumbnail generation and handling on send side

For direct attach only

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2017-04-19 21:23:57 -07:00
parent ad4657df1f
commit 8e7c7a9c54
8 changed files with 68 additions and 8 deletions

View File

@ -355,7 +355,7 @@ public class ConversationItem extends LinearLayout
//noinspection ConstantConditions
mediaThumbnailStub.get().setImageResource(masterSecret,
((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(),
showControls);
showControls, false);
mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);

View File

@ -72,7 +72,7 @@ public class MediaAdapter extends CursorRecyclerViewAdapter<ViewHolder> {
Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment());
if (slide != null) {
imageView.setImageResource(masterSecret, slide, false);
imageView.setImageResource(masterSecret, slide, false, false);
}
imageView.setOnClickListener(new OnMediaClickListener(mediaRecord));

View File

@ -99,7 +99,7 @@ public class ThumbnailView extends FrameLayout {
this.backgroundColorHint = color;
}
public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull Slide slide, boolean showControls) {
public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull Slide slide, boolean showControls, boolean isPreview) {
if (showControls) {
getTransferControls().setSlide(slide);
getTransferControls().setDownloadClickListener(new DownloadClickDispatcher());
@ -107,7 +107,9 @@ public class ThumbnailView extends FrameLayout {
getTransferControls().setVisibility(View.GONE);
}
if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() && slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() &&
(slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE || isPreview))
{
this.playOverlay.setVisibility(View.VISIBLE);
} else {
this.playOverlay.setVisibility(View.GONE);

View File

@ -50,6 +50,8 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -527,8 +529,20 @@ public class AttachmentDatabase extends Database {
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
if (partData != null) {
Log.w(TAG, "Submitting thumbnail generation job...");
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId));
if (MediaUtil.hasVideoThumbnail(attachment.getDataUri())) {
Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getDataUri());
if (bitmap != null) {
ThumbnailData thumbnailData = new ThumbnailData(bitmap);
updateAttachmentThumbnail(masterSecret.getMasterSecret().get(), attachmentId, thumbnailData.toDataStream(), thumbnailData.getAspectRatio());
} else {
Log.w(TAG, "Retrieving video thumbnail failed, submitting thumbnail generation job...");
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId));
}
} else {
Log.w(TAG, "Submitting thumbnail generation job...");
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId));
}
}
return attachmentId;

View File

@ -247,7 +247,7 @@ public class AttachmentManager {
documentView.setDocument((DocumentSlide) slide, false);
removableMediaView.display(documentView, false);
} else {
thumbnail.setImageResource(masterSecret, slide, false);
thumbnail.setImageResource(masterSecret, slide, false, true);
removableMediaView.display(thumbnail, mediaType == MediaType.IMAGE);
}

View File

@ -2,13 +2,17 @@ package org.thoughtcrime.securesms.mms;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import com.bumptech.glide.load.data.StreamLocalUriFetcher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@ -28,6 +32,16 @@ public class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher {
@Override
protected InputStream loadResource(Uri uri, ContentResolver contentResolver) throws FileNotFoundException {
if (MediaUtil.hasVideoThumbnail(uri)) {
Bitmap thumbnail = MediaUtil.getVideoThumbnail(context, uri);
if (thumbnail != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
thumbnail.compress(Bitmap.CompressFormat.JPEG, 100, baos);
return new ByteArrayInputStream(baos.toByteArray());
}
}
try {
return PartAuthority.getAttachmentStream(context, masterSecret, uri);
} catch (IOException ioe) {

View File

@ -24,6 +24,7 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ResUtil;
import ws.com.google.android.mms.ContentType;
@ -31,7 +32,7 @@ import ws.com.google.android.mms.ContentType;
public class VideoSlide extends Slide {
public VideoSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize, false, null));
super(context, constructAttachmentFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize, MediaUtil.hasVideoThumbnail(uri), null));
}
public VideoSlide(Context context, Attachment attachment) {

View File

@ -1,8 +1,10 @@
package org.thoughtcrime.securesms.util;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
@ -162,6 +164,33 @@ public class MediaUtil {
return !TextUtils.isEmpty(contentType) && contentType.trim().startsWith("video/");
}
public static boolean hasVideoThumbnail(Uri uri) {
Log.w(TAG, "Checking: " + uri);
if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
return false;
}
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
return uri.getLastPathSegment().contains("video");
}
return false;
}
public static @Nullable Bitmap getVideoThumbnail(Context context, Uri uri) {
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
long videoId = Long.parseLong(uri.getLastPathSegment().split(":")[1]);
return MediaStore.Video.Thumbnails.getThumbnail(context.getContentResolver(),
videoId,
MediaStore.Images.Thumbnails.MINI_KIND,
null);
}
return null;
}
public static @Nullable String getDiscreteMimeType(@NonNull String mimeType) {
final String[] sections = mimeType.split("/", 2);
return sections.length > 1 ? sections[0] : null;