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 //noinspection ConstantConditions
mediaThumbnailStub.get().setImageResource(masterSecret, mediaThumbnailStub.get().setImageResource(masterSecret,
((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(), ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(),
showControls); showControls, false);
mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener()); mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener); mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener); mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);

View File

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

View File

@ -99,7 +99,7 @@ public class ThumbnailView extends FrameLayout {
this.backgroundColorHint = color; 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) { if (showControls) {
getTransferControls().setSlide(slide); getTransferControls().setSlide(slide);
getTransferControls().setDownloadClickListener(new DownloadClickDispatcher()); getTransferControls().setDownloadClickListener(new DownloadClickDispatcher());
@ -107,7 +107,9 @@ public class ThumbnailView extends FrameLayout {
getTransferControls().setVisibility(View.GONE); 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); this.playOverlay.setVisibility(View.VISIBLE);
} else { } else {
this.playOverlay.setVisibility(View.GONE); 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.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@ -527,8 +529,20 @@ public class AttachmentDatabase extends Database {
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId); AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
if (partData != null) { if (partData != null) {
Log.w(TAG, "Submitting thumbnail generation job..."); if (MediaUtil.hasVideoThumbnail(attachment.getDataUri())) {
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId)); 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; return attachmentId;

View File

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

View File

@ -2,13 +2,17 @@ package org.thoughtcrime.securesms.mms;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.bumptech.glide.load.data.StreamLocalUriFetcher; import com.bumptech.glide.load.data.StreamLocalUriFetcher;
import org.thoughtcrime.securesms.crypto.MasterSecret; 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.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -28,6 +32,16 @@ public class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher {
@Override @Override
protected InputStream loadResource(Uri uri, ContentResolver contentResolver) throws FileNotFoundException { 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 { try {
return PartAuthority.getAttachmentStream(context, masterSecret, uri); return PartAuthority.getAttachmentStream(context, masterSecret, uri);
} catch (IOException ioe) { } catch (IOException ioe) {

View File

@ -24,6 +24,7 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ResUtil; import org.thoughtcrime.securesms.util.ResUtil;
import ws.com.google.android.mms.ContentType; import ws.com.google.android.mms.ContentType;
@ -31,7 +32,7 @@ import ws.com.google.android.mms.ContentType;
public class VideoSlide extends Slide { public class VideoSlide extends Slide {
public VideoSlide(Context context, Uri uri, long dataSize) { 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) { public VideoSlide(Context context, Attachment attachment) {

View File

@ -1,8 +1,10 @@
package org.thoughtcrime.securesms.util; package org.thoughtcrime.securesms.util;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
@ -162,6 +164,33 @@ public class MediaUtil {
return !TextUtils.isEmpty(contentType) && contentType.trim().startsWith("video/"); 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) { public static @Nullable String getDiscreteMimeType(@NonNull String mimeType) {
final String[] sections = mimeType.split("/", 2); final String[] sections = mimeType.split("/", 2);
return sections.length > 1 ? sections[0] : null; return sections.length > 1 ? sections[0] : null;